Rubric
Contents — domains, guide and mocks

Tool implementation

CCDV-F 8.117 min read · checked 21 September 2026

Task statementTool Implementation (4.4%) — tool use and function calling, configuration for external system interaction, tool description writing, error handling, agentic harness dispatch, client- versus server-side tools, approval patterns, and tool set construction

One tool call, end to end

Claude API
Your harness
Your system
Step 1: Claude API to Your harness: tool_use · name, id, input
Step 2: Your harness : Look up handler, validate input
Step 3: Your harness : Check approval policy
Step 4: Your harness to Your system: Execute the operation
Step 5: Your system to Your harness: Result, or an exception
Step 6: Your harness to Claude API: tool_result · id, content
Notice who does what. The model never touches your systems; it asks, and your harness decides whether and how to carry the request out.

The definition, and what each field is actually for

A tool definition has three required fields — name, description and input_schema — plus an optional strict flag that makes the arguments conform to the schema exactly. The mechanics of the round trip are 2.3's subject: stop_reason of tool_use, one tool_result per tool_use_id. What belongs here is the design of the definition itself, because that is what the model actually reads when deciding what to do.

The description is the most under-invested field in most codebases. Anthropic's engineering guidance on writing tools is blunt about the return on it: even small refinements to tool descriptions can yield dramatic improvements, and the framing to use is explaining the tool to a new team member who has all the general competence and none of your context. Make implicit knowledge explicit — what the tool does, when to reach for it, when not to, what it returns, and any precondition. Name parameters unambiguously: user_id rather than user, because the second one invites an email address.

The same tool, described twice

Weaktext

name: search

description:
Searches for stuff.

input_schema:
  query: string
  id: string

Strongtext

name: orders_search

description:
Find orders by customer
email or order number.
Use for “where is my
order”. Does NOT return
refunds — use
refunds_search for
those. Returns up to 20
orders, newest first.

input_schema:
  customer_email: string
  order_number: string
  limit: integer (max 20)
Nothing about the implementation changed. The right-hand version tells the model when to choose this tool, what it costs, and what it will get back.

The schema is a second description, read by both the model and your validator. Use enums where the value set is fixed, mark genuinely required fields required, and set additionalProperties: false so nothing unexpected arrives. Note the limits that 6.3 covers: minimum, maximum and pattern are not enforced, so a tool that accepts a quantity still has to range-check it in code. With strict: true you get the declared shape reliably; you never get the declared meaning for free.

Tool set construction

The most common mistake in a first tool integration is to publish one tool per API endpoint. It feels complete and it performs badly, because the agent then has to compose four calls to answer one question, burning context and getting one of the four wrong. The guidance is to build tools around workflows rather than endpoints: a schedule_event tool instead of separate list_users, list_events and create_event; a get_customer_context that returns recent transactions and notes together instead of three retrieval tools the agent must stitch.

The counterweight is ambiguity. Anthropic's context-engineering guidance names bloated tool sets — too much functionality, or ambiguous decision points about which tool to use — as a specific failure mode, and the tools guidance states plainly that more tools do not always lead to better outcomes: a few thoughtful tools aimed at high-impact workflows beat comprehensive coverage. Where you do have many related tools, namespace them with a consistent prefix — asana_search, jira_search, or by resource, asana_projects_search — so that the name itself disambiguates.

SymptomLikely causeFix in the tool layer
Agent picks the wrong toolTwo descriptions overlapSay what each is not for; namespace the names
Agent chains four calls for one answerTools mirror endpointsConsolidate into a workflow tool
Agent calls with a missing argumentAmbiguous parameter nameRename (user_id, not user); mark it required
Context fills with tool outputNo limits on the responsePaginate, filter, add a concise response_format
Agent hallucinates record idsOpaque UUIDs in responsesReturn human-meaningful names where you can

