@@ -4,6 +4,10 @@ import { z } from "zod";
4
4
import { ClientRequest } from "@modelcontextprotocol/sdk/types.js" ;
5
5
import { DEFAULT_INSPECTOR_CONFIG } from "../../constants" ;
6
6
import { SSEClientTransportOptions } from "@modelcontextprotocol/sdk/client/sse.js" ;
7
+ import {
8
+ ElicitResult ,
9
+ ElicitRequest ,
10
+ } from "@modelcontextprotocol/sdk/types.js" ;
7
11
8
12
// Mock fetch
9
13
global . fetch = jest . fn ( ) . mockResolvedValue ( {
@@ -198,6 +202,252 @@ describe("useConnection", () => {
198
202
) . rejects . toThrow ( "MCP client not connected" ) ;
199
203
} ) ;
200
204
205
+ describe ( "Elicitation Support" , ( ) => {
206
+ beforeEach ( ( ) => {
207
+ jest . clearAllMocks ( ) ;
208
+ } ) ;
209
+
210
+ test ( "declares elicitation capability during client initialization" , async ( ) => {
211
+ const Client = jest . requireMock (
212
+ "@modelcontextprotocol/sdk/client/index.js" ,
213
+ ) . Client ;
214
+
215
+ const { result } = renderHook ( ( ) => useConnection ( defaultProps ) ) ;
216
+
217
+ await act ( async ( ) => {
218
+ await result . current . connect ( ) ;
219
+ } ) ;
220
+
221
+ expect ( Client ) . toHaveBeenCalledWith (
222
+ expect . objectContaining ( {
223
+ name : "mcp-inspector" ,
224
+ version : expect . any ( String ) ,
225
+ } ) ,
226
+ expect . objectContaining ( {
227
+ capabilities : expect . objectContaining ( {
228
+ elicitation : { } ,
229
+ } ) ,
230
+ } ) ,
231
+ ) ;
232
+ } ) ;
233
+
234
+ test ( "sets up elicitation request handler when onElicitationRequest is provided" , async ( ) => {
235
+ const mockOnElicitationRequest = jest . fn ( ) ;
236
+ const propsWithElicitation = {
237
+ ...defaultProps ,
238
+ onElicitationRequest : mockOnElicitationRequest ,
239
+ } ;
240
+
241
+ const { result } = renderHook ( ( ) => useConnection ( propsWithElicitation ) ) ;
242
+
243
+ await act ( async ( ) => {
244
+ await result . current . connect ( ) ;
245
+ } ) ;
246
+
247
+ const elicitRequestHandlerCall =
248
+ mockClient . setRequestHandler . mock . calls . find ( ( call ) => {
249
+ try {
250
+ const schema = call [ 0 ] ;
251
+ const testRequest = {
252
+ method : "elicitation/create" ,
253
+ params : {
254
+ message : "test message" ,
255
+ requestedSchema : {
256
+ type : "object" ,
257
+ properties : {
258
+ name : { type : "string" } ,
259
+ } ,
260
+ } ,
261
+ } ,
262
+ } ;
263
+ const parseResult =
264
+ schema . safeParse && schema . safeParse ( testRequest ) ;
265
+ return parseResult ?. success ;
266
+ } catch {
267
+ return false ;
268
+ }
269
+ } ) ;
270
+
271
+ expect ( elicitRequestHandlerCall ) . toBeDefined ( ) ;
272
+ expect ( mockClient . setRequestHandler ) . toHaveBeenCalledWith (
273
+ expect . any ( Object ) ,
274
+ expect . any ( Function ) ,
275
+ ) ;
276
+ } ) ;
277
+
278
+ test ( "does not set up elicitation request handler when onElicitationRequest is not provided" , async ( ) => {
279
+ const { result } = renderHook ( ( ) => useConnection ( defaultProps ) ) ;
280
+
281
+ await act ( async ( ) => {
282
+ await result . current . connect ( ) ;
283
+ } ) ;
284
+
285
+ const elicitRequestHandlerCall =
286
+ mockClient . setRequestHandler . mock . calls . find ( ( call ) => {
287
+ try {
288
+ const schema = call [ 0 ] ;
289
+ const testRequest = {
290
+ method : "elicitation/create" ,
291
+ params : {
292
+ message : "test message" ,
293
+ requestedSchema : {
294
+ type : "object" ,
295
+ properties : {
296
+ name : { type : "string" } ,
297
+ } ,
298
+ } ,
299
+ } ,
300
+ } ;
301
+ const parseResult =
302
+ schema . safeParse && schema . safeParse ( testRequest ) ;
303
+ return parseResult ?. success ;
304
+ } catch {
305
+ return false ;
306
+ }
307
+ } ) ;
308
+
309
+ expect ( elicitRequestHandlerCall ) . toBeUndefined ( ) ;
310
+ } ) ;
311
+
312
+ test ( "elicitation request handler calls onElicitationRequest callback" , async ( ) => {
313
+ const mockOnElicitationRequest = jest . fn ( ) ;
314
+ const propsWithElicitation = {
315
+ ...defaultProps ,
316
+ onElicitationRequest : mockOnElicitationRequest ,
317
+ } ;
318
+
319
+ const { result } = renderHook ( ( ) => useConnection ( propsWithElicitation ) ) ;
320
+
321
+ await act ( async ( ) => {
322
+ await result . current . connect ( ) ;
323
+ } ) ;
324
+
325
+ const elicitRequestHandlerCall =
326
+ mockClient . setRequestHandler . mock . calls . find ( ( call ) => {
327
+ try {
328
+ const schema = call [ 0 ] ;
329
+ const testRequest = {
330
+ method : "elicitation/create" ,
331
+ params : {
332
+ message : "test message" ,
333
+ requestedSchema : {
334
+ type : "object" ,
335
+ properties : {
336
+ name : { type : "string" } ,
337
+ } ,
338
+ } ,
339
+ } ,
340
+ } ;
341
+ const parseResult =
342
+ schema . safeParse && schema . safeParse ( testRequest ) ;
343
+ return parseResult ?. success ;
344
+ } catch {
345
+ return false ;
346
+ }
347
+ } ) ;
348
+
349
+ expect ( elicitRequestHandlerCall ) . toBeDefined ( ) ;
350
+ const [ , handler ] = elicitRequestHandlerCall ;
351
+
352
+ const mockElicitationRequest : ElicitRequest = {
353
+ method : "elicitation/create" ,
354
+ params : {
355
+ message : "Please provide your name" ,
356
+ requestedSchema : {
357
+ type : "object" ,
358
+ properties : {
359
+ name : { type : "string" } ,
360
+ } ,
361
+ required : [ "name" ] ,
362
+ } ,
363
+ } ,
364
+ } ;
365
+
366
+ mockOnElicitationRequest . mockImplementation ( ( _request , resolve ) => {
367
+ resolve ( { action : "accept" , content : { name : "test" } } ) ;
368
+ } ) ;
369
+
370
+ await act ( async ( ) => {
371
+ await handler ( mockElicitationRequest ) ;
372
+ } ) ;
373
+
374
+ expect ( mockOnElicitationRequest ) . toHaveBeenCalledWith (
375
+ mockElicitationRequest ,
376
+ expect . any ( Function ) ,
377
+ ) ;
378
+ } ) ;
379
+
380
+ test ( "elicitation request handler returns a promise that resolves with the callback result" , async ( ) => {
381
+ const mockOnElicitationRequest = jest . fn ( ) ;
382
+ const propsWithElicitation = {
383
+ ...defaultProps ,
384
+ onElicitationRequest : mockOnElicitationRequest ,
385
+ } ;
386
+
387
+ const { result } = renderHook ( ( ) => useConnection ( propsWithElicitation ) ) ;
388
+
389
+ await act ( async ( ) => {
390
+ await result . current . connect ( ) ;
391
+ } ) ;
392
+
393
+ const elicitRequestHandlerCall =
394
+ mockClient . setRequestHandler . mock . calls . find ( ( call ) => {
395
+ try {
396
+ const schema = call [ 0 ] ;
397
+ const testRequest = {
398
+ method : "elicitation/create" ,
399
+ params : {
400
+ message : "test message" ,
401
+ requestedSchema : {
402
+ type : "object" ,
403
+ properties : {
404
+ name : { type : "string" } ,
405
+ } ,
406
+ } ,
407
+ } ,
408
+ } ;
409
+ const parseResult =
410
+ schema . safeParse && schema . safeParse ( testRequest ) ;
411
+ return parseResult ?. success ;
412
+ } catch {
413
+ return false ;
414
+ }
415
+ } ) ;
416
+
417
+ const [ , handler ] = elicitRequestHandlerCall ;
418
+
419
+ const mockElicitationRequest : ElicitRequest = {
420
+ method : "elicitation/create" ,
421
+ params : {
422
+ message : "Please provide your name" ,
423
+ requestedSchema : {
424
+ type : "object" ,
425
+ properties : {
426
+ name : { type : "string" } ,
427
+ } ,
428
+ required : [ "name" ] ,
429
+ } ,
430
+ } ,
431
+ } ;
432
+
433
+ const mockResponse : ElicitResult = {
434
+ action : "accept" ,
435
+ content : { name : "John Doe" } ,
436
+ } ;
437
+
438
+ mockOnElicitationRequest . mockImplementation ( ( _request , resolve ) => {
439
+ resolve ( mockResponse ) ;
440
+ } ) ;
441
+
442
+ let handlerResult ;
443
+ await act ( async ( ) => {
444
+ handlerResult = await handler ( mockElicitationRequest ) ;
445
+ } ) ;
446
+
447
+ expect ( handlerResult ) . toEqual ( mockResponse ) ;
448
+ } ) ;
449
+ } ) ;
450
+
201
451
describe ( "URL Port Handling" , ( ) => {
202
452
const SSEClientTransport = jest . requireMock (
203
453
"@modelcontextprotocol/sdk/client/sse.js" ,
0 commit comments