Skip to content

Commit 257cf79

Browse files
Fix SEP-1036 URL elicitation conformance tests
- Use elicitInput() instead of raw request() for proper capability checking - Add completion notification test (notifications/elicitation/complete) - Add test_elicitation_sep1036_complete tool to everything-server
1 parent 552c5f2 commit 257cf79

File tree

2 files changed

+175
-18
lines changed

2 files changed

+175
-18
lines changed

examples/servers/typescript/everything-server.ts

Lines changed: 48 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,11 @@ import {
1313
ResourceTemplate
1414
} from '@modelcontextprotocol/sdk/server/mcp.js';
1515
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
16-
import { ElicitResultSchema, McpError, ErrorCode } from '@modelcontextprotocol/sdk/types.js';
16+
import {
17+
ElicitResultSchema,
18+
McpError,
19+
ErrorCode
20+
} from '@modelcontextprotocol/sdk/types.js';
1721
import { z } from 'zod';
1822
import express from 'express';
1923
import cors from 'cors';
@@ -579,27 +583,18 @@ function createMcpServer() {
579583
async () => {
580584
try {
581585
const elicitationId = `sep1036-test-${randomUUID()}`;
582-
const result = await mcpServer.server.request(
583-
{
584-
method: 'elicitation/create',
585-
params: {
586-
mode: 'url',
587-
message: 'Please complete authorization to continue',
588-
url: 'https://mcp.example.com/authorize',
589-
elicitationId
590-
}
591-
},
592-
z
593-
.object({ method: z.literal('elicitation/create') })
594-
.passthrough() as any
595-
);
586+
const result = await mcpServer.server.elicitInput({
587+
mode: 'url',
588+
message: 'Please complete authorization to continue',
589+
url: 'https://mcp.example.com/authorize',
590+
elicitationId
591+
});
596592

597-
const elicitResult = result as any;
598593
return {
599594
content: [
600595
{
601596
type: 'text',
602-
text: `URL elicitation completed: action=${elicitResult.action}`
597+
text: `URL elicitation completed: action=${result.action}`
603598
}
604599
]
605600
};
@@ -643,6 +638,42 @@ function createMcpServer() {
643638
}
644639
);
645640

641+
// SEP-1036: URL mode elicitation with completion notification
642+
mcpServer.registerTool(
643+
'test_elicitation_sep1036_complete',
644+
{
645+
description:
646+
'Tests URL mode elicitation with completion notification per SEP-1036',
647+
inputSchema: {}
648+
},
649+
async () => {
650+
const elicitationId = `sep1036-complete-${randomUUID()}`;
651+
const result = await mcpServer.server.elicitInput({
652+
mode: 'url',
653+
message: 'Please complete the authorization flow',
654+
url: 'https://mcp.example.com/authorize-with-completion',
655+
elicitationId
656+
});
657+
658+
// Send completion notification after client accepts
659+
if (result.action === 'accept') {
660+
// Create a notifier for this elicitationId and send the notification
661+
const notifier =
662+
mcpServer.server.createElicitationCompletionNotifier(elicitationId);
663+
await notifier();
664+
}
665+
666+
return {
667+
content: [
668+
{
669+
type: 'text',
670+
text: `URL elicitation with completion: action=${result.action}, notification sent`
671+
}
672+
]
673+
};
674+
}
675+
);
676+
646677
// Dynamic tool (registered later via timer)
647678

648679
// ===== RESOURCES =====

src/scenarios/server/elicitation-url.ts

Lines changed: 127 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { ClientScenario, ConformanceCheck } from '../../types.js';
66
import { connectToServerWithUrlElicitation } from './client-helper.js';
77
import {
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
2122
1. \`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

Comments
 (0)