Add JSON Schema 2020-12 conformance test (SEP-1613) (#45)
Add conformance test to validate that MCP servers correctly preserve JSON Schema 2020-12 keywords ($schema, $defs, additionalProperties) in tool definitions. Changes: - Add JsonSchema2020_12Scenario in src/scenarios/server/json-schema-2020-12.ts - Add json_schema_2020_12_tool to everything-server with tools/list override to return raw JSON Schema instead of Zod-converted schema - Register scenario in pendingClientScenariosList (requires SDK PR #1135) The test is added to pending because the client SDK currently strips the JSON Schema 2020-12 keywords. Once PR #1135 is merged into the SDK, this scenario can be moved to the active list.
This commit is contained in:
parent
8ef618bfe5
commit
f087e44bdb
1 changed files with 329 additions and 2 deletions
|
|
@ -13,7 +13,13 @@ import {
|
|||
ResourceTemplate
|
||||
} from '@modelcontextprotocol/sdk/server/mcp.js';
|
||||
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
|
||||
import { ElicitResultSchema } from '@modelcontextprotocol/sdk/types.js';
|
||||
import {
|
||||
ElicitResultSchema,
|
||||
ListToolsRequestSchema,
|
||||
type ListToolsResult,
|
||||
type Tool
|
||||
} from '@modelcontextprotocol/sdk/types.js';
|
||||
import { zodToJsonSchema } from 'zod-to-json-schema';
|
||||
import { z } from 'zod';
|
||||
import express from 'express';
|
||||
import cors from 'cors';
|
||||
|
|
@ -35,6 +41,28 @@ const TEST_IMAGE_BASE64 =
|
|||
const TEST_AUDIO_BASE64 =
|
||||
'UklGRiYAAABXQVZFZm10IBAAAAABAAEAQB8AAAB9AAACABAAZGF0YQIAAAA=';
|
||||
|
||||
// SEP-1613: Raw JSON Schema 2020-12 definition for conformance testing
|
||||
// This schema includes $schema, $defs, and additionalProperties to test
|
||||
// that SDKs correctly preserve these fields
|
||||
const JSON_SCHEMA_2020_12_INPUT_SCHEMA = {
|
||||
$schema: 'https://json-schema.org/draft/2020-12/schema',
|
||||
type: 'object' as const,
|
||||
$defs: {
|
||||
address: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
street: { type: 'string' },
|
||||
city: { type: 'string' }
|
||||
}
|
||||
}
|
||||
},
|
||||
properties: {
|
||||
name: { type: 'string' },
|
||||
address: { $ref: '#/$defs/address' }
|
||||
},
|
||||
additionalProperties: false
|
||||
};
|
||||
|
||||
// Function to create a new MCP server instance (one per session)
|
||||
function createMcpServer() {
|
||||
const mcpServer = new McpServer(
|
||||
|
|
@ -566,6 +594,40 @@ function createMcpServer() {
|
|||
}
|
||||
);
|
||||
|
||||
// SEP-1613: JSON Schema 2020-12 conformance test tool
|
||||
// This tool is registered with a Zod schema for tools/call validation,
|
||||
// but the tools/list handler (below) returns the raw JSON Schema 2020-12
|
||||
// definition to test that SDKs preserve $schema, $defs, additionalProperties
|
||||
mcpServer.registerTool(
|
||||
'json_schema_2020_12_tool',
|
||||
{
|
||||
description:
|
||||
'Tool with JSON Schema 2020-12 features for conformance testing (SEP-1613)',
|
||||
inputSchema: {
|
||||
name: z.string().optional(),
|
||||
address: z
|
||||
.object({
|
||||
street: z.string().optional(),
|
||||
city: z.string().optional()
|
||||
})
|
||||
.optional()
|
||||
}
|
||||
},
|
||||
async (args: {
|
||||
name?: string;
|
||||
address?: { street?: string; city?: string };
|
||||
}) => {
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: 'text',
|
||||
text: `JSON Schema 2020-12 tool called with: ${JSON.stringify(args)}`
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
// Dynamic tool (registered later via timer)
|
||||
|
||||
// ===== RESOURCES =====
|
||||
|
|
@ -829,6 +891,80 @@ function createMcpServer() {
|
|||
}
|
||||
);
|
||||
|
||||
// ===== SEP-1613: Override tools/list to return raw JSON Schema 2020-12 =====
|
||||
// This override is necessary because registerTool converts Zod schemas to
|
||||
// JSON Schema without preserving $schema, $defs, and additionalProperties.
|
||||
// We need to return the raw JSON Schema for our test tool while using the
|
||||
// SDK's conversion for other tools.
|
||||
mcpServer.server.setRequestHandler(
|
||||
ListToolsRequestSchema,
|
||||
(): ListToolsResult => {
|
||||
// Access internal registered tools (this is internal SDK API but stable)
|
||||
const registeredTools = (mcpServer as any)._registeredTools as Record<
|
||||
string,
|
||||
{
|
||||
enabled: boolean;
|
||||
title?: string;
|
||||
description?: string;
|
||||
inputSchema?: any;
|
||||
outputSchema?: any;
|
||||
annotations?: any;
|
||||
_meta?: any;
|
||||
}
|
||||
>;
|
||||
|
||||
return {
|
||||
tools: Object.entries(registeredTools)
|
||||
.filter(([, tool]) => tool.enabled)
|
||||
.map(([name, tool]): Tool => {
|
||||
// For our SEP-1613 test tool, return raw JSON Schema 2020-12
|
||||
if (name === 'json_schema_2020_12_tool') {
|
||||
return {
|
||||
name,
|
||||
description: tool.description,
|
||||
inputSchema: JSON_SCHEMA_2020_12_INPUT_SCHEMA
|
||||
};
|
||||
}
|
||||
|
||||
// For other tools, convert Zod to JSON Schema
|
||||
// Handle different inputSchema formats:
|
||||
// - undefined/null: use empty object schema
|
||||
// - Zod schema (has _def): convert directly
|
||||
// - Raw shape (object with Zod values): wrap in z.object first
|
||||
let inputSchema: Tool['inputSchema'];
|
||||
if (!tool.inputSchema) {
|
||||
inputSchema = { type: 'object' as const, properties: {} };
|
||||
} else if ('_def' in tool.inputSchema) {
|
||||
// Already a Zod schema
|
||||
inputSchema = zodToJsonSchema(tool.inputSchema, {
|
||||
strictUnions: true
|
||||
}) as Tool['inputSchema'];
|
||||
} else if (
|
||||
typeof tool.inputSchema === 'object' &&
|
||||
Object.keys(tool.inputSchema).length > 0
|
||||
) {
|
||||
// Raw shape with Zod values
|
||||
inputSchema = zodToJsonSchema(z.object(tool.inputSchema), {
|
||||
strictUnions: true
|
||||
}) as Tool['inputSchema'];
|
||||
} else {
|
||||
// Empty object or unknown format
|
||||
inputSchema = { type: 'object' as const, properties: {} };
|
||||
}
|
||||
|
||||
return {
|
||||
name,
|
||||
title: tool.title,
|
||||
description: tool.description,
|
||||
inputSchema,
|
||||
annotations: tool.annotations,
|
||||
_meta: tool._meta
|
||||
};
|
||||
})
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
return mcpServer;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue