AI Cert Prep
Type to search documentation.

Domains

D5 · Tools and MCPs

Tool-use lifecycle, tool descriptions and schemas, tool_choice, parallel tools, client- vs server-side tools, tool search, MCP fundamentals, authoring MCP servers, and configuring MCP in Claude Code and the Messages API.

This domain is roughly 5 of 53 items. It tests whether you can define tools well (the description is the single most important lever), run the tool-use lifecycle correctly, choose between client- and server-side tools, and understand and author MCP servers. The theme: tools are how Claude acts on the world – describe them precisely and connect them safely.

Learning objectives

By the end of this page you should be able to:

  1. Run the tool-use / function-calling lifecycle with correct JSON.
  2. Write effective tool descriptions and design input_schema.
  3. Use tool_choice (auto/any/tool/none) and know the Fable 5.1 restriction.
  4. Use parallel tool calls and distinguish client-side vs server-side tools.
  5. Use tool search + defer_loading, programmatic tool calling, and approval patterns.
  6. Explain MCP fundamentals and author an MCP server in Python (FastMCP) and TypeScript.
  7. Configure MCP in Claude Code / Desktop and via the MCP connector in the Messages API.

5.1 The tool-use lifecycle

json
{
"tools": [{
"name": "get_weather",
"description": "Get the current weather for a city. Use when the user asks about weather, temperature, or conditions in a named location.",
"input_schema": {
"type": "object",
"properties": {
"city": {"type": "string", "description": "City name, e.g. 'Paris'"},
"unit": {"type": "string", "enum": ["celsius", "fahrenheit"], "default": "celsius"}
},
"required": ["city"]
}
}]
}

Errors are returned with "is_error": true in the tool_result so Claude can recover.

A complete round-trip on the wire

The four Tabs above are the four wire messages of one round-trip. Read them as a single conversation so the tool_use_id threading is unmistakable. The request declares tools; Claude answers with stop_reason: 'tool_use'; you run the function and send a tool_result referencing the same id; Claude produces the end_turn answer.

python
import anthropic
client = anthropic.Anthropic()
TOOLS = [{
"name": "get_weather",
"description": "Get current weather for a city. Use when the user asks about "
"weather, temperature, or conditions in a named location. "
"Returns a short text summary. Read-only, no side effects.",
"input_schema": {
"type": "object",
"properties": {
"city": {"type": "string", "description": "City name, e.g. 'Paris'"},
"unit": {"type": "string", "enum": ["celsius", "fahrenheit"], "default": "celsius"},
},
"required": ["city"],
},
}]
messages = [{"role": "user", "content": "What's the weather in Paris?"}]
# 1. Model turn -> Claude asks for the tool
resp = client.messages.create(
model="claude-sonnet-5", max_tokens=1024, tools=TOOLS, messages=messages,
)
assert resp.stop_reason == "tool_use" # NEVER parse text to decide this
messages.append({"role": "assistant", "content": resp.content})
# 2. You run the tool for each tool_use block
for block in resp.content:
if block.type == "tool_use":
result = fetch_weather(**block.input) # your real function
messages.append({"role": "user", "content": [{
"type": "tool_result",
"tool_use_id": block.id, # MUST match the originating id
"content": result,
}]})
# 3. Model turn again -> Claude gives the final answer
final = client.messages.create(
model="claude-sonnet-5", max_tokens=1024, tools=TOOLS, messages=messages,
)
assert final.stop_reason == "end_turn"
print(final.content[0].text)

Loop on stop_reason, not on text

The canonical agent loop is while stop_reason == 'tool_use': run tools; call again. Parsing the assistant’s prose to decide whether to continue is anti-pattern #1. When a tool fails, return {"type": "tool_result", "tool_use_id": id, "content": "Error: timeout", "is_error": true} so Claude can retry or apologise – do not silently drop the result (anti-pattern #7).


5.2 Tool descriptions – the most important lever

The description is the primary determinant of whether Claude uses a tool correctly. Invest here more than anywhere else. A good description reads like the docstring you would write for a colleague who has never seen the function.