That last row is worth dwelling on. Resolving arbitrary alphanumeric UUIDs into semantically meaningful language significantly improves precision in retrieval tasks by reducing hallucination — a model can carry “Northwind Logistics, invoice March 2026” through a reasoning chain far more reliably than b7f3…. And on size: implement pagination, filtering and truncation with sensible defaults. The tools guidance mentions a response_format enum letting the agent ask for a concise or detailed reply, which cut token consumption by roughly two thirds in their Slack example, and notes that Claude Code caps tool responses at 25,000 tokens by default. A tool that can return a megabyte is a context-engineering problem waiting to happen — 6.1's subject, created here.

Client tools and server tools

The objective's client-versus-server distinction is about who executes the tool, and it is entirely mechanical. A client tool runs in your application: Claude returns a tool_use block, your code executes it and sends back a tool_result. Every custom tool you write is a client tool, and so are the Anthropic-schema client tools such as the memory, bash, text editor, computer use and browser use tools — Anthropic defines the schema, you still run them.

A server tool runs on Anthropic's infrastructure. You enable it and the results come back in the response; there is no tool_result for you to construct. Web search, web fetch, code execution, the advisor tool, the tool search tool and the MCP connector are in this group. Some carry usage-based pricing beyond tokens — web search is charged per search, code execution by compute time. One edge case is worth remembering: if Claude calls server tools in parallel with client tools, you handle execution for the client ones in that same round trip.

Who runs it, and what that changes

Client tools — you execute

  • Any custom tool; memory, bash, editor, computer, browser
  • Reaches your database, your VPC, your credentials
  • You own errors, retries, timeouts and approval
  • You must return a tool_result per tool_use_id

Server tools — Anthropic executes

  • Web search, web fetch, code execution, advisor, tool search
  • No execution code and no tool_result to build
  • Some priced per use on top of tokens
  • Cannot reach systems only your network can see
The decision is rarely preference. It is whether the work needs your network, your credentials and your audit trail — in which case it cannot be a server tool.

Agentic harness dispatch

The harness is the code between the model and your systems, and dispatch is its core job: take a tool_use block, find the handler for that name, validate the input, run it, and turn whatever happened into a tool_result. Four rules make a dispatcher production-grade.

  1. Dispatch from a registry, never from `eval` or dynamic attribute lookup. An unknown tool name should produce an error result, not an exception and not an accidental call into your codebase.
  2. Validate the input against the schema before executing, and treat validation failure as a tool error the model can see and correct.
  3. Catch everything the handler can raise. A tool that throws takes down the loop; a tool that returns an error lets the agent recover.
  4. Return one `tool_result` per `tool_use` block, matched by `tool_use_id`, even for the calls that failed — a missing result is a malformed conversation.
A dispatcher that cannot take the loop downpython
HANDLERS = {"orders_search": orders_search, "refund_create": refund_create}

def dispatch(block):                       # block is one tool_use block
    result = {"type": "tool_result", "tool_use_id": block.id}
    handler = HANDLERS.get(block.name)
    if handler is None:                    # hallucinated or stale tool name
        return {**result, "content": f"Unknown tool: {block.name}", "is_error": True}
    try:
        args = SCHEMAS[block.name].validate(block.input)   # your own validation
        if needs_approval(block.name, args) and not approved(block):
            return {**result, "content": "Awaiting human approval.", "is_error": True}
        return {**result, "content": handler(**args)}
    except ValidationError as e:
        # Actionable: tells the model exactly what to fix and retry.
        return {**result, "content": f"Invalid arguments: {e}", "is_error": True}
    except Exception as e:
        # Never leak internals; never let it escape the loop.
        log.exception("tool failed", tool=block.name)
        return {**result, "content": f"{block.name} failed: {safe(e)}", "is_error": True}

The error path deserves its own emphasis, because it is where the objective's “error handling” lives. A failed tool is reported by returning a tool_result with is_error set to true and an explanation in content — not by raising, and not by silently substituting a default. Claude handles that gracefully: it can retry with corrected arguments, try a different tool, or tell the user it could not do the thing. What it cannot do is recover from information it never received. The rule of thumb is that error text should be actionable: “no customer found with email x@y.com — try orders_search with an order number” teaches the next attempt, where “Error 500” teaches nothing.

Controlling and approving tool calls

