Skip to content

Conversation

Jflick58
Copy link

Tool execution errors now return successful responses with isError: true
instead of JSON-RPC protocol errors, per the MCP specification.

Motivation and Context

This allows clients to distinguish between protocol-level errors and
tool execution errors, providing better error context.

Fixes #159

How Has This Been Tested?

Updated unit tests to support new error behavior. All existing tests passing.

Breaking Changes

If client libs are parsing the error message for the specific tool call error, then they will likely get a parser error due to the change of the error structure.

Current behavior:

 { "jsonrpc": "2.0", "id": 4, "error": { "code": -32603, "message": "Internal error", "data": "Internal error calling tool fetch_weather_data" } } 

Updated behavior (matches spec and readme):

 { "jsonrpc": "2.0", "id": 4, "result": { "content": [{ "type": "text", "text": "Internal error calling tool fetch_weather_data" }], "isError": true } } 

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update

Checklist

  • I have read the MCP Documentation
  • My code follows the repository's style guidelines
  • New and existing tests pass locally
  • I have added appropriate error handling
  • [x ] I have added or updated documentation as needed
 Tool execution errors now return successful responses with isError: true instead of JSON-RPC protocol errors, per the MCP specification. This allows clients to distinguish between protocol-level errors and tool execution errors, providing better error context. Fixes modelcontextprotocol#159

begin
call_tool_with_args(tool, arguments)
rescue => e
Copy link
Contributor

@atesgoral atesgoral Oct 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a step in the right direction, but I think we can take this a few more steps further:

This rescue should be at the call_tool level: Any mistake made by the caller that falls out of valid tool contracts (e.g. invalid tool name, invalid args) should be non-exception tool results with isError: true. Only JSON-RPC-level protocol errors should be allowed to be returned as JSON-RPC errors with error codes.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@atesgoral makes sense. This is how it should work now:

Original (Everything was a Protocol Error)

 { "jsonrpc": "2.0", "id": 1, "error": { "code": -32603, "message": "Internal error", "data": "Tool not found unknown_tool" } } 

Now - Protocol Error

 { "jsonrpc": "2.0", "id": 1, "error": { "code": -32601, "message": "Method not found", "data": "unknown_method" } } 

Now - Tool error:

 { "jsonrpc": "2.0", "id": 1, "result": { "content": [{ "type": "text", "text": "Tool not found: unknown_tool" }], "isError": true } } 

def call_tool(request)
tool_name = request[:name]
arguments = request[:arguments] || {}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems better to keep it at its original position on line 267, since there's no need to process it until it becomes necessary.

@koic
Copy link
Member

koic commented Oct 15, 2025

I've left one small comment, but overall it looks good to me. Can you address it and squash the commits in to one?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

3 participants