Good vs bad tool descriptions – four pairs

BadGood
get_data — ‘Gets data’get_order — ‘Retrieve one order by its ID. Use when the user references a specific order number. Returns status, line items and totals. Read-only.’
search — ‘Search’search_kb — ‘Full-text search of the help-centre knowledge base. Use for how-to and policy questions, NOT for live account data. Returns up to 5 article snippets with URLs.’
update — ‘Updates a record’update_shipping_address — ‘Change the shipping address on an unshipped order. Fails if the order has shipped. Side effect: writes to the orders DB. Confirm with the user first.’
run — ‘Runs a query’run_sql_readonly — ‘Execute a read-only SELECT against the analytics warehouse. Rejects INSERT/UPDATE/DELETE. Use for reporting questions. Returns at most 1000 rows as JSON.’

Each good description answers four questions: what it does, when to use it (and when not), what it returns, and what side effects / irreversibility it carries.

Good descriptionPoor description
States what the tool does, when to use it, when not to, and what it returns‘Gets data’
Documents each parameter with type, meaning and exampleUndocumented params
Notes side effects and irreversibilitySilent on side effects

Exam signal

“Claude picks the wrong tool / calls it with bad arguments” → improve the tool description and input_schema (types, enums, required, examples). Reach for the description before touching temperature or the model.


5.3 input_schema design checklist

The schema is JSON Schema. It constrains what Claude may pass and, with strict: true, guarantees the shape. Design it deliberately.

  • Types — give every property a type. Use the narrowest type (integer over number when whole).
  • Enums — constrain closed sets with enum so Claude cannot invent a value: {"type": "string", "enum": ["celsius", "fahrenheit"]}.
  • required — list every argument the tool genuinely needs. Omitted-but-required arguments are a top cause of malformed calls.
  • Descriptions per property — describe each field, with an example. Claude reads these.
  • Nullable / optional — optional params simply omit from required; to allow an explicit null, use {"type": ["string", "null"]}.
  • Bounds and formats — minimum, maximum, pattern, format: 'date' reduce garbage input.
  • strict: true — enable strict schema adherence so outputs conform exactly (also a way to get structured behaviour on Fable 5.1 where forced tools 400).
json
{
"name": "create_ticket",
"description": "Open a support ticket. Use only after the user confirms they want one.",
"input_schema": {
"type": "object",
"properties": {
"priority": {"type": "string", "enum": ["low", "normal", "high", "urgent"],
"description": "Urgency; default to 'normal' unless the user signals otherwise"},
"summary": {"type": "string", "description": "One-line summary, e.g. 'Cannot log in'"},
"due_date": {"type": ["string", "null"], "format": "date",
"description": "ISO date, or null if none"}
},
"required": ["priority", "summary"]
},
"strict": true
}

5.5 tool_choice

ValueBehaviourNotes
autoClaude decides whether to use a tool (default)Best general default; combine with a clear instruction
anyClaude must use one of the toolsForces some tool; 400 on Fable 5.1
{type: 'tool', name: '…'}Force one specific toolDeterministic single-tool extraction; 400 on Fable 5.1
noneDisable tools for this turnModel answers from context only

Fable 5.1 restriction

On Fable 5.1, tool_choice: 'any' and forced {type: 'tool'} return 400. Use auto with a clear instruction, strict: true schemas, or structured outputs (output_config.format). This is a client error — do not retry it with backoff.


5.6 Parallel tool calls

When subtasks are independent, Claude can emit multiple tool_use blocks in one response. Execute them concurrently and return all tool_result blocks in the next user message (matching each tool_use_id). If the calls are dependent (result of one feeds the next), Claude will serialise them across turns instead.

python
tool_uses = [b for b in resp.content if b.type == "tool_use"]
results = run_concurrently(tool_uses) # independent tools in parallel
messages.append({"role": "assistant", "content": resp.content})
messages.append({"role": "user", "content": [
{"type": "tool_result", "tool_use_id": u.id, "content": r}
for u, r in zip(tool_uses, results)]}) # ALL results in ONE user turn

