Skip to lesson

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

Bound retries and preserve unresolved evidence

Completed by the knowledge check

Learning objective

Retry transient failures locally within bounded limits and propagate unresolved errors with attempted steps and partial results.

Core idea

A transient tool failure should first be handled by the component that owns the failed operation and its recovery budget. A temporary lookup outage does not require the coordinator to rediscover the whole task. Local recovery can repeat an eligible read, retain successful work, and return either the recovered result or a precise account of what remains unresolved.

Local retries are also cheap for the coordinator. A subagent’s intermediate tool calls and results stay inside its own conversation, and only its final message returns to the parent (see 1.2.1), so three attempts at an archive read add nothing to the coordinator’s context. Anthropic’s account of its multi-agent research system gives the other half: restarts are expensive and frustrating for users, so it resumes from where the agent was and pairs the model’s adaptability with deterministic safeguards such as retry logic and regular checkpoints.

MCP defines error results and request timeout behavior. Your tool handler or client-side recovery controller implements the bounded attempt policy, and the subagent’s assignment defines what it must report to its coordinator.

How it works

Assign one local recovery owner

Use the error metadata from 2.2.2 to identify eligible transient failures. A valid read that encounters temporary service unavailability can qualify. A validation error needs changed input, a permission error needs authorized access, and a business-rule rejection needs explanation. Do not put all isError: true results into a blanket retry loop.

Place deterministic dependency retries in the MCP handler or a client-side recovery controller that can enforce the budget. If a subagent directs the recovery, give it an explicit assignment describing allowed retries and the required handoff. In the Python Agent SDK, AgentDefinition.prompt holds that subagent instruction; in TypeScript, the corresponding agent definition’s prompt serves that role. See 1.3.1 for defining specialists and 1.3.4 for evidence-bearing handoffs.

A prompt communicates the policy; the recovery controller enforces the limits, which is the deterministic half of that pairing. Account for retries in the Anthropic Python client too: use max_retries=0 when another layer deliberately owns those retries; see 0.4. A handler, subagent, and coordinator must not each restart independent budgets.

Bound attempts and elapsed time separately

For a concrete example, assign three total dependency attempts and a ten-second local deadline. These are scenario settings, not MCP or Anthropic defaults. Three total attempts means one initial attempt and at most two retries. The recovery controller records an application attemptCount and stops when either the attempt limit or deadline is exhausted.

A count limit does not bound a hung request. MCP revision 2026-07-28 recommends request timeouts, per-request configuration, and a maximum timeout despite progress notifications. On timeout, cancel and stop waiting. For Streamable HTTP, closing the SSE response stream signals cancellation; no notifications/cancelled message is required or expected. For stdio, the client must send notifications/cancelled referencing the original requestId.

In Claude Code, set MCP_TOOL_TIMEOUT or a per-server timeout in milliseconds in .mcp.json. The per-server value overrides the environment variable and enforces a hard wall-clock limit that progress cannot extend. Values below 1000 milliseconds fall through to MCP_TOOL_TIMEOUT, whose unset default is about 28 hours. In the TypeScript Agent SDK, pass timeout to createSdkMcpServer() to bound a single in-process server’s tool calls.

Claude Code also has an idle window controlled by CLAUDE_CODE_MCP_TOOL_IDLE_TIMEOUT: five minutes for HTTP/SSE/WebSocket and 30 minutes for stdio, excluding IDE and SDK in-process servers. For Claude Code v2.1.203 or later, a per-server timeout of at least 1000 milliseconds is also a floor on that idle window.

Tool-call timeouts bound caller waiting; the handler’s dependency deadline bounds upstream work, and both fit within the assignment’s deadline. The cited MCP pages prescribe no retry count or backoff schedule; those are application settings.

Preserve the difference between eligibility and exhaustion

A transient cause can remain transient after the permitted recovery budget is gone. Return the cause along with the exhausted state. In this example contract, errorCategory: "transient", isRetryable: false, and budgetExhausted: true mean the local owner cannot repeat the unchanged call within its assignment. The fields inside structuredContent are application-defined.

The coordinator can then decide whether a separate approved recovery action is warranted. It should not silently reset the same budget just because a different agent received the error. Preserve attemptCount, elapsed evidence, and the failed operation’s scope so any follow-up decision can account for work already performed.