tool_choice controls whether the model may call a tool at all. auto is the default and lets it decide; any forces it to call something; {"type": "tool", "name": …} forces one specific tool; none forbids tools for that request. auto also accepts disable_parallel_tool_use, which makes the model call tools one at a time instead of issuing several in one response. Forcing a tool costs slightly more system-prompt overhead than auto, and the presence of the tools parameter at all adds a few hundred tokens before your own schemas are counted.

Prompt wording nudges tool use — the documentation gives graded examples, from “use the tools to investigate before responding” through to “always call a tool first” — but the guidance is explicit that when you need a guarantee you set tool_choice rather than relying on the prompt. That principle generalises to the whole task statement, and it is the heart of approval patterns: what must not happen is not a prompting problem.

Claude Code's permission system is the reference implementation to reason from. It states the principle directly: permission rules are enforced by Claude Code, not by the model, and instructions in a prompt or CLAUDE.md shape what Claude tries to do but do not change what is allowed. Rules are allow, ask and deny entries in settings, written as a tool name with an optional specifier such as Bash(npm run *) or Read(./.env). Permission modes set the default posture — default prompts on first use of each tool, acceptEdits auto-accepts file edits, plan explores without editing, bypassPermissions skips prompts and is for isolated containers only. And a PreToolUse hook can evaluate a call programmatically before the prompt appears, with deny rules taking precedence over anything a hook allows.

Deciding what needs a human

Should this tool call run unattended?
  • Read-only, scoped
    Allowno prompt; log it
  • Reversible write
    Allow, then auditundo path must exist
  • Spends or notifies
    Ask a humanapproval before execution
  • Destructive
    Deny in policynot reachable by the agent
Sort by reversibility and blast radius, not by how clever the tool is. The question to ask of any tool is “what is the worst thing this can do if the model is wrong?”

Two patterns implement the middle column in an API application. Gate inside the handler: the dispatcher checks a policy before executing, and returns an “awaiting approval” tool result so the agent can explain the wait to the user, as in the code above. Split the tool: refund_request writes a pending row that a person releases, and there is no refund_execute in the tool set at all. The second is stronger, because the capability the agent lacks cannot be talked into existence. Either way the safest tool set is the smallest one that does the job — least privilege, which 7.2 develops.

Traps the wrong answers are built from

Tempting but wrongDo this instead
Publishing one tool per API endpointBuild tools around the workflows the agent performs, so one call answers one question.
Letting a handler raise, so a tool failure ends the conversationCatch it and return a tool_result with is_error and an actionable message, so the agent can recover.
Returning raw UUIDs and unbounded payloads from toolsReturn human-meaningful identifiers, paginate, and offer a concise response format.
Relying on the system prompt to stop a dangerous tool callEnforce it outside the model — permission rules, an approval gate in the dispatcher, or simply not offering the tool.
Giving an agent a general-purpose shell or query tool for convenienceOffer specific, scoped tools; a general tool is unbounded privilege that no description can narrow.

You should now be able to

  • Write a tool definition whose name, description and schema tell the model when to use it and when not to.
  • Construct a tool set around workflows, namespaced to remove ambiguity, sized for the job.
  • Distinguish client tools from server tools and say which is possible for a given integration.
  • Implement harness dispatch with registry lookup, input validation and total error containment.
  • Return tool errors as is_error results with actionable text, and classify model, tool and infrastructure failures.
  • Choose an approval pattern by reversibility and blast radius, and enforce it outside the model.

Practice questions

