@@ -6,6 +6,7 @@ import { ClientScenario, ConformanceCheck } from '../../types.js';
66import { connectToServerWithUrlElicitation } from './client-helper.js' ;
77import {
88 ElicitRequestSchema ,
9+ ElicitationCompleteNotificationSchema ,
910 ErrorCode ,
1011 McpError
1112} from '@modelcontextprotocol/sdk/types.js' ;
@@ -16,7 +17,7 @@ export class ElicitationUrlModeScenario implements ClientScenario {
1617
1718**Server Implementation Requirements:**
1819
19- Implement two tools:
20+ Implement three tools:
2021
21221. \`test_elicitation_sep1036_url\` (no arguments) - Requests URL mode elicitation from client with:
2223 - \`mode\`: "url"
@@ -30,6 +31,11 @@ Implement two tools:
3031 - Error code: -32042
3132 - Error data contains \`elicitations\` array with URL mode elicitation objects
3233
34+ 3. \`test_elicitation_sep1036_complete\` (no arguments) - Tests completion notification flow:
35+ - Requests URL mode elicitation
36+ - When client accepts, sends \`notifications/elicitation/complete\` notification
37+ - The notification must include the matching \`elicitationId\`
38+
3339**Example elicitation request:**
3440\`\`\`json
3541{
@@ -399,6 +405,126 @@ Implement two tools:
399405 } ) ;
400406 }
401407
408+ // Part 3: Test completion notification flow
409+ let completionNotificationReceived = false ;
410+ let receivedElicitationId : string | null = null ;
411+ let capturedElicitationIdFromRequest : string | null = null ;
412+
413+ // Set up notification handler for completion
414+ connection . client . setNotificationHandler (
415+ ElicitationCompleteNotificationSchema ,
416+ ( notification ) => {
417+ completionNotificationReceived = true ;
418+ receivedElicitationId = notification . params . elicitationId ;
419+ }
420+ ) ;
421+
422+ // Update the request handler to capture the elicitationId
423+ connection . client . setRequestHandler (
424+ ElicitRequestSchema ,
425+ async ( request ) => {
426+ capturedElicitationIdFromRequest = request . params . elicitationId ;
427+ return { action : 'accept' } ;
428+ }
429+ ) ;
430+
431+ try {
432+ await connection . client . callTool ( {
433+ name : 'test_elicitation_sep1036_complete' ,
434+ arguments : { }
435+ } ) ;
436+
437+ // Small delay to allow notification to be received
438+ await new Promise ( ( resolve ) => setTimeout ( resolve , 100 ) ) ;
439+
440+ // Check 10: Verify completion notification was received
441+ const notificationErrors : string [ ] = [ ] ;
442+ if ( ! completionNotificationReceived ) {
443+ notificationErrors . push (
444+ 'Server did not send notifications/elicitation/complete notification'
445+ ) ;
446+ }
447+
448+ checks . push ( {
449+ id : 'sep1036-url-completion-notification' ,
450+ name : 'URLCompletionNotification' ,
451+ description :
452+ 'Server sends notifications/elicitation/complete after out-of-band completion' ,
453+ status : notificationErrors . length === 0 ? 'SUCCESS' : 'FAILURE' ,
454+ timestamp : new Date ( ) . toISOString ( ) ,
455+ errorMessage :
456+ notificationErrors . length > 0
457+ ? notificationErrors . join ( '; ' )
458+ : undefined ,
459+ specReferences : [
460+ {
461+ id : 'SEP-1036' ,
462+ url : 'https://github.com/modelcontextprotocol/modelcontextprotocol/pull/887'
463+ }
464+ ] ,
465+ details : {
466+ notificationReceived : completionNotificationReceived
467+ }
468+ } ) ;
469+
470+ // Check 11: Verify elicitationId matches
471+ const idMatchErrors : string [ ] = [ ] ;
472+ if ( completionNotificationReceived ) {
473+ if ( ! receivedElicitationId ) {
474+ idMatchErrors . push ( 'Completion notification missing elicitationId' ) ;
475+ } else if (
476+ capturedElicitationIdFromRequest &&
477+ receivedElicitationId !== capturedElicitationIdFromRequest
478+ ) {
479+ idMatchErrors . push (
480+ `elicitationId mismatch: request had "${ capturedElicitationIdFromRequest } ", notification had "${ receivedElicitationId } "`
481+ ) ;
482+ }
483+ }
484+
485+ checks . push ( {
486+ id : 'sep1036-url-completion-id-match' ,
487+ name : 'URLCompletionIdMatch' ,
488+ description :
489+ 'Completion notification elicitationId matches the original request' ,
490+ status :
491+ completionNotificationReceived && idMatchErrors . length === 0
492+ ? 'SUCCESS'
493+ : completionNotificationReceived
494+ ? 'FAILURE'
495+ : 'SKIPPED' ,
496+ timestamp : new Date ( ) . toISOString ( ) ,
497+ errorMessage :
498+ idMatchErrors . length > 0 ? idMatchErrors . join ( '; ' ) : undefined ,
499+ specReferences : [
500+ {
501+ id : 'SEP-1036' ,
502+ url : 'https://github.com/modelcontextprotocol/modelcontextprotocol/pull/887'
503+ }
504+ ] ,
505+ details : {
506+ requestElicitationId : capturedElicitationIdFromRequest ,
507+ notificationElicitationId : receivedElicitationId
508+ }
509+ } ) ;
510+ } catch ( error ) {
511+ checks . push ( {
512+ id : 'sep1036-url-completion-notification' ,
513+ name : 'URLCompletionNotification' ,
514+ description :
515+ 'Server sends notifications/elicitation/complete after out-of-band completion' ,
516+ status : 'FAILURE' ,
517+ timestamp : new Date ( ) . toISOString ( ) ,
518+ errorMessage : `Tool call failed: ${ error instanceof Error ? error . message : String ( error ) } ` ,
519+ specReferences : [
520+ {
521+ id : 'SEP-1036' ,
522+ url : 'https://github.com/modelcontextprotocol/modelcontextprotocol/pull/887'
523+ }
524+ ]
525+ } ) ;
526+ }
527+
402528 await connection . close ( ) ;
403529 } catch ( error ) {
404530 checks . push ( {
0 commit comments