mission://guides/claude-ai-worker/give-it-tools
Book a call

Chapter 05 · 10 min read

Give it tools

Define tools as typed Python functions and let the SDK's tool runner do the loop.

Tools are what turn a model that talks into a worker that acts. A tool is a function in your code with a name, a description and a schema for its inputs. Claude decides when to call it; your code runs it and sends back the result.

How a tool call works

  1. You send the conversation and the list of tools.
  2. Claude replies with stop_reason: "tool_use" and one or more tool_use blocks (the tool name and its inputs).
  3. Your code runs each tool and sends the results back as tool_result blocks.
  4. Repeat until Claude answers without asking for a tool.
The agent loop Your code sends the conversation and the tools to Claude. Claude either answers, which ends the turn, or asks for tools. Your code runs the tools against your systems and sends the results back, and the loop repeats. Your codethe tool runner Claudereads, decides Your toolsCRM, booking, database Answerstop_reason: end_turn 1. conversation + tools 2. tool_use: get_price(...) 3. tool_result, then back to step 1 or: done
One turn of the agent loop. Steps 1 to 3 repeat until Claude answers without asking for a tool.

That repeat is the "agent loop". You can write it yourself, but the SDK's tool runner does it for you.

Tools as Python functions, with the tool runner

import anthropic
from anthropic import beta_tool

client = anthropic.Anthropic()

@beta_tool
def get_repair_status(job_number: str) -> str:
    """Look up a repair by its job number. Call this whenever a customer asks
    about a repair they have already booked in.

    Args:
        job_number: The job number on the customer's receipt, e.g. FD-10482.
    """
    job = repairs.find(job_number)              # your own code
    if job is None:
        return "No repair found with that job number."
    return f"{job.device}: {job.status}. Ready: {job.ready_on or 'not yet known'}."

runner = client.beta.messages.tool_runner(
    model="claude-opus-5-5",
    max_tokens=16000,
    system=SYSTEM,
    tools=[get_repair_status],
    messages=[{"role": "user", "content": "Is FD-10482 ready yet?"}],
)

for message in runner:           # one message per turn; stops when Claude is done
    for block in message.content:
        if block.type == "text":
            print(block.text)

The decorator builds the input schema from the type hints and the docstring, so the docstring is not decoration: it is what Claude reads to decide when to use the tool.

The same tool in TypeScript

The TypeScript SDK has the same tool runner. Tools are defined with a Zod schema (or plain JSON Schema with betaTool) and a run function:

import Anthropic from "@anthropic-ai/sdk";
import { betaZodTool } from "@anthropic-ai/sdk/helpers/beta/zod";
import { z } from "zod";

const client = new Anthropic();

const getRepairStatus = betaZodTool({
  name: "get_repair_status",
  description: "Look up a repair by its job number. Call this whenever a customer asks about a repair they have already booked in.",
  inputSchema: z.object({
    job_number: z.string().describe("The job number on the receipt, e.g. FD-10482"),
  }),
  run: async ({ job_number }) => {
    const job = await repairs.find(job_number);      // your own code
    return job ? `${job.device}: ${job.status}` : "No repair found with that job number.";
  },
});

const finalMessage = await client.beta.messages.toolRunner({
  model: "claude-opus-5-5",
  max_tokens: 16000,
  system: SYSTEM,
  tools: [getRepairStatus],
  messages: [{ role: "user", content: "Is FD-10482 ready yet?" }],
});

for (const block of finalMessage.content) {
  if (block.type === "text") console.log(block.text);
}

Designing good tools

  • Say when to call it, not only what it does: "Call this whenever a customer asks about a repair they have already booked in."
  • Few, focused tools. Five clear tools beat twenty overlapping ones. If two tools could answer the same question, merge them.
  • Return plain, useful text. Return what a person would need to answer, not a raw database row with forty fields.
  • Return errors as results. When something fails, return a clear message ("No repair found with that job number") so Claude can ask the customer to check it. In a manual loop, set is_error: true on the result.
  • One tool per risky action. A dedicated book_slot or send_email tool can be checked, logged and gated. A general "run any command" tool cannot (see the guardrails chapter).

Exact inputs with strict mode

When you define a tool's schema yourself, add "strict": True and Claude's inputs are guaranteed to match it exactly. The schema then needs "additionalProperties": False and a required list:

book_slot_tool = {
    "name": "book_slot",
    "description": "Book a drop-off slot. Call only after the customer has chosen a slot from check_slots.",
    "strict": True,
    "input_schema": {
        "type": "object",
        "properties": {
            "slot_id": {"type": "string"},
            "customer_email": {"type": "string"},
        },
        "required": ["slot_id", "customer_email"],
        "additionalProperties": False,
    },
}

Several tools at once

Claude can ask for several tools in one turn (for example, the repair status and the free slots together). Run them, then send all the results back in a single message. The tool runner does this for you; in a manual loop, do not split them across messages, or Claude learns to stop doing it.

Forcing a tool

On Claude Opus 5.5 and Sonnet 5.5 you cannot force a specific tool call (tool_choice of any or tool returns an error). Leave tool_choice on auto and say in the job description which tool to use when. If you only wanted the tool to get JSON back, use structured outputs instead.

Tools Anthropic runs for you

Some tools run on Anthropic's side, so there is nothing for you to execute: web search, web fetch and code execution in a sandbox. You declare them in the same tools list. For a support worker you usually do not want web search at all: everything it says should come from your own systems.

Want this built for you?

I build AI workers on Claude for businesses in the UK, US and UAE.

Book a free call