Tool Implementation: CCDV-F study guide
CCDV-F · Tools and MCPs, topic weight 4.4% of the exam
Tool Implementation is the largest topic in Domain 8, Tools and MCPs (10.6% of the CCDV-F exam), at 4.4% on its own. It tests whether you can write tools Claude uses correctly and the code that runs them safely: the definition, the dispatch loop, error results and approval before risky actions. The design principles for tool descriptions and structured errors are covered in CCAR-F 2.1 and 2.2; this page is about building them.
What the official guide covers
The Claude Certified Developer Foundations exam guide (version 1.0, effective July 2026) describes this topic as tool implementation practices for Claude applications:
| What the guide lists | What it means in practice |
|---|---|
| Tool use and function calling | Claude returns tool_use blocks; your code runs each one and sends back a tool_result |
| Configuration for external system interaction | Endpoints, credentials and timeouts live in your handler's configuration, not in the schema Claude sees |
| Tool description writing | Describe the tool's job, when to call it and when to avoid it, what each parameter means, and what it does not return |
| Error handling | Return failures as tool_result blocks with is_error: true and a message that says what to try next |
| Tool usage patterns: harness dispatch, client-side vs server-side tools, approval patterns | Map tool names to handlers, know who executes each tool, and pause for a person before risky calls |
| Tool set construction | Fewer, consolidated tools with namespaced names and responses that carry only what Claude needs |
Client tools and server tools
| Client tools | Server tools | |
|---|---|---|
| Who runs them | Your application | Anthropic's infrastructure |
| Examples | Tools you define; Anthropic-defined tools such as bash, text editor, memory and computer use | Web search, web fetch, code execution, tool search |
| What you see | stop_reason: "tool_use" and tool_use blocks you must execute | The results, already in the response |
| What you build | The handler, the loop and the tool_result | Nothing to execute |
Anthropic-defined client tools have a schema Anthropic designed, but your code still carries out the action. That matters for security: a bash tool runs commands wherever your handler runs them.
Anatomy of a tool definition
A tool definition has a name (letters, digits, _ and -, up to 128 characters), a description, and an input_schema in JSON Schema. Two optional fields help:
input_examples: example inputs that must validate against the schema. Useful for nested or format-sensitive parameters.strict: true: constrains Claude's tool inputs so they always match your schema, which removes missing parameters and type mismatches. Strict schemas set"additionalProperties": false.
Anthropic calls detailed descriptions "by far the most important factor" in tool performance and suggests at least three to four sentences per tool.
The dispatch loop with errors and an approval gate (Python)
import json
import anthropic
client = anthropic.Anthropic()
TOOLS = [
{
"name": "orders_get",
"description": (
"Look up one order by its order ID and return its status, items and total. "
"Use this before answering any question about a specific order. "
"Order IDs look like ORD-12345. It does not return payment card details."
),
"input_schema": {
"type": "object",
"properties": {"order_id": {"type": "string", "description": "Order ID, e.g. ORD-12345"}},
"required": ["order_id"],
"additionalProperties": False,
},
"strict": True,
},
{
"name": "orders_cancel",
"description": (
"Cancel an order that has not shipped. Use only when the customer clearly asks "
"to cancel. Fails if the order has shipped; offer a return instead in that case."
),
"input_schema": {
"type": "object",
"properties": {"order_id": {"type": "string", "description": "Order ID, e.g. ORD-12345"}},
"required": ["order_id"],
"additionalProperties": False,
},
"strict": True,
},
]
HANDLERS = {"orders_get": get_order, "orders_cancel": cancel_order}
NEEDS_APPROVAL = {"orders_cancel"}
def run_tool(block) -> dict:
result = {"type": "tool_result", "tool_use_id": block.id}
if block.name in NEEDS_APPROVAL and not ask_operator(block.name, block.input):
return result | {"content": "A person declined this action. Tell the customer it was not done.",
"is_error": True}
try:
return result | {"content": json.dumps(HANDLERS[block.name](**block.input))}
except OrderNotFound:
return result | {"content": f"No order {block.input['order_id']}. Ask the customer to check the ID.",
"is_error": True}
except OrderShipped:
return result | {"content": "Order has shipped and cannot be cancelled. Offer a return.",
"is_error": True}
messages = [{"role": "user", "content": "Please cancel ORD-48213, it hasn't shipped yet."}]
while True:
response = client.messages.create(model=MODEL, max_tokens=1024, tools=TOOLS, messages=messages)
messages.append({"role": "assistant", "content": response.content})
if response.stop_reason == "end_turn":
break # the task is finished
if response.stop_reason == "pause_turn":
continue # server tool paused: send the turn back as it is
if response.stop_reason != "tool_use": # max_tokens, refusal, context full
raise RuntimeError(f"Loop stopped early: {response.stop_reason}")
messages.append({"role": "user", "content": [run_tool(b) for b in response.content if b.type == "tool_use"]})
Three things to notice. Every tool_use gets a tool_result with the same ID, all in one user message. Failures come back as results Claude can act on, not as exceptions that end the loop. The approval check runs in your code before the handler, so a declined cancel never reaches the order system.
When a call is invalid or missing parameters, Claude typically retries a few times with corrections; strict: true stops those calls happening in the first place. In the Agent SDK, a can_use_tool callback only runs when a call would otherwise prompt, so the SDK documentation points to a PreToolUse hook when every call of a tool must be checked.
Parallel calls and streamed calls
Claude makes several tool calls in one response when the calls do not depend on each other. Run them concurrently, then send every tool_result back together in one user message; splitting them across separate messages teaches Claude to stop calling tools in parallel. When one call needs another's output, Claude makes them in separate turns on its own. To force one call at a time, set disable_parallel_tool_use: true inside tool_choice, not at the top level of the request.
tool_choice itself sets whether Claude must call a tool: auto (the default when tools are present), any (some tool), tool (one named tool) or none. With any or tool the API forces the call, so Claude writes no text before it. Forced choice returns an error with manual extended thinking and on some current models; there, use auto with strict: true, or structured outputs.
With streaming, a tool call arrives in pieces: content_block_start opens a tool_use block with an empty input, input_json_delta events carry fragments of the JSON, and only at content_block_stop is the string complete enough to parse. Two rules follow:
- Never run a tool on a partial block. Parse and execute only after the block closes.
- Append the assistant turn to history only after
message_stop. If the stream drops, throw the partial turn away and retry; a half-builttool_useblock in history makes the next request fail, and the error then points at the wrong turn.
Setting eager_input_streaming: true on a tool streams its input without server-side buffering or validation, which cuts the wait for large parameters. You then receive fragments that may be invalid JSON, so guard the parse and return an is_error result when it fails.
Required fields and exclusion conditions
Put a parameter in required only when the call makes no sense without it. A required field forces Claude to supply a value even when the conversation holds none, which invites a guess. Leave the rest optional and give them defaults in your handler.
When two tools overlap, write the boundary into both descriptions: one sentence on when to use the tool and one on when not to. A search_docs tool that says "do not use this when the answer is already in this conversation" and a session_summary tool that says "use this only when the answer is already in this conversation" give Claude a rule instead of two lookalikes. An exclusion that refers to earlier turns only works if you send the full history. If the descriptions still blur, merge the tools into one with an action parameter.
Start with the smallest tool set the task needs and add a tool only when a missing capability is confirmed. Registering tools "just in case" makes selection worse as the list grows.
Building a tool set
Anthropic's tool definition guidance gives four habits:
- Consolidate. One
pull_requesttool with anactionparameter beats separatecreate_pr,review_prandmerge_prtools. Fewer tools mean less confusion about which to pick. - Namespace. Prefix tools with the service, such as
github_list_prsorslack_send_message, once your tools span several systems. - Return high-signal data. Return stable identifiers and only the fields Claude needs for its next step. Bloated responses waste context.
- Write instructive errors. "Rate limit exceeded. Retry after 60 seconds." gives Claude something to do; "failed" does not.
Which pattern fits
| Situation | Choose | Why |
|---|---|---|
| Claude needs current public web results | A server tool such as web search | Anthropic runs it; no handler to build |
| Claude needs your internal order system | A client tool your code executes | Only your code can reach the system |
| The same capability is needed by several Claude apps | An MCP server (next topic) | Build once, connect many |
| The action moves money or deletes data | An approval gate before execution | A person decides before anything happens |
| Five near-identical tools confuse Claude | One tool with an action parameter | Less selection ambiguity |
| Claude sends malformed inputs | strict: true on the tool | Inputs match the schema |
| A call fails | tool_result with is_error: true and a next step | Claude can recover |
| Independent lookups must run one at a time | disable_parallel_tool_use: true in tool_choice | At most one tool call per response |
| A streamed tool call is cut off mid-input | Discard the partial turn and retry the request | A broken tool_use block in history fails the next call |
| A refund or delete is about to run | Pause for a person before the handler executes | Irreversible actions need a check before, not after |
Rules that decide exam answers
- Every
tool_usegets a matchingtool_result. Sametool_use_id, all results in the next user message, results before any text. - Errors are results, not crashes. Catch failures in the handler and return
is_error: truewith a useful message. - Approval happens before execution, in your code. Asking Claude to "check with the user first" is not an approval gate. Gate each risky call by tool name; one approval at the start cannot cover a call Claude has not proposed yet.
- Descriptions decide tool choice. When Claude picks the wrong tool, improve or consolidate the descriptions before adding more tools.
strict: truefixes the shape, not the logic. Your handler still checks business rules such as whether an order has shipped.- Know who executes. Server tools run on Anthropic's side; client tools, including Anthropic-defined ones, run in your code.
Where it appears in the exam
Tools and MCPs is Domain 8, 10.6% of the exam, and Tool Implementation is 4.4% of scored items, so expect two or three questions in a 53-item exam. Expect agents that call internal systems, loops that crash or stall on tool errors, tools Claude picks wrongly, and actions that need a person's approval.
Two sample questions
These are original Timo practice questions. They are not official exam questions.
Build exercise
- Define
orders_getandorders_cancelas above with stub handlers, and run the loop. Logstop_reasonand every tool call. - Make
orders_getraise for an unknown ID and return theis_errormessage. Check that Claude asks the user for the right ID instead of guessing. - Implement
ask_operatoras a yes/no prompt in your terminal. Decline one cancellation and read how Claude explains it to the user. - Cut the
orders_getdescription to five words and ask the same questions again. Note any change in which tool Claude picks, then restore it.
Practise this topic
- Claude Certified Developer practice exam: free, 20 questions, no sign-up
- CCDV-F study guide: all topics
- Worked example: Production Claude tool loop
- Same topic in another exam: 2.1 Tool interface design and 2.2 Structured errors for MCP tools
- Previous topic: Identity, Secrets and Key Management
- Next topic: MCP Server Development
Sources
- Claude Certified Developer Foundations Exam Guide, version 1.0, effective July 2026 (Anthropic), Domain 8 topic: Tool Implementation
- Claude Platform documentation: Define tools
- Claude Platform documentation: Handle tool calls
- Claude Platform documentation: Parallel tool use
- Claude Platform documentation: Fine-grained tool streaming
By Amotion AI