fix(server): handle undefined arguments for tools with all optional params (#1404)
Co-authored-by: Felix Weinberger <3823880+felixweinberger@users.noreply.github.com> Co-authored-by: Felix Weinberger <fweinberger@anthropic.com>
This commit is contained in:
parent
dcf708d892
commit
b2326afea6
2 changed files with 71 additions and 1 deletions
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,70 @@
|
|||
/**
|
||||
* Regression test for https://github.com/modelcontextprotocol/typescript-sdk/issues/400
|
||||
*
|
||||
* When a tool has all optional parameters, some LLM models call the tool without
|
||||
* providing an `arguments` field. This test verifies that undefined arguments are
|
||||
* handled correctly by defaulting to an empty object.
|
||||
*/
|
||||
|
||||
import { Client } from '@modelcontextprotocol/client';
|
||||
import { CallToolResultSchema, InMemoryTransport } from '@modelcontextprotocol/core';
|
||||
import { McpServer } from '@modelcontextprotocol/server';
|
||||
import type { ZodMatrixEntry } from '@modelcontextprotocol/test-helpers';
|
||||
import { zodTestMatrix } from '@modelcontextprotocol/test-helpers';
|
||||
|
||||
describe.each(zodTestMatrix)('Issue #400: $zodVersionLabel', (entry: ZodMatrixEntry) => {
|
||||
const { z } = entry;
|
||||
|
||||
test('should accept undefined arguments when all tool params are optional', async () => {
|
||||
const mcpServer = new McpServer({
|
||||
name: 'test server',
|
||||
version: '1.0'
|
||||
});
|
||||
const client = new Client({
|
||||
name: 'test client',
|
||||
version: '1.0'
|
||||
});
|
||||
|
||||
mcpServer.registerTool(
|
||||
'optional-params-tool',
|
||||
{
|
||||
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)]);
|
||||
|
||||
// Call tool without arguments (arguments is undefined)
|
||||
const result = await client.request(
|
||||
{
|
||||
method: 'tools/call',
|
||||
params: {
|
||||
name: 'optional-params-tool'
|
||||
// arguments is intentionally omitted (undefined)
|
||||
}
|
||||
},
|
||||
CallToolResultSchema
|
||||
);
|
||||
|
||||
expect(result.isError).toBeUndefined();
|
||||
expect(result.content).toEqual([
|
||||
{
|
||||
type: 'text',
|
||||
text: 'limit: default, offset: default'
|
||||
}
|
||||
]);
|
||||
});
|
||||
});
|
||||
Loading…
Reference in a new issue