Tools and MCPs
Three subdomains, 10.6% of the marks, on how Claude acts: writing tool definitions that get called correctly, understanding what an MCP server actually is, and choosing the smallest mechanism that closes the gap.
A tool is a typed contract between the model and your application. You declare an operation and the shape of its arguments; the model decides when to call it and with what values; something on your side executes. Two facts about that contract organise this whole domain: the model never sees your implementation — only the schema you wrote and the results you returned — and the quality of your tool design, not the model’s intelligence, is what the exam is really testing here.
A map before you set off. The exam names three subdomains; this page runs longer than three sections, on purpose. Sections 01–03 cover tool definitions, section 04 the execution loop, section 05 failures and approvals, section 06 MCP, and section 07 the decision ladder between mechanisms. Everything after that — the revision tables, the traps, the quiz — is study aid, not new syllabus. And a legend for the markup: the prose explains, the "Remember for the exam" boxes carry the facts questions are built from, and the traps are the wrong answers dressed as right ones. Anything labelled an SDK footnote is recognise-only — the exam reaches for the concept, not the wiring.
The block pair at the centre of the loop. tool_use is the model’s structured request — an id, the tool’s name, an input object. tool_result is your answer, echoing the same id. Every tool_use owes exactly one tool_result.
The response field that says why generation ended. Your loop keys on it: "tool_use" means execute and keep going; anything else means the turn is over.
A tool defined and executed on Anthropic’s infrastructure — web search, web fetch, code execution, tool search. Its result comes back already attached; you never build one.
An MCP tool result that validates against a declared output schema — machine-checkable data, rather than prose the model has to parse.
The client-side MCP primitive: a server asks the client to collect input or confirmation from the human, then waits for the answer. The server cannot reach the human itself.
The directories a client offers up to a server — a hint about where its filesystem lives. A well-behaved server stays inside them.
01When a tool is the answer
In plain language: reach for a tool when the model must do something, must learn something fresh, or must answer in a shape your code can consume — and skip it when prose alone is already the right output.
Four situations justify one: actions with side effects (sending, writing, updating); fresh or external data the model cannot know (prices, weather, database contents); outputs that must conform to a guaranteed structure; and calls into existing systems — databases, internal APIs, filesystems. The heuristic the docs offer for the borderline case is worth memorising verbatim in spirit: if you are writing a regex to extract a decision from the model’s prose, that decision should have been a tool call. And the anti-cases: summarisation, translation, general-knowledge Q&A — anything the model can do from its training alone, with no side effects, where a tool round-trip costs more than the work.
02Anatomy of a definition
A user-defined tool in the API’s tools array has a name (letters, digits, hyphens and underscores, up to 128 characters), a plaintext description, and an input_schema in JSON Schema. The optional properties around them: example inputs, cache_control, strict schema enforcement, deferred loading for large tool sets, and allowed_callers limiting which components may invoke the tool. Use the schema to make arguments safe: enums to bound a unit parameter to celsius or fahrenheit, required to distinguish mandatory from optional. The model reads the schema to construct arguments. But it reads the description to decide whether to call the tool at all.
That is the single most examinable fact in this domain: the docs call the description by far the most important factor in tool performance — and the mechanism explains the ranking, because the API assembles a dedicated system prompt out of your tool definitions, the tool configuration, and your own system prompt. A description is not metadata the model consults; it is prompt text, present every turn. A good one runs three or four sentences at minimum and covers what the tool does, when to use it, when not to, what each parameter means, and — the part everyone skips — what it does not return. "Gets the stock price" is the bad example; the rewrite names the exchanges it covers, the currency of the answer, and states plainly that it provides no other information about the company. Example inputs exist but solve a different problem: complex, nested or format-sensitive arguments — and each example must validate against the schema.
| Setting | What it forces |
|---|---|
| tool + the tool’s name | That exact tool, this turn |
| any | Some tool — the model picks which |
| auto | Nothing — the model’s choice, tools optional |
| none | Tools off entirely for the turn |
And when a tool call fails downstream, return a tool_result with the matching tool_use_id, is_error set to true, and a message the model can act on (what failed, whether and when to retry) — feeding it the raw error body as if it were data is the documented way to confuse it. Every tool_use must be answered by exactly one tool_result; an unanswered one makes the next request malformed.
03Designing the set
- —Consolidate — one tool with an action parameter beats create_pr, review_pr, merge_pr as three separate definitions; fewer, more capable tools give the model less to choose wrongly between.
- —Namespace — github_list_prs, slack_send_message — names that carry their service, which matters most once tool search is in play.
- —Return only high signal — stable semantic identifiers, the fields the model needs for its next step, nothing else. Bloated results burn context and bury the signal.
04The loop and who runs what
One question unlocks this whole section, and most of the domain with it: where does the tool execute? Where it runs decides who drives the loop, who returns the result, and who handles the failure — settle that first and everything else follows. The block identifiers give you the answer at a glance: toolu_ marks a client tool_use, srvtoolu_ a server_tool_use. Treat the prefixes as diagnostic clues to read, not a list to memorise cold.
Client tools put your application in the driving seat: Claude can ask for a call but cannot run your code, so the loop is yours to drive. When the model wants a tool, the response ends with stop_reason "tool_use" and one or more tool_use blocks — each an id, the tool’s name, and an input object shaped to your schema. You dispatch on the name, execute, and answer with a user message whose tool_result blocks echo those ids back. In code, the standard client is a while loop keyed on stop_reason:
Send the conversation plus the tools array — schemas and descriptions only. The model never sees your implementation.
Wants a tool: the response ends with stop_reason "tool_use" and one or more tool_use blocks, each carrying an id (toolu_…), the tool’s name, and the input.
Dispatch on the name and execute the call in your application.
Answer with a user message whose tool_result blocks echo those ids back — exactly one result per tool_use.
Send again, and keep looping while stop_reason reads "tool_use".
end_turn, max_tokens, stop_sequence, refusal — anything other than "tool_use" ends the loop.
Server tools invert the arrangement. Web search, web fetch, code execution and tool search run their own loop inside Anthropic’s infrastructure — one request can fire several searches before a response comes back — and that response pairs every server_tool_use block (srvtoolu_-prefixed ids) with its result already attached. You never construct a tool_result for these; the exam’s favourite trick in this territory is making you think you owe one. The behaviour to know cold is pause_turn: the server loop hit its iteration limit and the work is not finished.
Send one request with server tools enabled — web search, web fetch, code execution, tool search.
Runs the server loop inside its own infrastructure; several tool iterations can fire before any response comes back.
Returns every server_tool_use block (srvtoolu_-prefixed) with its result already attached.
Build nothing — no tool_result, ever, for a server tool.
stop_reason "pause_turn": the loop hit its iteration limit, unfinished. Re-send the conversation exactly as it arrived — dropping a tool from the array is a validation error — and cap your retries, because a resumed turn can pause again.
The subtle case is the mixed turn: Claude emits a server tool and a client tool in the same parallel group, and the API returns at once, without running the server tool, so that you can run yours first. It rewards a careful read — stop_reason "tool_use", not "pause_turn"; a server_tool_use block with no result attached as the tell; exactly one tool_result owed, and it is yours.
Emits a server tool and a client tool in the same parallel group.
Returns immediately, without running the server tool — you get to run yours first. stop_reason reads "tool_use", not "pause_turn".
A server_tool_use block with no result attached. With no client tool in the group that means pause_turn; here it means "waiting for you".
Run your client tool and send only its tool_result — one result, for the one tool you own.
Keep the tools array byte-identical — removing web_fetch earns a 400.
The deferred server tool runs next; its result is first in the following response.
05When tools fail, and who approves
The error taxonomy falls out cleanly once execution location is clear — the same one question again: where does the tool run? That tells you whose problem each failure is.
| The failure | What actually happens | Your move |
|---|---|---|
| Your tool fails mid-execution — the weather service is down | The failure becomes data: a tool_result with is_error true | Write the message the model can act on — what failed, whether and when to retry |
| Claude’s call is invalid — a required parameter missing | In development, a description problem; at runtime, an invalid-input error | Spell the parameter out in the description; at runtime return an is_error tool_result naming the mistake — Claude typically retries two or three times, corrected, before apologising to the user |
| A server tool fails — web search: too many requests, uses exhausted, query too long | Claude absorbs the error and answers around it | Nothing — no is_error from you; it is not your tool |
Approvals are the Agent SDK’s answer to "should this run at all" — and the concept matters more than the wiring. Register a can_use_tool callback (canUseTool in TypeScript) and it fires whenever a call was not already approved by a permission rule or mode. It is the one place you inspect the full input before anything executes: the actual command behind Bash, the actual path and contents behind Write. Return allow, optionally with an updated_input that substitutes for what was asked, or deny — execution is skipped, but Claude sees your message and can change course.
Two SDK footnotes, recognise-only: a tools array passed alongside the callback must include AskUserQuestion, and the Python SDK needs a no-op PreToolUse hook registered to hold the stream open. What is not a footnote: for hard denials, disallowed_tools is evaluated before any permission mode is consulted — so "Bash(rm -rf *)" stays blocked even under bypassPermissions, while a bare "Bash" would strip the entire tool from the model’s view, broader than you meant.
06MCP without the mystery
In plain language: MCP is a standard plug. A host application owns the conversation and the policy; inside it sits one client per server; and servers are the things people write, exposing capabilities through a uniform protocol. Anything that speaks it — an editor, Claude Desktop, Claude Code, your own app — can use the same server.
| Role | What it is | What it owns |
|---|---|---|
| Host | The application the human runs — Claude Code, Claude Desktop, your app | The conversation, the security policy, every consent and authorisation decision, the full history |
| Client | A connector inside the host | Exactly one server — one client per server connection |
| Server | The thing you write | The capabilities it exposes to the host |
The design principle the exam tests is isolation: a server cannot see other servers, cannot see the conversation, cannot reach into the host. If it needs something from the client side, it must request it and wait — elicitation to ask the human for input, roots to learn which directories it may touch, sampling to borrow the host’s model for a completion. One currency note: sampling was deprecated in the spec’s 2026-07-28 revision — recognise the name; new implementations integrate with LLM provider APIs directly.
Two transports, and the rule of thumb for telling them apart: a command means local, a URL means remote. Remote hygiene: bind local servers to loopback only, validate the Origin header on connections, authenticate properly. In Claude Code, registration is one line each way — a command for stdio, a URL for HTTP.
| Transport | How you spot it | How it talks | The hygiene rule |
|---|---|---|---|
| stdio — local | A command; the server runs as a child process | JSON-RPC, one message per line — and stdout belongs to the protocol absolutely | Bind to loopback only; logs go to stderr, and a well-behaved client will not treat stderr as failure |
| Streamable HTTP — remote | A URL | One endpoint; each request a POST whose Accept header lists both JSON and event-stream, and the server answers in either form | Validate the Origin header on connections; authenticate properly |
Before any capability is used, both sides declare and negotiate it for the session — a client may only call tools if the server declared the tools capability, and lists refresh by notification when they change.
Then the three primitives — one line each before the detail. A tool performs an action. A resource exposes readable data. A prompt packages a reusable interaction. They are separate primitives, not three views of one thing, and one server may expose any mix: the spec’s own example is a database server offering query tools, a schema resource, and a few-shot prompt at the same time.
- —Tools — the model-acting primitive: name, description, and a required input schema (a no-argument tool still declares an empty, closed object — {} with nothing permitted inside); optionally an output schema, which buys you machine-validatable structured content in results. Results carry a content array and an error flag; a tool may also signal that it needs human input before proceeding.
- —Resources — readable data addressed by URI, listed and read, optionally templated for parameterised families. The security rule is non-negotiable: a server must validate every URI and sanitise filesystem paths, because a crafted file:// URI walking out of its directory is the classic attack; unknown URIs answer with a protocol error.
- —Prompts — named, reusable message templates with arguments, filled on request. How an organisation packages its workflows for humans to invoke.
07Built-in, custom tool, MCP, or skill
In plain language: use what ships, write only the logic that is yours, plug into what already exists, and add knowledge — not tools — when the gap is knowledge. The ordering principle is that the cheapest mechanism is the one you do not have to build.
| Mechanism | What it is | Reach for it when | The tell it’s wrong |
|---|---|---|---|
| Built-ins | Ship with Claude Code and the Agent SDK — file reading and editing, shell, search, web fetch; some permission-free, some requiring approval | The job stays inside what ships — a refactor inside the repository needs nothing else | You are about to hand-write a file reader |
| Custom tool | Yours: your schema, your code | The logic is yours — internal APIs, domain calculations, data no built-in can reach | The capability already exists as a service |
| MCP server | Someone else’s server, local or remote | The capability exists — the tracker, the docs system, the database; connecting beats rewriting | You are hand-rolling an integration other teams already share |
| Skill | Not a tool at all — knowledge and workflow, a deployment checklist as a slash command | The gap is instructions, not actions | The fix would add a new action to the model’s hands |
One wiring fact explains a naming quirk and a framing at the same time: in the Agent SDK, custom tools are registered by wrapping them in an in-process MCP server. That is worth knowing twice over — it explains the mcp__-prefixed tool names, and it means "custom tool versus MCP" is really a question about where the logic runs: your process, or someone else’s server.
One distinction from the API side that keeps resurfacing in exam questions: Anthropic-defined schemas like bash and text_editor look like server tools but execute on your side — Anthropic writes the schema (you request it by its date-versioned type rather than defining your own), trains the model on it, and your application runs the call and returns the result. Only the true server tools — web search, web fetch, code execution, tool search — run on Anthropic’s infrastructure.
08One page to revise from
Seven sections of prose sit above this line. Here is the same domain as one table — who runs what, the question every scenario in this domain turns on — plus the night-before checklist. If you reread one thing the night before, reread this.
| Tool kind | Who executes | The ids | Your tool_result? | On failure |
|---|---|---|---|---|
| User-defined (client) tools | Your application | toolu_ | Yes — exactly one per tool_use | You write it: is_error true, actionable message |
| Anthropic-defined schemas — bash, text_editor | Your application — the row people get wrong | toolu_ | Yes — same as client tools | Same as client tools |
| Server tools — web search, web fetch, code execution, tool search | Anthropic’s infrastructure | srvtoolu_ | Never — the response pairs it | Claude absorbs it; nothing from you |
The night-before checklist. You should be able to say each of these without looking:
- —The five rules from the top of this page, out loud, in order.
- —Which stop_reason means what — and that pause_turn means unfinished, not failed.
- —The mixed turn in one sentence: server tool with no result plus a client tool in the group → run yours only, resend an identical tools array.
- —The three MCP roles, the one principle (isolation), and the transport rule: command → stdio with stdout reserved for protocol, URL → streamable HTTP.
- —Tool, resource and prompt in one line each — and that one server can expose all three.
- —The ladder cheapest-first — built-ins, custom, MCP, skill — and that a skill is not a tool.
A last sorting pass, because not everything above deserves equal rehearsal. Memorise cold: the five rules, the stop_reason values and what your code does for each, the three MCP roles, tool / resource / prompt in one line each. Recognise on sight: the toolu_ and srvtoolu_ prefixes (diagnostic clues, not a list to recite), the SDK footnotes around can_use_tool, the named web-search failure reasons. Reference only: everything else. This domain rewards judgement — which mechanism, whose failure, which primitive — far more than recall.
09The traps in this domain
- —"Claude picks the wrong tool — add input examples" — selection is a description problem; examples fix complex inputs.
- —"More, narrower tools make selection clearer" — the docs say consolidate: one tool, an action parameter.
- —"The MCP server can see the whole conversation" — isolation is the design; the host sees everything, the server sees requests.
- —"stdio servers can print to stdout" — stdout is the protocol channel; logs go to stderr.
- —"bash and text_editor run on Anthropic’s servers" — Anthropic-defined schemas still execute in your application.
- —"A skill is a kind of tool" — a skill adds knowledge and workflow, not an action.
- —"After a server tool you owe a tool_result" — never; Anthropic pairs server_tool_use with its result, and constructing one yourself is the error.
- —"server_tool_use with no result means pause_turn" — only when no client tool shares the group; otherwise it is a mixed turn and you owe exactly your client tool’s result.
- —"A thrown tool ends the loop" — an execution failure is data, not a crash: is_error true, an actionable message, and Claude relays it.
- —"bypassPermissions overrides every restriction" — deny rules run before any mode is consulted; disallowed_tools holds even there.
10Check yourself
Four questions, in roughly the exam’s register. Each one expands to show the answer and — more usefully — why the wrong options are wrong.
In testing, Claude builds correct arguments for your inventory tool but often picks a cheaper lookup tool when it should pick the inventory one. Where do you fix it?
- AAdd more input examples showing complex argument shapes
- BSet tool_choice to "any" so a tool is always used
- CRewrite the inventory tool’s description to say when to use it — and when not to
- DRename the tools so the inventory tool sorts first
→Check your answer
Selection is a description problem — the description is the prompt text the model reads to decide whether to call the tool at all. Examples fix a different problem: complex or format-sensitive arguments. "any" forces some tool but not which one, and array position is meaningless, so renaming cannot help.
A response arrives with stop_reason "tool_use", containing a server_tool_use for web_fetch with no result attached, and a tool_use for your get_weather. What does your code do?
- ABuild tool_results for both blocks before resending
- BDrop web_fetch from the tools array so the model cannot ask for it again
- CTreat it as pause_turn and resend the conversation with no results added
- DRun get_weather, return only its tool_result, and resend a byte-identical tools array
→Check your answer
This is the mixed turn: the server tool waits so you can run yours first. The unmatched server_tool_use is the tell, and stop_reason reads "tool_use", not "pause_turn". You owe exactly your client tool’s result — building one for web_fetch is the planted error — and the tools array must stay identical: dropping web_fetch earns a 400.
Your team’s docs MCP server should let the agent query the docs index, expose the style guide as readable content, and offer a reusable "incident writeup" template that takes arguments. Which primitives does it declare — and how many servers?
- ATools, resources and prompts — one server can expose all three
- BThree servers, one per primitive
- CTools and prompts only — resources must be local files
- DResources and prompts only — querying an index is a read, so it is a resource
→Check your answer
A tool performs the action (querying), a resource exposes the readable data (the style guide), a prompt packages the reusable interaction (the template) — and capabilities are declared per server, so one server can expose the lot; the spec’s own database-server example does exactly that. Querying is model-directed action, not passive data, which rules out the last option.
A reviewer wants Claude to walk the team’s deployment checklist before every release — the same steps each time, no new capabilities needed. Which mechanism fits?
- AA custom tool that runs the steps
- BAn MCP server wrapping the runbook
- CWeb fetch, pulling the checklist from the wiki each time
- DA skill — the checklist as a slash command
→Check your answer
The gap is knowledge and workflow, not action — and a skill is exactly knowledge and workflow, not a new tool. The other three all add machinery the ladder says you should not build: the cheapest mechanism is the one you do not have to build.