Skip to content

Commit 1372006

Browse files
Fix: Correctly detect MCP tool errors (#14937)
1 parent d522849 commit 1372006

2 files changed

Lines changed: 45 additions & 0 deletions

File tree

packages/core/src/tools/mcp-tool.test.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,44 @@ describe('DiscoveredMCPTool', () => {
223223
},
224224
);
225225

226+
it('should return a structured error if MCP tool reports a top-level isError (spec compliant)', async () => {
227+
const tool = new DiscoveredMCPTool(
228+
mockCallableToolInstance,
229+
serverName,
230+
serverToolName,
231+
baseDescription,
232+
inputSchema,
233+
);
234+
const params = { param: 'isErrorTopLevelCase' };
235+
const functionCall = {
236+
name: serverToolName,
237+
args: params,
238+
};
239+
240+
// Spec compliant error response: { isError: true } at the top level of content (or response object in this mapping)
241+
const errorResponse = { isError: true };
242+
const mockMcpToolResponseParts: Part[] = [
243+
{
244+
functionResponse: {
245+
name: serverToolName,
246+
response: errorResponse,
247+
},
248+
},
249+
];
250+
mockCallTool.mockResolvedValue(mockMcpToolResponseParts);
251+
const expectedErrorMessage = `MCP tool '${serverToolName}' reported tool error for function call: ${safeJsonStringify(
252+
functionCall,
253+
)} with response: ${safeJsonStringify(mockMcpToolResponseParts)}`;
254+
const invocation = tool.build(params);
255+
const result = await invocation.execute(new AbortController().signal);
256+
257+
expect(result.error?.type).toBe(ToolErrorType.MCP_TOOL_ERROR);
258+
expect(result.llmContent).toBe(expectedErrorMessage);
259+
expect(result.returnDisplay).toContain(
260+
`Error: MCP tool '${serverToolName}' reported an error.`,
261+
);
262+
});
263+
226264
it.each([
227265
{ isErrorValue: false, description: 'false (bool)' },
228266
{ isErrorValue: 'false', description: '"false" (str)' },

packages/core/src/tools/mcp-tool.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,13 @@ class DiscoveredMCPToolInvocation extends BaseToolInvocation<
133133
}
134134

135135
if (response) {
136+
// Check for top-level isError (MCP Spec compliant)
137+
const isErrorTop = (response as { isError?: boolean | string }).isError;
138+
if (isErrorTop === true || isErrorTop === 'true') {
139+
return true;
140+
}
141+
142+
// Legacy check for nested error object (keep for backward compatibility if any tools rely on it)
136143
const error = (response as { error?: McpError })?.error;
137144
const isError = error?.isError;
138145

0 commit comments

Comments
 (0)