1. Tool descriptions ARE the selection mechanism
The model chooses tools by reading their names and descriptions — nothing else. A mis-routing agent almost always has a description problem, and the exam's root-cause principle applies: fix the descriptions before adding a routing classifier, a bigger model, or more prompt.
A production-grade description states:
- What the tool does (specifically — not "handles data")
- When to call it (trigger conditions: "Call this when the user asks about...")
- When not to / how it differs from confusable siblings
- What each parameter means (in the schema's property descriptions)
| Weak |
Strong |
search — "Searches for things" |
search_orders — "Search the ORDER database by customer, date range, or status. Call when the user asks about an existing order's state, contents, or history. For refunds use issue_refund; for product catalog questions use search_catalog." |
2. Designing input schemas
Schema choices that separate reliable tools from flaky ones:
- Enums for closed sets —
"status": {"enum": ["open", "closed", "pending"]} beats a free string the model might invent values for.
- Describe every property — property descriptions are read at call time; "date in ISO 8601 YYYY-MM-DD" prevents a whole class of format bugs.
- Mark truly required fields only; give optional ones sensible defaults in your executor.
- Prefer specific parameters over one opaque blob —
{"customer_id", "date_from", "date_to"} beats {"query": "..."} when the backend is structured: the schema is documentation, and the harness can validate it.
- Names: verbs, specific, consistent (
get_, search_, create_ prefixes). get_weather not weather; search_orders not handler2.
3. Errors: the silent-failure trap and is_error
The silent-failure trap (a named exam pattern): a tool that returns an empty string / empty list / null on failure looks successful to the model. It will happily proceed — "I found no orders for this customer" — when the truth was "the database call timed out."
Reliable tools distinguish three outcomes, explicitly and machine-readably:
| Outcome |
Return |
| Success |
{"ok": true, "data": ...} |
| Genuinely empty result |
{"ok": true, "data": [], "note": "no orders matched customer 8813 in the last 90 days"} |
| Failure |
{"ok": false, "error": "orders-db timeout after 5s — result unknown, retry may succeed"} |
Two layers of error signaling in the Claude API:
- Content: the error payload above, inside the
tool_result content.
- Flag:
"is_error": true on the tool_result block — tells the model "this call failed" at the protocol level. (MCP has the same concept spelled isError: true — §6.)
Good error messages are actionable: what failed, why (if known), and what would help ("retry", "narrow the date range", "invalid ID format — expected ORD-XXXX").
📊 Diagram — “Success vs genuinely-empty vs failure outcomes and the silent-failure trap” (a version appears earlier on this page)
4. MCP: architecture in one diagram
The Model Context Protocol is an open standard for connecting AI applications to external systems — "USB-C for AI tools": build a server once, use it from any MCP client.
┌─────────────── host application ───────────────┐
│ (Claude Code, Claude Desktop, your app) │
│ ┌──────────┐ ┌──────────┐ │ ┌──────────────┐
│ │ model │◄──────►│MCP client│◄────────────┼─────►│ MCP server │
│ └──────────┘ └──────────┘ protocol │ │ (GitHub, DB, │
│ one client per server │ │ Jira, ...) │
└────────────────────────────────────────────────┘ └──────────────┘
- Host: the application the user interacts with.
- Client: the connector inside the host — one per server connection.
- Server: exposes capabilities (tools/resources/prompts) for one system.
Servers run locally (stdio transport) or remotely (HTTP-based transport). Configuration in Claude Code lives in .mcp.json (page 02 §8 covers scopes).
📊 Diagram — “MCP host/client/server diagram with the three primitives and their controllers” (a version appears earlier on this page)
5. The three MCP primitives — ★ the most-tested table in this domain
| Primitive |
What it is |
Who controls invocation |
Example |
| Tools |
Executable actions the model may call during generation |
Model-controlled |
create_issue, run_query |
| Resources |
Data/content exposed for context (file-like, addressable) |
Application-controlled |
file contents, DB schema, API response |
| Prompts |
Pre-built prompt templates/workflows |
User-controlled |
a "/summarize-pr" template the user picks |
The control column is the exam's favorite discriminator. Anchors:
- Model decides to act mid-conversation → tool.
- The app decides what context to provide (attach this file, load that schema) → resource.
- The user explicitly picks a canned workflow (slash-command-like) → prompt.
Question shape to expect: "The server should expose the database schema so the assistant can reference it while writing queries. Which primitive?" → Resource (the app supplies context; the model doesn't 'call' a schema). "...and expose query execution?" → Tool. "...and a standard 'optimize this query' workflow users trigger?" → Prompt.
6. MCP error handling: isError: true
MCP tool results carry an isError flag. The critical distinction:
| Failure kind |
Mechanism |
Who handles it |
| Tool executed and failed (API 404, timeout, bad input) |
Result with isError: true + error content |
The model — it sees the error and can adapt/retry/apologize |
| Protocol-level failure (unknown tool, malformed request, server crash) |
JSON-RPC protocol error |
The client/harness — the model never receives a result |
Exam-tested consequences:
- A tool that catches an exception and returns
isError: true with an actionable message lets the agent recover in-conversation — the reliable design.
- A tool that lets the exception explode into a protocol error takes the whole exchange down — the model can't adapt to what it never sees.
- Same silent-failure rule as §3: an MCP tool returning empty content with
isError: false on failure is the worst case — a lie the model will build on.
7. MCP configuration & scoped distribution
Recap of the .mcp.json scope table (details in page 02):
| Scope |
Distribution |
Use |
Project (.mcp.json, committed) |
Everyone who clones |
Team-standard servers |
| User |
One person, all projects |
Personal tooling |
| Local |
One person, one project |
Experiments, personal credentials |
Plus the security rules this domain re-tests:
- Secrets via environment-variable expansion (
"env": {"TOKEN": "${GITHUB_TOKEN}"}) — never literal keys in a committed file.
- MCP tools flow through the same permission system as built-in tools — a server being configured doesn't mean every tool is auto-approved.
- Third-party servers are supply chain: prefer official/vetted servers; an MCP server runs with whatever access you gave it.
8. Minimal tool surface
Fewer, sharper tools beat many overlapping ones:
- Selection accuracy: every added tool is another chance to mis-route; overlapping tools ("search", "find", "lookup") are the classic self-inflicted wound.
- Security: each tool is capability granted — least privilege applies to the definition list, not just permissions (Domain 1's
allowedTools, Domain 3's allowlists — one principle, three domains).
- Design smell: if two tools need a paragraph explaining when to use which, consider merging them behind one parameter (
"scope": {"enum": ["orders", "catalog"]}) — or make the split's trigger conditions unmistakable in the descriptions.
Root-cause ladder for a mis-routing agent (exam-aligned order): 1) sharpen descriptions → 2) reduce/merge overlapping tools → 3) only then consider a routing layer.
9½. Official exam-guide addenda (v1.0)
Specifics from the official task statements (Exam Guide v1.0, July 2026):
Description interference (TS 2.1). Tool selection isn't decided by descriptions alone — keyword-sensitive wording in the system prompt can override well-written descriptions and create unintended tool associations. When routing misbehaves, review both. And the flip side of §8's "merge overlapping tools": a single too-generic tool should be split into purpose-specific tools with defined contracts (the guide's example: analyze_document → extract_data_points, summarize_content, verify_claim_against_source).
The official error taxonomy (TS 2.2). Four categories every MCP tool should distinguish: transient (timeout, service down — retryable), validation (bad input — fix and retry), business (policy violation — not retryable, needs a customer-friendly explanation), permission (not retryable, needs escalation). Structured metadata: errorCategory, isRetryable boolean, human-readable description. And the multi-agent rule: subagents recover locally from transient failures; they propagate only errors they can't resolve — together with partial results and what was attempted.
MCP integration details (TS 2.4). The user-level config file is ~/.claude.json (project level is .mcp.json). MCP resources have a tested use-case: exposing content catalogs (issue summaries, documentation trees, database schemas) so agents see what data exists without exploratory tool calls. If the agent keeps using built-in tools (Grep) instead of a more capable MCP tool, the fix is richer MCP tool descriptions — the agent prefers what it understands. And build-vs-buy: community MCP servers for standard integrations (Jira, GitHub); custom servers only for team-specific workflows.