From 52dc7f116080a143ff65a559a4f10a3420ec4d57 Mon Sep 17 00:00:00 2001 From: yuna0x0 Date: Tue, 6 May 2025 01:49:33 +0800 Subject: [PATCH] Fix isZodRawShape return false on empty object and add test --- src/server/mcp.test.ts | 47 ++++++++++++++++++++++++++++++++++++++++++ src/server/mcp.ts | 7 +++++-- 2 files changed, 52 insertions(+), 2 deletions(-) diff --git a/src/server/mcp.test.ts b/src/server/mcp.test.ts index c9be5c76..5f50df68 100644 --- a/src/server/mcp.test.ts +++ b/src/server/mcp.test.ts @@ -622,6 +622,53 @@ describe("tool()", () => { }); }); + test("should register tool with description, empty params, and annotations", async () => { + const mcpServer = new McpServer({ + name: "test server", + version: "1.0", + }); + const client = new Client({ + name: "test client", + version: "1.0", + }); + + mcpServer.tool( + "test", + "A tool with everything but empty params", + {}, + { title: "Complete Test Tool with empty params", readOnlyHint: true, openWorldHint: false }, + async () => ({ + content: [{ type: "text", text: "Test response" }] + }) + ); + + const [clientTransport, serverTransport] = + InMemoryTransport.createLinkedPair(); + + await Promise.all([ + client.connect(clientTransport), + mcpServer.server.connect(serverTransport), + ]); + + const result = await client.request( + { method: "tools/list" }, + ListToolsResultSchema, + ); + + expect(result.tools).toHaveLength(1); + expect(result.tools[0].name).toBe("test"); + expect(result.tools[0].description).toBe("A tool with everything but empty params"); + expect(result.tools[0].inputSchema).toMatchObject({ + type: "object", + properties: {} + }); + expect(result.tools[0].annotations).toEqual({ + title: "Complete Test Tool with empty params", + readOnlyHint: true, + openWorldHint: false + }); + }); + test("should validate tool args", async () => { const mcpServer = new McpServer({ name: "test server", diff --git a/src/server/mcp.ts b/src/server/mcp.ts index 6204eb2a..dccfa04e 100644 --- a/src/server/mcp.ts +++ b/src/server/mcp.ts @@ -663,8 +663,11 @@ export class McpServer { // Helper to check if an object is a Zod schema (ZodRawShape) const isZodRawShape = (obj: unknown): obj is ZodRawShape => { if (typeof obj !== "object" || obj === null) return false; - // Check that at least one property is a ZodType instance - return Object.values(obj as object).some(v => v instanceof ZodType); + + const isEmptyObject = z.object({}).strict().safeParse(obj).success; + + // Check if object is empty or at least one property is a ZodType instance + return isEmptyObject || Object.values(obj as object).some(v => v instanceof ZodType); }; let description: string | undefined;