Merge pull request #527 from modelcontextprotocol/basil/structured_output_check

add server error if tool declares outputSchema but returns no structuredContent
This commit is contained in:
ihrpr 2025-05-21 21:03:50 +01:00 • committed by GitHub
commit 77a284c1ef
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 72 additions and 1 deletions

View file

@ -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
*/

View file

@ -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,