Tool Design & MCP Integration (18%)
What the exam tests: whether you can write tools Claude selects reliably, return errors it can act on, give each agent only the tools it needs, wire MCP servers into Claude Code at the right scope, and pick the right built-in tool. About 11 of 60 items.
3.1 Tool descriptions drive tool selection (Task 2.1)
Claude chooses a tool mainly from its name and description. Minimal descriptions such as "Retrieves customer information" and "Retrieves order details" leave it guessing when a user says "check my order #12345". The first fix is always the description.
- A good description states:
- what the tool does and what it returns;
- the input formats it accepts, with an example;
- example queries it is meant for;
- edge cases and limits;
when to use it instead of the similar tool next to it.
{
"name": "lookup_order",
"description": "Look up one order by order number (format ORD-12345 or #12345). Use for any question about an order's status, items, shipping or refund eligibility. Returns order status, line items, ship date and refund window. Do NOT use to find a customer; use get_customer for name, email or account questions.",
"input_schema": {"type": "object", "properties": {"order_id": {"type": "string"}}, "required": ["order_id"]}
}
When two tools overlap, remove the overlap: rename analyze_content to extract_web_results and give it a web-specific description, or split a generic analyze_document into extract_data_points, summarize_content and verify_claim_against_source, each with a clear contract. Also check the system prompt: a line like "always analyse the user's content carefully" can pull Claude toward a tool with "analyze" in its name, overriding a good description.
3.2 Structured error responses (Task 2.2)
An MCP tool reports failure by returning its result with isError: true. What goes inside matters. A generic "Operation failed" gives the agent nothing to decide with. Return a category, whether a retry can help, and a human-readable explanation.
Category Example Retryable Agent should
Transient Timeout, service unavailable Yes Retry, perhaps after a pause Validation Malformed order number After fixing input Correct the input or ask the user Business Refund exceeds policy No Explain the policy to the customer or escalate Permission Not authorised for this account No Escalate; do not retry {"isError": true, "content": [{"type": "text", "text": "{\"errorCategory\": \"business\", \"isRetryable\": false, \"message\": \"Refunds over 30 days after delivery need manager approval.\"}"}]}
Two more distinctions the exam returns to:
Access failure versus empty result. "The search timed out" and "the search ran and found nothing" are different facts. Reporting a timeout as zero results hides the failure. Recover locally, escalate the rest. A subagent should retry its own transient failures, and pass up only what it cannot fix, with what it tried and any partial results.
3.3 How many tools, and which agent gets them (Task 2.3)
More tools means harder choices. The guide's example: 18 tools degrade selection compared with 4 or 5. Agents also misuse tools outside their role, for example a synthesis agent that starts running web searches.
Give each subagent only the tools its role needs.
Replace open-ended tools with constrained ones, for example load_document that validates document URLs instead of a general fetch_url.
Add a narrow cross-role tool for a frequent need. In the sample question, 85% of the synthesis agent's checks are simple facts, so it gets a scoped verify_fact tool, while deep checks still go through the coordinator.
- tool_choice controls whether Claude must call a tool:
- Setting Behaviour Use it to
- {"type": "auto"} Claude may call a tool or
- reply in text
- Normal agent turns
- {"type": "any"} Claude must call some tool,
- its choice
- Guarantee structured output when several ex‐
- traction tools exist
- Setting Behaviour Use it to
- {"type": "tool", "name":
- "extract_metadata"}
Claude must call that tool Force a step to run first, then continue in later turns
3.4 MCP in Claude Code and agents (Task 2.4)
MCP servers expose tools (actions the model can take) and resources (content the client can read, such as an issue list, documentation tree or database schema). Exposing a catalogue as a resource lets the agent see what exists without spending tool calls exploring.
Scope File Use for
Project .mcp.json in the repo, committed Team-shared servers, such as the company Jira or GitHub server User ~/.claude.json Personal or experimental servers, not shared Keep secrets out of git with environment variable expansion:
Tools from every configured server are discovered at connection time and are available together. If the agent keeps using built-in Grep when your MCP code-search tool is better, improve the MCP tool's description so its advantages are explicit. For standard systems such as Jira, prefer an existing community server; build custom servers for workflows unique to your team.
3.5 Built-in tools (Task 2.5)
Tool Use for Example
Grep Searching file contents All callers of processRefund, an error message, an import Glob Finding files by path pattern **/*.test.tsx
Read Loading a whole file Following an import to its source
Edit A targeted change anchored on unique text Changing one function's signature Write Writing a whole file New files, or after Read when Edit cannot find a unique anchor Bash Running commands Tests, builds, git
Explore a codebase incrementally: Grep for entry points, then Read to follow imports. Do not read every file up front. To trace a function through wrapper modules, first list every name it is exported under, then Grep for each.
TOOL DESIGN & MCP INTEGRATION (18%)
Worked examples
Worked example 3.A: Rewriting two confusable tools
Situation. The research system has analyze_content ("Analyzes content and returns insights") and analyze_document ("Analyzes a document and returns key information"). Logs show web pages sent to analyze_document and PDFs sent to analyze_content about a third of the time. Diagnosis. The descriptions are nearly identical, and so are the names. Claude has no basis for choosing.
Fix, step 1: remove the overlap in names. Rename analyze_content to extract_web_results. Now the name alone says web.
Fix, step 2: write contracts, not slogans.
- Tool New description
- extract_w
- eb_result
- s
Extract key claims, figures and quotations from the HTML text of web search results. Input: the results array returned by web_search. Returns a list of {claim, excerpt, url, published}. Use only for web results; for PDFs or uploaded files use analyze_document.
analyze_d
ocument
Analyse one PDF, DOCX or uploaded file by document ID (format doc_ followed by 12 characters). Returns sections, key data points with page numbers, and stated methodology. Do not use for web pages. Fix, step 3 (if confusion persists): split by job. If analyze_document is still overloaded, split it into extract_data_points, summarize_content and verify_claim_against_source, each with its own input and output schema.
Check the system prompt as well. If it says "analyze everything the user shares carefully", the word analyze pulls toward whichever tool contains it. Rephrase to "review" or name the intended tool.
Worked example 3.B: An error the agent can act on
Situation. The lookup_order MCP tool returns {"error": "failed"} for timeouts, bad order numbers, and orders belonging to another customer. The agent retries all three, wasting turns, and once told a customer "our system is down" when they had mistyped the number. Fix. Return a structured, categorised error with isError: true. A Python MCP server using the SDK's FastMCP helper might look like this (illustrative):
- from mcp.server.fastmcp import FastMCP
- import json
- from mcp.server.fastmcp.exceptions import ToolError
- mcp = FastMCP("orders")
- def tool_error(category, retryable, message, **extra):
- raise ToolError(json.dumps({"errorCategory": category,
- "isRetryable": retryable,
- "message": message, **extra}))
- @mcp.tool()
- def lookup_order(order_id: str, customer_id: str) -> dict:
"""Look up one order by ID (ORD- followed by 5 digits) for a verified customer.""" if not ORDER_RE.match(order_id):
tool_error("validation", False,
"Order IDs look like ORD-12345. Ask the customer to check the number.")
- try:
- order = orders_api.get(order_id, timeout=5)
- except TimeoutError:
tool_error("transient", True, "Order service timed out. Safe to retry once.") if order is None:
- return {"found": False} # a valid empty result, not an error
- if order.customer_id != customer_id:
- tool_error("permission", False,
"This order belongs to a different account. Do not reveal details.")
return trim(order) # only the fields the agent needs
The server framework turns a raised tool error into a result with isError: true, and Claude reads the JSON text. Note the distinction between "not found" (a successful query with no match) and a timeout (a failure that might succeed later).
Worked example 3.C: Eighteen tools, one confused agent
Situation. A single developer-productivity agent has 18 tools: six code-search variants, four git tools, three ticketing tools, two documentation tools and three deployment tools. It picks the wrong search tool often and once tried to deploy while answering a question.
- Fix. Split by role and scope tools to each role:
- Agent Tools
- Explorer Grep, Glob, Read, one semantic code_search
- Change maker Read, Edit, Write, Bash (tests only)
- Ticket assistant search_issues, get_issue, comment_on_issue
Coordinator Task (to reach the others), get_issue for quick context Deployment tools leave the conversational system entirely and sit behind a human-approved pipeline. Each agent now chooses among 3 to 5 tools, and none holds a tool outside its job. The coordinator keeps one cheap cross-role tool (get_issue) because it needs ticket context on almost every request.
Worked example 3.D: Forcing the first step
Situation. An enrichment pipeline must extract document metadata (title, date, author, type) before calling lookup tools. Claude sometimes calls lookup_company first using a guessed name. Fix. On the first request set tool_choice to {"type": "tool", "name": "extract_metadata"}. Claude must call it. On the next request, with the metadata now in the conversation, switch to TOOL DESIGN & MCP INTEGRATION (18%)
{"type": "auto"} so it can choose enrichment tools freely. Forcing a tool is a per-request setting, so this sequencing is in your control.
Worked example 3.E: Team and personal MCP servers
Situation. The team wants everyone to have the Jira and GitHub MCP servers. One engineer is trialling an experimental vector-search server. A token for GitHub was accidentally committed last month.
Fix.
- .mcp.json (committed, shared):
- {
- "mcpServers": {
"jira": {"type": "http", "url": "https://mcp.example-jira.com/mcp", "headers": {"Authorization": "Bearer ${JIRA_TOKEN}"}},
"github": {"command": "npx", "args": ["-y", "@modelcontextprotocol/server-github"], "env": {"GITHUB_TOKEN": "${GITHUB_TOKEN}"}}
}
}
The experimental server goes in the engineer's ~/.claude.json (for example by adding it with user scope through the claude mcp add command). Both sets of tools are available together in that engineer's sessions; teammates see only the shared ones. Tokens come from each person's environment, never from the file. Rotate the leaked token.
Why not build a custom Jira server? The guide's preference is to use an established community or vendor server for standard integrations and reserve custom servers for workflows unique to the team.
Worked example 3.F: Resources instead of exploration
Situation. Before answering "is there already a ticket for the login timeout?", the agent calls search_issues eight times with different keywords.
Fix. Expose an MCP resource such as issues://open/summary: a compact catalogue of open issue IDs, titles and components. The client can load it into context, so Claude sees what exists before it searches, and one targeted get_issue call replaces eight exploratory ones. The same pattern works for documentation trees and database schemas.
Worked example 3.G: Choosing built-in tools
Task Right first tool Why not the others
Find where ERR_PAYMENT_DECLINED is raised Grep It is a content search List all Storybook files Glob **/*.stories.tsx It is a file-name pattern Understand checkout.ts fully Read You need the whole file
Change one import line Edit Small, unique anchor
Task Right first tool Why not the others
Edit fails: anchor appears 5 times Read, then Write Edit needs a unique match
Find every use of a function re-exported as pay and charge
- Grep the module for its exports, then Grep
- for each name
- Wrappers hide the origin‐
- al name
- Run the test suite Bash Tool execution
Deep dive: what goes in a tool's input schema
Tool descriptions do most of the work in selection, but the input schema shapes how well Claude fills the call.
Use enums for closed sets ("status": {"enum": ["open", "closed", "pending"]}) so Claude cannot invent values.
Describe formats inside the property ("order_id": {"type": "string", "description": "ORD- followed by 5 digits"}).
Mark only truly needed fields as required. A required field Claude cannot know invites guessing.
Prefer IDs over names for lookups, and say where the ID comes from ("from get_customer"). Return trimmed outputs. If the agent needs 5 of 40 fields, return 5; it saves context for the whole conversation (Chapter 6).
Deep dive: tools versus resources versus prompts in MCP
MCP servers can offer three kinds of capability. The exam focuses on the first two.
Primitive Controlled by Use for Example
Tool The model decides to call it Actions and queries with arguments
- create_issue, lookup_order
- Re‐
- source
- The application decides to load
- it
Read-only content and catalogues Issue summary list, schema, docs index
Prompt The user picks it Reusable prompt templates "Summarise this incident"
A quick rule: if it changes something or needs arguments chosen at run time, make it a tool. If it is reference content the agent benefits from seeing up front, make it a resource.
Common traps
Jumping to a routing classifier or tool consolidation when the first step is a better description. Returning empty success on a timeout.
TOOL DESIGN & MCP INTEGRATION (18%)
Giving every agent every tool "for flexibility".
Putting a personal server in .mcp.json, or a secret in plain text.
Using Grep to find files by name (that is Glob), or reading the whole repo before searching.
Check yourself
Two tools, search_docs and search_kb, are confused 30% of the time. What do you change first?
Your MCP tool returns {"error": "failed"} on every problem. What fields would you add?
Which tool_choice guarantees a tool call without forcing a specific one?
A teammate cannot see the Jira MCP server you configured. Where did you probably configure it?