diff --git a/src/client/index.test.ts b/src/client/index.test.ts index bbfa80faf..d555aa4d9 100644 --- a/src/client/index.test.ts +++ b/src/client/index.test.ts @@ -843,6 +843,7 @@ describe('outputSchema validation', () => { if (request.params.name === 'test-tool') { return { structuredContent: { result: 'success', count: 42 }, + content: [], // compatibility: remove in 1.12+ }; } throw new Error('Unknown tool'); @@ -918,6 +919,7 @@ describe('outputSchema validation', () => { // Return invalid structured content (count is string instead of number) return { structuredContent: { result: 'success', count: 'not a number' }, + content: [], // compatibility: remove in 1.12+ }; } throw new Error('Unknown tool'); @@ -1155,6 +1157,7 @@ describe('outputSchema validation', () => { created: '2023-01-01T00:00:00Z', }, }, + content: [], // compatibility: remove in 1.12+ }; } throw new Error('Unknown tool'); @@ -1235,6 +1238,7 @@ describe('outputSchema validation', () => { name: 'John', extraField: 'not allowed', }, + content: [], // compatibility: remove in 1.12+ }; } throw new Error('Unknown tool'); diff --git a/src/server/mcp.ts b/src/server/mcp.ts index a5f2d0f1a..4f506c003 100644 --- a/src/server/mcp.ts +++ b/src/server/mcp.ts @@ -40,12 +40,36 @@ import { ServerRequest, ServerNotification, ToolAnnotations, + ResultSchema, + ContentListSchema, + CallToolUnstructuredResultSchema, + Infer, } from "../types.js"; import { Completable, CompletableDef } from "./completable.js"; import { UriTemplate, Variables } from "../shared/uriTemplate.js"; import { RequestHandlerExtra } from "../shared/protocol.js"; import { Transport } from "../shared/transport.js"; +/** + * In SDK versions 1.12 and forward, the `content` array will be optional for structured + * tool call results. For convenience we use this type for defining tool callbacks on the + * server side, but provide a backward-compatible schema for the client in 1.11. + */ +const CallToolStructuredResultSchema_forward = ResultSchema.extend({ + structuredContent: z.object({}).passthrough(), + content: z.optional(ContentListSchema), + isError: z.optional(z.boolean()), +}); + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +const CallToolResultSchema_forward = z.union([ + CallToolUnstructuredResultSchema, + CallToolStructuredResultSchema_forward, +]); + +type CallToolResult_forward = Infer; + + /** * High-level MCP server that provides a simpler API for working with resources, tools, and prompts. * For advanced usage (like sending notifications or setting custom request handlers), use the underlying @@ -153,7 +177,7 @@ export class McpServer { ); } - let result: CallToolResult; + let result: CallToolResult_forward; if (tool.inputSchema) { const parseResult = await tool.inputSchema.safeParseAsync( @@ -236,7 +260,7 @@ export class McpServer { } } - return result; + return result as CallToolResult; }, ); @@ -1009,8 +1033,8 @@ export type ToolCallback = ? ( args: z.objectOutputType, extra: RequestHandlerExtra, - ) => CallToolResult | Promise - : (extra: RequestHandlerExtra) => CallToolResult | Promise; + ) => CallToolResult_forward | Promise + : (extra: RequestHandlerExtra) => CallToolResult_forward | Promise; export type RegisteredTool = { description?: string; diff --git a/src/types.ts b/src/types.ts index bd299c8f7..f118e1ed4 100644 --- a/src/types.ts +++ b/src/types.ts @@ -924,14 +924,14 @@ export const CallToolStructuredResultSchema = ResultSchema.extend({ /** * A list of content objects that represent the result of the tool call. * - * If the Tool defines an outputSchema, this field MAY be present in the result. + * Per the spec, if the Tool defines an outputSchema, this field MAY be present in the result. * - * Tools may use this field to provide compatibility with older clients that - * do not support structured content. + * In this SDK we automatically generate backwards-compatible `content` for older clients, + * so we define this field as non-optional for backwards compatibility in SDK versions 1.11.*. * * Clients that support structured content should ignore this field. */ - content: z.optional(ContentListSchema), + content: ContentListSchema, /** * Whether the tool call ended in an error. @@ -1319,7 +1319,7 @@ type Flatten = T extends Primitive ? { [K in keyof T]: Flatten } : T; -type Infer = Flatten>; +export type Infer = Flatten>; /* JSON-RPC types */ export type ProgressToken = Infer;