@@ -9,8 +9,15 @@ import {
99 ResultSchema ,
1010 LATEST_PROTOCOL_VERSION ,
1111 SUPPORTED_PROTOCOL_VERSIONS ,
12+ InitializeRequestSchema ,
13+ ListResourcesRequestSchema ,
14+ ListToolsRequestSchema ,
15+ CreateMessageRequestSchema ,
16+ ListRootsRequestSchema ,
1217} from "../types.js" ;
1318import { Transport } from "../shared/transport.js" ;
19+ import { Server } from "../server/index.js" ;
20+ import { InMemoryTransport } from "../inMemory.js" ;
1421
1522test ( "should initialize with matching protocol version" , async ( ) => {
1623 const clientTransport : Transport = {
@@ -35,10 +42,17 @@ test("should initialize with matching protocol version", async () => {
3542 } ) ,
3643 } ;
3744
38- const client = new Client ( {
39- name : "test client" ,
40- version : "1.0" ,
41- } ) ;
45+ const client = new Client (
46+ {
47+ name : "test client" ,
48+ version : "1.0" ,
49+ } ,
50+ {
51+ capabilities : {
52+ sampling : { } ,
53+ } ,
54+ } ,
55+ ) ;
4256
4357 await client . connect ( clientTransport ) ;
4458
@@ -77,10 +91,17 @@ test("should initialize with supported older protocol version", async () => {
7791 } ) ,
7892 } ;
7993
80- const client = new Client ( {
81- name : "test client" ,
82- version : "1.0" ,
83- } ) ;
94+ const client = new Client (
95+ {
96+ name : "test client" ,
97+ version : "1.0" ,
98+ } ,
99+ {
100+ capabilities : {
101+ sampling : { } ,
102+ } ,
103+ } ,
104+ ) ;
84105
85106 await client . connect ( clientTransport ) ;
86107
@@ -114,10 +135,17 @@ test("should reject unsupported protocol version", async () => {
114135 } ) ,
115136 } ;
116137
117- const client = new Client ( {
118- name : "test client" ,
119- version : "1.0" ,
120- } ) ;
138+ const client = new Client (
139+ {
140+ name : "test client" ,
141+ version : "1.0" ,
142+ } ,
143+ {
144+ capabilities : {
145+ sampling : { } ,
146+ } ,
147+ } ,
148+ ) ;
121149
122150 await expect ( client . connect ( clientTransport ) ) . rejects . toThrow (
123151 "Server's protocol version is not supported: invalid-version" ,
@@ -126,6 +154,210 @@ test("should reject unsupported protocol version", async () => {
126154 expect ( clientTransport . close ) . toHaveBeenCalled ( ) ;
127155} ) ;
128156
157+ test ( "should respect server capabilities" , async ( ) => {
158+ const server = new Server (
159+ {
160+ name : "test server" ,
161+ version : "1.0" ,
162+ } ,
163+ {
164+ capabilities : {
165+ resources : { } ,
166+ tools : { } ,
167+ } ,
168+ } ,
169+ ) ;
170+
171+ server . setRequestHandler ( InitializeRequestSchema , ( _request ) => ( {
172+ protocolVersion : LATEST_PROTOCOL_VERSION ,
173+ capabilities : {
174+ resources : { } ,
175+ tools : { } ,
176+ } ,
177+ serverInfo : {
178+ name : "test" ,
179+ version : "1.0" ,
180+ } ,
181+ } ) ) ;
182+
183+ server . setRequestHandler ( ListResourcesRequestSchema , ( ) => ( {
184+ resources : [ ] ,
185+ } ) ) ;
186+
187+ server . setRequestHandler ( ListToolsRequestSchema , ( ) => ( {
188+ tools : [ ] ,
189+ } ) ) ;
190+
191+ const [ clientTransport , serverTransport ] =
192+ InMemoryTransport . createLinkedPair ( ) ;
193+
194+ const client = new Client (
195+ {
196+ name : "test client" ,
197+ version : "1.0" ,
198+ } ,
199+ {
200+ capabilities : {
201+ sampling : { } ,
202+ } ,
203+ enforceStrictCapabilities : true ,
204+ } ,
205+ ) ;
206+
207+ await Promise . all ( [
208+ client . connect ( clientTransport ) ,
209+ server . connect ( serverTransport ) ,
210+ ] ) ;
211+
212+ // Server supports resources and tools, but not prompts
213+ expect ( client . getServerCapabilities ( ) ) . toEqual ( {
214+ resources : { } ,
215+ tools : { } ,
216+ } ) ;
217+
218+ // These should work
219+ await expect ( client . listResources ( ) ) . resolves . not . toThrow ( ) ;
220+ await expect ( client . listTools ( ) ) . resolves . not . toThrow ( ) ;
221+
222+ // This should throw because prompts are not supported
223+ await expect ( client . listPrompts ( ) ) . rejects . toThrow (
224+ "Server does not support prompts" ,
225+ ) ;
226+ } ) ;
227+
228+ test ( "should respect client notification capabilities" , async ( ) => {
229+ const server = new Server (
230+ {
231+ name : "test server" ,
232+ version : "1.0" ,
233+ } ,
234+ {
235+ capabilities : { } ,
236+ } ,
237+ ) ;
238+
239+ const client = new Client (
240+ {
241+ name : "test client" ,
242+ version : "1.0" ,
243+ } ,
244+ {
245+ capabilities : {
246+ roots : {
247+ listChanged : true ,
248+ } ,
249+ } ,
250+ } ,
251+ ) ;
252+
253+ const [ clientTransport , serverTransport ] =
254+ InMemoryTransport . createLinkedPair ( ) ;
255+
256+ await Promise . all ( [
257+ client . connect ( clientTransport ) ,
258+ server . connect ( serverTransport ) ,
259+ ] ) ;
260+
261+ // This should work because the client has the roots.listChanged capability
262+ await expect ( client . sendRootsListChanged ( ) ) . resolves . not . toThrow ( ) ;
263+
264+ // Create a new client without the roots.listChanged capability
265+ const clientWithoutCapability = new Client (
266+ {
267+ name : "test client without capability" ,
268+ version : "1.0" ,
269+ } ,
270+ {
271+ capabilities : { } ,
272+ enforceStrictCapabilities : true ,
273+ } ,
274+ ) ;
275+
276+ await clientWithoutCapability . connect ( clientTransport ) ;
277+
278+ // This should throw because the client doesn't have the roots.listChanged capability
279+ await expect ( clientWithoutCapability . sendRootsListChanged ( ) ) . rejects . toThrow (
280+ / ^ C l i e n t d o e s n o t s u p p o r t / ,
281+ ) ;
282+ } ) ;
283+
284+ test ( "should respect server notification capabilities" , async ( ) => {
285+ const server = new Server (
286+ {
287+ name : "test server" ,
288+ version : "1.0" ,
289+ } ,
290+ {
291+ capabilities : {
292+ logging : { } ,
293+ resources : {
294+ listChanged : true ,
295+ } ,
296+ } ,
297+ } ,
298+ ) ;
299+
300+ const client = new Client (
301+ {
302+ name : "test client" ,
303+ version : "1.0" ,
304+ } ,
305+ {
306+ capabilities : { } ,
307+ } ,
308+ ) ;
309+
310+ const [ clientTransport , serverTransport ] =
311+ InMemoryTransport . createLinkedPair ( ) ;
312+
313+ await Promise . all ( [
314+ client . connect ( clientTransport ) ,
315+ server . connect ( serverTransport ) ,
316+ ] ) ;
317+
318+ // These should work because the server has the corresponding capabilities
319+ await expect (
320+ server . sendLoggingMessage ( { level : "info" , data : "Test" } ) ,
321+ ) . resolves . not . toThrow ( ) ;
322+ await expect ( server . sendResourceListChanged ( ) ) . resolves . not . toThrow ( ) ;
323+
324+ // This should throw because the server doesn't have the tools capability
325+ await expect ( server . sendToolListChanged ( ) ) . rejects . toThrow (
326+ "Server does not support notifying of tool list changes" ,
327+ ) ;
328+ } ) ;
329+
330+ test ( "should only allow setRequestHandler for declared capabilities" , ( ) => {
331+ const client = new Client (
332+ {
333+ name : "test client" ,
334+ version : "1.0" ,
335+ } ,
336+ {
337+ capabilities : {
338+ sampling : { } ,
339+ } ,
340+ } ,
341+ ) ;
342+
343+ // This should work because sampling is a declared capability
344+ expect ( ( ) => {
345+ client . setRequestHandler ( CreateMessageRequestSchema , ( ) => ( {
346+ model : "test-model" ,
347+ role : "assistant" ,
348+ content : {
349+ type : "text" ,
350+ text : "Test response" ,
351+ } ,
352+ } ) ) ;
353+ } ) . not . toThrow ( ) ;
354+
355+ // This should throw because roots listing is not a declared capability
356+ expect ( ( ) => {
357+ client . setRequestHandler ( ListRootsRequestSchema , ( ) => ( { } ) ) ;
358+ } ) . toThrow ( "Client does not support roots capability" ) ;
359+ } ) ;
360+
129361/*
130362 Test that custom request/notification/result schemas can be used with the Client class.
131363 */
@@ -171,10 +403,17 @@ test("should typecheck", () => {
171403 WeatherRequest ,
172404 WeatherNotification ,
173405 WeatherResult
174- > ( {
175- name : "WeatherClient" ,
176- version : "1.0.0" ,
177- } ) ;
406+ > (
407+ {
408+ name : "WeatherClient" ,
409+ version : "1.0.0" ,
410+ } ,
411+ {
412+ capabilities : {
413+ sampling : { } ,
414+ } ,
415+ } ,
416+ ) ;
178417
179418 // Typecheck that only valid weather requests/notifications/results are allowed
180419 false &&
0 commit comments