Tool Design & MCP Integration
Everything Domain 2 tests: why tool descriptions — not implementations — decide whether Claude picks the right tool, the Model Context Protocol's three primitives, structured MCP tool errors, .mcp.json configuration and transports, and scoping tool access per agent role. Every concept comes with the real schema, config, or error shape the exam expects you to recognise.
2.1 Why tool design is its own domain
Domain 1 is about how an agent orchestrates itself. Domain 2 is about something narrower and, on the exam, easy to underestimate: how Claude decides which tool to call. The exam's core thesis for this domain is simple to state and easy to forget under time pressure:
description string.That reframes a whole category of exam questions. A stem that describes a bug where "the wrong tool gets called" is very rarely testing your knowledge of the tool's internals — it is testing whether you will reach for the description field first. Distractors that propose fixing the backend, adding retries, or upgrading the model are all answering a question that was not asked.
The rest of this lesson builds outward from that thesis: what MCP actually gives you to describe (2.2), how to write descriptions that disambiguate (2.3), how to fail loudly and usefully at the protocol boundary (2.4), how servers get wired into a project (2.5), and how access itself — not just description quality — has to be scoped per agent (2.6).
2.2 MCP's three primitives
The Model Context Protocol (MCP) standardises how an application connects Claude to external capabilities and data. A server can expose exactly three kinds of primitive, and the exam expects you to know not just their names but who is in control of each one:
| Primitive | Who invokes it | What it's for |
|---|---|---|
| Tools | The model — Claude decides when to call one, exactly like Claude API tool_use | Actions and lookups: run a query, create a ticket, send a message |
| Resources | The host application — not invoked by the model directly | Data the client can read and attach to context: a file, a database record, a log |
| Prompts | The user — explicitly selected, like a slash command | Templated instructions a server exposes for the user to invoke on demand |
Illustrative server manifest — one of each primitive
MCP servers don't all expose their capabilities in exactly this JSON shape — this is a simplified, illustrative structure to show the three primitive types side by side, not a literal SDK dump:
{
"tools": [
{
"name": "create_ticket",
"description": "Create a new support ticket for a customer issue. Use when a new problem is reported that has no existing ticket.",
"input_schema": { "type": "object", "properties": { "summary": {"type":"string"} } }
}
],
"resources": [
{
"uri": "ticket://{id}",
"name": "Ticket record",
"description": "A single support ticket's full history. The host app decides when to attach one to context - the model cannot fetch it directly."
}
],
"prompts": [
{
"name": "summarize_ticket",
"description": "Template a user can select to summarize a ticket thread.",
"arguments": [{ "name": "ticket_id", "required": true }]
}
]
}2.3 Tool descriptions drive selection
The description field of a tool schema is the onlysignal Claude has about when to use it. It cannot read your implementation code, your tests, or your internal docs — if the description is vague or overlaps another tool's description, misselection is not a bug in Claude, it is a bug in your tool design.
Before: vague, overlapping descriptions
{ "name": "search_docs", "description": "Searches documentation.",
"input_schema": { "type": "object", "properties": { "query": {"type":"string"} } } }
{ "name": "search_code", "description": "Searches the codebase.",
"input_schema": { "type": "object", "properties": { "query": {"type":"string"} } } }Both descriptions are technically true and neither says what the tool is notfor. Ask "where is the refund logic implemented?" and Claude has no textual signal telling it that "implemented" means source code, not prose — it may call either tool, or the wrong one first.
After: precise scope, plus explicit exclusions
{
"name": "search_docs",
"description": "Full-text search over markdown documentation in docs/. Use when the user asks how something is documented, explained, or described in prose. Do NOT use for locating function or class definitions - use search_code for that.",
"input_schema": { "type": "object", "properties": { "query": {"type":"string"} }, "required": ["query"] }
}
{
"name": "search_code",
"description": "Full-text search over source files (*.py, *.ts, *.go) for function names, class names, and code identifiers. Use when the user asks where something is implemented or defined. Do NOT use for documentation or prose questions - use search_docs for that.",
"input_schema": { "type": "object", "properties": { "query": {"type":"string"} }, "required": ["query"] }
}Two moves did the disambiguating work: stating precisely when each tool applies, and stating explicitly what to use insteadwhen it doesn't. "Do NOT use for X" is not defensive filler — for two tools whose surface behaviour looks similar, it is often the only line separating a clean selection from a coin flip.
2.4 Structured, actionable tool errors
Domain 1 taught that tool errors are data the model reasons over, not exceptions that crash the loop — an error result needs a category, a retryability signal, and a suggested next step. That same principle applies with equal force at the MCP server boundary: a well-designed MCP tool never hands the model a raw stack trace or a bare HTTP status code.
Bad: opaque failure
{
"isError": true,
"content": [{ "type": "text",
"text": "Error: Traceback (most recent call last):\n File \"server.py\", line 88\n IntegrityError: FK constraint failed (code 409)" }]
}Claude cannot act on that. It doesn't know if the failure is transient, whether retrying is safe, or what a customer-facing agent should tell the user in the meantime.
Good: structured, actionable failure
{
"isError": true,
"content": [{ "type": "text",
"text": "TICKET_ARCHIVED: Ticket #4471 is archived and cannot be updated directly. This is not retryable as-is. Suggested next step: call reopen_ticket first, or route this to close-notes if the customer only needs a status update." }]
}Notice the three ingredients: an error category (TICKET_ARCHIVED) the model and your logs can both key off, an explicit retryable signal, and a suggested next action or alternate tool (reopen_ticket). This is the same is_errordiscipline from Domain 1's agentic loop — Domain 2 just asks you to recognise it applies just as strongly when the tool sits behind an MCP server instead of being called in-process.
2.5 Configuring MCP servers: .mcp.json and transports
.mcp.json is the project-level configuration file listing the MCP servers that Claude Code — or an Agent SDK application — connects to. Each entry is either a local server launched as a subprocess, or a remote server reached over the network:
{
"mcpServers": {
"linear": {
"command": "npx",
"args": ["-y", "@example/mcp-server-linear"],
"env": { "LINEAR_API_KEY": "${LINEAR_API_KEY}" }
},
"shared-docs": {
"type": "http",
"url": "https://mcp.example.com/docs",
"headers": { "Authorization": "Bearer ${DOCS_MCP_TOKEN}" }
}
}
}url but no "type": "http" field. Claude Code reads an untyped entry as a stdio server by default and reports it as misconfigured — the type field is not optional decoration, it is what makes the entry resolve as a remote server at all.The two entries above show the two transport mechanisms the exam expects you to tell apart:
| Transport | Shape in .mcp.json | Typical use |
|---|---|---|
| stdio | command + args — spawns a local subprocess | Local dev tools, most common for individual developer setups |
| Streamable HTTP | url + "type": "http" — connects to a remote, network-reachable server | Remote or shared servers, reachable by multiple clients at once. The older standalone SSE transport still works but is deprecated in favour of Streamable HTTP. |
.mcp.json is typically checked into version control so the whole team or CI pipeline shares the same set of MCP servers — that is the point of the file. The corollary the exam tests directly: personal API keys and other secrets must never be committed inside it. Reference them as environment variables (${VAR_NAME}) resolved at runtime, exactly as both entries above do, and keep the actual values in an untracked.env or your CI secret store.
2.6 Tool scoping per agent role
Domain 1's anti-pattern #6 — unrestricted tool access — reappears in Domain 2 with an MCP twist: in a multi-agent or multi-MCP-server setup, each agent or subagent should only be connected to the servers and tools it actually needs for its role. A read-only research subagent has no legitimate reason to see a database-write MCP tool, even if that tool is available somewhere in the project.
The key distinction the exam is testing: .mcp.json declares every server the project uses. It does not follow that every agent in that project should have every one of those tools available. Scoping happens one layer down, at the agent or session level:
# .claude/agents/test-runner.md
---
name: test-runner
description: Runs the test suite and reports failures. Use after code changes,
never for deployment decisions.
tools: Bash, Read, mcp__test-execution__run_tests
---
You run the project's test suite and report failures as {file, test, error}.
You do not deploy, release, or modify infrastructure under any circumstance.Both a test-execution MCP tool and a deploy MCP tool can exist side by side in the project's .mcp.json. The test-runner subagent's own tools allowlist names only mcp__test-execution__run_tests— the deploy tool is never listed, so this subagent cannot invoke it even if it "discovers" the server is technically reachable. Scoping is a property of the agent definition, not of what the project has wired up.
Checkpoint quiz — Domain 2
Exam-style: one scenario, one correct answer, three plausible mistakes. Click an option to lock in and see the explanation.
Your team's MCP server exposes search_docs ("Searches documentation.") and search_code ("Searches the codebase."). Claude keeps calling the wrong one for questions like "where is refund logic implemented?" What is the architecturally correct fix?
A research subagent needs the full text of one specific archived report to ground its summary. The MCP server exposes that report as a Resource, not a Tool. What should the architecture do?
A refund-processing MCP tool fails because the target ticket is archived. Which tool result is architecturally correct to return to the model?
You're committing a shared .mcp.json for a CI pipeline. One remote MCP server requires a private API key. What should the committed file contain for that key?
Both a test-execution MCP tool and a deploy MCP tool are declared in the project's .mcp.json. A test-runner subagent's job during code generation is only to run tests and report failures. How should its tool access be scoped?
Quick glossary
Every term below appears somewhere in this course. If a question uses a word you're unsure of, check here before re-reading the whole lesson.
Agentic loop
The repeating cycle behind any Claude-powered agent: send a request, check what Claude wants to do next (stop_reason), run a tool if asked, send the result back, repeat until Claude is done.
stop_reason
A field on Claude's response telling your code why it stopped talking — e.g. it wants to call a tool (tool_use), it finished (end_turn), or it ran out of room (max_tokens).
tool_use / tool_result
tool_use is Claude asking your code to run something (a search, a database lookup...). tool_result is your code's answer, sent back so Claude can continue.
Subagent / Agent tool
A helper Claude spawns to do one focused piece of work in its own separate memory space, then reports back a summary — so the main conversation doesn't get cluttered with the helper's scratch work.
Hooks (PreToolUse / PostToolUse)
Small scripts that run automatically around a tool call — one right before it (can block it entirely) and one right after (can react to the result). Used for rules that must always hold, not just usually hold.
MCP (Model Context Protocol)
A standard way to connect Claude to external systems — databases, internal tools, company APIs — through a shared plug-in format instead of custom one-off code every time.
MCP Tools vs Resources vs Prompts
The three things an MCP connection can offer: Tools are actions Claude decides to call itself; Resources are data the surrounding app decides to hand Claude; Prompts are ready-made instructions a person picks, like a menu option.
.mcp.json
A config file listing which external MCP connections a project uses — shared with the whole team via version control, with any secret keys kept out of it and read from environment variables instead.
CLAUDE.md
A plain-text file Claude reads automatically at the start of every session in a project — the place for standing instructions like coding conventions or how the codebase is organized.
.claude/rules/
Instructions that only switch on for certain files (e.g. only when editing test files), instead of loading into every single session the way CLAUDE.md does.
Agent Skill
A packaged set of instructions Claude loads by itself when a task matches, without anyone typing a command — think of it as an instruction manual Claude picks up automatically off the shelf.
allowed-tools vs disallowed-tools
Two different settings that sound alike: allowed-tools just skips the usual 'are you sure?' prompt for tools you've pre-approved — it doesn't block anything else. disallowed-tools is what actually removes a tool from what's available.
Plan mode
A mode where Claude describes what it's about to do and waits for a yes before touching any files — used for bigger or riskier changes, skipped for small obvious ones.
Message Batches API
A cheaper way to send Claude a large pile of requests when nobody is waiting on the answer right away — costs about half as much, but can take up to a day to come back.
Prompt caching
A way to avoid Claude re-reading the same large, unchanging block of text (like a system prompt or a codebase) on every single request — it's remembered for a while, which is both cheaper and faster.
Context window
The total amount of conversation and text Claude can 'see' at once for a given request. When a conversation runs long, older parts may need to be summarized or dropped to make room.
Hub-and-spoke (multi-agent)
A setup where one coordinator agent hands out tasks to several helper agents and collects their answers — the helpers never talk directly to each other, only to the coordinator.
Anti-pattern
A design choice that looks reasonable but causes real problems in practice — this course names seven specific ones the exam likes to hide inside wrong answers.