remove server requirements, move requirements to schenario description (#30)

This commit is contained in:
Inna Harper 2025-11-14 16:50:07 +00:00 • committed by GitHub
commit 38443d1a43
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 633 additions and 1165 deletions

File diff suppressed because it is too large Load diff

11
package-lock.json generated
View file

@ -1678,7 +1678,6 @@
"integrity": "sha512-BnOroVl1SgrPLywqxyqdJ4l3S2MsKVLDVxZvjI1Eoe8ev2r3kGDo+PcMihNmDE+6/KjkTubSJnmqGZZjQSBq/g==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"@typescript-eslint/scope-manager": "8.46.2",
"@typescript-eslint/types": "8.46.2",
@ -1897,7 +1896,6 @@
"integrity": "sha512-LKDTZxo2D2U75EZ+8xw0AJmG3VEqUVjlWu43IJLdl+Hjyhh2eFmfufhXw3mwPRAF6hEy+E3ldUtglmmliN/CiA==",
"dev": true,
"license": "Apache-2.0",
"peer": true,
"bin": {
"tsgo": "bin/tsgo.js"
},
@ -2139,7 +2137,6 @@
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
"dev": true,
"license": "MIT",
"peer": true,
"bin": {
"acorn": "bin/acorn"
},
@ -2727,7 +2724,6 @@
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.38.0.tgz",
"integrity": "sha512-t5aPOpmtJcZcz5UJyY2GbvpDlsK5E8JqRqoKtfiKE3cNh437KIqfJr3A3AKf5k64NPx6d0G3dno6XDY05PqPtw==",
"dev": true,
"peer": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.8.0",
"@eslint-community/regexpp": "^4.12.1",
@ -2946,7 +2942,6 @@
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz",
"integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==",
"peer": true,
"dependencies": {
"accepts": "^2.0.0",
"body-parser": "^2.2.0",
@ -4047,7 +4042,6 @@
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"dev": true,
"license": "MIT",
"peer": true,
"engines": {
"node": ">=12"
},
@ -4283,7 +4277,6 @@
"resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-beta.45.tgz",
"integrity": "sha512-iMmuD72XXLf26Tqrv1cryNYLX6NNPLhZ3AmNkSf8+xda0H+yijjGJ+wVT9UdBUHOpKzq9RjKtQKRCWoEKQQBZQ==",
"dev": true,
"peer": true,
"dependencies": {
"@oxc-project/types": "=0.95.0",
"@rolldown/pluginutils": "1.0.0-beta.45"
@ -4836,7 +4829,6 @@
"integrity": "sha512-ytQKuwgmrrkDTFP4LjR0ToE2nqgy886GpvRSpU0JAnrdBYppuY5rLkRUYPU1yCryb24SsKBTL/hlDQAEFVwtZg==",
"dev": true,
"license": "MIT",
"peer": true,
"dependencies": {
"esbuild": "~0.25.0",
"get-tsconfig": "^4.7.5"
@ -4883,7 +4875,6 @@
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
"dev": true,
"peer": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
@ -4970,7 +4961,6 @@
"resolved": "https://registry.npmjs.org/vite/-/vite-7.1.12.tgz",
"integrity": "sha512-ZWyE8YXEXqJrrSLvYgrRP7p62OziLW7xI5HYGWFzOvupfAlrLvURSzv/FyGyy0eidogEM3ujU+kUG1zuHgb6Ug==",
"dev": true,
"peer": true,
"dependencies": {
"esbuild": "^0.25.0",
"fdir": "^6.5.0",
@ -5191,7 +5181,6 @@
"resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz",
"integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==",
"license": "MIT",
"peer": true,
"funding": {
"url": "https://github.com/sponsors/colinhacks"
}

View file

@ -98,7 +98,10 @@ program
validated.scenario
);
const { failed } = printServerResults(result.checks);
const { failed } = printServerResults(
result.checks,
result.scenarioDescription
);
process.exit(failed > 0 ? 1 : 0);
} else {
// Run all active scenarios

View file

@ -4,12 +4,26 @@ import { ConformanceCheck } from '../types';
import { getClientScenario } from '../scenarios';
import { ensureResultsDir, createResultDir } from './utils';
/**
* Format markdown-style text for terminal output using ANSI codes
*/
function formatMarkdown(text: string): string {
return (
text
// Bold text: **text** -> bold
.replace(/\*\*([^*]+)\*\*/g, '\x1b[1m$1\x1b[0m')
// Inline code: `code` -> dim/gray
.replace(/`([^`]+)`/g, '\x1b[2m$1\x1b[0m')
);
}
export async function runServerConformanceTest(
serverUrl: string,
scenarioName: string
): Promise<{
checks: ConformanceCheck[];
resultDir: string;
scenarioDescription: string;
}> {
await ensureResultsDir();
const resultDir = createResultDir(scenarioName, 'server');
@ -33,11 +47,15 @@ export async function runServerConformanceTest(
return {
checks,
resultDir
resultDir,
scenarioDescription: scenario.description
};
}
export function printServerResults(checks: ConformanceCheck[]): {
export function printServerResults(
checks: ConformanceCheck[],
scenarioDescription: string
): {
passed: number;
failed: number;
denominator: number;
@ -54,14 +72,15 @@ export function printServerResults(checks: ConformanceCheck[]): {
console.log(`Passed: ${passed}/${denominator}, ${failed} failed`);
if (failed > 0) {
console.log('\nFailed Checks:');
console.log('\n=== Failed Checks ===');
checks
.filter((c) => c.status === 'FAILURE')
.forEach((c) => {
console.log(` - ${c.name}: ${c.description}`);
console.log(`\n - ${c.name}: ${c.description}`);
if (c.errorMessage) {
console.log(` Error: ${c.errorMessage}`);
}
console.log(`\n${formatMarkdown(scenarioDescription)}`);
});
}

View file

@ -8,8 +8,29 @@ import { ElicitRequestSchema } from '@modelcontextprotocol/sdk/types.js';
export class ElicitationDefaultsScenario implements ClientScenario {
name = 'elicitation-sep1034-defaults';
description =
'Test elicitation with default values for all primitive types (SEP-1034)';
description = `Test elicitation with default values for all primitive types (SEP-1034).
**Server Implementation Requirements:**
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:
- \`name\` (string): default "John Doe"
- \`age\` (integer): default 30
- \`score\` (number): default 95.5
- \`status\` (string enum: ["active", "inactive", "pending"]): default "active"
- \`verified\` (boolean): default true
**Returns**: Text content with the elicitation result
\`\`\`json
{
"content": [
{
"type": "text",
"text": "Elicitation completed: action=<accept/decline/cancel>, content={...}"
}
]
}
\`\`\``;
async run(serverUrl: string): Promise<ConformanceCheck[]> {
const checks: ConformanceCheck[] = [];

View file

@ -8,7 +8,30 @@ import { ElicitRequestSchema } from '@modelcontextprotocol/sdk/types.js';
export class ElicitationEnumsScenario implements ClientScenario {
name = 'elicitation-sep1330-enums';
description = 'Test elicitation with enum schema improvements (SEP-1330)';
description = `Test elicitation with enum schema improvements (SEP-1330).
**Server Implementation Requirements:**
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:
1. **Untitled single-select**: \`{ type: "string", enum: ["option1", "option2", "option3"] }\`
2. **Titled single-select**: \`{ type: "string", oneOf: [{ const: "value1", title: "First Option" }, ...] }\`
3. **Legacy titled (deprecated)**: \`{ type: "string", enum: ["opt1", "opt2", "opt3"], enumNames: ["Option One", "Option Two", "Option Three"] }\`
4. **Untitled multi-select**: \`{ type: "array", items: { type: "string", enum: ["option1", "option2", "option3"] } }\`
5. **Titled multi-select**: \`{ type: "array", items: { anyOf: [{ const: "value1", title: "First Choice" }, ...] } }\`
**Returns**: Text content with the elicitation result
\`\`\`json
{
"content": [
{
"type": "text",
"text": "Elicitation completed: action=<accept/decline/cancel>, content={...}"
}
]
}
\`\`\``;
async run(serverUrl: string): Promise<ConformanceCheck[]> {
const checks: ConformanceCheck[] = [];

View file

@ -7,7 +7,18 @@ import { connectToServer } from './client-helper.js';
export class ServerInitializeScenario implements ClientScenario {
name = 'server-initialize';
description = 'Test basic server initialization handshake';
description = `Test basic server initialization handshake.
**Server Implementation Requirements:**
**Endpoint**: \`initialize\`
**Requirements**:
- Accept \`initialize\` request with client info and capabilities
- Return valid initialize response with server info, protocol version, and capabilities
- Accept \`initialized\` notification from client after handshake
This test verifies the server can complete the two-phase initialization handshake successfully.`;
async run(serverUrl: string): Promise<ConformanceCheck[]> {
const checks: ConformanceCheck[] = [];

View file

@ -7,7 +7,18 @@ import { connectToServer } from './client-helper.js';
export class PromptsListScenario implements ClientScenario {
name = 'prompts-list';
description = 'Test listing available prompts';
description = `Test listing available prompts.
**Server Implementation Requirements:**
**Endpoint**: \`prompts/list\`
**Requirements**:
- Return array of all available prompts
- Each prompt MUST have:
- \`name\` (string)
- \`description\` (string)
- \`arguments\` (array, optional) - list of required arguments`;
async run(serverUrl: string): Promise<ConformanceCheck[]> {
const checks: ConformanceCheck[] = [];
@ -76,7 +87,25 @@ export class PromptsListScenario implements ClientScenario {
export class PromptsGetSimpleScenario implements ClientScenario {
name = 'prompts-get-simple';
description = 'Test getting a simple prompt without arguments';
description = `Test getting a simple prompt without arguments.
**Server Implementation Requirements:**
Implement a prompt named \`test_simple_prompt\` with no arguments that returns:
\`\`\`json
{
"messages": [
{
"role": "user",
"content": {
"type": "text",
"text": "This is a simple prompt for testing."
}
}
]
}
\`\`\``;
async run(serverUrl: string): Promise<ConformanceCheck[]> {
const checks: ConformanceCheck[] = [];
@ -142,7 +171,29 @@ export class PromptsGetSimpleScenario implements ClientScenario {
export class PromptsGetWithArgsScenario implements ClientScenario {
name = 'prompts-get-with-args';
description = 'Test parameterized prompt';
description = `Test parameterized prompt.
**Server Implementation Requirements:**
Implement a prompt named \`test_prompt_with_arguments\` with arguments:
- \`arg1\` (string, required) - First test argument
- \`arg2\` (string, required) - Second test argument
Returns (with args \`{arg1: "hello", arg2: "world"}\`):
\`\`\`json
{
"messages": [
{
"role": "user",
"content": {
"type": "text",
"text": "Prompt with arguments: arg1='hello', arg2='world'"
}
}
]
}
\`\`\``;
async run(serverUrl: string): Promise<ConformanceCheck[]> {
const checks: ConformanceCheck[] = [];
@ -215,7 +266,39 @@ export class PromptsGetWithArgsScenario implements ClientScenario {
export class PromptsGetEmbeddedResourceScenario implements ClientScenario {
name = 'prompts-get-embedded-resource';
description = 'Test prompt with embedded resource content';
description = `Test prompt with embedded resource content.
**Server Implementation Requirements:**
Implement a prompt named \`test_prompt_with_embedded_resource\` with argument:
- \`resourceUri\` (string, required) - URI of the resource to embed
Returns:
\`\`\`json
{
"messages": [
{
"role": "user",
"content": {
"type": "resource",
"resource": {
"uri": "<resourceUri from arguments>",
"mimeType": "text/plain",
"text": "Embedded resource content for testing."
}
}
},
{
"role": "user",
"content": {
"type": "text",
"text": "Please process the embedded resource above."
}
}
]
}
\`\`\``;
async run(serverUrl: string): Promise<ConformanceCheck[]> {
const checks: ConformanceCheck[] = [];
@ -288,7 +371,33 @@ export class PromptsGetEmbeddedResourceScenario implements ClientScenario {
export class PromptsGetWithImageScenario implements ClientScenario {
name = 'prompts-get-with-image';
description = 'Test prompt with image content';
description = `Test prompt with image content.
**Server Implementation Requirements:**
Implement a prompt named \`test_prompt_with_image\` with no arguments that returns:
\`\`\`json
{
"messages": [
{
"role": "user",
"content": {
"type": "image",
"data": "<base64-encoded-png>",
"mimeType": "image/png"
}
},
{
"role": "user",
"content": {
"type": "text",
"text": "Please analyze the image above."
}
}
]
}
\`\`\``;
async run(serverUrl: string): Promise<ConformanceCheck[]> {
const checks: ConformanceCheck[] = [];

View file

@ -7,7 +7,19 @@ import { connectToServer } from './client-helper.js';
export class ResourcesListScenario implements ClientScenario {
name = 'resources-list';
description = 'Test listing available resources';
description = `Test listing available resources.
**Server Implementation Requirements:**
**Endpoint**: \`resources/list\`
**Requirements**:
- Return array of all available **direct resources** (not templates)
- Each resource MUST have:
- \`uri\` (string)
- \`name\` (string)
- \`description\` (string)
- \`mimeType\` (string, optional)`;
async run(serverUrl: string): Promise<ConformanceCheck[]> {
const checks: ConformanceCheck[] = [];
@ -75,7 +87,23 @@ export class ResourcesListScenario implements ClientScenario {
export class ResourcesReadTextScenario implements ClientScenario {
name = 'resources-read-text';
description = 'Test reading text resource';
description = `Test reading text resource.
**Server Implementation Requirements:**
Implement resource \`test://static-text\` that returns:
\`\`\`json
{
"contents": [
{
"uri": "test://static-text",
"mimeType": "text/plain",
"text": "This is the content of the static text resource."
}
]
}
\`\`\``;
async run(serverUrl: string): Promise<ConformanceCheck[]> {
const checks: ConformanceCheck[] = [];
@ -145,7 +173,23 @@ export class ResourcesReadTextScenario implements ClientScenario {
export class ResourcesReadBinaryScenario implements ClientScenario {
name = 'resources-read-binary';
description = 'Test reading binary resource';
description = `Test reading binary resource.
**Server Implementation Requirements:**
Implement resource \`test://static-binary\` that returns:
\`\`\`json
{
"contents": [
{
"uri": "test://static-binary",
"mimeType": "image/png",
"blob": "<base64-encoded-png>"
}
]
}
\`\`\``;
async run(serverUrl: string): Promise<ConformanceCheck[]> {
const checks: ConformanceCheck[] = [];
@ -213,7 +257,27 @@ export class ResourcesReadBinaryScenario implements ClientScenario {
export class ResourcesTemplateReadScenario implements ClientScenario {
name = 'resources-templates-read';
description = 'Test reading resource from template';
description = `Test reading resource from template.
**Server Implementation Requirements:**
Implement resource template \`test://template/{id}/data\` that substitutes parameters.
**Behavior**: When client requests \`test://template/123/data\`, substitute \`{id}\` with \`123\`
Returns (for \`uri: "test://template/123/data"\`):
\`\`\`json
{
"contents": [
{
"uri": "test://template/123/data",
"mimeType": "application/json",
"text": "{"id":"123","templateTest":true,"data":"Data for ID: 123"}"
}
]
}
\`\`\``;
async run(serverUrl: string): Promise<ConformanceCheck[]> {
const checks: ConformanceCheck[] = [];
@ -286,7 +350,27 @@ export class ResourcesTemplateReadScenario implements ClientScenario {
export class ResourcesSubscribeScenario implements ClientScenario {
name = 'resources-subscribe';
description = 'Test subscribing to resource updates';
description = `Test subscribing to resource updates.
**Server Implementation Requirements:**
**Endpoint**: \`resources/subscribe\`
**Requirements**:
- Accept subscription request with URI
- Track subscribed URIs
- Return empty object \`{}\`
Example request:
\`\`\`json
{
"method": "resources/subscribe",
"params": {
"uri": "test://watched-resource"
}
}
\`\`\``;
async run(serverUrl: string): Promise<ConformanceCheck[]> {
const checks: ConformanceCheck[] = [];
@ -336,7 +420,17 @@ export class ResourcesSubscribeScenario implements ClientScenario {
export class ResourcesUnsubscribeScenario implements ClientScenario {
name = 'resources-unsubscribe';
description = 'Test unsubscribing from resource';
description = `Test unsubscribing from resource.
**Server Implementation Requirements:**
**Endpoint**: \`resources/unsubscribe\`
**Requirements**:
- Accept unsubscribe request with URI
- Remove URI from subscriptions
- Stop sending update notifications for that URI
- Return empty object \`{}\``;
async run(serverUrl: string): Promise<ConformanceCheck[]> {
const checks: ConformanceCheck[] = [];
@ -363,7 +457,7 @@ export class ResourcesUnsubscribeScenario implements ClientScenario {
specReferences: [
{
id: 'MCP-Resources-Subscribe',
url: 'https://modelcontextprotocol.io/specification/2025-06-18/server/resources#resource-subscriptions'
url: 'https://modelcontextprotocol.io/specification/2025-06-18/schema#unsubscriberequest'
}
]
});

View file

@ -13,7 +13,18 @@ import {
export class ToolsListScenario implements ClientScenario {
name = 'tools-list';
description = 'Test listing available tools';
description = `Test listing available tools.
**Server Implementation Requirements:**
**Endpoint**: \`tools/list\`
**Requirements**:
- Return array of all available tools
- Each tool MUST have:
- \`name\` (string)
- \`description\` (string)
- \`inputSchema\` (valid JSON Schema object)`;
async run(serverUrl: string): Promise<ConformanceCheck[]> {
const checks: ConformanceCheck[] = [];
@ -84,7 +95,22 @@ export class ToolsListScenario implements ClientScenario {
export class ToolsCallSimpleTextScenario implements ClientScenario {
name = 'tools-call-simple-text';
description = 'Test calling a tool that returns simple text';
description = `Test calling a tool that returns simple text.
**Server Implementation Requirements:**
Implement tool \`test_simple_text\` with no arguments that returns:
\`\`\`json
{
"content": [
{
"type": "text",
"text": "This is a simple text response for testing."
}
]
}
\`\`\``;
async run(serverUrl: string): Promise<ConformanceCheck[]> {
const checks: ConformanceCheck[] = [];
@ -153,7 +179,25 @@ export class ToolsCallSimpleTextScenario implements ClientScenario {
export class ToolsCallImageScenario implements ClientScenario {
name = 'tools-call-image';
description = 'Test calling a tool that returns image content';
description = `Test calling a tool that returns image content.
**Server Implementation Requirements:**
Implement tool \`test_image_content\` with no arguments that returns:
\`\`\`json
{
"content": [
{
"type": "image",
"data": "<base64-encoded-png>",
"mimeType": "image/png"
}
]
}
\`\`\`
**Implementation Note**: Use a minimal test image (e.g., 1x1 red pixel PNG)`;
async run(serverUrl: string): Promise<ConformanceCheck[]> {
const checks: ConformanceCheck[] = [];
@ -222,7 +266,35 @@ export class ToolsCallImageScenario implements ClientScenario {
export class ToolsCallMultipleContentTypesScenario implements ClientScenario {
name = 'tools-call-mixed-content';
description = 'Test tool returning multiple content types';
description = `Test tool returning multiple content types.
**Server Implementation Requirements:**
Implement tool \`test_multiple_content_types\` with no arguments that returns:
\`\`\`json
{
"content": [
{
"type": "text",
"text": "Multiple content types test:"
},
{
"type": "image",
"data": "<base64>",
"mimeType": "image/png"
},
{
"type": "resource",
"resource": {
"uri": "test://mixed-content-resource",
"mimeType": "application/json",
"text": "{"test":"data","value":123}"
}
}
]
}
\`\`\``;
async run(serverUrl: string): Promise<ConformanceCheck[]> {
const checks: ConformanceCheck[] = [];
@ -294,7 +366,20 @@ export class ToolsCallMultipleContentTypesScenario implements ClientScenario {
export class ToolsCallWithLoggingScenario implements ClientScenario {
name = 'tools-call-with-logging';
description = 'Test tool that sends log messages during execution';
description = `Test tool that sends log messages during execution.
**Server Implementation Requirements:**
Implement tool \`test_tool_with_logging\` with no arguments.
**Behavior**: During execution, send 3 log notifications at info level:
1. "Tool execution started"
2. "Tool processing data" (after ~50ms delay)
3. "Tool execution completed" (after another ~50ms delay)
**Returns**: Text content confirming execution
**Implementation Note**: The delays are important to test that clients can receive multiple log notifications during tool execution`;
async run(serverUrl: string): Promise<ConformanceCheck[]> {
const checks: ConformanceCheck[] = [];
@ -366,7 +451,27 @@ export class ToolsCallWithLoggingScenario implements ClientScenario {
export class ToolsCallErrorScenario implements ClientScenario {
name = 'tools-call-error';
description = 'Test tool error reporting';
description = `Test tool error reporting.
**Server Implementation Requirements:**
Implement tool \`test_error_handling\` with no arguments.
**Behavior**: Always throw an error
**Returns**: JSON-RPC response with \`isError: true\`
\`\`\`json
{
"isError": true,
"content": [
{
"type": "text",
"text": "This tool intentionally returns an error for testing"
}
]
}
\`\`\``;
async run(serverUrl: string): Promise<ConformanceCheck[]> {
const checks: ConformanceCheck[] = [];
@ -430,7 +535,35 @@ export class ToolsCallErrorScenario implements ClientScenario {
export class ToolsCallWithProgressScenario implements ClientScenario {
name = 'tools-call-with-progress';
description = 'Test tool that reports progress notifications';
description = `Test tool that reports progress notifications.
**Server Implementation Requirements:**
Implement tool \`test_tool_with_progress\` with no arguments.
**Behavior**: If \`_meta.progressToken\` is provided in request:
- Send progress notification: \`0/100\`
- Wait ~50ms
- Send progress notification: \`50/100\`
- Wait ~50ms
- Send progress notification: \`100/100\`
If no progress token provided, just execute with delays.
**Returns**: Text content confirming execution
**Progress Notification Format**:
\`\`\`json
{
"method": "notifications/progress",
"params": {
"progressToken": "<from request._meta.progressToken>",
"progress": 50,
"total": 100
}
}
\`\`\``;
async run(serverUrl: string): Promise<ConformanceCheck[]> {
const checks: ConformanceCheck[] = [];
@ -521,7 +654,49 @@ export class ToolsCallWithProgressScenario implements ClientScenario {
export class ToolsCallSamplingScenario implements ClientScenario {
name = 'tools-call-sampling';
description = 'Test tool that requests LLM sampling from client';
description = `Test tool that requests LLM sampling from client.
**Server Implementation Requirements:**
Implement tool \`test_sampling\` with argument:
- \`prompt\` (string, required) - The prompt to send to the LLM
**Behavior**: Request LLM sampling from the client using \`sampling/createMessage\`
**Sampling Request**:
\`\`\`json
{
"method": "sampling/createMessage",
"params": {
"messages": [
{
"role": "user",
"content": {
"type": "text",
"text": "<prompt from arguments>"
}
}
],
"maxTokens": 100
}
}
\`\`\`
**Returns**: Text content with the LLM's response
\`\`\`json
{
"content": [
{
"type": "text",
"text": "LLM response: <response from sampling>"
}
]
}
\`\`\`
**Implementation Note**: If the client doesn't support sampling (no \`sampling\` capability), return an error.`;
async run(serverUrl: string): Promise<ConformanceCheck[]> {
const checks: ConformanceCheck[] = [];
@ -606,7 +781,54 @@ export class ToolsCallSamplingScenario implements ClientScenario {
export class ToolsCallElicitationScenario implements ClientScenario {
name = 'tools-call-elicitation';
description = 'Test tool that requests user input (elicitation) from client';
description = `Test tool that requests user input (elicitation) from client.
**Server Implementation Requirements:**
Implement tool \`test_elicitation\` with argument:
- \`message\` (string, required) - The message to show the user
**Behavior**: Request user input from the client using \`elicitation/create\`
**Elicitation Request**:
\`\`\`json
{
"method": "elicitation/create",
"params": {
"message": "<message from arguments>",
"requestedSchema": {
"type": "object",
"properties": {
"username": {
"type": "string",
"description": "User's response"
},
"email": {
"type": "string",
"description": "User's email address"
}
},
"required": ["username", "email"]
}
}
}
\`\`\`
**Returns**: Text content with the user's response
\`\`\`json
{
"content": [
{
"type": "text",
"text": "User response: <action: accept/decline/cancel, content: {...}>"
}
]
}
\`\`\`
**Implementation Note**: If the client doesn't support elicitation (no \`elicitation\` capability), return an error.`;
async run(serverUrl: string): Promise<ConformanceCheck[]> {
const checks: ConformanceCheck[] = [];
@ -689,7 +911,25 @@ export class ToolsCallElicitationScenario implements ClientScenario {
export class ToolsCallAudioScenario implements ClientScenario {
name = 'tools-call-audio';
description = 'Test calling a tool that returns audio content';
description = `Test calling a tool that returns audio content.
**Server Implementation Requirements:**
Implement tool \`test_audio_content\` with no arguments that returns:
\`\`\`json
{
"content": [
{
"type": "audio",
"data": "<base64-encoded-wav>",
"mimeType": "audio/wav"
}
]
}
\`\`\`
**Implementation Note**: Use a minimal test audio file`;
async run(serverUrl: string): Promise<ConformanceCheck[]> {
const checks: ConformanceCheck[] = [];
@ -765,7 +1005,26 @@ export class ToolsCallAudioScenario implements ClientScenario {
export class ToolsCallEmbeddedResourceScenario implements ClientScenario {
name = 'tools-call-embedded-resource';
description = 'Test calling a tool that returns embedded resource content';
description = `Test calling a tool that returns embedded resource content.
**Server Implementation Requirements:**
Implement tool \`test_embedded_resource\` with no arguments that returns:
\`\`\`json
{
"content": [
{
"type": "resource",
"resource": {
"uri": "test://embedded-resource",
"mimeType": "text/plain",
"text": "This is an embedded resource content."
}
}
]
}
\`\`\``;
async run(serverUrl: string): Promise<ConformanceCheck[]> {
const checks: ConformanceCheck[] = [];

View file

@ -7,7 +7,26 @@ import { connectToServer } from './client-helper.js';
export class LoggingSetLevelScenario implements ClientScenario {
name = 'logging-set-level';
description = 'Test setting logging level';
description = `Test setting logging level.
**Server Implementation Requirements:**
**Endpoint**: \`logging/setLevel\`
**Requirements**:
- Accept log level setting
- Filter subsequent log notifications based on level
- Return empty object \`{}\`
**Log Levels** (in order of severity):
- \`debug\`
- \`info\`
- \`notice\`
- \`warning\`
- \`error\`
- \`critical\`
- \`alert\`
- \`emergency\``;
async run(serverUrl: string): Promise<ConformanceCheck[]> {
const checks: ConformanceCheck[] = [];
@ -66,7 +85,48 @@ export class LoggingSetLevelScenario implements ClientScenario {
export class CompletionCompleteScenario implements ClientScenario {
name = 'completion-complete';
description = 'Test completion endpoint';
description = `Test completion endpoint.
**Server Implementation Requirements:**
**Endpoint**: \`completion/complete\`
**Requirements**:
- Accept completion requests for prompt or resource template arguments
- Provide contextual suggestions based on partial input
- Return array of completion values ranked by relevance
**Request Format**:
\`\`\`json
{
"method": "completion/complete",
"params": {
"ref": {
"type": "ref/prompt",
"name": "test_prompt_with_arguments"
},
"argument": {
"name": "arg1",
"value": "par"
}
}
}
\`\`\`
**Response Format**:
\`\`\`json
{
"completion": {
"values": ["paris", "park", "party"],
"total": 150,
"hasMore": false
}
}
\`\`\`
**Implementation Note**: For conformance testing, completion support can be minimal or return empty arrays. The capability just needs to be declared and the endpoint must respond correctly.`;
async run(serverUrl: string): Promise<ConformanceCheck[]> {
const checks: ConformanceCheck[] = [];