Questions? Call 1.800.716.8955

Chapter 9

Tools and MCPs (10.6%)

What the exam tests: implementing tool use well, building and integrating MCP servers, and choosing between built-in tools, custom tools, Skills, and MCP for a given need.

Tool implementation (4.4%)

Defining a tool. Each tool has a name, a description, and an input_schema (JSON Schema). The description is the most important part: say what the tool does, when to use it, when not to, what each parameter means, and what it returns. Aim for three or four sentences or more for anything non-trivial. {

"name": "get_order_status",

"description": "Look up the current status of a customer order by order ID. Use this when the user asks where an order is or when it will arrive. Returns status, carrier, and estimated delivery date. Does not modify orders.",

  • "input_schema": {
  • "type": "object",
  • "properties": {

"order_id": {"type": "string", "description": "Order ID such as ORD-12345"} },

  • "required": ["order_id"],
  • "additionalProperties": false
  • },
  • "strict": true
  • }

The tool-use loop.

Send tools with the request.

Claude returns stop_reason: "tool_use" and one or more tool_use blocks, each with an id, name, and input.

Your code runs each tool.

Send back a user message containing a tool_result block per call, each referencing its tool_use_id.

Repeat until stop_reason is end_turn.

Parallel tool calls. Claude may request several tools in one turn. Run them concurrently and return all results in a single user message. Splitting results across messages teaches the model to stop calling tools in parallel.

Errors. If a tool fails, return a tool_result with is_error: true and a helpful message ("Order not found; IDs look like ORD-12345"). Never silently drop it.

tool_choice. auto (default; Claude decides), none (no tools), any (must use some tool), or tool (must use a named tool). On the newest models, any and forced tool return an error; use auto with a clear instruction, strict: true, or structured outputs instead. disable_parallel_tool_use limits Claude to at most one call per turn.

Client-side vs server-side tools.

Type Who executes Examples

Client tools (userdefined)Your code Database lookups, internal APIs, anything custom

Anthropic-defined cli‐

ent tools

Your code, with a schema Anthropic defines Bash, text editor, computer use, memory Server tools Anthropic's infrastructure; results arrive in the

  • same response
  • Web search, web fetch, code execution,
  • tool search

Approval patterns. For risky tools, pause before execution and ask a human (or a policy check) to approve. In agent harnesses this is a permission rule or a PreToolUse hook; in your own loop it is a check between steps 2 and 3.

Tool set construction. Fewer, well-designed tools beat many overlapping ones. Consolidate related operations, use clear distinct names (namespacing such as crm_search_contacts), return concise high-signal results, and paginate. With very large tool sets, use tool search with defer_loading: true so only relevant tool definitions load.

MCP server development (2.1%)

