-
Notifications
You must be signed in to change notification settings - Fork 10
fix(mcp-server): return tool errors as isError results per MCP spec #1458
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
|
Coverage Impact This PR will not change total coverage. Modified Files with Diff Coverage (1)
🛟 Help
|
Per the MCP specification, tool execution errors should be reported within the result object (isError: true) rather than thrown as protocol-level errors. This ensures the LLM receives the actual error message and can handle it appropriately. See: https://modelcontextprotocol.io/docs/concepts/tools Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
4ba700a to
91ca294
Compare
| try { | ||
| return await handler(args as TArgs, extra); | ||
| } catch (error) { | ||
| const message = error instanceof Error ? error.message : String(error); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think JSON.stringify(error) is better than String(error) to avoid returning things like "[object]"
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
JSON.stringify returns undefined for undefined/Symbol/Function values, and throws on circular references. Add fallback to String() for both cases to ensure error message is always a string. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
8356eaf to
3be6265
Compare
Code review1 issue found — weak assertions in 4 test files. Weak
|

Summary
tool-with-logging.tsand return them as{ isError: true }results instead of throwingProblem
When a tool handler throws an error, the MCP SDK catches it and may replace the original message with a generic text. This means the LLM client receives an unhelpful error message and cannot adapt its behavior.
Solution
By returning
{ content: [{ type: 'text', text: message }], isError: true }instead of throwing, the error message is preserved and forwarded to the LLM as-is.Test plan
yarn workspace @forestadmin/mcp-server test— 483 tests passyarn workspace @forestadmin/mcp-server lint— 0 errors🤖 Generated with Claude Code