Original questions written for this lesson, in the exam’s style. Answer first, then open the reasoning — every option is explained, including why the wrong ones are tempting.

  1. Question 1

    An agent has search_docs (“searches documents”) and search_kb (“searches the knowledge base”). It picks the wrong one roughly a third of the time. A stronger system prompt listing which to use when has not helped.

    What is the most effective change?

    1. ASet tool_choice to force search_kb, which handles most queries correctly.
    2. BRewrite both descriptions to state their content, their boundary and what each is not for.
    3. CMerge them into one tool that searches both sources and returns combined results.
    4. DAdd examples of correct tool selection to the system prompt.
    Show answer and reasoning
    1. AIncorrect. Forcing one tool removes the choice rather than clarifying it, and guarantees a wrong answer for every query the other tool serves.
    2. BCorrect. Ambiguous selection is a description problem, and the guidance is that small refinements to descriptions produce disproportionate improvements — including an explicit pointer to the sibling tool.
    3. CIncorrect. Sometimes right, but it discards a real distinction, and if the two corpora have different permissions or freshness, merging causes a worse class of error.
    4. DIncorrect. Examples can help, but the prompt has already been strengthened without effect; the model is reading the descriptions when it chooses.
  2. Question 2

    During a payment provider outage, an inventory agent's check_stock tool raises a connection timeout. The exception propagates out of the tool loop and the user's session ends with a generic error page.

    What should the harness do instead?

    1. ARetry the tool indefinitely until the provider recovers, so the agent eventually completes the task.
    2. BReturn a normal tool_result containing an empty stock list so the agent can continue.
    3. CRetry with backoff, then return a tool_result with is_error explaining that stock lookup is unavailable.
    4. DRemove check_stock from the tool list for the rest of the session so the model stops calling it.
    Show answer and reasoning
    1. AIncorrect. An unbounded retry holds the conversation open through an outage of unknown length and can amplify load on a service that is already failing.
    2. BIncorrect. Substituting a plausible default is the worst option: the agent will state confidently that nothing is in stock, which is a fabricated fact.
    3. CCorrect. Transient infrastructure failures deserve bounded retries, and reporting the failure as an error result lets the agent apologise or offer an alternative instead of crashing.
    4. DIncorrect. Changing the tool set mid-conversation invalidates the prefix and leaves the model with no way to describe what went wrong.
  3. Question 3

    A team is designing an agent that must query an internal Postgres database inside their VPC, search the public web for supplier news, and email a summary to a manager.

    Which two statements about tool implementation are correct? (Select 2.)

    1. AThe database query must be a client tool, because only their application can reach the VPC.
    2. BSending the email should be gated by approval or split into a request the agent cannot execute alone.
    3. CWeb search must also be a client tool, since it reaches systems outside Anthropic.
    4. DA single run_sql tool is preferable, since it covers any future query the agent may need.
    5. ESetting tool_choice to any will make the agent use each of the three tools in the right order.
    Show answer and reasoning
    1. ACorrect. Server tools execute on Anthropic's infrastructure and cannot see a private network, so anything requiring their credentials or network path has to run in their harness.
    2. BCorrect. Sending a message to a person is externally visible and not reversible, which puts it in the category that needs a human in front of it.
    3. CIncorrect. Web search is one of the server tools executed on Anthropic's infrastructure, with results returned directly and no tool_result to build.
    4. DIncorrect. A general query tool is unbounded privilege dressed as convenience; specific, scoped tools are both safer and easier for the model to choose correctly.
    5. EIncorrect. any only forces some tool to be called; it does not sequence them or influence which one is chosen.
  4. Question 4

    A finance agent can issue supplier payments. The team's proposal is a detailed system prompt stating that payments above 5,000 euros require sign-off, and an instruction to ask the user before calling payment_create.

    What is the strongest objection to this design?

    1. AThe threshold should be enforced by the dispatcher or by not exposing an executing tool at all.
    2. BThe system prompt will be too long once every threshold rule is included.
    3. CThe agent may ask for confirmation too often, which will annoy finance staff.
    4. Dpayment_create should use strict so the amount always matches the schema.
    Show answer and reasoning
    1. ACorrect. A prompt shapes what the model tries, not what the system permits, so a limit that matters must be enforced in code that runs regardless of what the model decides.
    2. BIncorrect. Prompt length is a real concern but a secondary one; the design would still be unsafe if it were short.
    3. CIncorrect. Over-asking is an experience problem, whereas the scenario's risk is a payment that proceeds without asking at all.
    4. DIncorrect. Strict tool use guarantees the shape of the arguments, not that the payment was authorised.

Sources

Drafted with AI assistance and checked against the sources above; expert review is in progress. Spotted an error? Tell us and it gets fixed, dated and listed on how this is written.