Return every result, once

You must return exactly one tool_result per tool_use block, in the same user turn, each keyed to its tool_use_id. Missing or duplicated results cause a 400. To discourage parallelism when tools are stateful, set disable_parallel_tool_use: true in tool_choice.


5.7 Client-side vs server-side tools

TypeRuns whereExamples
Client-side (custom)Your codeYour APIs, DB queries, business functions
Server-side (Anthropic-hosted)AnthropicWeb search, code execution, computer use, text editor, bash, memory

Server-side tools are enabled by declaring them; Anthropic executes them and may pause the turn (pause_turn) while running.

Server-side built-in tools

ToolWhat it doesTypical use
Web searchLive web queries with citationsCurrent events, facts past the knowledge cutoff
Code executionRuns code in a sandboxData analysis, calculation, plotting
Text editorViews and edits files via a str-replace interfaceMulti-file code edits
BashRuns shell commands in a sandboxBuild/test/install steps in agentic coding
MemoryCross-session persistent storeRemembering user preferences across sessions
Computer useScreenshots + mouse/keyboard controlOperating a GUI when no API exists

Exam signal

“Search the live web / run code in a sandbox / edit files / operate a computer / remember across sessions” → server-side built-in tools. “Call our internal API / database / business function” → client-side custom tool.


