Skip to content

Commit 0e8f13b

Browse files
feat: add SEP-1034 conformance test for elicitation defaults (#14)
Add comprehensive conformance test for SEP-1034 which extends MCP elicitation to support default values across all primitive types. Changes: - Add test_elicitation_sep1034_defaults tool to reference server - Create ElicitationDefaultsScenario with 5 granular checks: * String schema default value * Integer schema default value * Number schema default value * Enum schema default value (validates it's a valid enum member) * Boolean schema default value (regression test) - Register scenario in index.ts The test validates that servers properly include default values in elicitation requestedSchema per SEP-1034 specification. Test results: All 5 checks pass against TypeScript reference server
1 parent 69f02a4 commit 0e8f13b

File tree

3 files changed

+374
-0
lines changed

3 files changed

+374
-0
lines changed

examples/servers/typescript/everything-server.ts

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -396,6 +396,82 @@ function createMcpServer() {
396396
}
397397
);
398398

399+
// SEP-1034: Elicitation with default values for all primitive types
400+
mcpServer.registerTool(
401+
'test_elicitation_sep1034_defaults',
402+
{
403+
description: 'Tests elicitation with default values per SEP-1034',
404+
inputSchema: {}
405+
},
406+
async () => {
407+
try {
408+
// Request user input with default values for all primitive types
409+
const result = await mcpServer.server.request(
410+
{
411+
method: 'elicitation/create',
412+
params: {
413+
message: 'Please review and update the form fields with defaults',
414+
requestedSchema: {
415+
type: 'object',
416+
properties: {
417+
name: {
418+
type: 'string',
419+
description: 'User name',
420+
default: 'John Doe'
421+
},
422+
age: {
423+
type: 'integer',
424+
description: 'User age',
425+
default: 30
426+
},
427+
score: {
428+
type: 'number',
429+
description: 'User score',
430+
default: 95.5
431+
},
432+
status: {
433+
type: 'string',
434+
description: 'User status',
435+
enum: ['active', 'inactive', 'pending'],
436+
default: 'active'
437+
},
438+
verified: {
439+
type: 'boolean',
440+
description: 'Verification status',
441+
default: true
442+
}
443+
},
444+
required: []
445+
}
446+
}
447+
},
448+
z
449+
.object({ method: z.literal('elicitation/create') })
450+
.passthrough() as any
451+
);
452+
453+
const elicitResult = result as any;
454+
return {
455+
content: [
456+
{
457+
type: 'text',
458+
text: `Elicitation completed: action=${elicitResult.action}, content=${JSON.stringify(elicitResult.content || {})}`
459+
}
460+
]
461+
};
462+
} catch (error: any) {
463+
return {
464+
content: [
465+
{
466+
type: 'text',
467+
text: `Elicitation not supported or error: ${error.message}`
468+
}
469+
]
470+
};
471+
}
472+
}
473+
);
474+
399475
// Dynamic tool (registered later via timer)
400476

401477
// ===== RESOURCES =====

src/scenarios/index.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ import {
2626
ToolsCallEmbeddedResourceScenario
2727
} from './server/tools.js';
2828

29+
import { ElicitationDefaultsScenario } from './server/elicitation-defaults.js';
30+
2931
import {
3032
ResourcesListScenario,
3133
ResourcesReadTextScenario,
@@ -71,6 +73,9 @@ export const clientScenarios = new Map<string, ClientScenario>([
7173
['tools-call-sampling', new ToolsCallSamplingScenario()],
7274
['tools-call-elicitation', new ToolsCallElicitationScenario()],
7375

76+
// Elicitation scenarios (SEP-1034)
77+
['elicitation-sep1034-defaults', new ElicitationDefaultsScenario()],
78+
7479
// Resources scenarios
7580
['resources-list', new ResourcesListScenario()],
7681
['resources-read-text', new ResourcesReadTextScenario()],
Lines changed: 293 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,293 @@
1+
/**
2+
* SEP-1034: Elicitation default values test scenarios for MCP servers
3+
*/
4+
5+
import { ClientScenario, ConformanceCheck } from '../../types.js';
6+
import { connectToServer } from './client-helper.js';
7+
import { ElicitRequestSchema } from '@modelcontextprotocol/sdk/types.js';
8+
9+
export class ElicitationDefaultsScenario implements ClientScenario {
10+
name = 'elicitation-sep1034-defaults';
11+
description =
12+
'Test elicitation with default values for all primitive types (SEP-1034)';
13+
14+
async run(serverUrl: string): Promise<ConformanceCheck[]> {
15+
const checks: ConformanceCheck[] = [];
16+
17+
try {
18+
const connection = await connectToServer(serverUrl);
19+
20+
let capturedRequest: any = null;
21+
connection.client.setRequestHandler(
22+
ElicitRequestSchema,
23+
async (request) => {
24+
capturedRequest = request;
25+
return {
26+
action: 'accept',
27+
content: {
28+
name: 'Jane Smith',
29+
age: 25,
30+
score: 88.0,
31+
status: 'inactive',
32+
verified: false
33+
}
34+
};
35+
}
36+
);
37+
38+
await connection.client.callTool({
39+
name: 'test_elicitation_sep1034_defaults',
40+
arguments: {}
41+
});
42+
43+
// Validate that elicitation was requested
44+
if (!capturedRequest) {
45+
checks.push({
46+
id: 'elicitation-sep1034-general',
47+
name: 'ElicitationSEP1034General',
48+
description: 'Server requests elicitation with default values',
49+
status: 'FAILURE',
50+
timestamp: new Date().toISOString(),
51+
errorMessage: 'Server did not request elicitation from client',
52+
specReferences: [
53+
{
54+
id: 'SEP-1034',
55+
url: 'https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1034'
56+
}
57+
]
58+
});
59+
await connection.close();
60+
return checks;
61+
}
62+
63+
const schema = capturedRequest.params?.requestedSchema;
64+
const properties = schema?.properties;
65+
66+
// Validate string default
67+
const stringErrors: string[] = [];
68+
if (!properties?.name) {
69+
stringErrors.push('Missing string field "name"');
70+
} else {
71+
if (properties.name.type !== 'string') {
72+
stringErrors.push(
73+
`Expected type "string", got "${properties.name.type}"`
74+
);
75+
}
76+
if (!('default' in properties.name)) {
77+
stringErrors.push('Missing default field');
78+
} else if (properties.name.default !== 'John Doe') {
79+
stringErrors.push(
80+
`Expected default "John Doe", got "${properties.name.default}"`
81+
);
82+
}
83+
}
84+
85+
checks.push({
86+
id: 'elicitation-sep1034-string-default',
87+
name: 'ElicitationSEP1034StringDefault',
88+
description: 'String schema includes default value',
89+
status: stringErrors.length === 0 ? 'SUCCESS' : 'FAILURE',
90+
timestamp: new Date().toISOString(),
91+
errorMessage:
92+
stringErrors.length > 0 ? stringErrors.join('; ') : undefined,
93+
specReferences: [
94+
{
95+
id: 'SEP-1034',
96+
url: 'https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1034'
97+
}
98+
],
99+
details: {
100+
field: 'name',
101+
schema: properties?.name
102+
}
103+
});
104+
105+
// Validate integer default
106+
const integerErrors: string[] = [];
107+
if (!properties?.age) {
108+
integerErrors.push('Missing integer field "age"');
109+
} else {
110+
if (properties.age.type !== 'integer') {
111+
integerErrors.push(
112+
`Expected type "integer", got "${properties.age.type}"`
113+
);
114+
}
115+
if (!('default' in properties.age)) {
116+
integerErrors.push('Missing default field');
117+
} else if (properties.age.default !== 30) {
118+
integerErrors.push(
119+
`Expected default 30, got ${properties.age.default}`
120+
);
121+
}
122+
}
123+
124+
checks.push({
125+
id: 'elicitation-sep1034-integer-default',
126+
name: 'ElicitationSEP1034IntegerDefault',
127+
description: 'Integer schema includes default value',
128+
status: integerErrors.length === 0 ? 'SUCCESS' : 'FAILURE',
129+
timestamp: new Date().toISOString(),
130+
errorMessage:
131+
integerErrors.length > 0 ? integerErrors.join('; ') : undefined,
132+
specReferences: [
133+
{
134+
id: 'SEP-1034',
135+
url: 'https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1034'
136+
}
137+
],
138+
details: {
139+
field: 'age',
140+
schema: properties?.age
141+
}
142+
});
143+
144+
// Validate number default
145+
const numberErrors: string[] = [];
146+
if (!properties?.score) {
147+
numberErrors.push('Missing number field "score"');
148+
} else {
149+
if (properties.score.type !== 'number') {
150+
numberErrors.push(
151+
`Expected type "number", got "${properties.score.type}"`
152+
);
153+
}
154+
if (!('default' in properties.score)) {
155+
numberErrors.push('Missing default field');
156+
} else if (properties.score.default !== 95.5) {
157+
numberErrors.push(
158+
`Expected default 95.5, got ${properties.score.default}`
159+
);
160+
}
161+
}
162+
163+
checks.push({
164+
id: 'elicitation-sep1034-number-default',
165+
name: 'ElicitationSEP1034NumberDefault',
166+
description: 'Number schema includes default value',
167+
status: numberErrors.length === 0 ? 'SUCCESS' : 'FAILURE',
168+
timestamp: new Date().toISOString(),
169+
errorMessage:
170+
numberErrors.length > 0 ? numberErrors.join('; ') : undefined,
171+
specReferences: [
172+
{
173+
id: 'SEP-1034',
174+
url: 'https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1034'
175+
}
176+
],
177+
details: {
178+
field: 'score',
179+
schema: properties?.score
180+
}
181+
});
182+
183+
// Validate enum default
184+
const enumErrors: string[] = [];
185+
if (!properties?.status) {
186+
enumErrors.push('Missing enum field "status"');
187+
} else {
188+
if (properties.status.type !== 'string') {
189+
enumErrors.push(
190+
`Expected type "string", got "${properties.status.type}"`
191+
);
192+
}
193+
if (!properties.status.enum || !Array.isArray(properties.status.enum)) {
194+
enumErrors.push('Missing or invalid enum array');
195+
}
196+
if (!('default' in properties.status)) {
197+
enumErrors.push('Missing default field');
198+
} else {
199+
if (properties.status.default !== 'active') {
200+
enumErrors.push(
201+
`Expected default "active", got "${properties.status.default}"`
202+
);
203+
}
204+
if (
205+
properties.status.enum &&
206+
!properties.status.enum.includes(properties.status.default)
207+
) {
208+
enumErrors.push(
209+
`Default value "${properties.status.default}" is not a valid enum member`
210+
);
211+
}
212+
}
213+
}
214+
215+
checks.push({
216+
id: 'elicitation-sep1034-enum-default',
217+
name: 'ElicitationSEP1034EnumDefault',
218+
description: 'Enum schema includes valid default value',
219+
status: enumErrors.length === 0 ? 'SUCCESS' : 'FAILURE',
220+
timestamp: new Date().toISOString(),
221+
errorMessage: enumErrors.length > 0 ? enumErrors.join('; ') : undefined,
222+
specReferences: [
223+
{
224+
id: 'SEP-1034',
225+
url: 'https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1034'
226+
}
227+
],
228+
details: {
229+
field: 'status',
230+
schema: properties?.status
231+
}
232+
});
233+
234+
// Validate boolean default (regression test - already supported)
235+
const booleanErrors: string[] = [];
236+
if (!properties?.verified) {
237+
booleanErrors.push('Missing boolean field "verified"');
238+
} else {
239+
if (properties.verified.type !== 'boolean') {
240+
booleanErrors.push(
241+
`Expected type "boolean", got "${properties.verified.type}"`
242+
);
243+
}
244+
if (!('default' in properties.verified)) {
245+
booleanErrors.push('Missing default field');
246+
} else if (properties.verified.default !== true) {
247+
booleanErrors.push(
248+
`Expected default true, got ${properties.verified.default}`
249+
);
250+
}
251+
}
252+
253+
checks.push({
254+
id: 'elicitation-sep1034-boolean-default',
255+
name: 'ElicitationSEP1034BooleanDefault',
256+
description: 'Boolean schema includes default value',
257+
status: booleanErrors.length === 0 ? 'SUCCESS' : 'FAILURE',
258+
timestamp: new Date().toISOString(),
259+
errorMessage:
260+
booleanErrors.length > 0 ? booleanErrors.join('; ') : undefined,
261+
specReferences: [
262+
{
263+
id: 'SEP-1034',
264+
url: 'https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1034'
265+
}
266+
],
267+
details: {
268+
field: 'verified',
269+
schema: properties?.verified
270+
}
271+
});
272+
273+
await connection.close();
274+
} catch (error) {
275+
checks.push({
276+
id: 'elicitation-sep1034-general',
277+
name: 'ElicitationSEP1034General',
278+
description: 'Server requests elicitation with default values',
279+
status: 'FAILURE',
280+
timestamp: new Date().toISOString(),
281+
errorMessage: `Failed: ${error instanceof Error ? error.message : String(error)}`,
282+
specReferences: [
283+
{
284+
id: 'SEP-1034',
285+
url: 'https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1034'
286+
}
287+
]
288+
});
289+
}
290+
291+
return checks;
292+
}
293+
}

0 commit comments

Comments
 (0)