Skip to content

Commit fc9ca5f

Browse files
committed
fix: update ToolCallback type to include output arguments for better type safety
1 parent 744b9ea commit fc9ca5f

File tree

2 files changed

+45
-26
lines changed

2 files changed

+45
-26
lines changed

src/server/mcp.test.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1248,8 +1248,7 @@ describe("tool()", () => {
12481248
processedInput: input,
12491249
resultType: "structured",
12501250
// Missing required 'timestamp' field
1251-
someExtraField: "unexpected" // Extra field not in schema
1252-
},
1251+
} as unknown as { processedInput: string; resultType: string; timestamp: string }, // Type assertion to bypass TypeScript validation for testing purposes
12531252
})
12541253
);
12551254

src/server/mcp.ts

Lines changed: 44 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ export class McpServer {
169169
}
170170

171171
const args = parseResult.data;
172-
const cb = tool.callback as ToolCallback<ZodRawShape>;
172+
const cb = tool.callback as ToolCallback<ZodRawShape, ZodRawShape>;
173173
try {
174174
result = await Promise.resolve(cb(args, extra));
175175
} catch (error) {
@@ -184,7 +184,7 @@ export class McpServer {
184184
};
185185
}
186186
} else {
187-
const cb = tool.callback as ToolCallback<undefined>;
187+
const cb = tool.callback as ToolCallback<undefined, ZodRawShape>;
188188
try {
189189
result = await Promise.resolve(cb(extra));
190190
} catch (error) {
@@ -760,7 +760,7 @@ export class McpServer {
760760
inputSchema: ZodRawShape | undefined,
761761
outputSchema: ZodRawShape | undefined,
762762
annotations: ToolAnnotations | undefined,
763-
callback: ToolCallback<ZodRawShape | undefined>
763+
callback: ToolCallback<ZodRawShape | undefined, ZodRawShape | undefined>
764764
): RegisteredTool {
765765
const registeredTool: RegisteredTool = {
766766
title,
@@ -917,7 +917,7 @@ export class McpServer {
917917
outputSchema?: OutputArgs;
918918
annotations?: ToolAnnotations;
919919
},
920-
cb: ToolCallback<InputArgs>
920+
cb: ToolCallback<InputArgs, OutputArgs>
921921
): RegisteredTool {
922922
if (this._registeredTools[name]) {
923923
throw new Error(`Tool ${name} is already registered`);
@@ -932,7 +932,7 @@ export class McpServer {
932932
inputSchema,
933933
outputSchema,
934934
annotations,
935-
cb as ToolCallback<ZodRawShape | undefined>
935+
cb as ToolCallback<ZodRawShape | undefined, ZodRawShape | undefined>
936936
);
937937
}
938938

@@ -1126,6 +1126,16 @@ export class ResourceTemplate {
11261126
}
11271127
}
11281128

1129+
/**
1130+
* Type helper to create a strongly-typed CallToolResult with structuredContent
1131+
*/
1132+
type TypedCallToolResult<OutputArgs extends undefined | ZodRawShape> =
1133+
OutputArgs extends ZodRawShape
1134+
? CallToolResult & {
1135+
structuredContent?: z.objectOutputType<OutputArgs, ZodTypeAny>;
1136+
}
1137+
: CallToolResult;
1138+
11291139
/**
11301140
* Callback for a tool handler registered with Server.tool().
11311141
*
@@ -1136,36 +1146,46 @@ export class ResourceTemplate {
11361146
* - `content` if the tool does not have an outputSchema
11371147
* - Both fields are optional but typically one should be provided
11381148
*/
1139-
export type ToolCallback<Args extends undefined | ZodRawShape = undefined> =
1140-
Args extends ZodRawShape
1149+
export type ToolCallback<
1150+
InputArgs extends undefined | ZodRawShape = undefined,
1151+
OutputArgs extends undefined | ZodRawShape = undefined
1152+
> = InputArgs extends ZodRawShape
11411153
? (
1142-
args: z.objectOutputType<Args, ZodTypeAny>,
1143-
extra: RequestHandlerExtra<ServerRequest, ServerNotification>,
1144-
) => CallToolResult | Promise<CallToolResult>
1145-
: (extra: RequestHandlerExtra<ServerRequest, ServerNotification>) => CallToolResult | Promise<CallToolResult>;
1154+
args: z.objectOutputType<InputArgs, ZodTypeAny>,
1155+
extra: RequestHandlerExtra<ServerRequest, ServerNotification>
1156+
) =>
1157+
| TypedCallToolResult<OutputArgs>
1158+
| Promise<TypedCallToolResult<OutputArgs>>
1159+
: (
1160+
extra: RequestHandlerExtra<ServerRequest, ServerNotification>
1161+
) =>
1162+
| TypedCallToolResult<OutputArgs>
1163+
| Promise<TypedCallToolResult<OutputArgs>>;
11461164

11471165
export type RegisteredTool = {
11481166
title?: string;
11491167
description?: string;
11501168
inputSchema?: AnyZodObject;
11511169
outputSchema?: AnyZodObject;
11521170
annotations?: ToolAnnotations;
1153-
callback: ToolCallback<undefined | ZodRawShape>;
1171+
callback: ToolCallback<ZodRawShape | undefined, ZodRawShape | undefined>;
11541172
enabled: boolean;
11551173
enable(): void;
11561174
disable(): void;
1157-
update<InputArgs extends ZodRawShape, OutputArgs extends ZodRawShape>(
1158-
updates: {
1159-
name?: string | null,
1160-
title?: string,
1161-
description?: string,
1162-
paramsSchema?: InputArgs,
1163-
outputSchema?: OutputArgs,
1164-
annotations?: ToolAnnotations,
1165-
callback?: ToolCallback<InputArgs>,
1166-
enabled?: boolean
1167-
}): void
1168-
remove(): void
1175+
update<
1176+
InputArgs extends ZodRawShape,
1177+
OutputArgs extends ZodRawShape
1178+
>(updates: {
1179+
name?: string | null;
1180+
title?: string;
1181+
description?: string;
1182+
paramsSchema?: InputArgs;
1183+
outputSchema?: OutputArgs;
1184+
annotations?: ToolAnnotations;
1185+
callback?: ToolCallback<InputArgs, OutputArgs>
1186+
enabled?: boolean
1187+
}): void;
1188+
remove(): void;
11691189
};
11701190

11711191
const EMPTY_OBJECT_JSON_SCHEMA = {

0 commit comments

Comments
 (0)