@@ -9,8 +9,15 @@ import {
9
9
ResultSchema ,
10
10
LATEST_PROTOCOL_VERSION ,
11
11
SUPPORTED_PROTOCOL_VERSIONS ,
12
+ InitializeRequestSchema ,
13
+ ListResourcesRequestSchema ,
14
+ ListToolsRequestSchema ,
15
+ CreateMessageRequestSchema ,
16
+ ListRootsRequestSchema ,
12
17
} from "../types.js" ;
13
18
import { Transport } from "../shared/transport.js" ;
19
+ import { Server } from "../server/index.js" ;
20
+ import { InMemoryTransport } from "../inMemory.js" ;
14
21
15
22
test ( "should initialize with matching protocol version" , async ( ) => {
16
23
const clientTransport : Transport = {
@@ -35,10 +42,17 @@ test("should initialize with matching protocol version", async () => {
35
42
} ) ,
36
43
} ;
37
44
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
+ ) ;
42
56
43
57
await client . connect ( clientTransport ) ;
44
58
@@ -77,10 +91,17 @@ test("should initialize with supported older protocol version", async () => {
77
91
} ) ,
78
92
} ;
79
93
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
+ ) ;
84
105
85
106
await client . connect ( clientTransport ) ;
86
107
@@ -114,10 +135,17 @@ test("should reject unsupported protocol version", async () => {
114
135
} ) ,
115
136
} ;
116
137
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
+ ) ;
121
149
122
150
await expect ( client . connect ( clientTransport ) ) . rejects . toThrow (
123
151
"Server's protocol version is not supported: invalid-version" ,
@@ -126,6 +154,210 @@ test("should reject unsupported protocol version", async () => {
126
154
expect ( clientTransport . close ) . toHaveBeenCalled ( ) ;
127
155
} ) ;
128
156
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
+
129
361
/*
130
362
Test that custom request/notification/result schemas can be used with the Client class.
131
363
*/
@@ -171,10 +403,17 @@ test("should typecheck", () => {
171
403
WeatherRequest ,
172
404
WeatherNotification ,
173
405
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
+ ) ;
178
417
179
418
// Typecheck that only valid weather requests/notifications/results are allowed
180
419
false &&
0 commit comments