The model never runs your tool — it emits a request carrying an id, and your code is what actually executes and hands a result back under that id.
Why you'd careThe problem it solves
You wrote a careful skill describing how expenses get filed: which system, which fields, which approver. The model read it and produced a beautiful narration of the process. Nothing was filed. This is the most common single confusion in the whole area, and it is not subtle once you see it: a skill is text the model reads, and text cannot reach your expense system. A tool is the only construct in the request that creates an execution path back into your code. If the outcome you want involves state changing somewhere outside the conversation — a row written, a message sent, a build triggered — a tool is not one option among several. It is the mechanism. Everything else in this family decides what the model knows; tools decide what it can cause.
ConceptWhat it is
A tool is a declaration you attach to the request: a name, a description, and an input_schema in JSON Schema. Nothing about it is executable from the model's side. When the model decides to use one, the response comes back with stop_reason of tool_use and a content block carrying an id, the tool's name, and an input object matching your schema. You run whatever that implies, then continue the conversation with a tool_result block quoting the same tool_use_id.
Three kinds share that shape and differ in who executes. Custom tools are yours: you write the schema and the handler. Server-side tools — web search, web fetch, code execution, tool search — run on Anthropic's infrastructure; you declare a type and results arrive as content blocks in the same response, with no loop of yours involved. Anthropic-defined client-executed tools — bash, the text editor, memory — are the odd middle case: the schema is built into the model and you must not supply one, but your harness performs the action. Declaring a custom tool you happen to name bash gets you a different, dumber thing.
The boundary worth holding: the tool description is instruction, and it is read on every request. The tool itself is capability. A skill can carry three pages about how to use a tool well; the tool is what makes the call possible at all.
How it worksThe mechanics
The declaration and the round trip:
tools = [{
"name": "create_ticket",
"description": "Open a support ticket. Call this when the user reports a\n defect and has given a component and a description.",
"input_schema": {
"type": "object",
"properties": {
"component": {"type": "string"},
"summary": {"type": "string"},
"severity": {"type": "string", "enum": ["low", "medium", "high"]}
},
"required": ["component", "summary"]
}
}]The model replies with a tool_use block; you execute and append a user message containing a tool_result with the matching tool_use_id. On failure, return the result with is_error set to true rather than dropping it — a missing result is a hung turn, an error result is something the model can recover from. When several tool_use blocks arrive in one response, execute them and return all the results in a single user message; splitting them across messages quietly teaches the model to stop calling tools in parallel.
Two levers change the schema contract. tool_choice takes auto, any, a named tool, or none. Setting strict to true on a tool definition guarantees the input validates exactly, provided the schema sets additionalProperties to false and lists its required fields.
The tool set is no longer necessarily frozen. Older advice was that swapping tools per mode is an expensive design, because tools render at the very front of the prompt and any edit invalidates the entire cache. That is still true by default. On Claude Opus 5, Opus 4.8, Fable 5 and 5.1 and Mythos 5 and 5.1 (not Sonnet 5), behind the beta header mid-conversation-tool-changes-2026-07-01, you can instead append a system message carrying a tool_addition or tool_removal block that references a tool by name, and the cached prefix survives. The tool you plan to add must already appear in tools with defer_loading set to true. Check your model before you architect around the old constraint — and note that changing a tool's definition still takes two requests, a removal then a re-declaration.
At a glanceSee it
A tool call is a request the model emits and someone else executes before the turn continues.
Where it runsSurfaces and availability
| Surface | Status | Notes |
|---|---|---|
| Claude Code | Yes | The harness supplies its own tools and executes them; you extend the set through MCP servers rather than a tools array. Confirmed against “Connect Claude Code to tools via MCP” on code.claude.com. |
| Claude API / Messages API | Yes | The canonical surface. Custom tools, tool_choice, strict schemas and parallel calls are all GA. The “Features overview” table has no separate row for basic tool use. |
| Managed Agents | Yes | Beta. Custom tools are declared on the Agent; the call surfaces as an event and your orchestrator returns the result over the same stream. |
| Claude Desktop / claude.ai | Yes | Tools reach these surfaces through connectors rather than a request parameter — there is no tools array you control. claude.ai connectors are remote MCP servers, browsable in the Anthropic Directory and governed by your organisation’s settings. Confirmed against “Connect Claude Code to tools via MCP” (Disable claude.ai connectors), which documents claude.ai connectors and their org-level management. |
| Claude Agent SDK | Yes | Ships built-in Read, Write, Edit, Bash, Glob, Grep, WebSearch and WebFetch tools plus MCP; you are not assembling a tools array by hand. Confirmed against “Agent SDK overview” (Capabilities → Built-in tools) on code.claude.com. |
| Amazon Bedrock | Yes | Custom tools work. Server-side tools largely do not: web search, web fetch and code execution are listed under “Features not supported” on “Claude in Amazon Bedrock”. Tool search is available, but — quoting the “Tool search tool” page — “On Amazon Bedrock, server-side tool search is available only through the InvokeModel API, not the Converse API.” |
| Google Vertex AI | Yes | Custom tools work, and both web search and tool search are supported. Web fetch and code execution are listed under “Features not supported” on “Claude on Google Cloud”. (An earlier revision of this row claimed web search is restricted to the basic tool variant on Vertex; no primary page states that, so the claim has been withdrawn rather than asserted.) |
| Microsoft Foundry | Yes | Core tool use is GA. The server-side tools — web search, web fetch, code execution and tool search — are GA rather than beta, but partly hosting-option dependent: code execution needs a Hosted on Anthropic deployment, and Hosted on Azure supports only the basic web_search_20250305 and web_fetch_20250910 versions; requests beyond that return 400 Bad Request. Confirmed against the “Features overview” table and its Foundry footnote, plus “Claude in Microsoft Foundry”. |
| OpenAI, Google Gemini, others | Yes | Function calling is genuinely cross-vendor as a concept. Field names, block shapes and the parallel-call contract differ per vendor — do not port a request body. |
Read that table as two layers. Custom function calling is the portable one: every serious vendor has it, and a Bedrock or Vertex deployment loses nothing. Everything Anthropic hosts on its own side — code execution, web fetch, programmatic tool calling, MCP toolsets — is where the resellers thin out, and on Foundry the same list turns on which hosting option you deployed rather than on the platform as a whole. If your design leans on a server-side tool, that decision has pinned your deployment target, and it is worth knowing that before the migration rather than during it.
ExampleIn the real world
An internal ops assistant is asked to “raise a P1 for the checkout service, payments are timing out”. The request carries one custom tool, create_ticket, whose description says explicitly when to call it rather than only what it does.
The model returns a text block — one sentence acknowledging what it is about to do — and a tool_use block with an id and an input of component checkout, summary Payment requests timing out, severity high. The turn stops there with stop_reason of tool_use. No ticket exists yet.
The harness sees the block, checks the caller's permissions, posts to the ticketing API, and appends a user message with a tool_result quoting that id: OPS-4471 created. The next model turn reads that result and replies “Raised OPS-4471 against checkout at P1.”
What the operator sees is one exchange. What actually happened is two model turns with your code in the middle, and the ticket exists because your code created it. Swap the tool for a skill describing the ticketing process and the same conversation produces an accurate description of how to raise a P1 and no ticket at all.
Not thisWhat it is often confused with
- Not a skilla skill is instructions the model reads; a tool is a capability it invokes. The two compose well: put the schema in the tool and the judgment about when and how to use it in a skill, or in the tool's own description.
- Not an MCP serverMCP is how tools can arrive, not a different kind of thing the model sees. By the time an MCP-supplied tool reaches the model it is an ordinary tool with a name, a description and a schema.
- Not a server-side tool, if it is yoursdeclaring
code_executionorweb_searchmeans Anthropic runs it and you get results, not requests. Custom tools are the opposite contract. Mixing up which one you declared explains a lot of “why is nothing being called”. - Not a function the model calls directlythere is no execution inside the model. Every custom tool call is a request that ends the turn and waits for you. If nothing in your code answers it, the conversation stalls.
- Not a permission systemdeclaring a tool grants the model the ability to ask. Approval gates, allowlists and audit belong in your handler, where you can see the caller.
LimitsWhen not to reach for it
- The model only needs to know something.A procedure, a format, a policy. That is a skill or a system-prompt line; wrapping knowledge in a tool call adds a round trip for nothing.
- You have hundreds of tools and a handful are relevant.Declaring all of them burns context on every request. Use tool search with
defer_loadingso schemas load on demand and the cached prefix survives. - The call chain is long and the intermediates are large.Three sequential calls means three round trips and three large payloads in context. Programmatic tool calling lets the model write a script that calls the tools and returns only the final result.
- The secret must never leave your infrastructure.Do not hand a credential to a sandbox to make a tool convenient. Keep the authenticated call on your side and expose it as a custom tool your orchestrator answers.
- You are reaching for tools to fake modes.Swapping the whole set per mode was the classic expensive design and is only partly rehabilitated. Prefer a mode value in the conversation, or the mid-conversation tool-change path on a model that supports it.
Verified 2026-09-12. Moves on a scale of months. Re-check before you depend on it. Provider: Anthropic, Cross-vendor.