diff --git a/src/server/mcp.test.ts b/src/server/mcp.test.ts index 37f0c228..49f852d6 100644 --- a/src/server/mcp.test.ts +++ b/src/server/mcp.test.ts @@ -1160,6 +1160,70 @@ describe("tool()", () => { expect(JSON.parse(textContent.text)).toEqual(result.structuredContent); }); + /*** + * Test: Tool with Output Schema Must Provide Structured Content + */ + test("should throw error when tool with outputSchema returns no structuredContent", async () => { + const mcpServer = new McpServer({ + name: "test server", + version: "1.0", + }); + + const client = new Client( + { + name: "test client", + version: "1.0", + }, + { + capabilities: { + tools: {}, + }, + }, + ); + + // Register a tool with outputSchema that returns only content without structuredContent + mcpServer.registerTool( + "test", + { + description: "Test tool with output schema but missing structured content", + inputSchema: { + input: z.string(), + }, + outputSchema: { + processedInput: z.string(), + resultType: z.string(), + }, + }, + async ({ input }) => ({ + // Only return content without structuredContent + content: [ + { + type: "text", + text: `Processed: ${input}`, + }, + ], + }) + ); + + const [clientTransport, serverTransport] = + InMemoryTransport.createLinkedPair(); + + await Promise.all([ + client.connect(clientTransport), + mcpServer.server.connect(serverTransport), + ]); + + // Call the tool and expect it to throw an error + await expect( + client.callTool({ + name: "test", + arguments: { + input: "hello", + }, + }), + ).rejects.toThrow(/Tool test has an output schema but no structured content was provided/); + }); + /*** * Test: Schema Validation Failure for Invalid Structured Content */ diff --git a/src/server/mcp.ts b/src/server/mcp.ts index e68691f4..5b864b8b 100644 --- a/src/server/mcp.ts +++ b/src/server/mcp.ts @@ -198,7 +198,14 @@ export class McpServer { } } - if (tool.outputSchema && result.structuredContent) { + if (tool.outputSchema) { + if (!result.structuredContent) { + throw new McpError( + ErrorCode.InvalidParams, + `Tool ${request.params.name} has an output schema but no structured content was provided`, + ); + } + // if the tool has an output schema, validate structured content const parseResult = await tool.outputSchema.safeParseAsync( result.structuredContent,