Skip to content

Commit 030e97c

Browse files
authored
Merge branch 'main' into pcarleton/auth-back-compat
2 parents 5c3a5c0 + 38443d1 commit 030e97c

File tree

10 files changed

+633
-1154
lines changed

10 files changed

+633
-1154
lines changed

SERVER_REQUIREMENTS.md

Lines changed: 0 additions & 1120 deletions
This file was deleted.

src/index.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,10 @@ program
9898
validated.scenario
9999
);
100100

101-
const { failed } = printServerResults(result.checks);
101+
const { failed } = printServerResults(
102+
result.checks,
103+
result.scenarioDescription
104+
);
102105
process.exit(failed > 0 ? 1 : 0);
103106
} else {
104107
// Run all active scenarios

src/runner/server.ts

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,26 @@ import { ConformanceCheck } from '../types';
44
import { getClientScenario } from '../scenarios';
55
import { ensureResultsDir, createResultDir } from './utils';
66

7+
/**
8+
* Format markdown-style text for terminal output using ANSI codes
9+
*/
10+
function formatMarkdown(text: string): string {
11+
return (
12+
text
13+
// Bold text: **text** -> bold
14+
.replace(/\*\*([^*]+)\*\*/g, '\x1b[1m$1\x1b[0m')
15+
// Inline code: `code` -> dim/gray
16+
.replace(/`([^`]+)`/g, '\x1b[2m$1\x1b[0m')
17+
);
18+
}
19+
720
export async function runServerConformanceTest(
821
serverUrl: string,
922
scenarioName: string
1023
): Promise<{
1124
checks: ConformanceCheck[];
1225
resultDir: string;
26+
scenarioDescription: string;
1327
}> {
1428
await ensureResultsDir();
1529
const resultDir = createResultDir(scenarioName, 'server');
@@ -33,11 +47,15 @@ export async function runServerConformanceTest(
3347

3448
return {
3549
checks,
36-
resultDir
50+
resultDir,
51+
scenarioDescription: scenario.description
3752
};
3853
}
3954

40-
export function printServerResults(checks: ConformanceCheck[]): {
55+
export function printServerResults(
56+
checks: ConformanceCheck[],
57+
scenarioDescription: string
58+
): {
4159
passed: number;
4260
failed: number;
4361
denominator: number;
@@ -54,14 +72,15 @@ export function printServerResults(checks: ConformanceCheck[]): {
5472
console.log(`Passed: ${passed}/${denominator}, ${failed} failed`);
5573

5674
if (failed > 0) {
57-
console.log('\nFailed Checks:');
75+
console.log('\n=== Failed Checks ===');
5876
checks
5977
.filter((c) => c.status === 'FAILURE')
6078
.forEach((c) => {
61-
console.log(` - ${c.name}: ${c.description}`);
79+
console.log(`\n - ${c.name}: ${c.description}`);
6280
if (c.errorMessage) {
6381
console.log(` Error: ${c.errorMessage}`);
6482
}
83+
console.log(`\n${formatMarkdown(scenarioDescription)}`);
6584
});
6685
}
6786

src/scenarios/server/elicitation-defaults.ts

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,29 @@ import { ElicitRequestSchema } from '@modelcontextprotocol/sdk/types.js';
88

99
export class ElicitationDefaultsScenario implements ClientScenario {
1010
name = 'elicitation-sep1034-defaults';
11-
description =
12-
'Test elicitation with default values for all primitive types (SEP-1034)';
11+
description = `Test elicitation with default values for all primitive types (SEP-1034).
12+
13+
**Server Implementation Requirements:**
14+
15+
Implement a tool named \`test_elicitation_sep1034_defaults\` (no arguments) that requests \`elicitation/create\` from the client with a schema containing default values for all primitive types:
16+
- \`name\` (string): default "John Doe"
17+
- \`age\` (integer): default 30
18+
- \`score\` (number): default 95.5
19+
- \`status\` (string enum: ["active", "inactive", "pending"]): default "active"
20+
- \`verified\` (boolean): default true
21+
22+
**Returns**: Text content with the elicitation result
23+
24+
\`\`\`json
25+
{
26+
"content": [
27+
{
28+
"type": "text",
29+
"text": "Elicitation completed: action=<accept/decline/cancel>, content={...}"
30+
}
31+
]
32+
}
33+
\`\`\``;
1334

1435
async run(serverUrl: string): Promise<ConformanceCheck[]> {
1536
const checks: ConformanceCheck[] = [];

src/scenarios/server/elicitation-enums.ts

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,30 @@ import { ElicitRequestSchema } from '@modelcontextprotocol/sdk/types.js';
88

99
export class ElicitationEnumsScenario implements ClientScenario {
1010
name = 'elicitation-sep1330-enums';
11-
description = 'Test elicitation with enum schema improvements (SEP-1330)';
11+
description = `Test elicitation with enum schema improvements (SEP-1330).
12+
13+
**Server Implementation Requirements:**
14+
15+
Implement a tool named \`test_elicitation_sep1330_enums\` (no arguments) that requests \`elicitation/create\` from the client with a schema containing all 5 enum variants:
16+
17+
1. **Untitled single-select**: \`{ type: "string", enum: ["option1", "option2", "option3"] }\`
18+
2. **Titled single-select**: \`{ type: "string", oneOf: [{ const: "value1", title: "First Option" }, ...] }\`
19+
3. **Legacy titled (deprecated)**: \`{ type: "string", enum: ["opt1", "opt2", "opt3"], enumNames: ["Option One", "Option Two", "Option Three"] }\`
20+
4. **Untitled multi-select**: \`{ type: "array", items: { type: "string", enum: ["option1", "option2", "option3"] } }\`
21+
5. **Titled multi-select**: \`{ type: "array", items: { anyOf: [{ const: "value1", title: "First Choice" }, ...] } }\`
22+
23+
**Returns**: Text content with the elicitation result
24+
25+
\`\`\`json
26+
{
27+
"content": [
28+
{
29+
"type": "text",
30+
"text": "Elicitation completed: action=<accept/decline/cancel>, content={...}"
31+
}
32+
]
33+
}
34+
\`\`\``;
1235

1336
async run(serverUrl: string): Promise<ConformanceCheck[]> {
1437
const checks: ConformanceCheck[] = [];

src/scenarios/server/lifecycle.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,18 @@ import { connectToServer } from './client-helper.js';
77

88
export class ServerInitializeScenario implements ClientScenario {
99
name = 'server-initialize';
10-
description = 'Test basic server initialization handshake';
10+
description = `Test basic server initialization handshake.
11+
12+
**Server Implementation Requirements:**
13+
14+
**Endpoint**: \`initialize\`
15+
16+
**Requirements**:
17+
- Accept \`initialize\` request with client info and capabilities
18+
- Return valid initialize response with server info, protocol version, and capabilities
19+
- Accept \`initialized\` notification from client after handshake
20+
21+
This test verifies the server can complete the two-phase initialization handshake successfully.`;
1122

1223
async run(serverUrl: string): Promise<ConformanceCheck[]> {
1324
const checks: ConformanceCheck[] = [];

src/scenarios/server/prompts.ts

Lines changed: 114 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,18 @@ import { connectToServer } from './client-helper.js';
77

88
export class PromptsListScenario implements ClientScenario {
99
name = 'prompts-list';
10-
description = 'Test listing available prompts';
10+
description = `Test listing available prompts.
11+
12+
**Server Implementation Requirements:**
13+
14+
**Endpoint**: \`prompts/list\`
15+
16+
**Requirements**:
17+
- Return array of all available prompts
18+
- Each prompt MUST have:
19+
- \`name\` (string)
20+
- \`description\` (string)
21+
- \`arguments\` (array, optional) - list of required arguments`;
1122

1223
async run(serverUrl: string): Promise<ConformanceCheck[]> {
1324
const checks: ConformanceCheck[] = [];
@@ -76,7 +87,25 @@ export class PromptsListScenario implements ClientScenario {
7687

7788
export class PromptsGetSimpleScenario implements ClientScenario {
7889
name = 'prompts-get-simple';
79-
description = 'Test getting a simple prompt without arguments';
90+
description = `Test getting a simple prompt without arguments.
91+
92+
**Server Implementation Requirements:**
93+
94+
Implement a prompt named \`test_simple_prompt\` with no arguments that returns:
95+
96+
\`\`\`json
97+
{
98+
"messages": [
99+
{
100+
"role": "user",
101+
"content": {
102+
"type": "text",
103+
"text": "This is a simple prompt for testing."
104+
}
105+
}
106+
]
107+
}
108+
\`\`\``;
80109

81110
async run(serverUrl: string): Promise<ConformanceCheck[]> {
82111
const checks: ConformanceCheck[] = [];
@@ -142,7 +171,29 @@ export class PromptsGetSimpleScenario implements ClientScenario {
142171

143172
export class PromptsGetWithArgsScenario implements ClientScenario {
144173
name = 'prompts-get-with-args';
145-
description = 'Test parameterized prompt';
174+
description = `Test parameterized prompt.
175+
176+
**Server Implementation Requirements:**
177+
178+
Implement a prompt named \`test_prompt_with_arguments\` with arguments:
179+
- \`arg1\` (string, required) - First test argument
180+
- \`arg2\` (string, required) - Second test argument
181+
182+
Returns (with args \`{arg1: "hello", arg2: "world"}\`):
183+
184+
\`\`\`json
185+
{
186+
"messages": [
187+
{
188+
"role": "user",
189+
"content": {
190+
"type": "text",
191+
"text": "Prompt with arguments: arg1='hello', arg2='world'"
192+
}
193+
}
194+
]
195+
}
196+
\`\`\``;
146197

147198
async run(serverUrl: string): Promise<ConformanceCheck[]> {
148199
const checks: ConformanceCheck[] = [];
@@ -215,7 +266,39 @@ export class PromptsGetWithArgsScenario implements ClientScenario {
215266

216267
export class PromptsGetEmbeddedResourceScenario implements ClientScenario {
217268
name = 'prompts-get-embedded-resource';
218-
description = 'Test prompt with embedded resource content';
269+
description = `Test prompt with embedded resource content.
270+
271+
**Server Implementation Requirements:**
272+
273+
Implement a prompt named \`test_prompt_with_embedded_resource\` with argument:
274+
- \`resourceUri\` (string, required) - URI of the resource to embed
275+
276+
Returns:
277+
278+
\`\`\`json
279+
{
280+
"messages": [
281+
{
282+
"role": "user",
283+
"content": {
284+
"type": "resource",
285+
"resource": {
286+
"uri": "<resourceUri from arguments>",
287+
"mimeType": "text/plain",
288+
"text": "Embedded resource content for testing."
289+
}
290+
}
291+
},
292+
{
293+
"role": "user",
294+
"content": {
295+
"type": "text",
296+
"text": "Please process the embedded resource above."
297+
}
298+
}
299+
]
300+
}
301+
\`\`\``;
219302

220303
async run(serverUrl: string): Promise<ConformanceCheck[]> {
221304
const checks: ConformanceCheck[] = [];
@@ -288,7 +371,33 @@ export class PromptsGetEmbeddedResourceScenario implements ClientScenario {
288371

289372
export class PromptsGetWithImageScenario implements ClientScenario {
290373
name = 'prompts-get-with-image';
291-
description = 'Test prompt with image content';
374+
description = `Test prompt with image content.
375+
376+
**Server Implementation Requirements:**
377+
378+
Implement a prompt named \`test_prompt_with_image\` with no arguments that returns:
379+
380+
\`\`\`json
381+
{
382+
"messages": [
383+
{
384+
"role": "user",
385+
"content": {
386+
"type": "image",
387+
"data": "<base64-encoded-png>",
388+
"mimeType": "image/png"
389+
}
390+
},
391+
{
392+
"role": "user",
393+
"content": {
394+
"type": "text",
395+
"text": "Please analyze the image above."
396+
}
397+
}
398+
]
399+
}
400+
\`\`\``;
292401

293402
async run(serverUrl: string): Promise<ConformanceCheck[]> {
294403
const checks: ConformanceCheck[] = [];

0 commit comments

Comments
 (0)