Skip to lesson

AnthropicClaude Certified Architect – Foundations2026-102.2 Implement structured error responses for MCP tools

Separate execution failures from protocol errors

Completed by the knowledge check

Learning objective

Return MCP execution failures as isError tool results, distinguishing them from protocol errors and successful empty results.

Core idea

A failed business operation can still produce a valid protocol response. In Model Context Protocol (MCP), a tool execution failure belongs inside the result of a tools/call response with isError: true. A protocol failure belongs in a JSON-RPC error response. A successful query that finds nothing is a successful tool result, even when its returned collection is empty.

These distinctions determine what the agent can do next. A recognized tool that rejects an out-of-range value can explain how to correct the input. An unknown tool name points to a discovery or dispatch problem. A completed search with no matches provides evidence about the searched scope. Collapsing all three into “Operation failed” removes the information needed to recover or answer accurately.

How it works

Choose the correct response layer

The MCP tools specification lists unknown tools, malformed requests that fail the CallToolRequest schema, and server errors as protocol errors. It lists API failures, input validation errors, and business logic errors as tool execution errors. The deciding boundary is the failed contract: the protocol request structure or the recognized tool’s operation.

Input validation deserves particular care. A well-formed tools/call request can contain a date that violates the tool’s domain requirements. That is a tool execution error, not automatically a malformed protocol request. “The departure date must be in the future” gives the model a correction it can apply. A request whose structure fails CallToolRequest belongs at the protocol layer instead.

A protocol error carries the JSON-RPC error object, including an integer code and a message. The specification’s unknown-tool example uses code: -32602. Do not treat that example as a business error category or put a declined refund into it merely because the refund did not happen. The error response addresses the protocol call; it does not represent a successfully dispatched tool’s domain outcome.

The failed contract decides the layer: a protocol failure ends in a JSON-RPC error response, and only the recognized tool’s operation decides between an isError: true result and a successful one, empty or not.

The failed contract decides the layer; only the recognized tool's operation decides between an error result and a successful oneStarts at a tools/call request. An unknown tool or a malformed request ends in a JSON-RPC error response. Otherwise the recognized tool's operation runs; a failed operation ends in a result with isError true, and a completed one ends in a successful tool result, even when its collection is empty.yesnoyesnotools/call requestUnknown tool ormalformedrequest?JSON-RPC errorresponseRecognized tool'soperationTool operationfailed?Result withisError: trueSuccessful toolresult, even whenempty

Return an execution failure as a result

For an execution failure, return content explaining the failure and set the MCP field isError to true. Here is a shortened JSON-RPC response for a recognized lookup tool whose upstream service failed:

{
  "jsonrpc": "2.0",
  "id": 7,
  "result": {
    "content": [
      {
        "type": "text",
        "text": "Order lookup is unavailable. No order status was retrieved."
      }
    ],
    "isError": true
  }
}

The response is syntactically a protocol result, but its tool operation failed. The isError field, not the presence of result, decides whether execution succeeded.

In the TypeScript Agent SDK, tool() handlers run through createSdkMcpServer; Python uses @tool with create_sdk_mcp_server. These in-process servers convert an uncaught handler exception into an error result carrying the raw exception message, and the agent loop continues. Catching the exception and returning isError: true in TypeScript or "is_error": True in Python lets you compose the actionable message Claude reads.

MCP recommends that clients provide tool execution errors to language models so they can self-correct. Anthropic’s tool guidance likewise recommends specific, actionable error responses rather than opaque codes or tracebacks. Preserve the operation’s meaning when converting internal exceptions into model-visible content. An unavailable source should say that retrieval failed; it should not fabricate an empty collection as if retrieval succeeded.

Preserve success when nothing matches

For a completed query with no matches, return a successful result with isError: false. A server-defined object such as {"matches": [], "searchedScope": "customer-orders"} can go in structuredContent. Neither matches nor searchedScope is an MCP field; the protocol supplies the container, while the tool defines the data inside it.

See 2.2.2 for structured metadata delivery, output schemas, and Agent SDK support. See 2.2.4 for interpreting no-match evidence and choosing recovery when access fails.

Keep the Claude API spelling separate

If your own integration bridges MCP tools to Claude client tools in the Messages API, that API uses a tool_result block with is_error: true, not isError. Its tool_use_id identifies the corresponding tool call. These are different interfaces with different spellings. Preserve the error meaning while adapting the envelope rather than copying the MCP result verbatim into an incompatible Messages block.

See 1.1.2 for history ordering and call correlation. This lesson owns the classification at the MCP boundary, not the rest of the Messages execution loop.

Key distinctions and trade-offs

Protocol validity versus execution success: Receiving a valid JSON-RPC result does not prove that the tool accomplished its operation. Inspect isError and the result contract.

Invalid domain input versus malformed request: A date outside the allowed range can be actionable tool feedback. A broken request envelope is a protocol problem. Calling both “validation errors” without identifying the layer obscures the remedy.

Empty versus unavailable: Preserve the success or error signal even when no records are returned; see 2.2.4 for the evidence distinction.

Detailed errors improve recovery, but the MCP specification also requires servers to sanitize outputs. Give the model the relevant reason and allowed next step without returning an internal traceback or unrelated sensitive data.

Architecture / decision scenario

A customer support agent asks an MCP server to search a customer’s orders. The server recognizes search_orders and validates the call. The order service is unavailable, so the handler returns result with isError: true and an explanation that no status was retrieved. The agent has a failed lookup to recover from; it cannot tell the customer that no orders exist.

A second call names a tool the server does not recognize. The dispatcher returns a JSON-RPC protocol error, following the unknown-tool mechanism. The integration investigates the tool name or discovery state rather than retrying an order-service query that never ran.

A third call is valid, authorized, and completes with no matching orders. It returns isError: false and an empty matches collection. The agent can report no matches in the searched scope or request a different order identifier. These responses differ by the layer that failed and by whether the search produced evidence, not by how much data they contain.

Common exam traps

  • Treating every JSON-RPC result as successful execution.
  • Returning a protocol error for every domain validation failure.
  • Using an empty collection to conceal an upstream access failure.
  • Confusing MCP isError with Messages API is_error.
  • Assuming an error code alone tells the agent what to correct.
  • Assuming an uncaught handler exception stops the Agent SDK loop; it becomes an error result carrying the raw exception message.

Where the exam guide differs

Note:

The exam guide (version 1.0, effective July 2026, first drafted in February 2026) names no MCP revision; it was written while revision 2025-11-25 was the published specification. This lesson cites revision 2026-07-28, a later revision, and the differences between the two do not change what it teaches for the exam. Both revisions return input validation failures as isError: true tool results; revision 2025-11-25 clarified that rule after revision 2025-06-18 had listed invalid arguments among protocol errors. Revision 2026-07-28 also adds a required resultType field to every result, which this lesson leaves out.

What to remember

Use JSON-RPC errors for protocol failures and isError: true tool results for execution failures. Keep successful empty results successful. Preserve these meanings across any Claude API adapter, including its different field spelling, so recovery decisions follow evidence rather than the absence of data.

Knowledge check

References