A tool logic failure rides the JSON-RPC `result` with `isError: true` so the model sees it; only protocol faults like unknown methods use the `error` field.
Imagine ordering food through a waiter. If the kitchen burns your dish, the waiter still comes back to your table and tells you 'sorry, the dish failed' so you can decide what to do next. That is a tool error: the request was understood, but the work failed, and the result comes back to you with a 'this went wrong' note attached. Now imagine you mumble an order for a dish that does not exist on the menu. The waiter cannot even take the order. That is a protocol error: the request itself was malformed. MCP keeps these two cases on separate tracks, so the model gets handed the kitchen failures it can react to, while the truly broken requests bounce back differently.
Concept explanation~2 min read
Everything you need to truly understand this topic: intuition, mechanics, step by step explanation, code, formulas, and worked example. Click to expand.
Concept explanation~2 min read
Everything you need to truly understand this topic: intuition, mechanics, step by step explanation, code, formulas, and worked example. Click to expand.
This question is rated hard for a reason: it sits exactly on the seam where the Model Context Protocol meets its JSON-RPC transport, and getting the seam wrong is one of the most common production MCP bugs. The instinct from years of API design is that an error is an error, so it should go in the error field. MCP deliberately breaks that instinct, and candidates who have only built REST services almost always answer it wrong on the first pass.
The core idea is that there are two different kinds of failure, with two different audiences. A tool that ran and failed produces information the model must see and act on. A request that was never executable produces a fault the host must absorb. MCP routes these to two separate channels so each audience gets what it needs. The boolean that distinguishes them, isError, is small, but the design philosophy behind it is the whole answer.
This deep dive walks through both channels, the exact response shapes, why the design routes recoverable failures through the success path, the production discipline that keeps the two from leaking into each other, and the security surface that error content opens up once it reaches the model.
Two failure modes, two channels
Start with the transport. MCP messages are JSON-RPC 2.0. Every response is either a success carrying a result object, or a failure carrying an error object with a numeric code and a message. That envelope is fixed by JSON-RPC and predates MCP entirely. The specification reserves a band of codes for transport faults, and application code is expected to stay out of that band.
MCP then layers tool semantics on top. A tools/call request invokes a named tool with arguments. Two completely different things can go wrong, and they map to the two envelopes. The whole difficulty of the question is that both can follow a single tools/call, so naming the right channel for each is the skill being tested.
The first mode is a protocol error. The request was malformed, the method name is unknown, or the parameters failed validation before any tool logic ran. The call never reached the tool. These map cleanly to the JSON-RPC error field, with standard codes such as method-not-found or invalid-params. The defining trait is that no business logic executed; the fault is in the request itself.
The second mode is a tool execution error. The method exists, the arguments parsed, the tool ran, and its business logic failed: a query errored, a file was missing, an upstream API returned 500. The tool genuinely executed and genuinely failed. This does not use the error field at all. The request was perfectly well-formed and fully understood; only the downstream work fell over, and that distinction is what dictates the response shape.
Situations where this technique stops working.
2–4 min · Everything important, quickly.
// Tool logic failure -> result with isError, NOT a thrown JSON-RPC error
server.setRequestHandler(CallToolRequestSchema, async (request) => {
try {
const rows = await runQuery(request.params.arguments.sql);
return { content: [{ type: "text", text: JSON.stringify(rows) }] };
} catch (err) {
return {
content: [{ type: "text", text: `Query failed: ${err.message}` }],
isError: true // model sees this and can react
};
}
});| Aspect | Tool execution error | JSON-RPC protocol error |
|---|---|---|
| Where it lives | result, with isError true | error field, with a code |
| What failed | Tool logic ran and threw | Request never executed |
| Examples | DB timeout, missing file, API 500 | Unknown method, bad params, malformed JSON |
| Who reacts | The model reads and retries | The host catches and logs |
| Visible to model | Yes, in the content array | No, intercepted by the host |
Real products, models, and research that use this idea.
- The official MCP filesystem server returns isError true with a message when a read targets a missing path, rather than throwing a JSON-RPC error.
- Claude Code surfaces an isError tool result back into the model's context so Claude Opus 4.7 can retry a failed shell command on the next turn.
What an interviewer would ask next. Try answering before peeking at the approach.
QHow should a client distinguish a tool error from a protocol error when both can appear after a tools/call?
Check the response envelope first: an error field means a protocol fault; a result with isError true means the tool ran and failed. Branch the handler on which envelope arrived.
Red flags & common mistakes
The phrases that signal junior thinking. Click to expand.
Red flags & common mistakes
The phrases that signal junior thinking. Click to expand.
Returning a tool failure as a JSON-RPC error. That hides it from the model, which can no longer see the message or self-correct on the next turn.
60 second bullets to scan on the way to the call.
The two error channels MCP exposes and how they differ
Which channel a tool business logic failure uses
Primary sources. Browse if you want the original framing.
Same topic, related formats. Practice these next.