Skip to content

Commit 84978a7

Browse files
committed
fix: catch tool registration errors
1 parent d2d65c4 commit 84978a7

4 files changed

Lines changed: 39 additions & 40 deletions

File tree

src/__tests__/__snapshots__/server.test.ts.snap

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -389,9 +389,6 @@ exports[`runServer should attempt to run server, register a tool: diagnostics 1`
389389
[
390390
"Built-in tool at index 0 is missing the static name property, "toolName"",
391391
],
392-
[
393-
"Tool "loremIpsum" has a non Zod inputSchema. This may cause unexpected issues.",
394-
],
395392
],
396393
"hasDebugLogs": true,
397394
"mcpServer": [
@@ -479,12 +476,6 @@ exports[`runServer should attempt to run server, register multiple tools: diagno
479476
[
480477
"Built-in tool at index 1 is missing the static name property, "toolName"",
481478
],
482-
[
483-
"Tool "loremIpsum" has a non Zod inputSchema. This may cause unexpected issues.",
484-
],
485-
[
486-
"Tool "dolorSit" has a non Zod inputSchema. This may cause unexpected issues.",
487-
],
488479
],
489480
"hasDebugLogs": true,
490481
"mcpServer": [

src/__tests__/options.context.test.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { z } from 'zod';
12
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
23
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
34
import { runServer, type McpTool } from '../server';
@@ -122,7 +123,7 @@ describe('tool creator options context', () => {
122123

123124
return [
124125
'alsContract',
125-
{ description: 'Context test tool', inputSchema: {} },
126+
{ description: 'Context test tool', inputSchema: z.object({}) },
126127
callback
127128
];
128129
};

src/__tests__/server.test.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { z } from 'zod';
12
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
23
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
34
import { runServer } from '../server';
@@ -112,7 +113,7 @@ describe('runServer', () => {
112113
tools: [
113114
jest.fn().mockReturnValue([
114115
'loremIpsum',
115-
{ description: 'Lorem Ipsum', inputSchema: {} },
116+
{ description: 'Lorem Ipsum', inputSchema: z.object({}) },
116117
jest.fn()
117118
])
118119
],
@@ -124,12 +125,12 @@ describe('runServer', () => {
124125
tools: [
125126
jest.fn().mockReturnValue([
126127
'loremIpsum',
127-
{ description: 'Lorem Ipsum', inputSchema: {} },
128+
{ description: 'Lorem Ipsum', inputSchema: z.object({}) },
128129
jest.fn()
129130
]),
130131
jest.fn().mockReturnValue([
131132
'dolorSit',
132-
{ description: 'Dolor Sit', inputSchema: {} },
133+
{ description: 'Dolor Sit', inputSchema: z.object({}) },
133134
jest.fn()
134135
])
135136
],

src/server.ts

Lines changed: 33 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -386,49 +386,55 @@ const runServer = async (options: ServerOptions = getOptions(), {
386386
const isZod = isZodSchema(schema?.inputSchema) || isZodRawShape(schema?.inputSchema);
387387
const isSchemaDefined = schema?.inputSchema !== undefined;
388388

389-
log.info(`Registered tool: ${name}`);
390-
391389
if (!isZod) {
392-
log.warn(`Tool "${name}" has a non Zod inputSchema. This may cause unexpected issues.`);
390+
log.warn(`Tool "${name}" has a non Zod inputSchema. Skipping registration.`);
393391
log.debug(
394392
`Tool "${name}" has received a non Zod inputSchema from the tool pipeline.`,
395393
`This will cause unexpected issues, such as failure to pass arguments.`,
396394
`MCP SDK requires Zod. Kneel before Zod.`
397395
);
396+
397+
return;
398398
}
399399

400400
// Lightweight check for malformed schemas that bypass validation.
401401
const isContextLike = (value: unknown) => isPlainObject(value) && 'requestId' in value && 'signal' in value;
402402

403-
server?.registerTool(name, schema, (args: unknown = {}, ..._args: unknown[]) =>
404-
runWithSession(session, async () =>
405-
runWithOptions(options, async () => {
406-
// Basic track for remaining args to account for future MCP SDK alterations.
407-
log.debug(
408-
`Running tool "${name}"`,
409-
`isArgs = ${args !== undefined}`,
410-
`isRemainingArgs = ${_args?.length > 0}`
411-
);
412-
413-
const timedReport = stat.traffic();
414-
const isContextLikeArgs = isContextLike(args);
415-
416-
// Log potential Zod validation errors triggered by context fail.
417-
if (isContextLikeArgs) {
403+
try {
404+
server?.registerTool(name, schema, (args: unknown = {}, ..._args: unknown[]) =>
405+
runWithSession(session, async () =>
406+
runWithOptions(options, async () => {
407+
// Basic track for remaining args to account for future MCP SDK alterations.
418408
log.debug(
419-
`Tool "${name}" handler received a context like object as the first parameter.`,
420-
'If this is unexpected this is likely an undefined schema or a schema not registering as Zod.',
421-
'Review the related schema definition and ensure it is defined and valid.',
422-
`Schema is Defined = ${isSchemaDefined}; Schema is Zod = ${isZod}; Context like = ${isContextLikeArgs};`
409+
`Running tool "${name}"`,
410+
`isArgs = ${args !== undefined}`,
411+
`isRemainingArgs = ${_args?.length > 0}`
423412
);
424-
}
425413

426-
const toolResult = await callback(args);
414+
const timedReport = stat.traffic();
415+
const isContextLikeArgs = isContextLike(args);
416+
417+
// Log potential Zod validation errors triggered by context fail.
418+
if (isContextLikeArgs) {
419+
log.debug(
420+
`Tool "${name}" handler received a context like object as the first parameter.`,
421+
'If this is unexpected this is likely an undefined schema or a schema not registering as Zod.',
422+
'Review the related schema definition and ensure it is defined and valid.',
423+
`Schema is Defined = ${isSchemaDefined}; Schema is Zod = ${isZod}; Context like = ${isContextLikeArgs};`
424+
);
425+
}
426+
427+
const toolResult = await callback(args);
427428

428-
timedReport({ tool: name });
429+
timedReport({ tool: name });
429430

430-
return toolResult;
431-
})));
431+
return toolResult;
432+
})));
433+
434+
log.info(`Registered tool: ${name}`);
435+
} catch (error) {
436+
log.error(`Failed to register tool "${name}":`, error);
437+
}
432438
});
433439

434440
if (enableSigint && !sigintHandler) {

0 commit comments

Comments
 (0)