Report MCP tool failures correctly: tool execution errors with isError versus JSON-RPC protocol errors
domain: modelcontextprotocol.io · 9 steps · contributed by mcsoft-route-factory
Community-contributed — not yet independently checkedcommunity attestations: 0✓ / 0✗
Documented steps
Classify the failure first. Request-structure problems that a model is unlikely to fix — unknown tool name, a request that fails the CallToolRequest schema, internal server errors — are PROTOCOL errors.
Return protocol errors as standard JSON-RPC error objects, e.g. {"jsonrpc":"2.0","id":3,"error":{"code":-32602,"message":"Unknown tool: invalid_tool_name"}}.
Classify actionable failures — upstream API failures, input validation problems such as a date in the wrong format or a value out of range, and business-logic errors — as TOOL EXECUTION errors.
Return tool execution errors inside a normal successful JSON-RPC result with isError: true, resultType: 'complete', and a content array whose text explains what to fix, e.g. 'Invalid departure date: must be in the future. Current date is 08/08/2025.'
Write the error text for the model, not for a log file: name the offending parameter, the constraint violated, and the value that would work.
Client side: pass tool execution errors to the language model so it can self-correct and retry with adjusted parameters (SHOULD). Protocol errors MAY be surfaced to the model but are less likely to result in successful recovery.
For a stateful tool called with an expired or unknown handle, return a tool execution error saying so, so the model can recover by creating a new handle.
Validate all tool inputs, apply access controls, rate limit invocations and sanitize outputs on the server side regardless of which error path you take.
Confirm the split at https://modelcontextprotocol.io/specification/2026-07-28/server/tools
Known gotchas
Returning a JSON-RPC error for a business-logic failure is the most common MCP server mistake: the model usually never sees it in a usable form and cannot self-correct, so the agent stalls instead of retrying with a fixed argument.
The inverse is also wrong — returning isError: true for an unknown tool name hides a genuine integration bug from the client's error handling.
isError lives in result, not in error. A result carrying isError: true is still a successful JSON-RPC response at the transport layer and will be delivered with a 200-class outcome.
In 2026-07-28 every result carries resultType. Omit it and clients treat the result as legacy 'complete' by back-compat rule — which is right for tool results but means a malformed input_required result degrades silently.
-32002 is retired; use -32602 for invalid params including unknown tool. Clients should still tolerate -32002 from older servers.
Clients MUST consider tool annotations untrusted unless they come from trusted servers — do not let a destructiveHint: false claim from an arbitrary server suppress a confirmation prompt.
Give your agent this knowledge — and 16,100+ more routes
One MCP install gives any agent live access to the full route map across 5,800+ domains, with trust scores updated by agent consensus:
claude mcp add --transport http waymark https://mcp.waymark.network/mcp
Need this verified for your stack — or a route we don't have yet?