The Model Context Protocol (MCP) is an open standard for connecting AI applications to tools and data. Build an integration once as an MCP server; any MCP-capable client (Claude Code, Claude Desktop, claude.ai connectors, the Agent SDK, the API's MCP connector) can use it.

Figure 8. MCP architecture: one client per server inside the host

Architecture. A host application (such as Claude Desktop) runs one MCP client per connected server. The server exposes capabilities; the client relays them to the model.

Server primitive Controlled by Purpose

Tools The model Actions Claude can call, such as create_ticket

Resources The application Read-only data the app can attach as context, such as files or records Prompts The user Reusable templates, often surfaced as slash commands Transports.

stdio: the client launches the server as a local subprocess and talks over standard input and output. Best for local tools on the developer's machine.

Streamable HTTP (over sockets): the server runs remotely as a web service, supports many clients, and uses standard auth such as OAuth. Best for shared, hosted integrations. (The older HTTP+SSE transport is deprecated.)

Authoring. Use the official SDKs (Python, TypeScript, and others). Register tools with names, descriptions, and schemas just as you would for API tools. Validate inputs, enforce the caller's permissions, and return errors as results.

Integration with Claude. In Claude Code, add servers with claude mcp add or a project .mcp.json. In the Messages API, the MCP connector lets you list remote servers in the request (mcp_servers) alongside an mcp_toolset entry in tools.

Agentic customization (4.1%)

Choosing the right extension mechanism is a favorite scenario theme.

Functions you define in your app's request One app needs a specific action tied to its own code Skills Packaged instructions, scripts, and resources

  • loaded on demand
  • You need repeatable know-how or a procedure (a style
  • guide, a report workflow), not a new connection

Mcp

  • servers
  • Standalone services exposing tools, re‐
  • sources, prompts over a standard protocol
  • The same integration must be reused across apps, clients,
  • or teams, and maintained independently

Rules of thumb: Skills teach Claude how; tools and MCP give Claude access. If several applications need it, lean MCP. If only one app needs it and it is simple, a custom tool is enough. If it already exists built in, do not rebuild it.

Worked example 9.1: a weak and a strong tool definition

Weak:

{ "name": "search", "description": "Searches.",

"input_schema": { "type": "object", "properties": { "q": { "type": "string" } } } } Claude cannot tell what this searches, when to prefer it over other tools, what q should look like, or what comes back.

  • Strong:
  • {
  • "name": "kb_search_articles",

"description": "Search the customer help-center knowledge base for published support articles. Use this when the customer asks how to do something in the product or reports a common problem. Do not use it for account-specific questions such as billing status; use crm_get_account for those. Returns up to 5 articles with title, URL, and a 2-sentence excerpt, ranked by relevance.",

  • "input_schema": {
  • "type": "object",
  • "properties": {

"query": { "type": "string", "description": "Short natural-language search, e.g. 'reset two-factor authentication'" },

"product_area": { "type": "string", "enum": ["billing", "security", "integrations", "general"],

Improvements: a namespaced, specific name; what, when, and when not; a pointer to the alternative tool; parameter descriptions with an example; an enum for the closed set; return-value description; and strict: true so inputs always match the schema.

Worked example 9.2: the wire format for parallel tool calls Claude asks for two tools at once:

{"role": "assistant", "content": [

{"type": "text", "text": "I'll check both the order and the account."}, {"type": "tool_use", "id": "toolu_01A", "name": "get_order", "input": {"order_id":

"ord-881"}},

{"type": "tool_use", "id": "toolu_01B", "name": "crm_get_account", "input": {"email": "sam@example.com"}}

]}

The correct reply is one user message with both results, each matched by tool_use_id: {"role": "user", "content": [

{"type": "tool_result", "tool_use_id": "toolu_01A", "content": "{\"status\": \"shipped\", \"eta\": \"2026-11-02\"}"},

{"type": "tool_result", "tool_use_id": "toolu_01B", "is_error": true, "content": "No account found for sam@example.com. Ask the customer to confirm their email."}

]}

Note the second result is an error with guidance, which lets Claude respond helpfully instead of stalling.

Worked example 9.3: using a server tool

Server tools run on Anthropic's side; you declare them and results come back in the same response. response = client.messages.create(

model="claude-opus-5-5",

max_tokens=4096,

tools=[{"type": "web_search_20260209", "name": "web_search", "max_uses": 5, "allowed_domains": ["docs.python.org", "peps.python.org"]}],

messages=[{"role": "user", "content": "What changed in the latest Python release's typing module?"}],

)

There is no loop to write for the search itself. Domain allow-lists and max_uses are your controls for scope and cost. If a long server-tool turn returns stop_reason: "pause_turn", send the response back as-is to let it continue. (Tool type version strings change over time; use the current one from the docs.)

TOOLS AND MCPS (10.6%)

Worked example 9.4: building an MCP server

An inventory MCP server in Python with the official SDK's FastMCP helper:

  • from mcp.server.fastmcp import FastMCP
  • mcp = FastMCP("inventory")
  • @mcp.tool()
  • def get_stock(sku: str) -> dict:

"""Return on-hand quantity and warehouse location for a product SKU.

  • Use when a user asks whether an item is in stock."""
  • row = db.lookup(sku)
  • if row is None:

raise ValueError(f"Unknown SKU {sku}. SKUs look like AB-12345.")

return {"sku": sku, "on_hand": row.qty, "warehouse": row.location} @mcp.resource("inventory://warehouses")

  • def list_warehouses() -> str:
  • """Reference list of warehouses and their regions."""
  • return render_warehouse_table()
  • @mcp.prompt()
  • def restock_report(region: str) -> str:
  • """Template for a weekly restock report."""

return f"Using the inventory tools, list SKUs below reorder level in {region}..." if __name__ == "__main__":

mcp.run() # stdio by default, for local clients

# mcp.run(transport="streamable-http") # for a shared remote deployment All three primitives appear: a tool (model-controlled action), a resource (application-attached data), and a prompt (user-invoked template).

Connecting it.

Claude Code, local: claude mcp add inventory -- python server.py, or commit a project .mcp.json such as {"mcpServers": {"inventory": {"command": "python", "args": ["server.py"]}}} so the whole team gets it.

Claude Code, remote: add the server's HTTPS URL with the HTTP transport option. Messages API: the MCP connector (a beta) lists the remote server in mcp_servers and enables it with an mcp_toolset entry in tools. Claude Desktop and claude.ai: add it as a connector.

Deployment choices.

Choice stdio Streamable HTTP

Runs where On each user's machine as a subprocess Central server or cloud service Users One Many

Auth Inherits local user's environment OAuth or tokens per user

Choice stdio Streamable HTTP

Updates Each user updates Deploy once

Typical use Developer tools, local files Company-wide integrations

Worked example 9.5: choosing the extension mechanism

Need Best fit Why

Claude should search the public web Built-in web search tool Already exists; nothing to build One internal app needs to call its own pricing

  • function
  • Custom tool Single app, simple, lives with the
  • app's code

Six teams' apps need to read and update Jira MCP server Reuse across apps and clients, maintained by one team

  • Every financial model must follow the firm's
  • formatting and checks
  • Skill It is know-how and procedure, not
  • a connection
  • Claude Code should generate release notes
  • from git history in a house style
  • Skill (with a helper script) Procedure plus a small script,
  • loaded on demand

An agent needs 300 tools from many systems MCP servers plus tool search with deferred loading

Keeps context small while keeping

access

A one-off data cleanup this afternoon Built-in code execution or bash No reason to build anything reusable

Worked example 9.6: tool-set design review

A team's agent has 14 tools: get_user, get_user_by_email, get_user_by_id, find_user, lookup_customer, search_customers, and eight more. Claude often picks the wrong one. Recommendations:

Consolidate. Replace the six user tools with one crm_find_customer(id | email | name). Namespace. Prefix by system (crm_, billing_, kb_) so purposes are obvious.

Return high-signal data. Return name, plan, status, and ID, not 80 raw database columns. Paginate. Large results return the first 10 plus a cursor.

Test descriptions. Add tool-choice cases to the eval set ("user asks X, correct tool is Y").

Scenario drills

A developer splits parallel tool results into separate user messages and notices Claude stops calling tools in parallel. Why?

An MCP server works on the developer's laptop but colleagues cannot use it. It uses stdio. What should change?

A tool returns a 5 MB JSON blob on every call. What is the problem and the fix? Your code receives tool_use input that fails your own validation. What should you return? A team wants one approval step before any tool that spends money runs. Where should it go?

TOOLS AND MCPS (10.6%)

When would you choose a custom tool over an MCP server?

Answers:

Splitting results teaches the model that parallel calls are not handled well; always return all results in one message.

Deploy it as a remote server over Streamable HTTP with per-user auth (or have each colleague run it locally). Context bloat and cost; return a concise summary or the fields needed, with pagination or a follow-up tool for detail.

A tool_result with is_error: true explaining what was invalid, so Claude can correct the call (and consider strict: true).

In code between receiving the tool_use and executing it (a permission rule or PreToolUse hook in agent harnesses), surfaced to a human.

When only one application needs it, it is tightly bound to that app's code, and nobody else will reuse it.

Common traps

One-line tool descriptions. They are the main cause of wrong or missing tool calls. Returning tool results in separate user messages after parallel calls.

Hard-coding integration logic into prompts instead of tools.

Building an MCP server for a one-off, single-app action.

Assuming built-in tools can reach your private internal APIs.

Using stdio for a server many remote users must share.

Check yourself

Claude keeps calling search when it should call lookup_customer. What do you fix first?

Three tools are called in parallel. How do you return the results?

Which MCP primitive is controlled by the model, and which by the user?

A team wants their brand writing guidelines applied whenever Claude drafts marketing copy. Tool, Skill, or MCP?

Which transport suits an MCP server hosted for a whole company?

Answers: The tool names and descriptions, making their purposes distinct. All three tool_resul t blocks in one user message. Tools are model-controlled; prompts are user-controlled (resources are application-controlled). A Skill. Streamable HTTP.

Use ← → to move between sections