parent
3b9da98c6a
commit
e751676b04
8 changed files with 1107 additions and 968 deletions
|
|
@ -2,8 +2,7 @@
|
||||||
|
|
||||||
A framework for testing MCP (Model Context Protocol) client implementations against the specification.
|
A framework for testing MCP (Model Context Protocol) client implementations against the specification.
|
||||||
|
|
||||||
> [!WARNING]
|
> [!WARNING] This repository is a work in progress and is unstable. Join the conversation in the #conformance-testing-wg in the MCP Contributors discord.
|
||||||
> This repository is a work in progress and is unstable. Join the conversation in the #conformance-testing-wg in the MCP Contributors discord.
|
|
||||||
|
|
||||||
## Quick Start
|
## Quick Start
|
||||||
|
|
||||||
|
|
|
||||||
2046
package-lock.json
generated
2046
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -44,4 +44,4 @@ export function createClientInitializationCheck(initializeRequest: any, expected
|
||||||
errorMessage: errors.length > 0 ? errors.join('; ') : undefined,
|
errorMessage: errors.length > 0 ? errors.join('; ') : undefined,
|
||||||
logs: errors.length > 0 ? errors : undefined
|
logs: errors.length > 0 ? errors : undefined
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,5 @@
|
||||||
import * as client from './client.js';
|
import * as client from './client.js';
|
||||||
import * as server from './server.js';
|
import * as server from './server.js';
|
||||||
|
|
||||||
|
|
||||||
export const clientChecks = client;
|
export const clientChecks = client;
|
||||||
export const serverChecks = server;
|
export const serverChecks = server;
|
||||||
|
|
|
||||||
|
|
@ -5,13 +5,14 @@ export function createServerInitializationCheck(initializeResponse: any, expecte
|
||||||
const protocolVersion = result?.protocolVersion;
|
const protocolVersion = result?.protocolVersion;
|
||||||
const serverInfo = result?.serverInfo;
|
const serverInfo = result?.serverInfo;
|
||||||
const capabilities = result?.capabilities;
|
const capabilities = result?.capabilities;
|
||||||
|
|
||||||
const errors: string[] = [];
|
const errors: string[] = [];
|
||||||
if (!initializeResponse?.jsonrpc) errors.push('Missing jsonrpc field');
|
if (!initializeResponse?.jsonrpc) errors.push('Missing jsonrpc field');
|
||||||
if (!initializeResponse?.id) errors.push('Missing id field');
|
if (!initializeResponse?.id) errors.push('Missing id field');
|
||||||
if (!result) errors.push('Missing result field');
|
if (!result) errors.push('Missing result field');
|
||||||
if (!protocolVersion) errors.push('Missing protocolVersion in result');
|
if (!protocolVersion) errors.push('Missing protocolVersion in result');
|
||||||
if (protocolVersion !== expectedSpecVersion) errors.push(`Protocol version mismatch: expected ${expectedSpecVersion}, got ${protocolVersion}`);
|
if (protocolVersion !== expectedSpecVersion)
|
||||||
|
errors.push(`Protocol version mismatch: expected ${expectedSpecVersion}, got ${protocolVersion}`);
|
||||||
if (!serverInfo) errors.push('Missing serverInfo in result');
|
if (!serverInfo) errors.push('Missing serverInfo in result');
|
||||||
if (!serverInfo?.name) errors.push('Missing server name in serverInfo');
|
if (!serverInfo?.name) errors.push('Missing server name in serverInfo');
|
||||||
if (!serverInfo?.version) errors.push('Missing server version in serverInfo');
|
if (!serverInfo?.version) errors.push('Missing server version in serverInfo');
|
||||||
|
|
@ -33,4 +34,4 @@ export function createServerInitializationCheck(initializeResponse: any, expecte
|
||||||
errorMessage: errors.length > 0 ? errors.join('; ') : undefined,
|
errorMessage: errors.length > 0 ? errors.join('; ') : undefined,
|
||||||
logs: errors.length > 0 ? errors : undefined
|
logs: errors.length > 0 ? errors : undefined
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,9 +8,7 @@ export const scenarios = new Map<string, Scenario>([
|
||||||
['tools-call', new ToolsCallScenario()]
|
['tools-call', new ToolsCallScenario()]
|
||||||
]);
|
]);
|
||||||
|
|
||||||
export const clientScenarios = new Map<string, ClientScenario>([
|
export const clientScenarios = new Map<string, ClientScenario>([['initialize', new ServerInitializeClientScenario()]]);
|
||||||
['initialize', new ServerInitializeClientScenario()]
|
|
||||||
]);
|
|
||||||
|
|
||||||
export function registerScenario(name: string, scenario: Scenario): void {
|
export function registerScenario(name: string, scenario: Scenario): void {
|
||||||
scenarios.set(name, scenario);
|
scenarios.set(name, scenario);
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ export class ServerInitializeClientScenario implements ClientScenario {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
'Accept': 'application/json, text/event-stream',
|
Accept: 'application/json, text/event-stream'
|
||||||
},
|
},
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
jsonrpc: '2.0',
|
jsonrpc: '2.0',
|
||||||
|
|
@ -36,7 +36,7 @@ export class ServerInitializeClientScenario implements ClientScenario {
|
||||||
}
|
}
|
||||||
|
|
||||||
const responseText = await response.text();
|
const responseText = await response.text();
|
||||||
|
|
||||||
// Handle SSE format
|
// Handle SSE format
|
||||||
let result;
|
let result;
|
||||||
if (responseText.startsWith('event:') || responseText.includes('\ndata:')) {
|
if (responseText.startsWith('event:') || responseText.includes('\ndata:')) {
|
||||||
|
|
@ -53,13 +53,13 @@ export class ServerInitializeClientScenario implements ClientScenario {
|
||||||
// Regular JSON response
|
// Regular JSON response
|
||||||
result = JSON.parse(responseText);
|
result = JSON.parse(responseText);
|
||||||
}
|
}
|
||||||
|
|
||||||
const check = serverChecks.createServerInitializationCheck(result);
|
const check = serverChecks.createServerInitializationCheck(result);
|
||||||
checks.push(check);
|
checks.push(check);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
checks.push({
|
checks.push({
|
||||||
id: 'server-initialize-request',
|
id: 'server-initialize-request',
|
||||||
name: 'ServerInitializeRequest',
|
name: 'ServerInitializeRequest',
|
||||||
description: 'Tests server response to initialize request',
|
description: 'Tests server response to initialize request',
|
||||||
status: 'FAILURE',
|
status: 'FAILURE',
|
||||||
timestamp: new Date().toISOString(),
|
timestamp: new Date().toISOString(),
|
||||||
|
|
@ -79,4 +79,4 @@ export class ServerInitializeClientScenario implements ClientScenario {
|
||||||
|
|
||||||
return checks;
|
return checks;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -98,4 +98,4 @@ async function main(): Promise<void> {
|
||||||
|
|
||||||
if (import.meta.url === `file://${process.argv[1]}`) {
|
if (import.meta.url === `file://${process.argv[1]}`) {
|
||||||
main();
|
main();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue