Skip to content
Domain 8Task 8.1

Tool Implementation

Learn this interactively

What You Need to Know

A tool is an action Claude can ask for while it works. Some tools run in your application and others run on Anthropic's infrastructure. A tool you define has three parts: a name, a description and an input_schema. The tool set is every tool that you send with a request.

This lesson uses one made-up example throughout: an HR software company whose product has an assistant for payroll administrators. One tool, payroll_run_status, reports whether a payroll run is ready to pay. Another, payroll_run_checks, returns the check results that the status came from. A third returns the overtime hours recorded in a pay period. When this assistant fails, it calls the wrong tool, invents a figure, or keeps calling the same tool. The cause is almost never the model. The cause is what a tool says, what a tool returns, or what the tool set looks like from outside.

Three Surfaces and Nothing Else

When the model decides whether to call a tool, it reads three things: the name, the description and the input_schema. It cannot see your handler, the docstring on the Python function or the OpenAPI contract behind it. Nor can it see the ticket that explains why the tool exists, or the conversation in which your team agreed which tool to use when.

If the model chose badly, the information it needed was missing from one of those three places. The fix is to put that information there. A bigger model, a lower temperature or an extra layer of routing supplies no information, so none of them can fix an information problem.

In the HR product, an administrator knows that "is the March run ready?" means the status, not the check results. The model only knows that if a description says so.

What a Description Has to Say

Anthropic ranks the description above the schema and the model. The tool-definition best practices open with "Provide extremely detailed descriptions. This is by far the most important factor in tool performance".

Learn the docs' list of what a description should contain, and use it as a checklist. The list is exactly what a weak description omits.

  • what the tool does
  • when it should be used, and when it should not
  • what each parameter means and how it affects the tool's behaviour
  • any important caveats or limitations, explicitly including what information the tool does not return

The docs also set a minimum: at least three to four sentences for each tool description, and more if the tool is complex.

The usual failure is a description that paraphrases the name. The first example below is the payroll_run_status tool. It is the version that is released when a developer thinks about the endpoint, not the reader.

python
POOR = {
    "name": "payroll_run_status",
    "description": "Gets the status for a run.",
    "input_schema": {
        "type": "object",
        "properties": {"run_id": {"type": "string"}},
        "required": ["run_id"],
    },
}

Every word of that description was already in the name. Here is the same tool, written for a reader who is new to the system behind it.

python
PAYROLL_RUN_STATUS = {
    "name": "payroll_run_status",
    "description": (
        "Returns the status recorded against a payroll run at its most "
        "recent validation pass: READY, REVIEW or BLOCKED, together with the "
        "validation date and the reviewer's one-line note. Use this when "
        "someone asks whether a run is ready to pay, or what its current status "
        "is. Do not use it to obtain the check results the status was derived "
        "from; payroll_run_checks returns those. It does not return the "
        "run's correction history, the employees it covers, or any check "
        "result."
    ),
    "input_schema": {
        "type": "object",
        "properties": {
            "run_id": {
                "type": "string",
                "description": (
                    "Run identifier as shown on the payroll calendar, in the form PR-4471."
                ),
            }
        },
        "required": ["run_id"],
    },
}

That is five components in four sentences. They are what the tool does, when to use it, its boundary against the related tool, what it returns and what it omits.

The payroll_run_checks tool needs the same five components.

Disambiguation Is a Sentence, Not an Inference

The commonest tool failure in production is two tools that could each plausibly answer the same request. Neither tool has to be badly written for this failure to happen. In the HR product, the status tool and the checks tool are both about a payroll run. An administrator asks "is the March run all right?". A model that chooses between two four-word descriptions will pick inconsistently. A new team member would also pick inconsistently, for the same reason: neither description says which tool answers the question.

The fix is in the text, and it is cheap. Each description names the other tool and states the boundary, in both directions. payroll_run_status says not to use it for the check results, because payroll_run_checks returns those. payroll_run_checks says the reverse for the status. The when-not-to-use clause is the half that candidates omit.

A routing tool sounds like good engineering, but it is a trap

At this point, a real developer proposes a third tool whose job is to choose between the first two. This router adds a layer, but the cause stays in place. The router must make the same distinction from the same two uninformative descriptions. The model must now also decide whether to call the router at all. The model spends a turn, and it faces the original ambiguity again. Indirection, an extra layer such as a router, cannot supply information that a description does not have. Only the description can.

Sometimes the boundary is hard to state in one sentence. That difficulty is evidence that the two tools should be one tool with a parameter. The docs recommend this consolidation, and this lesson covers it below. Rewriting descriptions fixes overlap. Consolidation fixes sprawl, which is having too many tools.

The Schema Is the Other Half of the Contract

The description tells the model when to use a tool. The schema tells it what a well-formed call looks like. The description is a suggestion, and the schema is enforced.

Closed sets, which are fixed lists of allowed values, belong in enum, not in prose that asks for one of three values. Mandatory fields belong in required. Units and formats belong in the per-property description. "Date" is not a format. A model that is asked for a date will pick whichever of five plausible forms reads naturally. If the product's leave request tool takes a start date, its schema should say which form. A schema that accepts invalid input will receive invalid input. The handler then checks, again and again, for inputs that the schema could have refused.

Two current properties extend the schema. input_examples attaches an array of example input objects to a tool definition. Each example is validated against the tool's own input_schema. An invalid example returns a 400 error. It does not silently mislead the model. input_examples costs roughly 20 to 50 tokens for a simple example and 100 to 200 for a complex nested object. So it is worth its cost on tools with nested objects, optional parameters or format-sensitive fields. It is not worth its cost on a one-field lookup such as payroll_run_status. input_examples is not available on server tools, nor on the computer use and browser use client toolsets.

strict: true on a tool definition guarantees that the inputs Claude sends match your schema exactly. This setting prevents the kind of failure where a parameter is missing, instead of catching it in the handler. Neither property replaces the description. Both constrain the shape of a call that the model has already decided to make.

What the Tool Returns When It Fails

What a tool returns when it fails is interface design, not exception handling.

An administrator asks how this pay period's overtime hours compare with the last one. The overtime tool calls the timesheet service, and the call times out. The handler catches the timeout and returns an empty list, so the loop of model turns and tool calls continues. The model receives a well-formed result. The result says that nothing went wrong and that there is nothing there. The most probable next step is a confident answer built from the surrounding context. From outside, that answer looks like fabrication. Returning the last known good value is worse still. That value is wrong, but it looks correct and current, and it leaves no trace that anything failed.

The pattern that works is a tool_result that carries is_error: true. Its body is written for a reader who has to decide what to do next. The handler below shows this pattern on the overtime tool.

python
def tool_result(tool_use_id: str, content: str, is_error: bool = False) -> dict:
    block = {"type": "tool_result", "tool_use_id": tool_use_id, "content": content}
    if is_error:
        block["is_error"] = True
    return block


def overtime_hours_series(tool_use_id: str, period_ref: str) -> dict:
    try:
        series = timesheets.lookup(period_ref, timeout=8.0)
    except timesheets.Timeout:
        return tool_result(
            tool_use_id,
            "Timesheet lookup timed out after 8s. The service is intermittent this "
            "morning; a single retry is worth attempting. Do not report overtime "
            "hours from any other source.",
            is_error=True,
        )
    except timesheets.UnknownRef:
        return tool_result(
            tool_use_id,
            "No pay period matches that reference. Valid references look like "
            "P-2026-0417: a P, the year, and a four-digit sequence.",
            is_error=True,
        )
    return tool_result(tool_use_id, format_series(series))

The docs state the design instruction directly: "Write instructive error messages. Instead of generic errors like failed, include what went wrong and what Claude should try next (for example, Rate limit exceeded. Retry after 60 seconds.). This gives Claude the context it needs to recover or adapt without guessing". The overtime tool above says that the timesheet lookup timed out and that one retry is worth trying.

People claim too much on two points here. First, on invalid tool calls the documented behaviour is that Claude "will retry 2-3 times with corrections before apologizing to the user". The unofficial story that a bad result causes an endless identical retry is not a documented mechanism. Second, server tools are handled differently. The API handles their errors itself. In the docs' words, "Unlike client tools, you do not need to handle is_error results for server tools".

Ask which fault the option actually addresses

A longer timeout and a bounded retry are both good engineering. Neither changes what the model receives when the call finally does fail. They change only how often the call fails. A product whose overtime tool times out less often still gets invented overtime hours when the tool does time out.

Where the Code Runs

One tools array can hold tools from two execution models together.

A client tool is yours. The model emits a tool_use block. Your process runs the function under your credentials and inside your network. You return a tool_result. Anthropic's infrastructure never runs it. That is why a client tool can reach a database on a private subnet, such as the company's payroll database.

A server tool is Anthropic's. The call appears as a server_tool_use block whose id has the srvtoolu_ prefix. The docs state the consequence: "The API executes the tool internally. You see the call and its result in the response, but you don't handle execution. Unlike client tool_use blocks, you don't need to respond with a tool_result". Its result block arrives in the same assistant turn, and tool_use_id pairs it with the call.

Where the code runs decides what it can reach, whose credentials it carries, who owns its failure modes, and which of them you must handle at all.

Anthropic-provided is not the same claim as Anthropic-executed. The Tool reference states this in one line. Anthropic provides server tools that execute on Anthropic's infrastructure. It also provides client tools where Anthropic defines the schema but your application handles execution. Several familiar Anthropic-schema tools are on the client side, and your process runs them. If the HR assistant used one, the company's own code would run it.

Dispatch, and Approval as a Design Decision

The harness is the code between the model and your tool functions. It is the code that makes a set of tools work as one system. The harness receives the tool-use request and resolves the name to a handler. It validates the input again if you do not trust the model to be strict. It applies whatever policy the operation's classification demands. It runs the tool under a timeout. It logs the call and its outcome. It formats the result that the loop continues from. When you put these jobs at the dispatch point, where the harness passes each call to its handler, and not in each handler, each tool stays a plain function. You can unit-test that function with no model involved.

Execution order is a dispatch decision too. As of September 2026 the API does not prescribe an order for the tool_use blocks in one turn. Independent, read-only calls are usually safe to run in parallel. Anything with side effects, shared state or ordering requirements is better run one at a time. If one turn checks the status of two payroll runs and submits a leave request, the two checks can run together, and the submission can run alone. If you run a batch in sequence and stop at a failure, still return a tool_result for each skipped call. Give each one is_error: true and a short explanation.

Approval is the policy that belongs in the harness. The classification behind approval is a design decision. You make it once, when you design the tool set. The table below is one way to write the classification.

typescript
type Band = "automatic" | "confirm" | "two-party";

const APPROVAL: Record<string, Band> = {
  payroll_run_status: "automatic",   // read-only
  payroll_run_checks: "automatic",   // read-only
  timesheets_overtime_hours_series: "automatic",
  leave_submit_request: "confirm",     // consequential, reversible with effort
  payroll_run_release: "two-party"   // pays every employee; not undone quietly
};

export function bandFor(toolName: string): Band {
  return APPROVAL[toolName] ?? "two-party"; // unknown tools are never automatic
}

Read-only and reversible operations run automatically. Consequential or irreversible ones warrant a confirmation step. The genuinely dangerous ones warrant an approval step that a second person has to complete. In the HR product, checking a run's status runs automatically. Submitting a leave request asks the administrator to confirm. Releasing a payroll run needs a second person to approve it. The Model Context Protocol (MCP) is an open protocol for connecting an application to an outside system. MCP states the same principle from the protocol side: tools may require user consent before execution. Applications implement that consent through tool visibility, per-execution approval dialogues, pre-approval settings and activity logs.

As of September 2026 an MCP tool can carry annotations such as readOnlyHint and destructiveHint. The specification calls them hints that are not guaranteed to describe the tool faithfully. Clients must treat them as untrusted unless they come from trusted servers. So a server you have not vetted cannot put its own tools in the automatic band by calling them read-only. Give them the band that you would give any unclassified tool. Even a harness that treats a tool with no hint as destructive cannot trust a hint that the same server does set.

Three details of that table each show a place where a real harness fails.

First, the default matters more than the entries. ?? "two-party" means that if somebody adds a tool next quarter and forgets to classify it, that tool needs approval and is not automatic. A lookup that defaults to "automatic" lets every tool that someone forgets to classify in future run automatically, with no review.

Second, the classification belongs to the operation, not to the caller. If you choose the band by which user is asking, you put an authorisation decision in a table that was built to describe consequences. The authorisation decision and the table of consequences diverge as soon as a second interface appears. Releasing a payroll run has the same consequences whether a payroll manager asks or a new administrator does.

Third, the band is not what enforces the policy. The table decides what the harness asks for. That decision is a policy statement, and the model has no part in it. Code that the harness runs before dispatch stops a two-party call from proceeding. The band itself does not stop it.

Confirmation also has a cost that is easy to underestimate. Every confirm band makes a person approve each call. A tool set that asks for confirmation too often trains its operators to approve without reading. An approval that nobody reads is a worse control than none, because it creates a record that looks like oversight. An administrator who is asked to approve every status check would soon stop reading. So when you assign bands, the useful question is not "could this do damage?". It is "would a competent operator, at the moment they are asked, have the information to answer?". If the answer is no, the approval step belongs at a point where the answer is knowable, or the operation belongs in a narrower tool.

The Shape of the Tool Set

A tool set is a list of options that the model reads on every turn. It fails in two opposite ways.

Sprawl is one tool per API endpoint. Eleven endpoints become eleven tools, and several of them are thin wrappers over closely related operations. Accuracy falls as the set grows. Every near-duplicate adds a selection decision, and every definition costs context on every request. A team that wrapped each payroll endpoint as its own tool would have this problem. The documented remedy is consolidation. The docs say: "Rather than creating a separate tool for every action (create_pr, review_pr, merge_pr), group them into a single tool with an action parameter. Fewer, more capable tools reduce selection ambiguity". Anthropic's engineering write-up makes the same point, stated the other way round: more tools do not always lead to better outcomes. A few thoughtful tools that target specific high-impact workflows do better than wrapping every endpoint.

Overlap is the other way it fails. It is the disambiguation problem again, as with the run status and the run checks.

Two further properties keep a growing tool set readable. The first is namespacing by service or resource: for example, github_list_prs, not list_prs. Namespacing makes boundaries obvious as the tool set grows, and it matters more once you use tool search, which finds tools on demand. The second is result shape. Return only high-signal information, meaning only what is useful. Use semantic, stable identifiers such as slugs or UUIDs rather than opaque internal references. Include only the fields that the next decision needs. A tool that returns the whole upstream payload unchanged fills the context and hides the two fields that mattered.

Namespacing also resolves name clashes between servers. As of September 2026 the MCP specification scopes tool-name uniqueness to a single server. So a client that aggregates several servers can see two tools both called search. That client should prefix each tool with a server identifier, not with the server's self-reported serverInfo name. The specification says that name is not guaranteed to be unique.

Result shape covers attribution as well. A client tool's tool_result can contain search_result blocks. Each block has a source and a title that you supply. Citations are off by default. When citations are enabled, Claude cites these blocks the way it cites web search results. The attribution then comes from your data, not from a reference that Claude has to copy correctly.

Sometimes a tool set really is large. defer_loading excludes a tool definition from the initial system prompt. The definition loads on demand when the tool search tool returns a reference to it. You can also configure it per tool on an MCP toolset. Large connected tool sets usually come from MCP toolsets. Deferring everything has its own cost: each deferred tool needs a search step before Claude can call it. So the tool search docs advise keeping your three to five most frequently used tools non-deferred. The run status check would be one.

A second technique works on results, not definitions. Programmatic tool calling is generally available as of September 2026. It lets Claude write code in the code execution container, and that code calls your tools. If you add a code execution caller such as code_execution_20260120 to the allowed_callers of a tool, that code can call the tool. Each call still returns to your application, which runs it. The intermediate results never enter Claude's context. So a job that calls tools over many records, or filters a large result, uses the model once instead of once per call. Totalling a year of overtime hours for every team is that kind of job. Tools with strict: true and tools from the MCP connector cannot be called this way.

From Symptom to Surface

Start from the symptom and find the place that caused it.

The assistant called the wrong tool. It chose from names, descriptions and schemas, so the fault is in one of those three. Ask whether any sentence in either description distinguishes the two cases. If none does, add the disambiguating sentence and a when-not-to-use clause to both. In the HR product, the two payroll run tools each name the other.

The assistant invented data. A well-formed result carried no information and no error, so inventing the missing data from context was the most probable next step. The fix is what the tool returns when it fails. Retries, timeouts and larger models make the failure rarer, but they do not change what happens when it occurs. In the HR product, the overtime tool returns is_error: true and says what failed.

The assistant called the same tool again and again, or stalled. The usual cause is an unactionable result. It says the request failed, but not whether a retry would help or what a valid input looks like. The model has nothing to change between attempts.

In the exam

This is the sub-skill with the most weight in Domain 8, and its questions rarely name the sub-skill that they test. Half of them present a symptom. So run the routine above instead of matching patterns under time pressure.

The API request and response details belong to Domain 2: the tools request parameter, the tool_use and tool_result content blocks, and tool_choice with its four values. Domain 8 covers the higher-level design question: what the name, description and schema should contain. A question that gives you a trace and asks you to name the failure is Domain 4. Designing the result that prevents it is this sub-skill. Deciding which operations belong in which approval band, and expressing that in the tool definitions, is 8.1. Making the approval step an enforceable control that acts whether or not the model cooperates is Domain 7. The exam tests both sides of the boundary between 8.1 and Domain 7. An option that relies on an operator noticing something is not a control on either side.

For a wrong-tool question, the right answer rewrites both descriptions so each names the other and says when not to use itself. The when-not-to-use clause is the half that the exam tests. A candidate who can name the five components of a good description can rewrite any thin one under exam pressure. Options about bigger models, sampling or routing are wrong options. The exam also builds wrong options from the overlap and sprawl pair. Offering consolidation where a description rewrite belongs, or the reverse, is the strongest wrong answer available. input_examples and strict: true are both mechanism names this cert can test.

What a tool returns on failure is the most consequential item in this sub-skill. For a fabrication question, the answer is what the result says on failure. A longer timeout and a bounded retry appear as fully stated, confident wrong options. An option that depends on the endless identical retry story asserts something that the docs do not say.

The exam can test the difference between the two execution models. It can also test what follows from that difference: reach, credentials, failure ownership, and which failures you must handle. The provided-versus-executed distinction misleads candidates, and it recurs in 8.3. An option that asserts that any built-in tool runs on Anthropic's servers is not safe to rely on. A question can still depend on where a named tool runs. A fair question says so in its question text or options. For example, an option states that your backend runs the text editor's commands. Do not assume a location that the question does not state.

Sources and Dates

The poor and good description pair follows the shape of the docs' own poor and good get_stock_price exemplar. The tool and its wording are ours. Which list a tool is in, the server list or the client list, changes between releases. The dated version strings of the tools also change between releases. That inventory belongs in the domain quick reference, with a date on it.

Exam Traps

Exam Trap

Lowering the temperature to make tool selection deterministic

Temperature changes how the model samples among continuations (the possible next tokens). It cannot supply a distinction two descriptions failed to state. Rewrite both descriptions so each names the other and says when not to use itself.

Exam Trap

Adding a third tool to decide which of two confusable tools to call

The router draws the same distinction from the same uninformative descriptions, and the model must also decide whether to call it. Rewrite both descriptions so each names the other and says when not to use itself.

Exam Trap

Choosing a longer timeout or automatic retries to stop the agent reporting figures nobody recorded

Neither changes what the model receives when the call finally fails, so the invented figures still appear, but less often. Return a result with is_error true and a body saying what failed and what to try next.

Exam Trap

Pointing an Anthropic-executed tool at an internal service, assuming credentials or the right address will reach it

Anthropic-executed tools run on Anthropic's infrastructure, which has no route to hosts the public internet cannot reach, so credentials do not help. Run the call where the service is reachable: a client tool your application executes, or an MCP server.

Practice Scenario

A project management software company runs an assistant with eleven tools over its product. Two of them are described in four words each. One returns the health rating recorded against a project at its last review, and one returns the task counts behind that rating. The assistant picks the wrong one about one request in fifteen. Separately, when the time-tracking tool times out it returns a zero-length series, and the assistant then reports logged hours nobody recorded. A developer proposes a twelfth tool that decides which of the other eleven to call. Which change addresses both faults?

Build Exercise

Build Exercise

Two Confusable Tools, One Rewrite, One Honest Failure

Difficulty
35 minutes

What you'll learn

  • How the name, description and schema are the only things about a tool that the model reads when it selects one
  • How a disambiguating sentence in both descriptions fixes inconsistent selection
  • How an informative is_error result prevents the model from inventing data when a failure is silent
  • How to tell which of two documented remedies, consolidation or disambiguation, a fault actually calls for
  1. Define two deliberately confusable tools over the same system, each described in a single clause that restates its name, and write down twenty representative requests together with the tool you expect for each

    Why: A baseline you predicted in advance is the only way to see selection accuracy move, and predicting it exposes how much you were relying on knowledge the model never had.

    You should see: Two thin tool definitions and a twenty-row table of request and expected tool.

  2. Run all twenty and record which tool was actually called each time

    Why: The baseline will be worse than you expect, which is the point: the model is not choosing badly, it is choosing between two descriptions that do not distinguish themselves.

    You should see: A hit rate noticeably below what you predicted, with the misses clustered on the ambiguous requests.

  3. Change nothing but the two descriptions. Each states what the tool does, when to use it, what each parameter means, what comes back, and one sentence naming the other tool and saying when NOT to use this one. Re-run the same twenty

    Why: Isolating the change to the description proves the claim that detailed descriptions are by far the most important factor in tool performance, with your own results.

    You should see: A materially higher hit rate with no code change anywhere else.

  4. Now break the other tool: make it time out and return an empty result, and run five requests that need it. Record verbatim what the assistant tells the user

    Why: Watching a well-formed empty result turn into a confident invented answer makes the fabrication mechanism concrete rather than theoretical.

    You should see: At least one answer stating something the tool never returned, stated with no sign of doubt.

  5. Replace the empty result with a tool_result carrying is_error true and a body saying what failed, whether a retry would help, and what a valid input looks like. Re-run the same five

    Why: The contrast between the two transcripts is the whole teaching point of the error-design material, and it is the pattern of every fabrication question in this sub-skill.

    You should see: The assistant reporting the failure, or retrying, rather than inventing the missing data from context.

  6. Write down which of the two changes gave the bigger improvement, whether either needed a code change outside the description and the failure path, and which of your eleven-tool sprawl or two-tool overlap problems each one actually addressed

    Why: Naming which remedy fits which fault is exactly the discrimination the exam builds its strongest wrong options from.

    You should see: A short note that separates consolidation, which fixes sprawl, from disambiguation, which fixes overlap.

Sources