Skip to content

Commit f02ff1a

Browse files
committed
outputSchema fix - return CallToolResult on structuredContent not returned or structuredContent validation error
1 parent 2cc4918 commit f02ff1a

File tree

3 files changed

+47
-36
lines changed

3 files changed

+47
-36
lines changed

package-lock.json

Lines changed: 1 addition & 9 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/server/mcp.test.ts

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -947,7 +947,7 @@ describe('tool()', () => {
947947
expect.arrayContaining([
948948
{
949949
type: 'text',
950-
text: expect.stringContaining('Invalid arguments')
950+
text: expect.stringContaining('Input validation error: Invalid arguments for tool test')
951951
}
952952
])
953953
);
@@ -971,7 +971,7 @@ describe('tool()', () => {
971971
expect.arrayContaining([
972972
{
973973
type: 'text',
974-
text: expect.stringContaining('Invalid arguments')
974+
text: expect.stringContaining('Input validation error: Invalid arguments for tool test (new api)')
975975
}
976976
])
977977
);
@@ -1168,14 +1168,23 @@ describe('tool()', () => {
11681168
await Promise.all([client.connect(clientTransport), mcpServer.server.connect(serverTransport)]);
11691169

11701170
// Call the tool and expect it to throw an error
1171-
await expect(
1172-
client.callTool({
1171+
const result =
1172+
await client.callTool({
11731173
name: 'test',
11741174
arguments: {
11751175
input: 'hello'
11761176
}
1177-
})
1178-
).rejects.toThrow(/Tool test has an output schema but no structured content was provided/);
1177+
});
1178+
1179+
expect(result.isError).toBe(true);
1180+
expect(result.content).toEqual(
1181+
expect.arrayContaining([
1182+
{
1183+
type: 'text',
1184+
text: expect.stringContaining('Output validation error: Tool test has an output schema but no structured content was provided')
1185+
}
1186+
])
1187+
);
11791188
});
11801189
/***
11811190
* Test: Tool with Output Schema Must Provide Structured Content
@@ -1290,14 +1299,22 @@ describe('tool()', () => {
12901299
await Promise.all([client.connect(clientTransport), mcpServer.server.connect(serverTransport)]);
12911300

12921301
// Call the tool and expect it to throw a server-side validation error
1293-
await expect(
1294-
client.callTool({
1302+
const result = await client.callTool({
12951303
name: 'test',
12961304
arguments: {
12971305
input: 'hello'
12981306
}
1299-
})
1300-
).rejects.toThrow(/Invalid structured content for tool test/);
1307+
});
1308+
1309+
expect(result.isError).toBe(true);
1310+
expect(result.content).toEqual(
1311+
expect.arrayContaining([
1312+
{
1313+
type: 'text',
1314+
text: expect.stringContaining('Output validation error: Invalid structured content for tool test')
1315+
}
1316+
])
1317+
);
13011318
});
13021319

13031320
/***

src/server/mcp.ts

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ export class McpServer {
144144
if (!parseResult.success) {
145145
throw new McpError(
146146
ErrorCode.InvalidParams,
147-
`Invalid arguments for tool ${request.params.name}: ${parseResult.error.message}`
147+
`Input validation error: Invalid arguments for tool ${request.params.name}: ${parseResult.error.message}`
148148
);
149149
}
150150

@@ -155,27 +155,29 @@ export class McpServer {
155155
const cb = tool.callback as ToolCallback<undefined>;
156156
result = await Promise.resolve(cb(extra));
157157
}
158+
159+
if (tool.outputSchema && !result.isError) {
160+
if (!result.structuredContent) {
161+
throw new McpError(
162+
ErrorCode.InvalidParams,
163+
`Output validation error: Tool ${request.params.name} has an output schema but no structured content was provided`
164+
);
165+
}
166+
167+
// if the tool has an output schema, validate structured content
168+
const parseResult = await tool.outputSchema.safeParseAsync(result.structuredContent);
169+
if (!parseResult.success) {
170+
throw new McpError(
171+
ErrorCode.InvalidParams,
172+
`Output validation error: Invalid structured content for tool ${request.params.name}: ${parseResult.error.message}`
173+
);
174+
}
175+
}
158176
} catch (error) {
159177
return this.createToolError(error instanceof Error ? error.message : String(error));
160178
}
161179

162-
if (tool.outputSchema && !result.isError) {
163-
if (!result.structuredContent) {
164-
throw new McpError(
165-
ErrorCode.InvalidParams,
166-
`Tool ${request.params.name} has an output schema but no structured content was provided`
167-
);
168-
}
169180

170-
// if the tool has an output schema, validate structured content
171-
const parseResult = await tool.outputSchema.safeParseAsync(result.structuredContent);
172-
if (!parseResult.success) {
173-
throw new McpError(
174-
ErrorCode.InvalidParams,
175-
`Invalid structured content for tool ${request.params.name}: ${parseResult.error.message}`
176-
);
177-
}
178-
}
179181

180182
return result;
181183
});

0 commit comments

Comments
 (0)