5.8 Tool search, defer_loading and programmatic calling

  • Beyond ~10 tools, use the tool search tool with defer_loading: true so the full tool definitions are loaded on demand rather than all upfront — reduces context bloat and wrong-tool selection (anti-pattern #8).
  • Programmatic tool calling lets code invoke tools directly in a controlled loop.
  • Approval patterns: require human/hook approval before irreversible tool actions (see hooks, Domains 3 and 6).
python
# Large catalogue: defer full definitions, expose the search tool.
tools = [
{"type": "tool_search_tool_20250000", "name": "tool_search"}, # lets Claude find tools on demand
{"name": "get_order", "description": "...", "input_schema": {...}, "defer_loading": True},
{"name": "issue_refund", "description": "...", "input_schema": {...}, "defer_loading": True},
# ...50 more deferred tools; only the ones Claude searches for are hydrated into context
]
resp = client.messages.create(model="claude-sonnet-5", max_tokens=2048,
tools=tools, messages=messages)

The pattern keeps the active tool set small (4–5 in context at a time) even when the catalogue is large, which is exactly the fix for anti-pattern #8.


5.9 MCP fundamentals

The Model Context Protocol is an open standard (JSON-RPC 2.0) for connecting Claude to external tools and data.

PrimitiveControlled byExample
ToolsModelCallable functions
ResourcesApplicationFiles, records the app exposes
PromptsUserReusable prompt templates
  • Transports: stdio (local subprocess) and Streamable HTTP (remote; SSE is legacy).
  • Capability negotiation happens on initialize.
  • OAuth 2.1 secures remote servers.
text
Host (Claude Desktop / Code)
│ JSON-RPC 2.0
├── stdio ──► local MCP server (subprocess)
└── Streamable HTTP ──► remote MCP server (OAuth 2.1)

5.10 Authoring an MCP server

A minimal but complete server: it names itself, declares one tool with a typed schema and a docstring description, handles its own errors, and runs over stdio. Both SDKs below are runnable as-is.

python
# weather_server.py — run: python weather_server.py
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("weather")
@mcp.tool()
def get_weather(city: str, unit: str = "celsius") -> str:
"""Get current weather for a city.
Use when the user asks about weather, temperature or conditions in a
named location. Returns a short text summary. Read-only, no side effects.
"""
if not city:
raise ValueError("city is required")
return fetch_weather(city, unit)
@mcp.resource("config://units")
def default_units() -> str:
"""Application-controlled resource: the default unit system."""
return "celsius"
if __name__ == "__main__":
mcp.run() # stdio transport by default; use mcp.run(transport='streamable-http') for remote

5.11 Configuring MCP

Terminal window
# stdio (local subprocess)
claude mcp add weather --scope project -- python weather_server.py
# HTTP (remote server)
claude mcp add --transport http docs https://mcp.example.com/mcp --scope user
# list / remove
claude mcp list
claude mcp remove weather

Scopes: local (this machine, private), project (shared via .mcp.json, committed), user (all your projects).


5.12 MCP vs custom tool vs Skill – decision table

These three ways of extending Claude are frequently confused on the exam.

ChooseWhenRuns / livesControlled by
Custom tool (Messages API)You need one or a few functions for a single app; full control of executionYour code, per requestYour API integration
MCP serverYou want reusable tools/resources shared across apps, hosts (Code, Desktop, API) and teamsA separate process (stdio/HTTP), reused everywhereAn open protocol
SkillYou want to package instructions + files + scripts that load progressively on demand in Claude Code.claude/skills/<name>/SKILL.md, loaded when relevantA markdown file with frontmatter

Rule of thumb: one app, few functions → custom tools; reuse across hosts/teams → MCP; packaged know-how and helper scripts for Claude Code → Skill.

Exam signal

“Share the same tools across Claude Code, Desktop and our API” → MCP. “Bundle a workflow with reference files and helper scripts that Claude loads only when needed” → Skill. “Just call our one internal endpoint from this service” → custom tool.


5.13 MCP transports, capability negotiation and OAuth 2.1

The wire details of MCP recur in exam items. MCP is JSON-RPC 2.0; a host and server negotiate capabilities on initialize, then exchange tool/resource/prompt calls over a transport.

AspectstdioStreamable HTTP
Where the server runsLocal subprocessRemote endpoint
AuthLocal process trust / envOAuth 2.1
Best forLocal dev tools, Desktop/Code pluginsShared/team servers, the Messages API connector
Legacy note—SSE-only transport is legacy; prefer Streamable HTTP
text
Host ──initialize──► Server (capability negotiation: tools? resources? prompts?)
Host ◄─capabilities─ Server
Host ──tools/list───► Server (discover model-controlled tools)
Host ──tools/call───► Server (Claude invokes a tool)
Host ──resources/read► Server (application-controlled data)
Host ──prompts/get──► Server (user-controlled templates)

Exam signal

‘Local subprocess tool’ → stdio. ‘Remote/shared server, secure it’ → Streamable HTTP + OAuth 2.1. ‘Which primitive does the model decide to call?’ → tools (resources = application, prompts = user). Capability negotiation happens on initialize.


5.14 Approval and human-in-the-loop for irreversible tools

Some tools are irreversible (refunds, deletes, sends, deploys). The correct design gates them deterministically, not by trusting the model.

ControlMechanismNotes
AllowlistOnly expose the tools the task needsLeast privilege; keep the irreversible ones out of routine agents
PreToolUse hookBlock (exit 2) and route to approvalThe model cannot argue past it (anti-pattern #3)
Approval workflowHuman sign-off before executionFor payments/deletes/deploys
is_error on resultsReport tool failures back so Claude recoversNever drop a result silently (anti-pattern #7)
python
# Gate an irreversible tool behind approval; return is_error so Claude can adapt.
if block.name == "issue_refund" and block.input["amount_cents"] > 50000:
result = {"type": "tool_result", "tool_use_id": block.id,
"content": "Refund over $500 requires human approval; request queued.",
"is_error": True}

Approval is enforcement, not a prompt

‘Ask the user before refunding’ in the tool description is guidance the model may skip. The guarantee comes from a hook/approval gate plus keeping the tool out of the allowlist where it is not needed.


5.15 Common misconceptions

MisconceptionRealityWhy it matters on the exam
A vague description is fine if the schema is tightThe description is the primary lever for correct tool useWrong-tool/bad-args questions
tool_choice: 'any' works everywhereFable 5.1 rejects any/forced tools (400)A recurring breaking-change trap
A 400 on forced tool choice is transientIt is deterministic; change the request, do not backoffDistinguishes layers
Web search is a tool you implementWeb search and code execution are server-side built-insClient vs server-side sorting
Resources are model-controlled like toolsResources are application-controlled; prompts are user-controlledMCP primitive mapping
Parallel tool results can span multiple turnsAll results go in one user turn, each keyed to its tool_use_idProtocol-shape 400s
More tools make selection betterBeyond ~10, selection degrades; use tool search + defer_loadingAnti-pattern #8
A Skill can share tools across hostsSkills are Claude-Code packaged know-how; cross-host reuse is MCPSkill/MCP/tool decision

5.16 Scenario walkthrough: a payments tool that must be safe and portable

Scenario. A fintech wants Claude to look up orders, search a help centre, and — carefully — issue refunds, and they want the same tools available in Claude Code, Claude Desktop and a production Messages API service, maintained in one place. They are on Fable 5.1 for its reasoning quality. Early attempts force the refund tool with tool_choice: {type: 'tool', name: 'issue_refund'} (getting 400s), sometimes drop a tool result when two tools run in parallel (getting 400s), and issue refunds with no approval step.

Expert reasoning trace.

  1. Choose the reuse mechanism. ‘Same tools across Code, Desktop and the API, one place’ → build an MCP server and connect each host (stdio locally, Streamable HTTP for the shared/remote server secured with OAuth 2.1; the Messages API uses the MCP connector). Copying custom-tool JSON into each app is the maintenance trap.
  2. Fix the Fable 5.1 400s on tool choice. Fable 5.1 rejects forced/any tool choice. Use tool_choice: 'auto' with a clear instruction, or structured outputs / strict: true for the extraction parts. Retrying the 400 with backoff is wrong — it is deterministic.
  3. Fix the parallel-result 400s. Return exactly one tool_result per tool_use block, all in the same following user turn, each keyed to its tool_use_id; on failure return is_error: true so Claude can recover. Dropping one result causes the 400.
  4. Make refunds safe. Refunds are irreversible → gate them with a PreToolUse hook / approval workflow and keep the refund tool out of any routine read-only agent’s allowlist. ‘Confirm before refunding’ in the description is not enforcement.
  5. Right-size the catalogue. Keep ~4–5 active tools; if the catalogue grows, use tool search + defer_loading.
  6. Reject the tempting alternatives. ‘Force the refund tool for determinism’ — 400 on Fable 5.1. ‘Retry the 400 with backoff’ — deterministic error. ‘Put the tools in CLAUDE.md to share them’ — CLAUDE.md holds instructions, not executable tools. ‘Use a Skill to share across hosts’ — Skills are Claude-Code-only packaged know-how.

Correct decision. An MCP server (OAuth 2.1 for the remote transport) connected to all hosts; auto + instruction or structured outputs on Fable 5.1 instead of forced tool choice; correct parallel tool_result handling with is_error; refunds behind an approval hook and out of routine allowlists; tool search + defer_loading if the catalogue grows.


Exam traps in this domain

TrapWhy it is wrong
Blaming the model when it mis-uses a toolFix the description and input_schema first
Forcing a tool on Fable 5.1any/forced return 400; use structured outputs / strict / auto
Loading 18 tools upfrontOverloads selection; use tool search + defer_loading (anti-pattern #8)
Returning tool errors as plain successMark is_error so Claude can recover
Mismatching tool_use_id on resultsResults must reference the originating tool_use_id
Using a client tool for web searchWeb search is a server-side built-in
Treating MCP resources as model-controlledResources are application-controlled; tools are model-controlled
Ignoring OAuth for remote MCPRemote servers use OAuth 2.1
Parsing assistant text to decide whether to keep calling toolsLoop on stop_reason == 'tool_use' (anti-pattern #1)
Using a Skill when tools must be shared across hostsCross-host/team reuse is MCP; Skills are Claude-Code packaged know-how
Hard-coding secrets in .mcp.jsonReference env vars (${VAR}); never commit credentials
Securing a remote MCP server with a static header instead of OAuth 2.1Remote/Streamable HTTP servers use OAuth 2.1
Returning parallel tool results across multiple user turnsAll results go in one user turn, each keyed to its tool_use_id
Relying on ‘confirm before refunding’ in the tool descriptionGuidance, not enforcement; gate irreversible tools with a hook/approval and least privilege
Retrying a forced-tool 400 on Fable 5.1 with backoffDeterministic client error; use auto/structured outputs, do not retry
Using stdio for a shared, remote team serverstdio is for local subprocesses; remote/shared → Streamable HTTP + OAuth 2.1

Practice questions

Q1 · Claude frequently calls the wrong tool and passes malformed arguments. What is the FIRST thing to improve? (Select one)

A. Lower temperature. B. Rewrite the tool descriptions and tighten input_schema (types, enums, required, examples, when-to-use). C. Switch to Opus 5. D. Increase max_tokens.

Answer: B. The description and schema are the primary levers for correct tool use. Temperature (A), model (C) and max_tokens (D) are secondary.

Q2 · A developer sets `tool_choice: 'any'` on Fable 5.1 and receives a 400. What is the correct approach? (Select one)

A. Retry with backoff. B. Use tool_choice: 'auto' with an instruction, or structured outputs / strict: true, since Fable 5.1 rejects forced tool choice. C. Switch to none. D. Increase max_tokens.

Answer: B. Fable 5.1 rejects any/forced tool choice; use auto + instruction or structured outputs. It is a 400 client error, not transient (A); none (C) disables tools.

Q3 · An agent has grown to 18 tools and selection quality has dropped. What is the recommended fix? (Select one)

A. Add more descriptive tools. B. Reduce to a focused set and use the tool search tool with defer_loading for the larger catalogue. C. Force a tool every turn. D. Raise temperature.

Answer: B. Too many tools is anti-pattern #8; the fix is fewer tools plus tool search + defer_loading. More tools (A) worsens it.

Q4 · Which of the following are server-side (Anthropic-hosted) tools? (Select two)

A. Web search. B. Your company’s internal orders API. C. Code execution. D. A custom database query function you wrote. E. A local shell script you maintain.

Answer: A and C. Web search and code execution are Anthropic-hosted server-side tools. Your API (B), custom DB function (D) and local script (E) are client-side.

Q5 · A team wants a remote MCP server's tools available directly from the Messages API without re-declaring each tool. What enables this? (Select one)

A. The Files API. B. The MCP connector in the Messages API. C. Prompt caching. D. Batch API.

Answer: B. The MCP connector lets the Messages API call remote MCP servers directly. The Files API (A), caching (C) and batching (D) are unrelated.

Q6 · In the tool-use loop, how should code decide whether to run another tool round-trip? (Select one)

A. Parse the assistant’s text for phrases like ‘let me check’. B. Check whether resp.stop_reason == 'tool_use' and, if so, run the tools and call the API again. C. Loop a fixed 5 times regardless of output. D. Continue until the response is empty.

Answer: B. The API tells you deterministically via stop_reason. Parsing prose (A) is anti-pattern #1; a fixed cap (C) is anti-pattern #2; empty-response detection (D) is unreliable.

Q7 · A `get_order` tool returns a timeout. How should the result be sent back to Claude? (Select one)

A. Omit the tool_result so Claude ignores it. B. Return an empty string as the content. C. Return a tool_result with the same tool_use_id, an error message, and is_error: true. D. Raise an exception and stop the conversation.

Answer: C. Errors must be reported back with the matching id and is_error: true so Claude can retry or apologise. Omitting (A) causes a 400; empty content (B) is a silent failure (anti-pattern #7); crashing (D) hides diagnostics.

Q8 · Which properties in an `input_schema` best prevent Claude from inventing an invalid argument value for a closed set like a priority level? (Select one)

A. A high max_tokens. B. An enum constraining the allowed values, plus listing the field in required. C. Setting tool_choice: 'any'. D. Lowering temperature.

Answer: B. enum restricts the value space and required guarantees the field is provided. max_tokens (A) and temperature (D) are unrelated to schema validity; tool_choice (C) governs whether a tool is used, not its arguments.

Q9 · A support agent needs to reliably extract structured order data on Fable 5.1, but forcing a specific tool returns 400. What are the TWO valid approaches? (Select two)

A. Use tool_choice: 'auto' with a clear instruction to call the extraction tool. B. Retry the forced call with exponential backoff. C. Use structured outputs via output_config.format with a JSON schema. D. Switch tool_choice to none. E. Downgrade to Haiku 4.5 for all traffic.

Answer: A and C. Fable 5.1 rejects forced/any tool choice, so use auto + instruction or structured outputs. Backoff (B) will not fix a 400 client error; none (D) disables tools; changing model for everything (E) is an over-broad, unwarranted change.

Q10 · A developer wants the SAME set of tools available in Claude Code, Claude Desktop, and their production Messages API service, maintained in one place. What is the best mechanism? (Select one)

A. Copy the custom tool JSON into each app. B. Build an MCP server and connect each host to it (stdio/HTTP; connector for the API). C. Write a Skill in .claude/skills/. D. Put the tools in CLAUDE.md.

Answer: B. MCP is the reuse-across-hosts mechanism. Copying JSON (A) duplicates maintenance; a Skill (C) is Claude-Code-only packaged know-how, not a cross-host tool server; CLAUDE.md (D) is instructions, not executable tools.

Q11 · Which MCP primitive is model-controlled, meaning Claude decides when to invoke it? (Select one)

A. Resources. B. Prompts. C. Tools. D. Transports.

Answer: C. Tools are model-controlled. Resources (A) are application-controlled, Prompts (B) are user-controlled, and transports (D) are the connection mechanism (stdio / Streamable HTTP), not a primitive Claude invokes.

Q12 · Claude asks for two independent tools (`get_weather` for Paris and `get_weather` for Tokyo) in a single response. How must the results be returned? (Select one)

A. Two separate user turns, one result each. B. One user turn containing both tool_result blocks, each keyed to its own tool_use_id. C. A single tool_result combining both cities. D. Ignore one and answer the other.

Answer: B. Parallel tool_use blocks are answered with all tool_result blocks in one user turn, each matching its originating tool_use_id. Splitting turns (A), merging ids (C), or dropping a result (D) all break the protocol / cause a 400.

Q13 · You need to bundle a multi-step invoice-processing workflow with reference templates and a helper Python script so Claude Code loads it only when relevant. Which mechanism fits best? (Select one)

A. A custom Messages API tool. B. A remote MCP server. C. A Skill (SKILL.md with frontmatter and bundled files, loaded progressively). D. A PreToolUse hook.

Answer: C. Skills package instructions plus files and scripts that load on demand in Claude Code — exactly this scenario. A custom tool (A) is a single function; an MCP server (B) is for cross-host tool sharing; a hook (D) is a deterministic guardrail, not a packaged workflow.

Q14 · A team must expose a shared, remote MCP server to Claude Desktop, Claude Code and the Messages API. Which transport and auth are appropriate? (Select one)

A. stdio with local process trust. B. Streamable HTTP secured with OAuth 2.1. C. A static bearer header hard-coded in .mcp.json. D. SSE-only transport, which is the current recommended default.

Answer: B. Remote/shared servers use Streamable HTTP secured with OAuth 2.1. stdio (A) is for local subprocesses; a hard-coded header (C) is a secrets/hygiene problem and not the standard auth; SSE-only (D) is legacy, not the recommended default.

Q15 · Claude asks for two independent tools in one response, but the code returns each result in its own separate user turn, and requests start failing with 400. What is the fix? (Select one)

A. Merge both outputs into a single combined tool_result. B. Return both tool_result blocks in one user turn, each keyed to its own tool_use_id. C. Drop one result to simplify the turn. D. Retry the 400 with exponential backoff.

Answer: B. Parallel tool_use blocks must be answered with all tool_result blocks in one user turn, each matching its originating id. Merging ids (A) or dropping a result (C) breaks the protocol; the 400 is deterministic, so backoff (D) will not help.

Q16 · An agent may issue refunds, but any refund over $500 needs human approval. Where must this be enforced? (Select one)

A. In the issue_refund tool description (‘confirm before refunding over $500’). B. In a PreToolUse hook / approval workflow that blocks the over-threshold refund and routes to a human, with the tool kept out of routine agents’ allowlists. C. By lowering the model’s temperature. D. By asking the model to double-check the amount.

Answer: B. Irreversible actions are gated deterministically by a hook/approval and least privilege, not by the description or self-checks. Description text (A) and self-checks (D) are guidance the model can skip (anti-pattern #3); temperature (C) is irrelevant.

Q17 · Which MCP interaction happens on `initialize`, and how are the three primitives controlled? (Select one)

A. Capability negotiation occurs on initialize; tools are model-controlled, resources application-controlled, prompts user-controlled. B. Authentication occurs on initialize; all three primitives are model-controlled. C. Nothing happens on initialize; primitives are negotiated per call. D. initialize returns the OAuth token; tools are user-controlled.

Answer: A. Hosts and servers negotiate capabilities on initialize, and the control mapping is tools (model), resources (application), prompts (user). B mislabels all primitives; C is wrong (negotiation is at init); D scrambles the mapping and misstates OAuth.

Q18 · A support agent on Fable 5.1 must reliably extract structured order data, but forcing the extraction tool returns 400. Which TWO approaches are valid? (Select two)

A. Use tool_choice: 'auto' with a clear instruction to call the extraction tool. B. Use structured outputs via output_config.format with a JSON schema. C. Retry the forced-tool call with backoff. D. Switch tool_choice to none. E. Downgrade all traffic to Haiku 4.5.

Answer: A and B. Fable 5.1 rejects forced/any tool choice, so auto + instruction or structured outputs are valid. Backoff (C) will not fix a deterministic 400; none (D) disables tools; changing every request’s model (E) is an over-broad, unwarranted change.

Q19 · A remote MCP server tool call fails with a timeout during a parallel call alongside a successful tool. How should the failed call be represented? (Select one)

A. Omit its tool_result so Claude moves on. B. Return a tool_result with the same tool_use_id, an error message, and is_error: true, in the same user turn as the successful result. C. Retry silently and return empty content. D. Crash the conversation to force a restart.

Answer: B. Every tool_use needs a matching tool_result (with is_error: true on failure) in the same turn so Claude can recover. Omitting it (A) causes a 400 and hides the failure; empty content (C) is silent suppression (anti-pattern #7); crashing (D) loses diagnostics.

Key takeaways

  • The tool-use lifecycle: define tools → tool_use → run and return tool_result (matching tool_use_id) → final answer.
  • The tool description and input_schema are the single most important lever for correct tool use.
  • tool_choice is auto/any/tool/none; Fable 5.1 rejects any and forced tools – use structured outputs / strict / auto.
  • Independent tools can run in parallel; return all results in one user turn.
  • Client-side tools run in your code; server-side built-ins (web search, code execution, computer use, text editor, bash, memory) run at Anthropic.
  • Beyond ~10 tools, use tool search + defer_loading; require approval for irreversible actions.
  • MCP is JSON-RPC 2.0 with tools (model), resources (app), prompts (user); stdio for local, Streamable HTTP + OAuth 2.1 for remote; author with FastMCP (Python) or @modelcontextprotocol/sdk (TS).
  • Capability negotiation happens on initialize; choose stdio for local subprocess tools and Streamable HTTP + OAuth 2.1 for shared/remote servers (SSE-only is legacy).
  • Gate irreversible tools (refunds/deletes/sends/deploys) with a PreToolUse hook / approval workflow and least privilege — the tool description is guidance, not enforcement.
  • Return every parallel tool_result in one user turn keyed to its tool_use_id, and mark failures with is_error: true so Claude can recover.
  • A forced-tool 400 on Fable 5.1 is deterministic — switch to auto/structured outputs rather than retrying with backoff.

Last updated Sep 18, 2026