# Claude tool use: define tools and handle tool calls in Python

> Claude tool use lets the model ask your code to run a function. You describe each tool with a name, a description and a JSON Schema. When Claude wants one, the response ends with stop_reason tool_use. You run the function, then send a tool_result in your next user message. The model never runs anything itself.

Mindket editors · Published 2026-11-04 · Updated 2026-11-05 · https://mindket.com/learn/claude-api/claude-tool-use

## In short

- Describe each tool with a name, a long description and a JSON Schema.
- Answer every tool_use block with a tool_result that carries the same id.
- Send parallel results together, and report failures with is_error.
- Know which tool_choice values current models reject.

## Before you start

Finish [lesson 1](https://mindket.com/learn/claude-api/claude-api-quickstart) first: this lesson reuses its client, its key and its loop over content blocks. Tool use runs on the same Messages endpoint, with a `tools` list added to the request.

The model never runs anything. For client tools, the ones you define, your code runs the function and reports back. Server tools such as web search, web fetch and code execution run on Anthropic's side, so a response can arrive with their results already inside. This lesson covers client tools. The [tool use overview](https://platform.claude.com/docs/en/agents-and-tools/tool-use/overview) draws the same line.

## Build the tool_use and tool_result round trip

One request, one tool, two calls. The code below defines `get_weather`, asks a question that needs it and handles a single round trip. [Lesson 4](https://mindket.com/learn/claude-api/build-ai-agent-claude-api) wraps the same steps in a loop. The weather function is a stand-in written for this lesson, so replace it with a real lookup.

Two rules decide whether the second request is accepted. The user message with the results must come right after the assistant message that holds the `tool_use` blocks, and the `tool_result` blocks must come first in its content, before any text. Break either one and the API returns a 400.

````python
import json
import anthropic

client = anthropic.Anthropic()

tools = [
    {
        "name": "get_weather",
        "description": (
            "Get the current weather for a city. Use it when the user asks about "
            "weather, temperature or whether to bring an umbrella. It returns the "
            "temperature in Celsius and a short condition. It does not forecast."
        ),
        "input_schema": {
            "type": "object",
            "properties": {
                "city": {"type": "string", "description": "A city name, for example Lisbon"},
            },
            "required": ["city"],
        },
    }
]


def get_weather(city: str) -> str:
    # Stand-in for a real weather API call.
    return json.dumps({"city": city, "temp_c": 18, "condition": "light rain"})


messages = [{"role": "user", "content": "Do I need an umbrella in Lisbon today?"}]

response = client.messages.create(
    model="claude-opus-5-5", max_tokens=1024, tools=tools, messages=messages
)

if response.stop_reason == "tool_use":
    messages.append({"role": "assistant", "content": response.content})
    results = []
    for block in response.content:
        if block.type == "tool_use":
            results.append(
                {
                    "type": "tool_result",
                    "tool_use_id": block.id,
                    "content": get_weather(**block.input),
                }
            )
    messages.append({"role": "user", "content": results})
    response = client.messages.create(
        model="claude-opus-5-5", max_tokens=1024, tools=tools, messages=messages
    )

for block in response.content:
    if block.type == "text":
        print(block.text)
````

1. Define the tool with a `name`, a `description` and an `input_schema`, then pass the list as `tools`.
2. Send the user message. Claude replies with `stop_reason` set to `tool_use` and one or more `tool_use` blocks, each with an `id`, a `name` and an `input`.
3. Append `response.content` to the history as the assistant message, whole and unedited. The docs want every thinking block back unchanged when tools are in play.
4. Run your function for each `tool_use` block and build a `tool_result` block with the same id as `tool_use_id`.
5. Send all the results in one user message and call the API again. Claude now writes the final answer.

## Write tool definitions Claude can use

The description decides most of the outcome. [The docs](https://platform.claude.com/docs/en/agents-and-tools/tool-use/define-tools) call it the most important factor in tool performance. They ask for at least three or four sentences that say what the tool does, when to use it, what each parameter means and what it returns.

Fewer, broader tools work better than many narrow ones, so fold related operations into one tool with an `action` parameter. Prefix names by service when several tools sit side by side, as in `github_list_prs`. Return only high-signal data from the function, because everything it returns is input tokens on the next call.

| Field | Needed | What to know |
| --- | --- | --- |
| `name` | Yes | Must match `^[a-zA-Z0-9_-]{1,128}$` |
| `description` | Strongly advised | Plain text, three or four sentences at least |
| `input_schema` | Yes | A JSON Schema object with `properties` and `required` |
| `input_examples` | No | Sample inputs that must validate against the schema, about 20 to 50 tokens each for a simple tool and 100 to 200 for a nested one |
| `strict` | No | Set to `true` to make Claude's inputs follow the schema |
| `cache_control` | No | A cache breakpoint on the tool list, covered in lesson 3 |

## Steer the call with tool_choice

By default Claude decides for itself whether a request needs a tool. `tool_choice` overrides that. Its four values behave differently on current models, and the release notes show the forced ones being turned off one model at a time since September.

Parallel calls are controlled from the same field. With `auto`, setting `disable_parallel_tool_use` to `true` limits Claude to at most one tool call per turn.

> **Forced tool_choice returns a 400 on newer models**
>
> On Opus 5.5, Sonnet 5.5, Fable 5.1 and Mythos 5.1, the values `any` and `tool` return a 400. Haiku 5.5 still accepts them, though a forced call skips thinking. The docs point to `auto` with `strict` set to `true` on the tool, or to structured outputs, when you need a fixed shape. Changing `tool_choice` between requests also invalidates cached messages.

**Tool use changes in the Claude release notes**

1. **May 30, 2024** Tool use leaves beta. No beta header on the Claude API, Bedrock or Vertex AI.
2. **October 3, 2024** `disable_parallel_tool_use` arrives. Set it inside `tool_choice` to keep Claude to at most one tool per turn.
3. **February 27, 2025** `tool_choice` gains `none`. You can stop Claude from calling any tool.
4. **September 17, 2025** Tool helpers and the tool runner in beta. The Python and TypeScript SDKs can run the loop for you.
5. **February 17, 2026** Tool use examples drop the beta header. So do tool search, memory, code execution and web fetch.
6. **September 1, 2026** Fable 5.1 rejects forced `tool_choice`. On Fable 5.1 and Mythos 5.1, `any` and `tool` return an error.
7. **September 22, 2026** Opus 5.5 rejects it too. Tools can also be defined in a mid-conversation system message, in beta.
8. **September 28, 2026** Sonnet 5.5 follows. Forced tool use returns an error here as well.

Source: [Claude Docs: Release notes](https://platform.claude.com/docs/en/release-notes/overview) (read 2026-11-05)

| tool_choice | Behavior | Watch for |
| --- | --- | --- |
| `{"type": "auto"}` | Claude decides whether to call a tool | The default when you pass tools |
| `{"type": "any"}` | Claude must call one of your tools | A 400 on the models in the box below |
| `{"type": "tool", "name": "get_weather"}` | Claude must call that tool | The same 400, and a forced call skips thinking |
| `{"type": "none"}` | Claude may not call a tool | The default when you pass no tools |

## Several tool calls in one response

Ask about the weather in two cities and Claude can return two `tool_use` blocks in the same response. Your code decides whether to run them one after the other or at the same time. The reply matters more. Put every `tool_result` into a single user message.

Sending each result as its own user message works at first, but the [parallel tool use page](https://platform.claude.com/docs/en/agents-and-tools/tool-use/parallel-tool-use) warns that it teaches Claude to stop making parallel calls. Each result still needs the id of its own call. The API matches them by id, not by position.

````json
{
  "role": "user",
  "content": [
    { "type": "tool_result", "tool_use_id": "toolu_01", "content": "{\"temp_c\": 18}" },
    { "type": "tool_result", "tool_use_id": "toolu_02", "content": "{\"temp_c\": 24}" }
  ]
}
````

## Report errors so Claude can recover

A tool fails in two ways: your function raises, or Claude sent input that does not fit. Report both as a `tool_result` with `is_error` set to `true` and a message that says how to fix the call. For a call with invalid or missing parameters, the docs say Claude retries two or three times with corrections before it apologizes. A message such as the one below gives it something to correct with, and a bare "Error" does not.

A call you decide not to run still needs a `tool_result`, with `is_error` and the reason. Leave one out and the next request fails.

Treat everything a tool returns as untrusted. A web page or a file can carry text written to steer the model, which the docs call indirect prompt injection. The [handle tool calls page](https://platform.claude.com/docs/en/agents-and-tools/tool-use/handle-tool-calls) is why you enforce what a tool may touch in your own code, not in the prompt.

````json
{
  "type": "tool_result",
  "tool_use_id": "toolu_01",
  "is_error": true,
  "content": "City not found. Pass a city name such as Lisbon, not a country."
}
````

## What Anthropic and Alex Albert posted about designing tools

Anthropic [posted on X](https://x.com/AnthropicAI/status/1966236220868247701) on 11 September 2025 to share a guide from its engineering blog, [writing effective tools for agents](https://www.anthropic.com/engineering/writing-tools-for-agents). The guide holds that more tools do not always mean better results. Its example: a `search_logs` tool that returns the matching lines beats a `read_logs` tool that returns everything. The define tools page still links to it for naming and response shape, so give `get_weather` the same treatment and return two fields, not the whole weather API response.

Alex Albert, who works in research at Anthropic, [posted on 17 February 2026](https://x.com/alexalbert__/status/2023834875678298535) that tool use examples, tool search and programmatic tool calling had become generally available. Tool use examples are the `input_examples` field in the table of tool fields, and they need no beta header now. Get the description right first. Then add two or three examples to any tool with nested input.

> code execution, web fetch, memory, programmatic tool calling, tool search, and tool use examples are now generally available.
>
> Alex Albert, Research at Anthropic, [X](https://x.com/alexalbert__/status/2023834875678298535) (read 2026-11-05)

[![Anthropic (@AnthropicAI) on X, 11 September 2025, announcing a new Anthropic Engineering blog post on writing effective tools for LLM agents, with tips for developers on making tools more effective.](https://mindket.com/blog/shots/x-anthropicai-effective-tools.webp?v=585cb8fc6b6e)](https://x.com/AnthropicAI/status/1966236220868247701)

Anthropic on X, 11 September 2025

[![Alex Albert (@alexalbert__) on X, 17 February 2026: code execution, web fetch, memory, programmatic tool calling, tool search, and tool use examples are now generally available.](https://mindket.com/blog/shots/x-alexalbert-tool-use-examples-ga.webp?v=5651907c9990)](https://x.com/alexalbert__/status/2023834875678298535)

Alex Albert on X, 17 February 2026

> Screenshots of X's own embeds of the posts, taken on 5 November 2026, with the time in UTC. Select one to open the post on X. Both embeds leave out the link that ends the post.

## Common mistakes

- Sending each tool_result in a separate user message, which cuts down parallel calls.
- Putting text before the tool_result blocks in the same message.
- Adding a text block right after the results, which can make Claude end the turn with an empty reply.
- Rebuilding the assistant message from the text blocks only, and so dropping the thinking blocks the API wants back.
- Writing a one-line description and wondering why Claude picks the wrong tool.
- Forcing a tool with tool_choice on a model that rejects it.
- Returning a whole database row when Claude needs two fields.

## Try it yourself

- Add a second tool, `get_time`, and ask a question that needs both. How many `tool_use` blocks come back, and do you reply in one user message?
- Make `get_weather` raise for the city "Atlantis" and return the error JSON from this lesson. Does Claude retry with a different city or tell the user?
- Delete all but one sentence of the description and compare which questions still trigger the tool.
- Set `disable_parallel_tool_use` to `true` and repeat the two-city question.

## Next

The loop in this lesson stops after one round, and every extra round resends the same tool list and system prompt. [Lesson 3](https://mindket.com/learn/claude-api/claude-prompt-caching) caches them. If you would rather connect tools that already exist than write the schemas, the [MCP lesson on connecting servers](https://mindket.com/learn/mcp/connect-mcp-servers) shows how.

## Check yourself

1. A response ends with stop_reason "tool_use". What do you send next?

   A. A new system prompt
   B. An assistant message with the tool output
   C. A user message with tool_result blocks that carry the tool_use ids
   D. A request with tool_choice set to none

   Answer: C. Claude never runs the tool, so your code runs it and answers in a user message, one tool_result per tool_use id.

2. Claude returns two tool_use blocks in one response. How should you send the results?

   A. In one user message holding both tool_result blocks
   B. In two user messages, one per result
   C. Only the first result, then ask again
   D. In an assistant message

   Answer: A. Separate user messages teach Claude to stop making parallel calls, so every result goes into a single message.

3. A tool raises an exception in your code. What should the tool_result carry?

   A. Nothing, the block is skipped
   B. A bare "Error" string
   C. The full stack trace and nothing else
   D. is_error true and a message that says how to fix the call

   Answer: D. An instructive error says what went wrong and what to try next, so Claude can recover without guessing.

4. A request with tool_choice set to type "tool" returns a 400 on Opus 5.5. What do the docs suggest?

   A. Add a beta header
   B. Use auto with strict set to true on the tool, or structured outputs
   C. Raise max_tokens
   D. Drop the system prompt

   Answer: B. Forced tool_choice is rejected on those models, so the docs point to auto with strict or to structured outputs.

## Frequently asked questions

### Should I use the tool runner or write the loop myself?

The tool runner is in beta in the Python and TypeScript SDKs and runs the loop for you, turning a tool exception into an is_error result. The docs recommend a manual loop when you need human approval, custom logging or conditional execution.

### What error do I get if a tool_result is missing?

An invalid_request_error that reads tool_use ids were found without tool_result blocks immediately after. Check that the results sit in the next user message and that every tool_use id has a match.

### Why does the API want my thinking blocks back?

With tool use, the docs require every thinking and redacted_thinking block from the assistant turn to go back exactly as received, empty ones included. Editing, reordering or filtering them makes the request fail, so append the whole response content.

### Do tool definitions cost tokens?

Yes. Definitions, tool_use blocks and tool_result blocks all count toward your tokens. On Opus 5.5 the docs also list a 286-token tool-use system prompt, added when tool_choice is auto or none.

### Can I cache my tool definitions?

Yes. The cache order is tools, then system, then messages, and a cache_control breakpoint can sit on the last tool. Changing any tool definition resets the whole cache, which lesson 3 covers.

## Sources

- [Anthropic Docs: Tool use with Claude](https://platform.claude.com/docs/en/agents-and-tools/tool-use/overview) (read 2026-11-05)
- [Anthropic Docs: Define tools](https://platform.claude.com/docs/en/agents-and-tools/tool-use/define-tools) (read 2026-11-05)
- [Anthropic Docs: Handle tool calls](https://platform.claude.com/docs/en/agents-and-tools/tool-use/handle-tool-calls) (read 2026-11-04)
- [Anthropic Docs: Parallel tool use](https://platform.claude.com/docs/en/agents-and-tools/tool-use/parallel-tool-use) (read 2026-11-04)
- [Anthropic Docs: How tool use works](https://platform.claude.com/docs/en/agents-and-tools/tool-use/how-tool-use-works) (read 2026-11-04)
- [Anthropic Docs: Tool runner](https://platform.claude.com/docs/en/agents-and-tools/tool-use/tool-runner) (read 2026-11-04)
- [Anthropic Docs: Stop reasons and fallback](https://platform.claude.com/docs/en/build-with-claude/handling-stop-reasons) (read 2026-11-04)
- [Anthropic Docs: Prompt caching](https://platform.claude.com/docs/en/build-with-claude/prompt-caching) (read 2026-11-04)
- [Anthropic Docs: Claude API errors](https://platform.claude.com/docs/en/api/errors) (read 2026-11-04)
- [Claude Docs: Release notes](https://platform.claude.com/docs/en/release-notes/overview) (read 2026-11-05)
- [Anthropic Engineering: Writing effective tools for agents](https://www.anthropic.com/engineering/writing-tools-for-agents) (read 2026-11-05)
- [X: Anthropic shares its guide to writing tools for agents](https://x.com/AnthropicAI/status/1966236220868247701) (read 2026-11-05)
- [X: Alex Albert on tool use examples and tool search becoming generally available](https://x.com/alexalbert__/status/2023834875678298535) (read 2026-11-05)

[Browse AI agents](https://mindket.com/agents)
