From 5e2dbcde9256ee552080b3a07e22e46328b3a91e Mon Sep 17 00:00:00 2001 From: Adam Bloomston Date: Tue, 5 Aug 2025 11:00:58 -0600 Subject: [PATCH 01/10] test: add failing test showing unknown parameters are silently ignored This test demonstrates the current issue where tools accept unknown parameters (e.g. incorrect capitalization) without validation errors. The test uses 'username' and 'itemcount' instead of the expected 'userName' and 'itemCount' parameters, which should be rejected but currently aren't. This test is expected to fail until strict validation is implemented. --- src/server/mcp.test.ts | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/src/server/mcp.test.ts b/src/server/mcp.test.ts index 4bb42d7fc..f43d713ef 100644 --- a/src/server/mcp.test.ts +++ b/src/server/mcp.test.ts @@ -4085,4 +4085,44 @@ describe('elicitInput()', () => { } ]); }); + + test('should fail: tool with incorrect parameter capitalization is not rejected', async () => { + const mcpServer = new McpServer({ + name: 'test server', + version: '1.0' + }); + + const client = new Client({ + name: 'test client', + version: '1.0' + }); + + mcpServer.registerTool( + 'test-strict', + { + inputSchema: { userName: z.string().optional(), itemCount: z.number().optional() } + }, + async ({ userName, itemCount }) => ({ + content: [{ type: 'text', text: `${userName || 'none'}: ${itemCount || 0}` }] + }) + ); + + const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair(); + + await Promise.all([client.connect(clientTransport), mcpServer.server.connect(serverTransport)]); + + const result = await client.request( + { + method: 'tools/call', + params: { + name: 'test-strict', + arguments: { username: 'test', itemcount: 42 } + } + }, + CallToolResultSchema + ); + + expect(result.isError).toBe(true); + expect(result.content[0].text).toContain('Invalid arguments'); + }); }); From b2b22578ec2452bdbda21471dcdcea0136ba4793 Mon Sep 17 00:00:00 2001 From: Adam Bloomston Date: Tue, 5 Aug 2025 11:20:40 -0600 Subject: [PATCH 02/10] feat: add optional strict parameter to registerTool for Zod validation Add strict parameter to registerTool config that defaults to false for backward compatibility. When strict=true, applies .strict() to Zod schema creation for tool input validation to throw errors on unknown parameters instead of silently ignoring them. - Add strict?: boolean to registerTool config interface - Modify _createRegisteredTool to accept and use strict parameter - Apply z.object(inputSchema).strict() when strict=true - Update legacy tool() method to pass strict=false (backward compatibility) - Update test to verify strict validation rejects unknown parameters - All existing tests continue to pass (no breaking changes) This fixes the issue where parameter name typos are silently dropped, leading to confusing behavior where tools execute with missing data. --- src/server/mcp.test.ts | 40 ++++++++++++++++++++++++++++++++++++++++ src/server/mcp.ts | 9 ++++++--- 2 files changed, 46 insertions(+), 3 deletions(-) diff --git a/src/server/mcp.test.ts b/src/server/mcp.test.ts index f43d713ef..b01e2ddc6 100644 --- a/src/server/mcp.test.ts +++ b/src/server/mcp.test.ts @@ -4125,4 +4125,44 @@ describe('elicitInput()', () => { expect(result.isError).toBe(true); expect(result.content[0].text).toContain('Invalid arguments'); }); + + test('should reject unknown parameters when strict validation is enabled', async () => { + const mcpServer = new McpServer({ + name: 'test server', + version: '1.0' + }); + + const client = new Client({ + name: 'test client', + version: '1.0' + }); + + mcpServer.registerTool( + 'test-strict', + { + inputSchema: { userName: z.string().optional(), itemCount: z.number().optional() }, + strict: true + }, + async ({ userName, itemCount }) => ({ + content: [{ type: 'text', text: `${userName || 'none'}: ${itemCount || 0}` }] + }) + ); + + const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair(); + + await Promise.all([client.connect(clientTransport), mcpServer.server.connect(serverTransport)]); + + await expect( + client.request( + { + method: 'tools/call', + params: { + name: 'test-strict', + arguments: { username: 'test', itemcount: 42 } + } + }, + CallToolResultSchema + ) + ).rejects.toThrow('Invalid arguments'); + }); }); diff --git a/src/server/mcp.ts b/src/server/mcp.ts index cef1722d6..13945f7c7 100644 --- a/src/server/mcp.ts +++ b/src/server/mcp.ts @@ -647,13 +647,14 @@ export class McpServer { inputSchema: ZodRawShape | undefined, outputSchema: ZodRawShape | undefined, annotations: ToolAnnotations | undefined, + strict: boolean | undefined, _meta: Record | undefined, callback: ToolCallback ): RegisteredTool { const registeredTool: RegisteredTool = { title, description, - inputSchema: inputSchema === undefined ? undefined : z.object(inputSchema), + inputSchema: inputSchema === undefined ? undefined : strict === true ? z.object(inputSchema).strict() : z.object(inputSchema), outputSchema: outputSchema === undefined ? undefined : z.object(outputSchema), annotations, _meta, @@ -780,7 +781,7 @@ export class McpServer { } const callback = rest[0] as ToolCallback; - return this._createRegisteredTool(name, undefined, description, inputSchema, outputSchema, annotations, undefined, callback); + return this._createRegisteredTool(name, undefined, description, inputSchema, outputSchema, annotations, false, undefined, callback); } /** @@ -794,6 +795,7 @@ export class McpServer { inputSchema?: InputArgs; outputSchema?: OutputArgs; annotations?: ToolAnnotations; + strict?: boolean; _meta?: Record; }, cb: ToolCallback @@ -802,7 +804,7 @@ export class McpServer { throw new Error(`Tool ${name} is already registered`); } - const { title, description, inputSchema, outputSchema, annotations, _meta } = config; + const { title, description, inputSchema, outputSchema, annotations, strict, _meta } = config; return this._createRegisteredTool( name, @@ -811,6 +813,7 @@ export class McpServer { inputSchema, outputSchema, annotations, + strict, _meta, cb as ToolCallback ); From 57dacc447a5ba2ad5496b5a744f2ee102e5462ec Mon Sep 17 00:00:00 2001 From: Adam Bloomston Date: Tue, 5 Aug 2025 11:39:44 -0600 Subject: [PATCH 03/10] docs: add parameter validation documentation for strict mode Add Advanced Usage section explaining the strict parameter for registerTool: - Show examples of strict vs lenient validation - Explain when to use each mode (development vs production) - Document that strict parameter is only available in registerTool() - Note that legacy tool() method uses lenient validation for compatibility This helps developers understand when and how to use strict validation to catch parameter name typos and unexpected data. --- README.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/README.md b/README.md index bd4789d7a..04b5e414a 100644 --- a/README.md +++ b/README.md @@ -1110,6 +1110,31 @@ server.registerTool("tool3", ...).disable(); // Only one 'notifications/tools/list_changed' is sent. ``` +### Parameter Validation and Error Handling + +Control how tools handle parameter validation errors and unexpected inputs: + +```typescript +// Strict validation for development - catches typos immediately +const devTool = server.registerTool("dev-tool", { + inputSchema: { userName: z.string(), itemCount: z.number() }, + strict: true // Reject { username: "test", itemcount: 42 } +}, handler); + +// Lenient validation for production - handles client variations gracefully +const prodTool = server.registerTool("prod-tool", { + inputSchema: { userName: z.string().optional(), itemCount: z.number().optional() }, + strict: false // Accept extra parameters (default behavior) +}, handler); +``` + +**When to use strict validation:** +- Development and testing: Catch parameter name typos early +- Production APIs: Ensure clients send only expected parameters +- Security-sensitive tools: Prevent injection of unexpected data + +**Note:** The `strict` parameter is only available in `registerTool()`. The legacy `tool()` method uses lenient validation for backward compatibility. + ### Low-Level Server For more control, you can use the low-level Server class directly: From 8a232b527208879bd58023f8f5b87f64207b3478 Mon Sep 17 00:00:00 2001 From: Adam Bloomston Date: Tue, 7 Oct 2025 10:31:58 -0600 Subject: [PATCH 04/10] docs: clarify lenient validation maintains backwards compatibility MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Revised comment to explain that lenient validation (strict=false) maintains backwards compatibility with existing clients rather than advising leniency for production. The updated comment clarifies that extra parameters are accepted to support clients that may send additional fields. Changes linted and tested. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 04b5e414a..1cb27b4de 100644 --- a/README.md +++ b/README.md @@ -1115,16 +1115,16 @@ server.registerTool("tool3", ...).disable(); Control how tools handle parameter validation errors and unexpected inputs: ```typescript -// Strict validation for development - catches typos immediately +// Strict validation - catches typos and unexpected parameters immediately const devTool = server.registerTool("dev-tool", { inputSchema: { userName: z.string(), itemCount: z.number() }, strict: true // Reject { username: "test", itemcount: 42 } }, handler); -// Lenient validation for production - handles client variations gracefully +// Lenient validation (default) - maintains backwards compatibility with existing clients const prodTool = server.registerTool("prod-tool", { inputSchema: { userName: z.string().optional(), itemCount: z.number().optional() }, - strict: false // Accept extra parameters (default behavior) + strict: false // Accept extra parameters for backwards compatibility with clients that may send additional fields }, handler); ``` From 991639a8fb49f948e7eb38628ecd27f62ac8fb75 Mon Sep 17 00:00:00 2001 From: Adam Bloomston Date: Tue, 7 Oct 2025 10:33:34 -0600 Subject: [PATCH 05/10] refactor: rename strict to strictInputSchemaValidation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Renamed parameter from `strict` to `strictInputSchemaValidation` for clarity. The longer name makes it more explicit that this validation applies specifically to the input schema and helps distinguish it from other types of strictness. Updated: - src/server/mcp.ts: Parameter in registerTool config and _createRegisteredTool signature - src/server/mcp.test.ts: Test using the renamed parameter - README.md: Documentation examples and notes Changes linted and tested. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- README.md | 6 +++--- src/server/mcp.test.ts | 2 +- src/server/mcp.ts | 10 +++++----- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 1cb27b4de..b0049840c 100644 --- a/README.md +++ b/README.md @@ -1118,13 +1118,13 @@ Control how tools handle parameter validation errors and unexpected inputs: // Strict validation - catches typos and unexpected parameters immediately const devTool = server.registerTool("dev-tool", { inputSchema: { userName: z.string(), itemCount: z.number() }, - strict: true // Reject { username: "test", itemcount: 42 } + strictInputSchemaValidation: true // Reject { username: "test", itemcount: 42 } }, handler); // Lenient validation (default) - maintains backwards compatibility with existing clients const prodTool = server.registerTool("prod-tool", { inputSchema: { userName: z.string().optional(), itemCount: z.number().optional() }, - strict: false // Accept extra parameters for backwards compatibility with clients that may send additional fields + strictInputSchemaValidation: false // Accept extra parameters for backwards compatibility with clients that may send additional fields }, handler); ``` @@ -1133,7 +1133,7 @@ const prodTool = server.registerTool("prod-tool", { - Production APIs: Ensure clients send only expected parameters - Security-sensitive tools: Prevent injection of unexpected data -**Note:** The `strict` parameter is only available in `registerTool()`. The legacy `tool()` method uses lenient validation for backward compatibility. +**Note:** The `strictInputSchemaValidation` parameter is only available in `registerTool()`. The legacy `tool()` method uses lenient validation for backward compatibility. ### Low-Level Server diff --git a/src/server/mcp.test.ts b/src/server/mcp.test.ts index b01e2ddc6..d048237e8 100644 --- a/src/server/mcp.test.ts +++ b/src/server/mcp.test.ts @@ -4141,7 +4141,7 @@ describe('elicitInput()', () => { 'test-strict', { inputSchema: { userName: z.string().optional(), itemCount: z.number().optional() }, - strict: true + strictInputSchemaValidation: true }, async ({ userName, itemCount }) => ({ content: [{ type: 'text', text: `${userName || 'none'}: ${itemCount || 0}` }] diff --git a/src/server/mcp.ts b/src/server/mcp.ts index 13945f7c7..93c0f5219 100644 --- a/src/server/mcp.ts +++ b/src/server/mcp.ts @@ -647,14 +647,14 @@ export class McpServer { inputSchema: ZodRawShape | undefined, outputSchema: ZodRawShape | undefined, annotations: ToolAnnotations | undefined, - strict: boolean | undefined, + strictInputSchemaValidation: boolean | undefined, _meta: Record | undefined, callback: ToolCallback ): RegisteredTool { const registeredTool: RegisteredTool = { title, description, - inputSchema: inputSchema === undefined ? undefined : strict === true ? z.object(inputSchema).strict() : z.object(inputSchema), + inputSchema: inputSchema === undefined ? undefined : strictInputSchemaValidation === true ? z.object(inputSchema).strict() : z.object(inputSchema), outputSchema: outputSchema === undefined ? undefined : z.object(outputSchema), annotations, _meta, @@ -795,7 +795,7 @@ export class McpServer { inputSchema?: InputArgs; outputSchema?: OutputArgs; annotations?: ToolAnnotations; - strict?: boolean; + strictInputSchemaValidation?: boolean; _meta?: Record; }, cb: ToolCallback @@ -804,7 +804,7 @@ export class McpServer { throw new Error(`Tool ${name} is already registered`); } - const { title, description, inputSchema, outputSchema, annotations, strict, _meta } = config; + const { title, description, inputSchema, outputSchema, annotations, strictInputSchemaValidation, _meta } = config; return this._createRegisteredTool( name, @@ -813,7 +813,7 @@ export class McpServer { inputSchema, outputSchema, annotations, - strict, + strictInputSchemaValidation, _meta, cb as ToolCallback ); From 4955cc517a4985e80928eec437189a908ba150a6 Mon Sep 17 00:00:00 2001 From: Adam Bloomston Date: Tue, 7 Oct 2025 12:04:03 -0600 Subject: [PATCH 06/10] test: fix failing test to properly validate lenient behavior MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Changed test from expecting failure to properly testing that lenient validation (default behavior) accepts unknown parameters for backwards compatibility. Renamed test from "should fail..." to "should accept unknown parameters when strict validation is disabled (default)" to clarify it's testing the expected lenient behavior. This ensures all tests pass while documenting both validation modes: - Lenient (default): accepts unknown parameters - Strict: rejects unknown parameters Changes linted and tested - all 756 tests now pass. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- src/server/mcp.test.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/server/mcp.test.ts b/src/server/mcp.test.ts index d048237e8..f4aa6a94c 100644 --- a/src/server/mcp.test.ts +++ b/src/server/mcp.test.ts @@ -4086,7 +4086,7 @@ describe('elicitInput()', () => { ]); }); - test('should fail: tool with incorrect parameter capitalization is not rejected', async () => { + test('should accept unknown parameters when strict validation is disabled (default)', async () => { const mcpServer = new McpServer({ name: 'test server', version: '1.0' @@ -4098,7 +4098,7 @@ describe('elicitInput()', () => { }); mcpServer.registerTool( - 'test-strict', + 'test-lenient', { inputSchema: { userName: z.string().optional(), itemCount: z.number().optional() } }, @@ -4115,15 +4115,15 @@ describe('elicitInput()', () => { { method: 'tools/call', params: { - name: 'test-strict', + name: 'test-lenient', arguments: { username: 'test', itemcount: 42 } } }, CallToolResultSchema ); - expect(result.isError).toBe(true); - expect(result.content[0].text).toContain('Invalid arguments'); + expect(result.isError).toBeUndefined(); + expect(result.content[0].text).toBe('none: 0'); }); test('should reject unknown parameters when strict validation is enabled', async () => { From 8119632fcea4cc1fd25f1e01c758b0505e1fd156 Mon Sep 17 00:00:00 2001 From: Adam Bloomston Date: Tue, 7 Oct 2025 12:08:11 -0600 Subject: [PATCH 07/10] style: apply prettier formatting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Applied prettier auto-formatting to README.md and src/server/mcp.ts to fix code style issues. Changes linted and tested. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- README.md | 27 ++++++++++++++++++--------- src/server/mcp.ts | 7 ++++++- 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index b0049840c..c8cda28c7 100644 --- a/README.md +++ b/README.md @@ -1116,21 +1116,30 @@ Control how tools handle parameter validation errors and unexpected inputs: ```typescript // Strict validation - catches typos and unexpected parameters immediately -const devTool = server.registerTool("dev-tool", { - inputSchema: { userName: z.string(), itemCount: z.number() }, - strictInputSchemaValidation: true // Reject { username: "test", itemcount: 42 } -}, handler); +const devTool = server.registerTool( + 'dev-tool', + { + inputSchema: { userName: z.string(), itemCount: z.number() }, + strictInputSchemaValidation: true // Reject { username: "test", itemcount: 42 } + }, + handler +); // Lenient validation (default) - maintains backwards compatibility with existing clients -const prodTool = server.registerTool("prod-tool", { - inputSchema: { userName: z.string().optional(), itemCount: z.number().optional() }, - strictInputSchemaValidation: false // Accept extra parameters for backwards compatibility with clients that may send additional fields -}, handler); +const prodTool = server.registerTool( + 'prod-tool', + { + inputSchema: { userName: z.string().optional(), itemCount: z.number().optional() }, + strictInputSchemaValidation: false // Accept extra parameters for backwards compatibility with clients that may send additional fields + }, + handler +); ``` **When to use strict validation:** + - Development and testing: Catch parameter name typos early -- Production APIs: Ensure clients send only expected parameters +- Production APIs: Ensure clients send only expected parameters - Security-sensitive tools: Prevent injection of unexpected data **Note:** The `strictInputSchemaValidation` parameter is only available in `registerTool()`. The legacy `tool()` method uses lenient validation for backward compatibility. diff --git a/src/server/mcp.ts b/src/server/mcp.ts index 93c0f5219..77eb76131 100644 --- a/src/server/mcp.ts +++ b/src/server/mcp.ts @@ -654,7 +654,12 @@ export class McpServer { const registeredTool: RegisteredTool = { title, description, - inputSchema: inputSchema === undefined ? undefined : strictInputSchemaValidation === true ? z.object(inputSchema).strict() : z.object(inputSchema), + inputSchema: + inputSchema === undefined + ? undefined + : strictInputSchemaValidation === true + ? z.object(inputSchema).strict() + : z.object(inputSchema), outputSchema: outputSchema === undefined ? undefined : z.object(outputSchema), annotations, _meta, From fd01e58aab372ba2ec5f5e94b2aefbf6df48731e Mon Sep 17 00:00:00 2001 From: Adam Bloomston Date: Tue, 7 Oct 2025 12:09:55 -0600 Subject: [PATCH 08/10] docs: rename prod-tool example to lenient-tool for clarity MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Renamed example from prod-tool to lenient-tool to better reflect that lenient validation is about backwards compatibility rather than being specifically for production environments. Changes linted and tested. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c8cda28c7..f6d6761b6 100644 --- a/README.md +++ b/README.md @@ -1126,8 +1126,8 @@ const devTool = server.registerTool( ); // Lenient validation (default) - maintains backwards compatibility with existing clients -const prodTool = server.registerTool( - 'prod-tool', +const lenientTool = server.registerTool( + 'lenient-tool', { inputSchema: { userName: z.string().optional(), itemCount: z.number().optional() }, strictInputSchemaValidation: false // Accept extra parameters for backwards compatibility with clients that may send additional fields From 65de5c650062074a2f6c29dde59d407c6f00439a Mon Sep 17 00:00:00 2001 From: Adam Bloomston Date: Thu, 30 Oct 2025 10:48:44 -0600 Subject: [PATCH 09/10] fix: make strict validation errors throw instead of returning error responses MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When strictInputSchemaValidation is enabled, validation errors now properly throw (reject the request) instead of being caught and returned as error responses with isError: true. Changes: - Add strictInputSchemaValidation field to RegisteredTool type - Store strictInputSchemaValidation flag when creating tools - Modify error handling to rethrow validation errors when strict mode enabled This ensures tools with strict validation fail fast on invalid input, maintaining backwards compatibility for tools without strict validation where errors are returned as error responses. Changes linted and tested. 🤖 Created with Claude Code via Airchat --- src/server/mcp.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/server/mcp.ts b/src/server/mcp.ts index 42a101c3b..3c9b6a185 100644 --- a/src/server/mcp.ts +++ b/src/server/mcp.ts @@ -174,6 +174,15 @@ export class McpServer { } } } catch (error) { + if ( + tool && + tool.strictInputSchemaValidation && + error instanceof McpError && + error.code === ErrorCode.InvalidParams && + error.message.includes('Input validation error') + ) { + throw error; + } return this.createToolError(error instanceof Error ? error.message : String(error)); } @@ -665,6 +674,7 @@ export class McpServer { outputSchema: outputSchema === undefined ? undefined : z.object(outputSchema), annotations, _meta, + strictInputSchemaValidation, callback, enabled: true, disable: () => registeredTool.update({ enabled: false }), @@ -1035,6 +1045,7 @@ export type RegisteredTool = { outputSchema?: AnyZodObject; annotations?: ToolAnnotations; _meta?: Record; + strictInputSchemaValidation?: boolean; callback: ToolCallback; enabled: boolean; enable(): void; From 880ce4f269e2bc64041b82661890a0eda17f75b7 Mon Sep 17 00:00:00 2001 From: Adam Bloomston Date: Thu, 30 Oct 2025 11:06:02 -0600 Subject: [PATCH 10/10] fix: align strict validation error handling with PR #1044 pattern MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change strict validation errors to return CallToolResult with isError: true instead of throwing protocol-level errors, aligning with the pattern established in PR #1044 (commit 7387c44) where all validation errors return error responses rather than throwing. This ensures consistent error handling across all validation scenarios and enables LLMs to see validation errors and attempt self-correction, as specified in the MCP protocol specification. Changes: - Remove conditional throw for strict validation input errors - Update test expectations to check for error responses instead of thrown errors - Keep strictInputSchemaValidation field in RegisteredTool for future use The strictInputSchemaValidation field is retained as it still serves to apply .strict() to the Zod schema, rejecting unknown parameters. The difference is now the rejection returns an error response visible to LLMs rather than a protocol-level error. Changes linted and tested. 🤖 Created with Claude Code via Airchat --- src/server/mcp.test.ts | 31 +++++++++++++++++++------------ src/server/mcp.ts | 9 --------- 2 files changed, 19 insertions(+), 21 deletions(-) diff --git a/src/server/mcp.test.ts b/src/server/mcp.test.ts index 962e34f9d..21f54fdbf 100644 --- a/src/server/mcp.test.ts +++ b/src/server/mcp.test.ts @@ -4228,17 +4228,24 @@ describe('elicitInput()', () => { await Promise.all([client.connect(clientTransport), mcpServer.server.connect(serverTransport)]); - await expect( - client.request( - { - method: 'tools/call', - params: { - name: 'test-strict', - arguments: { username: 'test', itemcount: 42 } - } - }, - CallToolResultSchema - ) - ).rejects.toThrow('Invalid arguments'); + const result = await client.request( + { + method: 'tools/call', + params: { + name: 'test-strict', + arguments: { username: 'test', itemcount: 42 } + } + }, + CallToolResultSchema + ); + + expect(result.isError).toBe(true); + expect(result.content).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + text: expect.stringContaining('Input validation error') + }) + ]) + ); }); }); diff --git a/src/server/mcp.ts b/src/server/mcp.ts index 3c9b6a185..fc1b1a644 100644 --- a/src/server/mcp.ts +++ b/src/server/mcp.ts @@ -174,15 +174,6 @@ export class McpServer { } } } catch (error) { - if ( - tool && - tool.strictInputSchemaValidation && - error instanceof McpError && - error.code === ErrorCode.InvalidParams && - error.message.includes('Input validation error') - ) { - throw error; - } return this.createToolError(error instanceof Error ? error.message : String(error)); }