diff --git a/src/__tests__/__snapshots__/server.test.ts.snap b/src/__tests__/__snapshots__/server.test.ts.snap index b05a086..2f37de2 100644 --- a/src/__tests__/__snapshots__/server.test.ts.snap +++ b/src/__tests__/__snapshots__/server.test.ts.snap @@ -389,9 +389,6 @@ exports[`runServer should attempt to run server, register a tool: diagnostics 1` [ "Built-in tool at index 0 is missing the static name property, "toolName"", ], - [ - "Tool "loremIpsum" has a non Zod inputSchema. This may cause unexpected issues.", - ], ], "hasDebugLogs": true, "mcpServer": [ @@ -479,12 +476,6 @@ exports[`runServer should attempt to run server, register multiple tools: diagno [ "Built-in tool at index 1 is missing the static name property, "toolName"", ], - [ - "Tool "loremIpsum" has a non Zod inputSchema. This may cause unexpected issues.", - ], - [ - "Tool "dolorSit" has a non Zod inputSchema. This may cause unexpected issues.", - ], ], "hasDebugLogs": true, "mcpServer": [ diff --git a/src/__tests__/options.context.test.ts b/src/__tests__/options.context.test.ts index 5327f9c..9ce4dee 100644 --- a/src/__tests__/options.context.test.ts +++ b/src/__tests__/options.context.test.ts @@ -1,3 +1,4 @@ +import { z } from 'zod'; import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { runServer, type McpTool } from '../server'; @@ -122,7 +123,7 @@ describe('tool creator options context', () => { return [ 'alsContract', - { description: 'Context test tool', inputSchema: {} }, + { description: 'Context test tool', inputSchema: z.object({}) }, callback ]; }; diff --git a/src/__tests__/server.test.ts b/src/__tests__/server.test.ts index d34ce9d..b2a0195 100644 --- a/src/__tests__/server.test.ts +++ b/src/__tests__/server.test.ts @@ -1,3 +1,4 @@ +import { z } from 'zod'; import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; import { runServer } from '../server'; @@ -112,7 +113,7 @@ describe('runServer', () => { tools: [ jest.fn().mockReturnValue([ 'loremIpsum', - { description: 'Lorem Ipsum', inputSchema: {} }, + { description: 'Lorem Ipsum', inputSchema: z.object({}) }, jest.fn() ]) ], @@ -124,12 +125,12 @@ describe('runServer', () => { tools: [ jest.fn().mockReturnValue([ 'loremIpsum', - { description: 'Lorem Ipsum', inputSchema: {} }, + { description: 'Lorem Ipsum', inputSchema: z.object({}) }, jest.fn() ]), jest.fn().mockReturnValue([ 'dolorSit', - { description: 'Dolor Sit', inputSchema: {} }, + { description: 'Dolor Sit', inputSchema: z.object({}) }, jest.fn() ]) ], diff --git a/src/server.ts b/src/server.ts index ea058fe..1c5a306 100644 --- a/src/server.ts +++ b/src/server.ts @@ -386,49 +386,55 @@ const runServer = async (options: ServerOptions = getOptions(), { const isZod = isZodSchema(schema?.inputSchema) || isZodRawShape(schema?.inputSchema); const isSchemaDefined = schema?.inputSchema !== undefined; - log.info(`Registered tool: ${name}`); - if (!isZod) { - log.warn(`Tool "${name}" has a non Zod inputSchema. This may cause unexpected issues.`); + log.warn(`Tool "${name}" has a non Zod inputSchema. Skipping registration.`); log.debug( `Tool "${name}" has received a non Zod inputSchema from the tool pipeline.`, `This will cause unexpected issues, such as failure to pass arguments.`, `MCP SDK requires Zod. Kneel before Zod.` ); + + return; } // Lightweight check for malformed schemas that bypass validation. const isContextLike = (value: unknown) => isPlainObject(value) && 'requestId' in value && 'signal' in value; - server?.registerTool(name, schema, (args: unknown = {}, ..._args: unknown[]) => - runWithSession(session, async () => - runWithOptions(options, async () => { - // Basic track for remaining args to account for future MCP SDK alterations. - log.debug( - `Running tool "${name}"`, - `isArgs = ${args !== undefined}`, - `isRemainingArgs = ${_args?.length > 0}` - ); - - const timedReport = stat.traffic(); - const isContextLikeArgs = isContextLike(args); - - // Log potential Zod validation errors triggered by context fail. - if (isContextLikeArgs) { + try { + server?.registerTool(name, schema, (args: unknown = {}, ..._args: unknown[]) => + runWithSession(session, async () => + runWithOptions(options, async () => { + // Basic track for remaining args to account for future MCP SDK alterations. log.debug( - `Tool "${name}" handler received a context like object as the first parameter.`, - 'If this is unexpected this is likely an undefined schema or a schema not registering as Zod.', - 'Review the related schema definition and ensure it is defined and valid.', - `Schema is Defined = ${isSchemaDefined}; Schema is Zod = ${isZod}; Context like = ${isContextLikeArgs};` + `Running tool "${name}"`, + `isArgs = ${args !== undefined}`, + `isRemainingArgs = ${_args?.length > 0}` ); - } - const toolResult = await callback(args); + const timedReport = stat.traffic(); + const isContextLikeArgs = isContextLike(args); + + // Log potential Zod validation errors triggered by context fail. + if (isContextLikeArgs) { + log.debug( + `Tool "${name}" handler received a context like object as the first parameter.`, + 'If this is unexpected this is likely an undefined schema or a schema not registering as Zod.', + 'Review the related schema definition and ensure it is defined and valid.', + `Schema is Defined = ${isSchemaDefined}; Schema is Zod = ${isZod}; Context like = ${isContextLikeArgs};` + ); + } + + const toolResult = await callback(args); - timedReport({ tool: name }); + timedReport({ tool: name }); - return toolResult; - }))); + return toolResult; + }))); + + log.info(`Registered tool: ${name}`); + } catch (error) { + log.error(`Failed to register tool "${name}":`, error); + } }); if (enableSigint && !sigintHandler) {