@@ -7,6 +7,7 @@ import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js'
77import {
88 CreateMessageRequestSchema ,
99 type CreateMessageResult ,
10+ ElicitRequestSchema ,
1011 ResourceUpdatedNotificationSchema ,
1112} from '@modelcontextprotocol/sdk/types.js'
1213import { test , expect } from 'vitest'
@@ -352,3 +353,135 @@ test('Resource subscriptions: entry and tag', async () => {
352353 await new Promise ( ( r ) => setTimeout ( r , 200 ) )
353354 expect ( notifications ) . toHaveLength ( 0 )
354355} )
356+
357+ test ( 'Elicitation: delete_entry confirmation' , async ( ) => {
358+ await using setup = await setupClient ( { capabilities : { elicitation : { } } } )
359+ const { client } = setup
360+
361+ // Set up a handler for elicitation requests
362+ let elicitationRequest : any
363+ client . setRequestHandler ( ElicitRequestSchema , ( req ) => {
364+ elicitationRequest = req
365+ // Simulate user accepting the confirmation
366+ return {
367+ action : 'accept' ,
368+ content : { confirmed : true } ,
369+ }
370+ } )
371+
372+ // Create an entry to delete
373+ const entryResult = await client . callTool ( {
374+ name : 'create_entry' ,
375+ arguments : {
376+ title : 'Elicit Test Entry' ,
377+ content : 'Testing elicitation on delete.' ,
378+ } ,
379+ } )
380+ const entry = ( entryResult . structuredContent as any ) . entry
381+ invariant ( entry , '🚨 No entry resource found' )
382+ invariant ( entry . id , '🚨 No entry ID found' )
383+
384+ // Delete the entry, which should trigger elicitation
385+ const deleteResult = await client . callTool ( {
386+ name : 'delete_entry' ,
387+ arguments : { id : entry . id } ,
388+ } )
389+ const structuredContent = deleteResult . structuredContent as any
390+ invariant (
391+ structuredContent ,
392+ '🚨 No structuredContent returned from delete_entry' ,
393+ )
394+ invariant (
395+ 'success' in structuredContent ,
396+ '🚨 structuredContent missing success field' ,
397+ )
398+ expect ( structuredContent . success ) . toBe ( true )
399+
400+ invariant ( elicitationRequest , '🚨 No elicitation request was sent' )
401+ const params = elicitationRequest . params
402+ invariant ( params , '🚨 elicitationRequest missing params' )
403+ invariant (
404+ typeof params . message === 'string' ,
405+ '🚨 elicitationRequest.params.message must be a string' ,
406+ )
407+ invariant (
408+ params . message . match ( / A r e y o u s u r e y o u w a n t t o d e l e t e e n t r y / i) ,
409+ '🚨 elicitationRequest.params.message does not match expected confirmation prompt' ,
410+ )
411+ expect ( params . message ) . toMatch ( / A r e y o u s u r e y o u w a n t t o d e l e t e e n t r y / i)
412+
413+ invariant (
414+ params . requestedSchema ,
415+ '🚨 elicitationRequest.params.requestedSchema is missing' ,
416+ )
417+ invariant (
418+ params . requestedSchema . type === 'object' ,
419+ '🚨 elicitationRequest.params.requestedSchema.type must be "object"' ,
420+ )
421+ invariant (
422+ params . requestedSchema . properties &&
423+ typeof params . requestedSchema . properties === 'object' ,
424+ '🚨 elicitationRequest.params.requestedSchema.properties must be an object' ,
425+ )
426+ invariant (
427+ 'confirmed' in params . requestedSchema . properties ,
428+ '🚨 elicitationRequest.params.requestedSchema.properties must include confirmed' ,
429+ )
430+ invariant (
431+ params . requestedSchema . properties . confirmed . type === 'boolean' ,
432+ '🚨 elicitationRequest.params.requestedSchema.properties.confirmed.type must be boolean' ,
433+ )
434+ expect ( params . requestedSchema ) . toEqual (
435+ expect . objectContaining ( {
436+ type : 'object' ,
437+ properties : expect . objectContaining ( {
438+ confirmed : expect . objectContaining ( { type : 'boolean' } ) ,
439+ } ) ,
440+ } ) ,
441+ )
442+ } )
443+
444+ test ( 'Elicitation: delete_tag decline' , async ( ) => {
445+ await using setup = await setupClient ( { capabilities : { elicitation : { } } } )
446+ const { client } = setup
447+
448+ // Set up a handler for elicitation requests
449+ client . setRequestHandler ( ElicitRequestSchema , ( ) => {
450+ return {
451+ action : 'decline' ,
452+ }
453+ } )
454+
455+ // Create a tag to delete
456+ const tagResult = await client . callTool ( {
457+ name : 'create_tag' ,
458+ arguments : {
459+ name : 'Elicit Test Tag' ,
460+ description : 'Testing elicitation decline.' ,
461+ } ,
462+ } )
463+ const tag = ( tagResult . structuredContent as any ) . tag
464+ invariant ( tag , '🚨 No tag resource found' )
465+ invariant ( tag . id , '🚨 No tag ID found' )
466+
467+ // Delete the tag, which should trigger elicitation and be declined
468+ const deleteResult = await client . callTool ( {
469+ name : 'delete_tag' ,
470+ arguments : { id : tag . id } ,
471+ } )
472+ const structuredContent = deleteResult . structuredContent as any
473+ invariant (
474+ structuredContent ,
475+ '🚨 No structuredContent returned from delete_tag' ,
476+ )
477+ invariant (
478+ 'success' in structuredContent ,
479+ '🚨 structuredContent missing success field' ,
480+ )
481+ expect ( structuredContent . success ) . toBe ( false )
482+ invariant (
483+ 'message' in structuredContent ,
484+ '🚨 structuredContent missing message field' ,
485+ )
486+ expect ( structuredContent . message ) . toMatch ( / c a n c e l l e d / i)
487+ } )
0 commit comments