Bug
When a tool handler raises McpError, the call_tool handler in src/mcp/server/lowlevel/server.py catches it via except Exception as e
and wraps it as CallToolResult(isError=True). This loses the structured error code on the wire.
The existing UrlElicitationRequiredError (a subclass of McpError) already has a raise bypass for exactly this reason, but the parent
McpError class does not.
Note: The v2 high-level API (src/mcp/server/mcpserver/server.py on main) already handles this correctly with except MCPError: raise.
This issue is specific to the v1.x decorator-based low-level server.
Expected behavior
McpError raised in a tool handler should propagate to the transport layer's _handle_request, which converts it to a JSON-RPC error
response with the structured error code preserved — the same behavior as UrlElicitationRequiredError.
Reproduction
from mcp.shared.exceptions import McpError
@server.call_tool()
async def handle_call_tool(name, arguments):
raise McpError(code=-32000, message="server fault")
# Actual: client sees CallToolResult(isError=True, content="server fault")
# Expected: client sees JSON-RPC error with code=-32000
Proposed fix
In src/mcp/server/lowlevel/server.py, replace the tool call handler's exception chain:
# Before:
except UrlElicitationRequiredError:
raise
except Exception as e:
return self._make_error_result(str(e))
# After:
except McpError:
raise
except Exception as e:
return self._make_error_result(str(e))
This subsumes the existing except UrlElicitationRequiredError: raise since it's a subclass of McpError.
Bug
When a tool handler raises McpError, the call_tool handler in src/mcp/server/lowlevel/server.py catches it via except Exception as e
and wraps it as CallToolResult(isError=True). This loses the structured error code on the wire.
The existing UrlElicitationRequiredError (a subclass of McpError) already has a raise bypass for exactly this reason, but the parent
McpError class does not.
Note: The v2 high-level API (src/mcp/server/mcpserver/server.py on main) already handles this correctly with except MCPError: raise.
This issue is specific to the v1.x decorator-based low-level server.
Expected behavior
McpError raised in a tool handler should propagate to the transport layer's _handle_request, which converts it to a JSON-RPC error
response with the structured error code preserved — the same behavior as UrlElicitationRequiredError.
Reproduction
Proposed fix
In src/mcp/server/lowlevel/server.py, replace the tool call handler's exception chain:
This subsumes the existing except UrlElicitationRequiredError: raise since it's a subclass of McpError.