A timeout during a state-changing operation also needs outcome clarity. In this example workflow, only read-only lookups are eligible for automatic repetition. If a refund request times out after dispatch and its outcome is unknown, the result reports that uncertainty and requests a status check through the merchant’s approved mechanism. It does not infer that no refund happened or repeat payment execution as if it were a harmless read.

Propagate a useful unresolved result

When local recovery succeeds, return the valid success result. When it fails, use an isError: true result with an actionable explanation. See 2.2.2 for structured metadata delivery, output schemas, and the Python in-process support boundary.

The unresolved payload should identify the failed operation, the category, whether local recovery is exhausted, what was attempted, and what evidence is already available. This example uses attemptedSteps, partialResults, and unresolvedSources as schema keys. They are not MCP-reserved identifiers, but they make the exam’s reporting requirement concrete.

Record which source was queried, how many attempts occurred, and what each produced, and keep successful evidence with its source and scope. “Could not finish” is insufficient when only an archive lookup remains missing; the coordinator needs the gap, not a transcript.

Only an eligible transient failure loops back for another attempt, and the loop ends when either the attempt limit or the deadline is exhausted.

Only an eligible transient failure loops back for another attempt, until the attempt limit or the deadline is exhaustedInside the local recovery owner, a dependency attempt either succeeds and returns the valid success result, or fails. A failure that is not an eligible transient failure leaves the loop for changed input, authorized access or an explanation. An eligible transient failure loops back to another attempt until the attempt limit or the deadline is exhausted; then the unresolved result is propagated to the coordinator.Local recovery owneryesnonoyesnoyesDependencyattemptAttempt succeeds?Eligible transientfailure?Attempt limit ordeadline exhausted?Return the validsuccess resultChanged input,authorized accessor explanationPropagate theunresolved result

Key distinctions and trade-offs

Local recovery versus coordinator recovery: Local recovery handles eligible failures inside the assigned operation. The coordinator chooses among unresolved alternatives using the resulting evidence and remaining constraints.

Transient cause versus remaining budget: A temporary outage can persist after retries are exhausted. Classification describes the cause; exhaustion describes the permitted work remaining.

Partial evidence versus completion: Retained records are useful, but they do not prove that unresolved sources were checked. Report both the results and the gaps.

A bounded local policy caps latency and dependency traffic, and gives the coordinator a defined terminal outcome instead of an indefinite wait.

Architecture / decision scenario

A research subagent must check two authorized customer-history sources. Its MCP lookup controller has the scenario’s three-total-attempt limit and ten-second local deadline. The primary source succeeds. The archive returns a transient availability failure, so the controller retries only that eligible read within the remaining budget.

If the archive recovers, the subagent returns both sources’ evidence. If it remains unavailable, the controller returns isError: true with the transient category, budgetExhausted: true, attemptCount: 3, attempted archive steps, primary-source partial results, and the archive as unresolved. The subagent carries those fields into its coordinator handoff rather than discarding the primary evidence.

The coordinator can accept a qualified answer or authorize a targeted later archive check. It does not rerun the primary search or restart the same exhausted budget invisibly. A permission denial would have bypassed the transient retry path, and an empty successful archive query would have ended normally; neither is equivalent to the unresolved availability failure.

Common exam traps

  • Retrying every error regardless of category or side effects.
  • Counting three total attempts as three retries plus the initial call.
  • Using an attempt cap without request timeouts and a deadline.
  • Resetting an exhausted budget at each delegation boundary.
  • Escalating a generic failure without attempted steps or partial evidence.

Where the exam guide differs

Note:

The guide asks for “local error recovery within subagents” with attempted steps and partial results. This is an assignment and recovery policy you implement, not an automatic retry contract in MCP or the Agent SDK. The application payload fields here implement that handoff; see 2.2.2 for the metadata vocabulary. MCP request timeouts and Anthropic tool-call settings bound execution, while the retry count remains application policy.

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 recommend request timeouts and cancelling a request that times out. Revision 2025-11-25 cancels by sending a notifications/cancelled notification; revision 2026-07-28 makes closing the SSE response stream the signal on Streamable HTTP and keeps the notification for stdio.

What to remember

Keep eligible transient recovery local, finite, and owned by one controller. Use MCP timeout behavior to bound waiting and application limits to bound attempts. When recovery remains unresolved, preserve the cause, exhausted work, successful partial results, and missing scope so the coordinator can make a targeted decision.

Knowledge check

References