From 505f10e723adb0914e634417c1b908dd3a5bd88a Mon Sep 17 00:00:00 2001 From: Raashish Aggarwal <94279692+raashish1601@users.noreply.github.com> Date: Mon, 11 May 2026 20:48:10 +0530 Subject: [PATCH] fix optional prompt and tool arguments --- .../fix-optional-arguments-prompts-tools.md | 5 + src/server/mcp.ts | 4 +- test/server/mcp.test.ts | 107 ++++++++++++++++++ 3 files changed, 114 insertions(+), 2 deletions(-) create mode 100644 .changeset/fix-optional-arguments-prompts-tools.md diff --git a/.changeset/fix-optional-arguments-prompts-tools.md b/.changeset/fix-optional-arguments-prompts-tools.md new file mode 100644 index 0000000000..31a54a01b2 --- /dev/null +++ b/.changeset/fix-optional-arguments-prompts-tools.md @@ -0,0 +1,5 @@ +--- +'@modelcontextprotocol/sdk': patch +--- + +Treat omitted `arguments` as an empty object when validating prompts and tools with optional argument schemas. diff --git a/src/server/mcp.ts b/src/server/mcp.ts index 9fe0ed549c..cdebffc757 100644 --- a/src/server/mcp.ts +++ b/src/server/mcp.ts @@ -271,7 +271,7 @@ export class McpServer { // If that fails, use the schema directly (for union/intersection/etc) const inputObj = normalizeObjectSchema(tool.inputSchema); const schemaToParse = inputObj ?? (tool.inputSchema as AnySchema); - const parseResult = await safeParseAsync(schemaToParse, args); + const parseResult = await safeParseAsync(schemaToParse, args ?? {}); if (!parseResult.success) { const error = 'error' in parseResult ? parseResult.error : 'Unknown error'; const errorMessage = getParseErrorMessage(error); @@ -605,7 +605,7 @@ export class McpServer { if (prompt.argsSchema) { const argsObj = normalizeObjectSchema(prompt.argsSchema) as AnyObjectSchema; - const parseResult = await safeParseAsync(argsObj, request.params.arguments); + const parseResult = await safeParseAsync(argsObj, request.params.arguments ?? {}); if (!parseResult.success) { const error = 'error' in parseResult ? parseResult.error : 'Unknown error'; const errorMessage = getParseErrorMessage(error); diff --git a/test/server/mcp.test.ts b/test/server/mcp.test.ts index 575d6a300e..f9e88fc404 100644 --- a/test/server/mcp.test.ts +++ b/test/server/mcp.test.ts @@ -1147,6 +1147,57 @@ describe.each(zodTestMatrix)('$zodVersionLabel', (entry: ZodMatrixEntry) => { ); }); + test('should accept omitted arguments for tools with optional args', async () => { + const mcpServer = new McpServer({ + name: 'test server', + version: '1.0' + }); + const client = new Client({ + name: 'test client', + version: '1.0' + }); + + mcpServer.registerTool( + 'test', + { + inputSchema: { + limit: z.number().optional(), + offset: z.number().optional() + } + }, + async ({ limit, offset }) => ({ + content: [ + { + type: 'text', + text: `limit: ${limit ?? 'default'}, offset: ${offset ?? 'default'}` + } + ] + }) + ); + + 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' + } + }, + CallToolResultSchema + ); + + expect(result.isError).toBeUndefined(); + expect(result.content).toEqual([ + { + type: 'text', + text: 'limit: default, offset: default' + } + ]); + }); + /*** * Test: Preventing Duplicate Tool Registration */ @@ -3577,6 +3628,62 @@ describe.each(zodTestMatrix)('$zodVersionLabel', (entry: ZodMatrixEntry) => { ).rejects.toThrow(/Invalid arguments/); }); + test('should accept omitted arguments for prompts with optional args', async () => { + const mcpServer = new McpServer({ + name: 'test server', + version: '1.0' + }); + + const client = new Client({ + name: 'test client', + version: '1.0' + }); + + mcpServer.registerPrompt( + 'test', + { + argsSchema: { + context: z.string().optional() + } + }, + async ({ context }) => ({ + messages: [ + { + role: 'assistant', + content: { + type: 'text', + text: `context: ${context ?? 'none'}` + } + } + ] + }) + ); + + const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair(); + + await Promise.all([client.connect(clientTransport), mcpServer.server.connect(serverTransport)]); + + const result = await client.request( + { + method: 'prompts/get', + params: { + name: 'test' + } + }, + GetPromptResultSchema + ); + + expect(result.messages).toEqual([ + { + role: 'assistant', + content: { + type: 'text', + text: 'context: none' + } + } + ]); + }); + /*** * Test: Preventing Duplicate Prompt Registration */