One read of Claude Developer Platformapi-20260814T043121Z
565 pages moved out of 565 read.
What this read moved
26-50 of 565, page 2 of 23This capture is too large to show at once. Changes 26-50 of 565 are below, significant first; the rest are on the following screens.
agents-and-tools/mcp-tunnels/overview First recorded · 398 lines, first recorded
## How it works ## Prerequisites ### Network requirements ## Security model ### Security layers ### Shared responsibility model ### What the transport provider can observe ## Deploy a tunnel ## Use the tunneled MCP servers ### Managed Agents (Console) ### Messages API ## Next steps
The first capture of this source. The page was already there, and this is what it said.
---
title: MCP tunnels
url: https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/overview
description: Securely connect Claude to MCP servers running in your private network without opening inbound ports or exposing services to the public internet.
---
MCP tunnels let you connect Claude to Model Context Protocol (MCP) servers that run inside your private network. Traffic flows over an outbound-only connection, so you don't need to open inbound firewall ports, expose services to the public internet, or allowlist Anthropic's IP ranges on your origin.
<Note>
MCP tunnels are in research preview. [Request access](https://claude.com/form/claude-managed-agents) to try them. They are provided "as-is" without any uptime, support, or continuity commitment, and they depend on a third-party network provider (Cloudflare) that makes no availability commitment for the underlying transport. Anthropic may modify or discontinue MCP tunnels at any time.
</Note>
For Zero Data Retention and HIPAA BAA eligibility, see [API and data retention](https://platform.claude.com/docs/en/manage-claude/api-and-data-retention#feature-eligibility).
## How it works
The [tunnel stack](https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/concepts#components) is two components that run inside your network:
* **[cloudflared](https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/concepts#components):** Cloudflare's open-source tunnel connector. It initiates outbound-only connections to the [tunnel edge](https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/concepts#components) and carries encrypted traffic from Anthropic to your proxy.
* **[Proxy](https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/concepts#components):** Anthropic's routing component. It terminates [inner TLS](https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/concepts#components), validates that upstream IPs fall within an allowed range, and routes each request to the correct [upstream MCP server](https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/concepts#components) based on hostname.
Each MCP server you expose gets a hostname under your tunnel domain (for example, `docs.<your-tunnel-domain>`). You attach these hostnames to a Managed Agent session in the Claude Console, or pass them to the Messages API through the [MCP connector](https://platform.claude.com/docs/en/agents-and-tools/mcp-connector).
## Prerequisites
Before deploying, make sure you have:
* A deployment target: a Kubernetes cluster, or a VM with Docker and Docker Compose.
* A tunnel. Create one in the Claude Console (see [Create a tunnel](https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/console#create-a-tunnel)) or through the API; the Helm chart's setup hook can also create one for you during install.
* A way for your stack to authenticate to the Tunnels API. Choose one:
* **[Programmatic access](https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/concepts#credential-provisioning) (recommended).** Set up [Workload Identity Federation](https://platform.claude.com/docs/en/manage-claude/workload-identity-federation) when you create the tunnel. Your stack mints short-lived API tokens from your identity provider, fetches the tunnel token, and generates and registers a CA certificate automatically. Requires permission to manage federation rules, a registered OIDC issuer, and a federation rule with the `workspace:manage_tunnels` scope.
* **[Manual](https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/concepts#credential-provisioning).** Supply static credentials yourself: the tunnel token from the Console and a server certificate signed by a CA you register there. See [Get the connection details](https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/console#get-the-connection-details) and [Add a CA certificate](https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/console#add-a-ca-certificate).
* One or more MCP servers running in your private network. See [Remote MCP servers](https://platform.claude.com/docs/en/agents-and-tools/remote-mcp-servers) for examples.
* Outbound connectivity as listed under [Network requirements](https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/overview#network-requirements).
### Network requirements
| Component | Destination | Port / protocol | Used during |
| --------------- | ---------------------------------------------------- | ---------------- | ------------------------------- |
| Setup component | `api.anthropic.com` | 443 TCP | Provisioning and token rotation |
| cloudflared | Tunnel edge (`198.41.192.0/19`, `2606:4700:a0::/44`) | 7844 TCP and UDP | Runtime |
| Proxy | Your upstream MCP servers | As configured | Runtime |
## Security model
### Security layers
Three independent layers protect every request:
| Layer | Protects against |
| --------------------------------------------------------------------------- | ------------------------------------------------------------------------ |
| Outer mTLS between Anthropic and the transport provider, with IP validation | Unauthorized clients reaching the tunnel |
| Inner TLS from Anthropic's back end to your proxy | Payload inspection by the transport provider or any network intermediary |
| OAuth on each MCP server | Unauthorized use of MCP tools by authenticated tunnel traffic |
The tunnel transport runs on Cloudflare's network. Because the proxy terminates inner TLS using a certificate that only you hold, Cloudflare cannot read request or response payloads. Anthropic does not connect to a tunnel until a CA certificate is registered, so payloads are always encrypted when they cross Cloudflare's network. Cloudflare does receive connection metadata; see [What the transport provider can observe](https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/overview#what-the-transport-provider-can-observe).
### Shared responsibility model
| Anthropic handles | Your organization handles |
| ------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| Tunnel access control | All content and traffic that transits your tunnel, and compliance with applicable third-party acceptable-use policies (including Cloudflare's) |
| Validating your CA certificate before connecting to your proxy | Adherence to the deployment guidance on these pages |
| Ensuring Claude only sends requests to tunnels owned by your organization | Securing tunnel tokens and TLS private keys |
| | Managing the server certificate and renewing it before it expires |
| | Configuring OAuth on each MCP server |
| | Restricting network access for the proxy and MCP servers |
| | Notifying Anthropic if you suspect a breach |
<Warning>
If an attacker obtains your tunnel token **and** one of your TLS private keys, they could impersonate your proxy and read MCP request payloads. Treat both as high-value secrets. See [MCP tunnels security](https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/security) for hardening guidance.
</Warning>
### What the transport provider can observe
Cloudflare provides the outbound transport. It cannot read MCP request or response payloads, but it does receive the following connection metadata:
* the egress IP address of the host running cloudflared
* a cloudflared host fingerprint
* connection timing and byte-volume
* the `*.tunnel.anthropic.com` subdomain assigned to your tunnel
Anthropic's agreement with Cloudflare restricts Cloudflare's use of this telemetry. Cloudflare acts as a subprocessor for this research preview.
## Deploy a tunnel
If you're new to MCP tunnels, start with the quickstart to get a working tunnel locally before configuring a production deployment.
<CardGroup cols={2}>
<Card title="Quickstart" icon="rocket" href="https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/quickstart">
The shortest path to a working tunnel: Docker Compose with a sample MCP server.
</Card>
<Card title="Deploy with Helm" icon="stack" href="https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/deploy-helm">
Install on a Kubernetes cluster using the Anthropic Helm chart.
</Card>
<Card title="Deploy with Docker Compose" icon="cube" href="https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/deploy-compose">
Install on a VM using Docker Compose.
</Card>
</CardGroup>
Choosing between them:
* **Deployment target**
* **Helm** when deploying to Kubernetes.
* **Docker Compose** for a single host or local testing.
* **Authentication for setup**
* **Programmatic access** (through Workload Identity Federation) when you have an OIDC identity provider such as a Kubernetes cluster, cloud IAM, or SPIFFE.
* **Manual credentials** when you don't, or when you're testing.
## Use the tunneled MCP servers
Once your tunnel is active (it has an active CA certificate and your tunnel stack is connected), the upstream MCP servers are reachable from Claude Managed Agents and the Messages API.
<Note>
MCP tunnels created through the Console are not available as connectors in claude.ai.
</Note>
In both cases, the tunnel carries encrypted traffic to your MCP server but does not authenticate to it. If the upstream MCP server requires its own authentication (OAuth, bearer token), supply it the same way you would for any other MCP server; it is independent of the tunnel.
### Managed Agents (Console)
1. In **Managed Agents > Sessions**, create a session and choose **Create new agent** so you can edit the MCP server list.
2. Click **+ MCP Server** and open the dropdown. Tunnels in the session's workspace that have at least one active certificate appear at the top of the list, above the public connector catalog.
3. Select the tunnel and supply the **Subdomain** that your proxy routes to a specific MCP server, and the **Path** the upstream MCP server expects. The **Resolves to** line shows the exact URL.
### Messages API
Pass the upstream MCP server's URL in the `mcp_servers` array, the same way as any other remote MCP server. The request body and `anthropic-beta` header follow the standard [MCP connector](https://platform.claude.com/docs/en/agents-and-tools/mcp-connector) format; only the `url` is tunnel-specific. The following example uses the MCP connector's `mcp-client` beta header, which is separate from the `mcp-tunnels` beta used by the [Tunnels API](https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/reference). Use an API key for the workspace the tunnel was created in (Console **Settings > API keys**).
The URL's host is `<subdomain>.<your-tunnel-domain>`. The path depends on your upstream MCP server, not the tunnel: FastMCP's `streamable-http` transport serves at `/mcp`, and other servers may use `/` or a custom path (check the server's documentation). The proxy forwards the path untouched.
<CodeGroup>
```bash cURL
curl https://api.anthropic.com/v1/messages \
-H "Content-Type: application/json" \
-H "X-API-Key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "anthropic-beta: mcp-client-2025-11-20" \
-d '{
"model": "claude-opus-5",
"max_tokens": 1000,
"messages": [{"role": "user", "content": "Use the hello tool to greet tunnel."}],
"mcp_servers": [
{
"type": "url",
"url": "https://echo.YOUR_TUNNEL_DOMAIN_HERE/mcp",
"name": "echo"
}
],
"tools": [{"type": "mcp_toolset", "mcp_server_name": "echo"}]
}'
```
```bash CLI
ant beta:messages create --beta mcp-client-2025-11-20 <<'YAML'
model: claude-opus-5
max_tokens: 1000
messages:
- role: user
content: Use the hello tool to greet tunnel.
mcp_servers:
- type: url
url: https://echo.YOUR_TUNNEL_DOMAIN_HERE/mcp
name: echo
tools:
- type: mcp_toolset
mcp_server_name: echo
YAML
```
```python Python
client = anthropic.Anthropic()
response = client.beta.messages.create(
model="claude-opus-5",
max_tokens=1000,
messages=[{"role": "user", "content": "Use the hello tool to greet tunnel."}],
mcp_servers=[
{
"type": "url",
"url": "https://echo.YOUR_TUNNEL_DOMAIN_HERE/mcp",
"name": "echo",
}
],
tools=[{"type": "mcp_toolset", "mcp_server_name": "echo"}],
betas=["mcp-client-2025-11-20"],
)
print(response)
```
```typescript TypeScript
const anthropic = new Anthropic();
const response = await anthropic.beta.messages.create({
model: "claude-opus-5",
max_tokens: 1000,
messages: [
{
role: "user",
content: "Use the hello tool to greet tunnel."
}
],
mcp_servers: [
{
type: "url",
url: "https://echo.YOUR_TUNNEL_DOMAIN_HERE/mcp",
name: "echo"
}
],
tools: [
{
type: "mcp_toolset",
mcp_server_name: "echo"
}
],
betas: ["mcp-client-2025-11-20"]
});
console.log(response);
```
```csharp C#
AnthropicClient client = new();
var parameters = new MessageCreateParams
{
Model = Messages::Model.ClaudeOpus5,
MaxTokens = 1000,
Messages = new List<BetaMessageParam>
{
new() { Role = Role.User, Content = "Use the hello tool to greet tunnel." }
},
McpServers = new List<BetaRequestMcpServerUrlDefinition>
{
new()
{
Url = "https://echo.YOUR_TUNNEL_DOMAIN_HERE/mcp",
Name = "echo"
}
},
Tools = new List<BetaToolUnion>
{
new BetaMcpToolset("echo")
},
Betas = ["mcp-client-2025-11-20"]
};
var message = await client.Beta.Messages.Create(parameters);
Console.WriteLine(message);
```
```go Go
client := anthropic.NewClient()
response, err := client.Beta.Messages.New(context.TODO(), anthropic.BetaMessageNewParams{
Model: anthropic.ModelClaudeOpus5,
MaxTokens: 1000,
Messages: []anthropic.BetaMessageParam{
anthropic.NewBetaUserMessage(anthropic.NewBetaTextBlock("Use the hello tool to greet tunnel.")),
},
MCPServers: []anthropic.BetaRequestMCPServerURLDefinitionParam{
{
URL: "https://echo.YOUR_TUNNEL_DOMAIN_HERE/mcp",
Name: "echo",
},
},
Tools: []anthropic.BetaToolUnionParam{
{OfMCPToolset: &anthropic.BetaMCPToolsetParam{
MCPServerName: "echo",
}},
},
Betas: []anthropic.AnthropicBeta{
anthropic.AnthropicBetaMCPClient2025_11_20,
},
})
if err != nil {
log.Fatal(err)
}
fmt.Println(response)
```
```java Java
import com.anthropic.models.beta.messages.BetaMcpToolset;
// ...
import com.anthropic.models.beta.messages.BetaRequestMcpServerUrlDefinition;
// ...
void main() {
AnthropicClient client = AnthropicOkHttpClient.fromEnv();
Cut at 300 lines. The page has the rest.
agents-and-tools/mcp-tunnels/quickstart First recorded · 286 lines, first recorded
## What you'll build ## What you need ## Next steps
The first capture of this source. The page was already there, and this is what it said.
---
title: MCP tunnels quickstart
url: https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/quickstart
description: Connect Claude to a private MCP server using a local Docker Compose deployment.
---
<Note>
MCP tunnels are in research preview. [Request access](https://claude.com/form/claude-managed-agents) to try them.
</Note>
This quickstart takes you from zero to Claude calling a private MCP server through a tunnel. It uses Docker Compose with [manual](https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/concepts#credential-provisioning) credential provisioning, which is the shortest path for local testing. For production deployments, see [Deploy with Helm](https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/deploy-helm) or [Deploy with Docker Compose](https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/deploy-compose).
## What you'll build
A two-container [tunnel stack](https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/concepts#components) (the [proxy](https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/concepts#components) and [cloudflared](https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/concepts#components)) plus a sample MCP server running alongside it. When everything is running, the sample server is reachable from Claude at `https://echo.<your-tunnel-domain>/mcp` even though nothing is listening on a public port.
## What you need
* [Docker and Docker Compose](https://docs.docker.com/get-docker/) on a machine with outbound internet access.
* A role in the [Claude Console](https://platform.claude.com) that can manage MCP tunnels. See the [Console guide prerequisites](https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/console#prerequisites).
* [OpenSSL](https://openssl-library.org/source/) 1.1.1 or later. Preinstalled on macOS and most Linux distributions; on Windows, install it separately (the `openssl` binary must be on your `PATH`).
<Steps>
<Step title="Create a tunnel">
In the Claude Console sidebar, go to **Manage > MCP tunnels** and click **New tunnel**. Give it a name. Leave **Set up programmatic access** off; this quickstart uses manual credential provisioning.
After it's created, open the tunnel. Copy two values from the **Connection** section:
* **Domain** (looks like `abcd1234.tunnel.anthropic.com`)
* **Token** (click the eye icon, then copy)
</Step>
<Step title="Set up the deployment directory">
<Tabs>
<Tab title="macOS / Linux">
```bash
mkdir -p mcp-tunnel/{config,data}
cd mcp-tunnel
export TUNNEL_DOMAIN=YOUR_TUNNEL_DOMAIN_HERE # from step 1
export TUNNEL_TOKEN='eyJ...' # from step 1
```
</Tab>
<Tab title="Windows (PowerShell)">
```powershell
New-Item -ItemType Directory -Force -Path mcp-tunnel/config, mcp-tunnel/data | Out-Null
Set-Location mcp-tunnel
$env:TUNNEL_DOMAIN = "YOUR_TUNNEL_DOMAIN_HERE" # from step 1
$env:TUNNEL_TOKEN = "eyJ..." # from step 1
```
</Tab>
</Tabs>
</Step>
<Step title="Generate a CA and server certificate">
The proxy terminates [inner TLS](https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/concepts#components) using a certificate signed by a CA you control. Generate both:
<Tabs>
<Tab title="macOS / Linux">
```bash
openssl req -x509 -newkey rsa:2048 -nodes \
-keyout data/ca.key -out data/ca.crt \
-days 3650 -subj "/CN=mcp-tunnel-ca" \
-addext "basicConstraints=critical,CA:TRUE" \
-addext "keyUsage=critical,keyCertSign,cRLSign" \
-addext "subjectKeyIdentifier=hash"
cat > data/tls.ext <<EOF
subjectAltName = DNS:${TUNNEL_DOMAIN},DNS:*.${TUNNEL_DOMAIN}
authorityKeyIdentifier = keyid,issuer
extendedKeyUsage = serverAuth
EOF
openssl req -newkey rsa:2048 -nodes \
-keyout data/tls.key -out /tmp/server.csr \
-subj "/CN=${TUNNEL_DOMAIN}"
openssl x509 -req -in /tmp/server.csr \
-CA data/ca.crt -CAkey data/ca.key -CAcreateserial \
-out data/tls.crt -days 90 -extfile data/tls.ext
chmod 644 data/tls.key
```
</Tab>
<Tab title="Windows (PowerShell)">
```powershell
openssl req -x509 -newkey rsa:2048 -nodes `
-keyout data/ca.key -out data/ca.crt `
-days 3650 -subj "/CN=mcp-tunnel-ca" `
-addext "basicConstraints=critical,CA:TRUE" `
-addext "keyUsage=critical,keyCertSign,cRLSign" `
-addext "subjectKeyIdentifier=hash"
@"
subjectAltName = DNS:$env:TUNNEL_DOMAIN,DNS:*.$env:TUNNEL_DOMAIN
authorityKeyIdentifier = keyid,issuer
extendedKeyUsage = serverAuth
"@ | Set-Content -NoNewline -Encoding ascii -Path data/tls.ext
openssl req -newkey rsa:2048 -nodes `
-keyout data/tls.key -out data/server.csr `
-subj "/CN=$env:TUNNEL_DOMAIN"
openssl x509 -req -in data/server.csr `
-CA data/ca.crt -CAkey data/ca.key -CAcreateserial `
-out data/tls.crt -days 90 -extfile data/tls.ext
```
</Tab>
</Tabs>
Back in the Console, on the tunnel detail page, click **Add certificate** and upload `data/ca.crt` (or paste its contents). The tunnel status flips to **Active**.
</Step>
<Step title="Write the sample MCP server">
<Tabs>
<Tab title="macOS / Linux">
```bash
cat > hello_server.py <<'EOF'
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("hello-server", host="0.0.0.0", port=9000)
@mcp.tool()
def hello(name: str = "world") -> str:
"""Say hello to someone."""
return f"Hello, {name}!"
if __name__ == "__main__":
mcp.run(transport="streamable-http")
EOF
```
</Tab>
<Tab title="Windows (PowerShell)">
```powershell
@'
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("hello-server", host="0.0.0.0", port=9000)
@mcp.tool()
def hello(name: str = "world") -> str:
"""Say hello to someone."""
return f"Hello, {name}!"
if __name__ == "__main__":
mcp.run(transport="streamable-http")
'@ | Set-Content -NoNewline -Encoding ascii -Path hello_server.py
```
</Tab>
</Tabs>
</Step>
<Step title="Write the proxy config and compose file">
<Tabs>
<Tab title="macOS / Linux">
```bash
cat > config/mcp-proxy.yaml <<EOF
listen_addr: ":8080"
tunnel_domain: ${TUNNEL_DOMAIN}
tls:
cert_file: /data/tls.crt
key_file: /data/tls.key
routes:
echo: http://hello-mcp:9000
EOF
cat > docker-compose.yaml <<'EOF'
services:
mcp-proxy:
image: us-docker.pkg.dev/anthropic-public-registry/images/mcp-proxy@sha256:efb27b299d627e4134815663cb8896641eeaee025d734c0f695582b4df38f013
volumes:
- ./config/mcp-proxy.yaml:/etc/mcp-gateway/config.yaml:ro
- ./data:/data:ro
restart: unless-stopped
cloudflared:
image: cloudflare/cloudflared@sha256:6b599ca3e974349ead3286d178da61d291961182ec3fe9c505e1dd02c8ac31b0
command: tunnel --no-autoupdate run --url http://localhost:8080
environment:
- TUNNEL_TOKEN
network_mode: "service:mcp-proxy"
restart: unless-stopped
hello-mcp:
image: python:3.13-slim
working_dir: /app
volumes:
- ./hello_server.py:/app/hello_server.py:ro
command: sh -c "pip install --quiet mcp && python hello_server.py"
restart: unless-stopped
EOF
```
</Tab>
<Tab title="Windows (PowerShell)">
```powershell
@"
listen_addr: ":8080"
tunnel_domain: $env:TUNNEL_DOMAIN
tls:
cert_file: /data/tls.crt
key_file: /data/tls.key
routes:
echo: http://hello-mcp:9000
"@ | Set-Content -NoNewline -Encoding ascii -Path config/mcp-proxy.yaml
@'
services:
mcp-proxy:
image: us-docker.pkg.dev/anthropic-public-registry/images/mcp-proxy@sha256:efb27b299d627e4134815663cb8896641eeaee025d734c0f695582b4df38f013
volumes:
- ./config/mcp-proxy.yaml:/etc/mcp-gateway/config.yaml:ro
- ./data:/data:ro
restart: unless-stopped
cloudflared:
image: cloudflare/cloudflared@sha256:6b599ca3e974349ead3286d178da61d291961182ec3fe9c505e1dd02c8ac31b0
command: tunnel --no-autoupdate run --url http://localhost:8080
environment:
- TUNNEL_TOKEN
network_mode: "service:mcp-proxy"
restart: unless-stopped
hello-mcp:
image: python:3.13-slim
working_dir: /app
volumes:
- ./hello_server.py:/app/hello_server.py:ro
command: sh -c "pip install --quiet mcp && python hello_server.py"
restart: unless-stopped
'@ | Set-Content -NoNewline -Encoding ascii -Path docker-compose.yaml
```
</Tab>
</Tabs>
</Step>
<Step title="Start it">
<Tabs>
<Tab title="macOS / Linux">
```bash
docker compose up -d
docker compose logs mcp-proxy | grep "route configured"
docker compose logs cloudflared | grep "Registered tunnel connection"
```
</Tab>
<Tab title="Windows (PowerShell)">
```powershell
docker compose up -d
docker compose logs mcp-proxy | Select-String "route configured"
docker compose logs cloudflared | Select-String "Registered tunnel connection"
```
</Tab>
</Tabs>
You should see one `route configured` line for `echo` and four `Registered tunnel connection` lines. The containers take a few seconds to start; rerun the log commands if they come back empty.
</Step>
<Step title="Call it from Claude">
In the Console, go to **Managed Agents > Sessions** and create a session. In the agent picker choose **Create new agent**, give the agent a name, and keep the pre-filled model. Click **+ MCP Server**, select your tunnel, set **Subdomain** to `echo` and **Path** to `mcp`. Then ask:
> Use the hello tool to greet tunnel.
You should see a tool call followed by its result.
</Step>
</Steps>
## Next steps
The tunnel is verified end to end. To swap in your own MCP server, add it to `docker-compose.yaml` (or run it on the same Docker network), add a route for it in `config/mcp-proxy.yaml`, then restart the proxy (`docker compose restart mcp-proxy`).
For production deployments:
<CardGroup cols={2}>
<Card title="Deploy with Docker Compose" icon="cube" href="https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/deploy-compose">
Hardened single-host deployment, with or without programmatic access.
</Card>
<Card title="Deploy with Helm" icon="stack" href="https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/deploy-helm">
Kubernetes deployment with automatic credential management.
</Card>
</CardGroup>
agents-and-tools/mcp-tunnels/reference First recorded · 113 lines, first recorded
## Proxy configuration ### Route matching ## Tunnels API ## Certificate requirements ### CA certificate ### Server certificate ## Setup component ### `setup init` ### `setup renew-cert`
The first capture of this source. The page was already there, and this is what it said.
---
title: MCP tunnels reference
url: https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/reference
description: Proxy configuration fields, the Tunnels REST API, certificate requirements, and the setup component.
---
<Note>
MCP tunnels are in research preview. [Request access](https://claude.com/form/claude-managed-agents) to try them.
</Note>
## Proxy configuration
The [proxy](https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/concepts#components) reads its configuration from `/etc/mcp-gateway/config.yaml` (Compose) or the rendered ConfigMap (Helm, populated from `gateway.config.*`).
| Field | Description | Default |
| --------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------- |
| `listen_addr` | Address and port to listen on. | Required |
| `log_level` | Logging verbosity: `debug`, `info`, `warn`, or `error`. | `info` |
| `shutdown_timeout` | How long to wait for in-flight requests during graceful shutdown. | `30s` |
| `tunnel_domain` | Base domain assigned to the tunnel. When set, route lookup strips this suffix from incoming hostnames so `routes` keys can be bare subdomains (`wiki`). When empty, `routes` keys must be exact full hostnames. | Required when `routes` keys are bare subdomains |
| `tls.cert_file` | Path to the server TLS certificate. | Required |
| `tls.key_file` | Path to the server TLS private key. | Required |
| `routes` | Map of subdomain or full hostname to upstream URL. See [Route matching](https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/reference#route-matching). | Required |
| `upstream.allowed_ips` | IPv4 CIDR ranges or single addresses the proxy is permitted to connect to. Mutually exclusive with `disable_ip_validation`. | RFC1918 private ranges |
| `upstream.disable_ip_validation` | Disable upstream IP validation entirely. Mutually exclusive with `allowed_ips`. | `false` |
| `upstream.tls.ca_file` | CA bundle for validating upstream TLS. | None |
| `upstream.tls.include_system_cas` | Also trust the system CA bundle for upstream TLS. | `false` |
For `https://` upstream routes, set at least one of `upstream.tls.ca_file` or `upstream.tls.include_system_cas`; otherwise the proxy has no trust anchor for the upstream certificate.
### Route matching
`routes` is a flat string map (`map[string]string`), not a list. The proxy looks up the incoming hostname by exact match first, then by stripping the `tunnel_domain` suffix and matching the remaining subdomain. The match considers only the hostname; the request path and query string are forwarded to the [upstream MCP server](https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/concepts#components) unchanged.
Each upstream value must be exactly `scheme://host:port`. The port is mandatory. Including a path is rejected at config load with `invalid upstream (must be scheme://host:port)`.
## Tunnels API
The Tunnels REST API lives at `/v1/tunnels` and supports creating, listing, and archiving tunnels, registering CA certificates, and revealing or rotating the tunnel token. See the [Tunnels API reference](https://platform.claude.com/docs/en/api/beta/tunnels/list) for all endpoints, request and response schemas, and examples.
<Note>
The previous Admin API surface at `/v1/organizations/tunnels` (beta header `mcp-tunnels-2026-05-19`, scope `org:manage_tunnels`) continues to work during a migration window and remains documented in the [Admin API reference](https://platform.claude.com/docs/en/api/admin/mcp_tunnels) with a deprecation notice. To migrate, update the path to `/v1/tunnels`, the beta header to `mcp-tunnels-2026-06-22`, and your WIF token scope to `workspace:manage_tunnels`.
</Note>
<Warning>
All MCP tunnels endpoints require a bearer token with the `workspace:manage_tunnels` scope obtained through [Workload Identity Federation](https://platform.claude.com/docs/en/manage-claude/workload-identity-federation). Admin API keys are not accepted.
</Warning>
Required headers on every request:
| Header | Value |
| ------------------- | ------------------------------------------ |
| `Authorization` | `Bearer <token>` (the WIF-exchanged token) |
| `anthropic-version` | `2023-06-01` |
| `anthropic-beta` | `mcp-tunnels-2026-06-22` |
## Certificate requirements
The [setup component](https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/concepts#components) generates compliant certificates automatically. These requirements apply only if you issue certificates through your own PKI.
### CA certificate
Upload with `POST /v1/tunnels/{tunnel_id}/certificates`. A tunnel can hold up to two active CA certificates at a time, which allows zero-downtime rotation.
* PEM-encoded, single certificate, up to 8 kB.
* `BasicConstraints` extension present with `CA:TRUE`, marked critical.
* `SubjectKeyIdentifier` extension present.
* `KeyUsage` includes `keyCertSign`.
* Within its validity period.
* RSA 2048-bit or larger, or ECDSA P-256 or larger, with a SHA-256 or stronger signature.
### Server certificate
Presented by the proxy during [inner TLS](https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/concepts#components).
* Signed directly by a registered CA (no intermediates).
* `AuthorityKeyIdentifier` extension present and matching the CA's `SubjectKeyIdentifier`.
* Subject Alternative Name includes a DNS name matching `<route>.<tunnel-domain>`. A wildcard `*.<tunnel-domain>` covers all routes.
* If the `ExtendedKeyUsage` extension is present, it includes `serverAuth`.
* Within its validity period.
* RSA 2048-bit or larger, or ECDSA P-256 or larger, with a SHA-256 or stronger signature.
The setup component generates an ECDSA P-256 CA with five-year validity and an RSA 4096-bit server certificate with a wildcard SAN and 90-day validity.
## Setup component
The setup component ships inside the `mcp-proxy` image as the `setup` binary. Run it with `docker compose run --rm setup <subcommand>` (Compose) or rely on the chart's hooks and CronJobs (Helm).
### `setup init`
Attaches to an existing tunnel (or creates one when no tunnel ID is supplied), then generates a CA and server certificate, registers the CA, retrieves the tunnel token, and writes all outputs to the destination.
| Flag | Description | Default |
| ----------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- |
| `--api-url` | Claude API base URL. Also read from `API_URL`. | Required |
| `--tunnel-id` | Tunnel ID to attach to (`tnl_...`). Also read from `TUNNEL_ID`. When omitted, a new tunnel is created; a tunnel ID already stored in the output is reused on re-runs. | None (create a tunnel) |
| `--output` | Output destination: `dir:/path` or `k8s-secret:NAME`. The Helm chart passes `k8s-secret:<release>`. | `k8s-secret:mcp-tunnel` (auto-detected when running in a Kubernetes pod; required otherwise) |
| `--cert-duration` | Server certificate validity period. | `2160h` (90 days) |
| `--token-version` | Change-detection string. A new value triggers token rotation on re-run. The Helm chart and the Compose example both pass `1` as the initial value. | None |
The command authenticates through [Workload Identity Federation](https://platform.claude.com/docs/en/manage-claude/workload-identity-federation). It reads `ANTHROPIC_FEDERATION_RULE_ID`, `ANTHROPIC_ORGANIZATION_ID`, `ANTHROPIC_WORKSPACE_ID` (optional), and exactly one of `ANTHROPIC_IDENTITY_TOKEN_FILE` or `ANTHROPIC_IDENTITY_TOKEN`. See the [WIF reference](https://platform.claude.com/docs/en/manage-claude/wif-reference) for the current semantics of these variables; the setup component derives the service account from the federation rule, so it does not require `ANTHROPIC_SERVICE_ACCOUNT_ID` separately.
### `setup renew-cert`
Issues a new server certificate signed by the stored CA. Makes no API calls.
| Flag | Description | Default |
| ----------------- | --------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- |
| `--output` | Output destination: `dir:/path` or `k8s-secret:NAME`. The Helm chart passes `k8s-secret:<release>`. | `k8s-secret:mcp-tunnel` (auto-detected when running in a Kubernetes pod; required otherwise) |
| `--cert-duration` | New certificate validity period. | `2160h` (90 days) |
| `--renew-before` | Skip renewal if the existing certificate has more than this duration remaining. | `0` (always renew) |
Setting `--renew-before=720h` makes the command a no-op when more than 30 days of validity remain, so it's safe to run on a fixed schedule.
agents-and-tools/mcp-tunnels/security First recorded · 113 lines, first recorded
## Best practices ## Respond to a suspected breach ## Tear down a tunnel
The first capture of this source. The page was already there, and this is what it said.
---
title: MCP tunnels security
url: https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/security
description: Hardening guidance, credential rotation, breach response, and teardown for MCP tunnel deployments.
---
<Note>
MCP tunnels are in research preview. [Request access](https://claude.com/form/claude-managed-agents) to try them.
</Note>
The tunnel architecture provides strong defaults (outbound-only connectivity, end-to-end encryption, and IP validation), but the overall security of your [tunnel stack](https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/concepts#components) also depends on how you configure and operate it. This page covers recommended hardening, breach response, and how to decommission a tunnel.
## Best practices
* **Require OAuth on every MCP server.** Configure each [upstream MCP server](https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/concepts#components) to require OAuth as described in the [MCP authorization spec](https://modelcontextprotocol.io/specification/2025-11-25/basic/authorization). OAuth provides defense in depth on top of the tunnel's transport authentication and enables user-level authorization at the data layer.
* **Enable SSO for your organization.** Tunnels, federation rules, and service accounts are managed in the Claude Console. SSO enforces your identity provider's session controls on the admins who can change them.
* **Restrict `upstream.allowed_ips`.** Use the smallest CIDR ranges that cover your MCP servers. This is the [proxy](https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/concepts#components)'s primary SSRF defense.
* **Monitor logs.** Alert on warnings, errors, and unusual traffic patterns from the tunnel stack.
* **Rotate credentials.** Rotate the server certificate and tunnel token on a regular schedule, and immediately if you suspect compromise.
* **Keep images updated.** Track new proxy releases and pin images by SHA-256 digest.
* **Limit network reach.** The proxy and [cloudflared](https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/concepts#components) should only be able to reach the destinations listed in the [network requirements](https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/overview#network-requirements). Use NetworkPolicy (Kubernetes) or host firewall rules (Compose).
* **Limit MCP server scope.** Each server should expose only the tools and data required for its purpose.
* **Protect credentials at rest.** Apply your organization's secrets-management practices to private keys and tunnel tokens.
## Respond to a suspected breach
If you believe your tunnel token, TLS keys, or proxy host has been compromised:
<Steps>
<Step title="Stop the tunnel stack">
<Tabs>
<Tab title="Helm">
```bash
helm uninstall mcp-tunnel -n mcp-tunnel
```
</Tab>
<Tab title="Docker Compose">
```bash
docker compose down --timeout 0
```
</Tab>
</Tabs>
</Step>
<Step title="Detach the upstream MCP servers">
Remove the upstream MCP servers from any Managed Agent sessions that use them, and stop passing their URLs in the `mcp_servers` block of Messages API requests.
</Step>
<Step title="Archive the tunnel">
Archiving invalidates the tunnel token and detaches the domain. In the Console, [archive the tunnel](https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/console#archive-a-tunnel) from the **MCP tunnels** list. To archive over the API instead, see [Archive a tunnel](https://platform.claude.com/docs/en/api/beta/tunnels/archive).
</Step>
<Step title="Contact Anthropic">
Report the suspected compromise to Anthropic support.
</Step>
<Step title="Rotate downstream credentials">
Re-provision a fresh tunnel and rotate any OAuth tokens that the affected MCP servers issued.
</Step>
<Step title="Review logs before restoring service">
Inspect proxy, cloudflared, and MCP server logs for the window of suspected compromise before bringing the new tunnel online.
</Step>
</Steps>
## Tear down a tunnel
Follow these steps to decommission a tunnel and remove all stored credentials.
<Steps>
<Step title="Stop the tunnel stack">
<Tabs>
<Tab title="Helm">
```bash
helm uninstall mcp-tunnel -n mcp-tunnel
```
</Tab>
<Tab title="Docker Compose">
```bash
docker compose down
```
</Tab>
</Tabs>
</Step>
<Step title="Archive the tunnel">
In the Console, [archive the tunnel](https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/console#archive-a-tunnel) from the **MCP tunnels** list.
</Step>
<Step title="Remove stored credentials">
<Tabs>
<Tab title="Helm">
With programmatic access, the setup component created a single Secret named after the release. Without programmatic access, you created `mcp-tunnel-token` and `mcp-tunnel-cert` yourself. Delete whichever apply:
```bash
kubectl -n mcp-tunnel delete secret \
mcp-tunnel mcp-tunnel-token mcp-tunnel-cert \
--ignore-not-found
```
</Tab>
<Tab title="Docker Compose">
Private keys and certificates live in `data/`. The tunnel token lives in `data/tunnel-token` (programmatic flow) or in your shell environment (manual flow). The `config/` directory and `docker-compose.yaml` contain no secrets; keep them if you plan to re-provision, or remove them as well.
```bash
sudo rm -rf data
```
</Tab>
</Tabs>
</Step>
</Steps>
agents-and-tools/mcp-tunnels/troubleshooting First recorded · 124 lines, first recorded
## Quick reference ## OAuth fails behind a source-IP allowlist ## Setup component authentication failures ## Tunnel won't connect ## Certificate errors ## Upstream IP validation
The first capture of this source. The page was already there, and this is what it said.
---
title: Troubleshoot MCP tunnels
url: https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/troubleshooting
description: Diagnose connectivity, TLS, IP validation, and OAuth routing issues in a tunnel stack.
---
<Note>
MCP tunnels are in research preview. [Request access](https://claude.com/form/claude-managed-agents) to try them.
</Note>
A request through the tunnel can fail at one of three layers; diagnose them in order: the outbound connection to the [tunnel edge](https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/concepts#components), the [inner TLS](https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/concepts#components) from Anthropic to your [proxy](https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/concepts#components), then routing and IP validation toward the [upstream MCP server](https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/concepts#components).
## Quick reference
| Symptom | Cause | Fix |
| -------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Tunnel doesn't appear in the agent **+ MCP Server** picker | The picker only lists tunnels in the session's workspace that have at least one active certificate. | Register a CA certificate, or open the session in the workspace the tunnel was created in. |
| Caller sees HTTP 500; [cloudflared](https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/concepts#components) logs `No ingress rules were defined` | cloudflared has no local target. | Add `--url http://localhost:8080` and `network_mode: "service:mcp-proxy"` to the cloudflared service. |
| Proxy logs `no route for host` | `tunnel_domain` doesn't match the assigned domain, or `config.yaml` was edited without restarting. | Set `tunnel_domain` to the exact domain shown on the tunnel detail page, then restart the proxy (`docker compose restart mcp-proxy`). |
| Proxy logs `IP validation failed: <ip> is not a private address` | Upstream MCP server resolves outside RFC1918. | See [Upstream IP validation](https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/troubleshooting#upstream-ip-validation). |
| Proxy exits with `cannot unmarshal !!seq into map[string]string` | `routes` is a YAML list. | Use `routes: { name: http://host:port }`. |
| Proxy exits with `open /data/tls.key: permission denied` | The key is `0600`; the proxy container runs non-root. | `chmod 644 data/tls.key`. |
| `curl https://<proxy>:8080` fails with `wrong version number` | Expected; the listener is plaintext WebSocket. TLS happens inside the WS stream. | Verify through a [Managed Agent or the Messages API](https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/overview#use-the-tunneled-mcp-servers) instead. |
The following sections cover failures that need more than a one-line fix.
## OAuth fails behind a source-IP allowlist
OAuth flows fail when your authorization server's source-IP allowlist blocks Anthropic's backend from reaching `/token`, `/register`, and the discovery endpoints. If you'd rather not allowlist Anthropic's egress ranges, you can route the backend-to-backend OAuth calls through the tunnel while keeping the browser-facing `/authorize` endpoint on your existing public hostname.
<Steps>
<Step title="Add a proxy route for the authorization server">
```yaml
routes:
mcp: http://your-mcp-server:8080
auth: http://your-auth-server:8080
```
Restart the proxy after editing `routes` (`docker compose restart mcp-proxy`, or `helm upgrade`).
</Step>
<Step title="Serve split-endpoint discovery metadata">
Your authorization server's `/.well-known/oauth-authorization-server` response should point `authorization_endpoint` at your existing allowlisted hostname and everything else at the tunnel:
```json
{
"issuer": "https://auth.<tunnel-domain>",
"authorization_endpoint": "https://<your-allowlisted-host>/authorize",
"token_endpoint": "https://auth.<tunnel-domain>/token",
"registration_endpoint": "https://auth.<tunnel-domain>/register",
"code_challenge_methods_supported": ["S256"]
}
```
</Step>
<Step title="Point the MCP server at the tunnel issuer">
Your MCP server's `/.well-known/oauth-protected-resource` response should reference the tunnel hostname as its authorization server:
```json
{
"resource": "https://mcp.<tunnel-domain>",
"authorization_servers": ["https://auth.<tunnel-domain>"]
}
```
</Step>
</Steps>
With this configuration, the user's browser hits `/authorize` on your existing hostname (which your allowlist already permits), while Anthropic's backend reaches `/token`, `/register`, and the discovery documents through the tunnel.
## Setup component authentication failures
The [setup component](https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/concepts#components) (Helm Job or Compose `setup` service) authenticates to the Tunnels API by exchanging an OIDC JWT through your federation rule. When the exchange fails, see [Troubleshoot a failed exchange](https://platform.claude.com/docs/en/manage-claude/wif-reference#troubleshoot-a-failed-exchange) in the Workload Identity Federation reference; the failure modes (subject, audience, issuer, JWKS, lifetime) are the same.
Tunnels-specific causes:
* The chart's default audience is `api.anthropic.com` (no scheme). If your rule's audience is `https://api.anthropic.com`, set `api.wif.audience` to match.
* A `403` from the Tunnels API after a successful exchange means the rule's scope doesn't include `workspace:manage_tunnels`, or the rule's service account isn't a member of the tunnel's workspace. Set the scope and add the service account to the workspace.
On Helm, the setup component runs as a pre-install hook Job. On failure, the Job is left behind for inspection (`kubectl logs job/mcp-tunnel-setup -n mcp-tunnel`). Helm doesn't manage hook resources, so delete it before retrying:
```bash
helm uninstall mcp-tunnel -n mcp-tunnel
kubectl -n mcp-tunnel delete job mcp-tunnel-setup
```
## Tunnel won't connect
Check the cloudflared logs first. Common causes:
* The `TUNNEL_TOKEN` is missing, expired, or copied incorrectly.
* A firewall is blocking outbound TCP/UDP on port 7844 to the tunnel edge.
cloudflared may also log warnings about UDP receive buffer sizes; this is a QUIC tuning hint, not an error.
## Certificate errors
When Anthropic rejects the proxy's certificate during inner TLS, the proxy logs `tls handshake failed`. Verify that:
* The server certificate has not expired.
* The certificate's Subject Alternative Name matches `*.<tunnel-domain>`.
* The signing CA is registered with Anthropic for this tunnel.
See the [certificate requirements](https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/reference#certificate-requirements) for the full validation rules.
## Upstream IP validation
For SSRF protection, the proxy only dials addresses in the RFC1918 private ranges (`10.0.0.0/8`, `172.16.0.0/12`, `192.168.0.0/16`) by default. Only IPv4 is supported for the proxy-to-upstream connection. (The cloudflared-to-edge egress range in [Network requirements](https://platform.claude.com/docs/en/agents-and-tools/mcp-tunnels/overview#network-requirements) is a different hop.)
If the proxy logs `IP validation failed: <ip> is not a private address`, the upstream hostname resolved outside that set. On Kubernetes, some managed distributions allocate the Service CIDR outside RFC1918; if `kubectl get svc kubernetes -n default -o jsonpath='{.spec.clusterIP}'` returns an address outside the private ranges, look up your cluster's Service CIDR and add it.
If the address is legitimate, add the narrowest covering CIDR to `upstream.allowed_ips`. Setting `allowed_ips` **replaces** the RFC1918 default rather than extending it, so include the private ranges your other upstream MCP servers use:
```yaml config/mcp-proxy.yaml
upstream:
allowed_ips:
- 10.0.0.0/8
- 172.16.0.0/12
- 192.168.0.0/16
- 127.0.0.0/8 # loopback, for local testing only
```
<Warning>
Avoid `0.0.0.0/0` outside of local testing; it disables SSRF protection entirely.
</Warning>
agents-and-tools/remote-mcp-servers First recorded · 33 lines, first recorded
## Connecting to remote MCP servers ## Remote MCP server examples
The first capture of this source. The page was already there, and this is what it said.
--- title: Remote MCP servers url: https://platform.claude.com/docs/en/agents-and-tools/remote-mcp-servers description: Connect Claude to third-party remote MCP servers through the MCP connector API. Browse example servers and review the steps to connect. --- Several companies have deployed remote MCP servers that developers can connect to by using the Anthropic MCP connector API. These servers expand the capabilities available to developers and end users by providing remote access to various services and tools through the MCP protocol. <Note> The remote MCP servers listed below are third-party services designed to work with the Claude API. These servers are not owned, operated, or endorsed by Anthropic. Users should only connect to remote MCP servers they trust and should review each server's security practices and terms before connecting. </Note> ## Connecting to remote MCP servers To connect to a remote MCP server: 1. Review the documentation for the specific server you want to use. 2. Ensure you have the necessary authentication credentials. 3. Follow the server-specific connection instructions provided by each company. For more information about using remote MCP servers with the Claude API, see [MCP connector](https://platform.claude.com/docs/en/agents-and-tools/mcp-connector). <Note> Once connected, remote MCP tools follow the same triggering behavior as any other tool. See [When Claude uses MCP tools](https://platform.claude.com/docs/en/agents-and-tools/mcp-connector#when-claude-uses-mcp-tools). </Note> ## Remote MCP server examples <MCPServersTable platform="mcpConnector" /> <Note> **Looking for more?** [Find hundreds more MCP servers on GitHub](https://github.com/modelcontextprotocol/servers). </Note>
agents-and-tools/tool-use/advisor-tool First recorded · 1873 lines, first recorded
## When to use it ## Quick start ## How it works ## Tool parameters ## Response structure ### Successful advisor call ### Result variants ### Error results ## Multi-turn conversations ### Resuming a paused turn ### Mid-conversation nudge for under-calling executors ## Streaming ## Usage and billing ## Advisor prompt caching ### Executor-side caching ### Advisor-side caching ## Combining with other tools ## Best practices ### Prompting for coding and agent tasks #### Suggested system prompt for coding tasks #### Alternative system prompt for Haiku on coding workloads #### Increasing advisor calls on Opus executors #### Trimming advisor output length ### Capping advisor output ### Pairing with effort settings ### Cost control ## Model compatibility ### Platform availability ## Advisor on Claude Managed Agents ## Next steps
The first capture of this source. The page was already there, and this is what it said.
---
title: Advisor tool
url: https://platform.claude.com/docs/en/agents-and-tools/tool-use/advisor-tool
description: Pair a faster executor model with a higher-intelligence advisor model that provides strategic guidance mid-generation.
---
The advisor tool lets a faster, lower-cost **executor model** consult a higher-intelligence **advisor model** mid-generation for strategic guidance. The advisor reads the full conversation, produces a plan or course correction, and the executor continues with the task.
This pattern fits long-horizon agentic workloads (coding agents, computer use, multistep research pipelines) where most turns are mechanical but having an excellent plan is crucial. You get close to advisor-solo quality while the bulk of token generation happens at executor-model rates.
```mermaid
sequenceDiagram
participant U as Your application
participant E as Executor model
participant A as Advisor model
U->>E: Request with advisor tool
note over E: Executor begins the task
E->>A: server_tool_use (server-side)
note over A: Reads the full transcript,<br/>returns strategic guidance
A-->>E: advisor_tool_result
note over E: Executor continues,<br/>informed by the advice
E-->>U: Response
```
<Note>
For how zero data retention (ZDR) applies to this feature, see [API and data retention](https://platform.claude.com/docs/en/manage-claude/api-and-data-retention).
</Note>
## When to use it
The advisor fits these configurations:
* **You currently use Sonnet on complex tasks:** Add a higher-tier advisor. Opus keeps total cost similar or lower; Claude Fable 5 maximizes the quality lift.
* **You currently use Haiku and want a step up in intelligence:** Add an Opus or Fable advisor. Expect higher cost than Haiku alone, but lower than switching the executor to a larger model.
Results are task-dependent. Evaluate on your own workload.
The advisor is a weaker fit for single-turn Q\&A (nothing to plan), pure pass-through model pickers where your users already choose their own cost and quality tradeoff, or workloads where every turn genuinely requires the advisor model's full capability.
## Quick start
<Note>
The advisor tool is in beta. Include the beta header `advisor-tool-2026-03-01` in your requests.
</Note>
<CodeGroup>
```bash cURL
curl https://api.anthropic.com/v1/messages \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "anthropic-beta: advisor-tool-2026-03-01" \
-H "content-type: application/json" \
-d '{
"model": "claude-sonnet-5",
"max_tokens": 4096,
"tools": [
{
"type": "advisor_20260301",
"name": "advisor",
"model": "claude-opus-5"
}
],
"messages": [{
"role": "user",
"content": "Build a concurrent worker pool in Go with graceful shutdown."
}]
}'
```
```bash CLI
ant beta:messages create --beta advisor-tool-2026-03-01 <<'YAML'
model: claude-sonnet-5
max_tokens: 4096
tools:
- type: advisor_20260301
name: advisor
model: claude-opus-5
messages:
- role: user
content: Build a concurrent worker pool in Go with graceful shutdown.
YAML
```
```python Python
client = anthropic.Anthropic()
response = client.beta.messages.create(
model="claude-sonnet-5",
max_tokens=4096,
betas=["advisor-tool-2026-03-01"],
tools=[
{
"type": "advisor_20260301",
"name": "advisor",
"model": "claude-opus-5",
}
],
messages=[
{
"role": "user",
"content": "Build a concurrent worker pool in Go with graceful shutdown.",
}
],
)
print(response)
```
```typescript TypeScript
const client = new Anthropic();
const response = await client.beta.messages.create({
model: "claude-sonnet-5",
max_tokens: 4096,
betas: ["advisor-tool-2026-03-01"],
tools: [
{
type: "advisor_20260301",
name: "advisor",
model: "claude-opus-5"
}
],
messages: [
{
role: "user",
content: "Build a concurrent worker pool in Go with graceful shutdown."
}
]
});
console.log(response);
```
```csharp C#
using Anthropic.Models.Beta.Messages;
using Messages = Anthropic.Models.Messages;
var client = new AnthropicClient();
var parameters = new MessageCreateParams
{
Model = Messages::Model.ClaudeSonnet5,
MaxTokens = 4096,
Tools = new BetaToolUnion[]
{
new BetaAdvisorTool20260301
{
Model = Messages::Model.ClaudeOpus5
}
},
Messages =
[
new BetaMessageParam
{
Role = Role.User,
Content = "Build a concurrent worker pool in Go with graceful shutdown."
}
],
Betas = ["advisor-tool-2026-03-01"]
};
var response = await client.Beta.Messages.Create(parameters);
Console.WriteLine(response);
```
```go Go
client := anthropic.NewClient()
response, err := client.Beta.Messages.New(context.TODO(), anthropic.BetaMessageNewParams{
Model: anthropic.ModelClaudeSonnet5,
MaxTokens: 4096,
Tools: []anthropic.BetaToolUnionParam{
{OfAdvisorTool20260301: &anthropic.BetaAdvisorTool20260301Param{
Model: anthropic.ModelClaudeOpus5,
}},
},
Messages: []anthropic.BetaMessageParam{
anthropic.NewBetaUserMessage(anthropic.NewBetaTextBlock("Build a concurrent worker pool in Go with graceful shutdown.")),
},
Betas: []anthropic.AnthropicBeta{
anthropic.AnthropicBetaAdvisorTool2026_03_01,
},
})
if err != nil {
log.Fatal(err)
}
fmt.Println(response)
```
```java Java
import com.anthropic.models.beta.messages.BetaAdvisorTool20260301;
import com.anthropic.models.beta.messages.BetaMessage;
import com.anthropic.models.beta.messages.MessageCreateParams;
import com.anthropic.models.messages.Model;
void main() {
AnthropicClient client = AnthropicOkHttpClient.fromEnv();
MessageCreateParams params = MessageCreateParams.builder()
.model(Model.CLAUDE_SONNET_5)
.maxTokens(4096L)
.addTool(BetaAdvisorTool20260301.builder()
.model(Model.CLAUDE_OPUS_5)
.build())
.addUserMessage("Build a concurrent worker pool in Go with graceful shutdown.")
.addBeta("advisor-tool-2026-03-01")
.build();
BetaMessage response = client.beta().messages().create(params);
IO.println(response);
}
```
```php PHP
$client = new Client();
$response = $client->beta->messages->create(
maxTokens: 4096,
messages: [
[
'role' => 'user',
'content' => 'Build a concurrent worker pool in Go with graceful shutdown.',
],
],
model: 'claude-sonnet-5',
tools: [
[
'type' => 'advisor_20260301',
'name' => 'advisor',
'model' => 'claude-opus-5',
],
],
betas: ['advisor-tool-2026-03-01'],
);
echo $response;
```
```ruby Ruby
client = Anthropic::Client.new
response = client.beta.messages.create(
model: "claude-sonnet-5",
max_tokens: 4096,
tools: [
{
type: "advisor_20260301",
name: "advisor",
model: "claude-opus-5"
}
],
messages: [
{
role: "user",
content: "Build a concurrent worker pool in Go with graceful shutdown."
}
],
betas: ["advisor-tool-2026-03-01"]
)
puts response
```
</CodeGroup>
The response `content` includes an `advisor_tool_result` block carrying the advisor's guidance. With `claude-opus-5` as the advisor, as in this quick start, the block's `content` field is an `advisor_redacted_result` variant (encrypted; the executor reads it server-side, but your client does not). To see the advice text directly in your response, use `claude-opus-4-8` as the advisor model instead, which returns the plaintext `advisor_result` variant. See [Result variants](https://platform.claude.com/docs/en/agents-and-tools/tool-use/advisor-tool#result-variants) for both shapes side by side and which advisor models return which, and [Model compatibility](https://platform.claude.com/docs/en/agents-and-tools/tool-use/advisor-tool#model-compatibility) for the full list of valid pairs.
## How it works
When you add the advisor tool to your `tools` array, the executor model determines when to call it, like any other tool. When the executor calls the advisor:
1. The executor emits a [`server_tool_use`](https://platform.claude.com/docs/en/agents-and-tools/tool-use/server-tools) block with `name: "advisor"` and an empty `input`. The executor signals timing, and the server supplies context.
2. Anthropic runs a separate inference pass on the advisor model server-side. The advisor runs under its own Anthropic-supplied system prompt and receives the executor's full transcript as quoted context in its input. That transcript includes your system prompt, the tool definitions, the prior turns and tool results, and the text the executor has produced so far in this turn.
3. The advisor's response returns to the executor as an `advisor_tool_result` block.
4. The executor continues generating, informed by the advice.
All of this occurs inside a single `/v1/messages` request, with no extra round trips on your side. The exception is a turn that pauses mid-call, which you resume with a follow-up request (see [Resuming a paused turn](https://platform.claude.com/docs/en/agents-and-tools/tool-use/advisor-tool#resuming-a-paused-turn)).
The advisor itself runs without tools and without context management. Its thinking blocks are dropped before the result returns. Only the advice text reaches the executor.
## Tool parameters
| Parameter | Type | Default | Description |
| ------------ | -------------- | -------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `type` | string | *required* | Must be `"advisor_20260301"`. |
| `name` | string | *required* | Must be `"advisor"`. |
| `model` | string | *required* | The advisor model ID, such as claude-opus-5. Billed at this model's rates for the sub-inference. |
| `max_uses` | integer | unlimited | Maximum number of advisor calls allowed in a single request. Once the executor reaches this cap, further advisor calls return an `advisor_tool_result_error` with `error_code: "max_uses_exceeded"` and the executor continues without further advice. This is a per-request cap, not a per-conversation cap. See [Cost control](https://platform.claude.com/docs/en/agents-and-tools/tool-use/advisor-tool#cost-control) for conversation-level limits. |
| `max_tokens` | integer | advisor model's output cap | Caps the advisor's total output (thinking plus text) per call. Minimum 1024. See [Capping advisor output](https://platform.claude.com/docs/en/agents-and-tools/tool-use/advisor-tool#capping-advisor-output). |
| `caching` | object \| null | `null` (off) | Enables [prompt caching](https://platform.claude.com/docs/en/build-with-claude/prompt-caching) for the advisor's own transcript across calls within a conversation. See [Advisor prompt caching](https://platform.claude.com/docs/en/agents-and-tools/tool-use/advisor-tool#advisor-prompt-caching). |
The `caching` object has the shape `{"type": "ephemeral", "ttl": "5m" | "1h"}`. Unlike `cache_control` on content blocks, this is not a breakpoint marker. It is an on/off switch. The server determines where cache boundaries go.
The advisor tool also accepts the generic properties available on any tool definition: `cache_control`, `allowed_callers`, `defer_loading`, and `strict` (covered in [structured outputs](https://platform.claude.com/docs/en/build-with-claude/structured-outputs)). See the [Tool reference](https://platform.claude.com/docs/en/agents-and-tools/tool-use/tool-reference#tool-definition-properties) for their semantics.
## Response structure
### Successful advisor call
When the advisor is called, a `server_tool_use` block is followed by an `advisor_tool_result` block in the assistant's content. The following example shows the plaintext `advisor_result` variant returned by a Claude Opus 4.8 advisor. The [Quick start](https://platform.claude.com/docs/en/agents-and-tools/tool-use/advisor-tool#quick-start) uses Claude Opus 5, which returns the encrypted `advisor_redacted_result` variant instead; see [Result variants](https://platform.claude.com/docs/en/agents-and-tools/tool-use/advisor-tool#result-variants) for both shapes side by side.
Cut at 300 lines. The page has the rest.
agents-and-tools/tool-use/bash-tool First recorded · 1984 lines, first recorded
## Use cases ## Quick start ## How it works ## Parameters ## Tool versions ## Example: Multistep automation ## Implement the bash tool ### Handle errors ### Follow implementation best practices ## Security ## Pricing ## Common patterns ### Development workflows ### File operations ### System tasks ## Limitations ## Combining with other tools ## Next steps
The first capture of this source. The page was already there, and this is what it said.
---
title: Bash tool
url: https://platform.claude.com/docs/en/agents-and-tools/tool-use/bash-tool
description: Let Claude request shell commands that your application runs in a persistent bash session and returns as tool results.
---
<Note>
For how zero data retention (ZDR) applies to this feature, see [API and data retention](https://platform.claude.com/docs/en/manage-claude/api-and-data-retention).
</Note>
The bash tool is a [client tool](https://platform.claude.com/docs/en/agents-and-tools/tool-use/how-tool-use-works): Claude doesn't run commands itself. When you include the tool in a request, Claude replies with a `tool_use` block that names the command to run. Your application runs that command in a bash session it owns and returns the output in a `tool_result` block.
Your application keeps one bash process alive across tool calls, so state persists between commands. The working directory, environment variables, and any files a command creates are still there for the next command.
The current version of the tool is `bash_20250124`. For model support, beta headers, and the earlier version, see [Tool versions](https://platform.claude.com/docs/en/agents-and-tools/tool-use/bash-tool#tool-versions). For all Anthropic-provided tools, see the [Tool reference](https://platform.claude.com/docs/en/agents-and-tools/tool-use/tool-reference).
## Use cases
* **Development workflows:** Run build commands, tests, and development tools
* **System automation:** Execute scripts, manage files, automate tasks
* **Data processing:** Process files, run analysis scripts, manage datasets
* **Environment setup:** Install packages, configure environments
## Quick start
<CodeGroup>
```bash cURL
curl https://api.anthropic.com/v1/messages \
-H "content-type: application/json" \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-d '{
"model": "claude-opus-5",
"max_tokens": 1024,
"tools": [
{
"type": "bash_20250124",
"name": "bash"
}
],
"messages": [
{
"role": "user",
"content": "List all Python files in the current directory."
}
]
}'
```
```bash CLI
ant messages create \
--model claude-opus-5 \
--max-tokens 1024 \
--tool '{type: bash_20250124, name: bash}' \
--message '{role: user, content: List all Python files in the current directory.}'
```
```python Python
client = anthropic.Anthropic()
response = client.messages.create(
model="claude-opus-5",
max_tokens=1024,
tools=[{"type": "bash_20250124", "name": "bash"}],
messages=[
{"role": "user", "content": "List all Python files in the current directory."}
],
)
print(response)
```
```typescript TypeScript
const client = new Anthropic();
const response = await client.messages.create({
model: "claude-opus-5",
max_tokens: 1024,
tools: [{ type: "bash_20250124", name: "bash" }],
messages: [
{
role: "user",
content: "List all Python files in the current directory."
}
]
});
console.log(response);
```
```csharp C#
var client = new AnthropicClient();
var response = await client.Messages.Create(
new()
{
Model = Model.ClaudeOpus5,
MaxTokens = 1024,
Tools = [new ToolBash20250124()],
Messages =
[
new()
{
Role = Role.User,
Content = "List all Python files in the current directory.",
},
],
}
);
Console.WriteLine(response);
```
```go Go
client := anthropic.NewClient()
response, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{
Model: anthropic.ModelClaudeOpus5,
MaxTokens: 1024,
Tools: []anthropic.ToolUnionParam{
{OfBashTool20250124: &anthropic.ToolBash20250124Param{}},
},
Messages: []anthropic.MessageParam{
anthropic.NewUserMessage(anthropic.NewTextBlock("List all Python files in the current directory.")),
},
})
if err != nil {
log.Fatal(err)
}
fmt.Println(response)
```
```java Java
import com.anthropic.models.messages.ToolBash20250124;
void main() {
AnthropicClient client = AnthropicOkHttpClient.fromEnv();
Message response = client.messages().create(
MessageCreateParams.builder()
.model(Model.CLAUDE_OPUS_5)
.maxTokens(1024)
.addTool(ToolBash20250124.builder().build())
.addUserMessage("List all Python files in the current directory.")
.build()
);
IO.println(response);
}
```
```php PHP
use Anthropic\Messages\ToolBash20250124;
$client = new Client();
$response = $client->messages->create(
model: 'claude-opus-5',
maxTokens: 1024,
tools: [new ToolBash20250124()],
messages: [
['role' => 'user', 'content' => 'List all Python files in the current directory.'],
],
);
echo $response;
```
```ruby Ruby
client = Anthropic::Client.new
response = client.messages.create(
model: "claude-opus-5",
max_tokens: 1024,
tools: [{type: "bash_20250124", name: "bash"}],
messages: [
{role: "user", content: "List all Python files in the current directory."}
]
)
puts response
```
</CodeGroup>
Claude responds with `stop_reason: "tool_use"` and a `tool_use` block that contains the command for your application to run:
```json Output
{
"id": "msg_01XAbCDeFgHiJkLmNoPQrStU",
"model": "claude-opus-5",
"stop_reason": "tool_use",
"role": "assistant",
"content": [
{
"type": "text",
"text": "I'll list all Python files in the current directory for you."
},
{
"type": "tool_use",
"id": "toolu_01A09q90qw90lq917835lq9",
"name": "bash",
"input": {
"command": "ls *.py"
}
}
]
}
```
Run `input.command` in your bash session and send the output back as a `tool_result`. See [Implement the bash tool](https://platform.claude.com/docs/en/agents-and-tools/tool-use/bash-tool#implement-the-bash-tool) for the round trip.
## How it works
Each tool call is one round trip between Claude and your application:
1. Claude returns a `tool_use` block containing the `command` to run.
2. Your application runs the command in its bash session.
3. Your application returns the command's output, stdout and stderr together, to Claude in a `tool_result` block.
4. Claude either requests another command in the same session or responds with text.
Claude can also return several `tool_use` blocks in one response. Run them in order in the same session and return all of the results in one `user` message. See [Parallel tool use](https://platform.claude.com/docs/en/agents-and-tools/tool-use/parallel-tool-use).
The API is stateless. Nothing about your shell session travels between requests, so your application decides when the session starts, how long it lives, and when to restart it. For the full request and response cycle, see [Handle tool calls](https://platform.claude.com/docs/en/agents-and-tools/tool-use/handle-tool-calls).
## Parameters
A bash tool definition has two required fields, `type` and `name`, and the `name` must be `bash`. The tool is schema-less: you don't provide an `input_schema`, because the schema is built into Claude's model and can't be modified. The following table lists the input fields Claude sets when it calls the tool.
| Parameter | Required | Description |
| --------- | -------- | ----------------------------------------- |
| `command` | Yes\* | The bash command to run |
| `restart` | No | Set to `true` to restart the bash session |
\*Required unless using `restart`
To handle `restart: true`, kill the shell process, start a new one, and return a `tool_result` that confirms the restart. A restarted session starts clean: the working directory, environment variables, and any running processes are gone.
<Accordion title="Example usage">
Run a command:
```json
{
"command": "ls -la *.py"
}
```
Restart the session:
```json
{
"restart": true
}
```
</Accordion>
## Tool versions
`bash_20250124` is the current version of the tool, and it requires no beta header. Every model from Claude Sonnet 3.7 ([retired](https://platform.claude.com/docs/en/about-claude/model-deprecations)) onward accepts it, including all current Claude models.
The original `bash_20241022` version is part of the computer use beta, and the October 2024 Claude Sonnet 3.5 release ([retired](https://platform.claude.com/docs/en/about-claude/model-deprecations)) is the only model that accepts it. Requests that use it need the `anthropic-beta: computer-use-2024-10-22` header, and the SDKs expose it only in their beta namespaces. New integrations should use `bash_20250124`.
## Example: Multistep automation
Claude can chain commands across tool calls to complete a multistep task:
```text
User request:
"Install the requests library and create a simple Python script that
fetches a joke from an API, then run it."
Claude's tool uses:
1. Install package
{"command": "pip install requests"}
2. Create script
{"command": "cat > fetch_joke.py << 'EOF'\nimport requests\nresponse = requests.get('https://official-joke-api.appspot.com/random_joke')\njoke = response.json()\nprint(f\"Setup: {joke['setup']}\")\nprint(f\"Punchline: {joke['punchline']}\")\nEOF"}
3. Run script
{"command": "python fetch_joke.py"}
```
The session maintains state between commands, so files created in step 2 are available in step 3.
## Implement the bash tool
Claude determines which command to run. Your application owns everything else: the shell process, the timeout, and the safety checks. The following steps show a minimal implementation.
<Steps>
<Step title="Create a persistent bash session">
Start one long-lived bash process and run every command inside it. Because a pipe to a live process never reports end-of-file, the session prints a unique sentinel line after each command to mark where that command's output ends:
<CodeGroup exclude="shell">
```python Python
import subprocess
import uuid
class BashSession:
"""A bash process that stays alive between commands so state persists."""
Cut at 300 lines. The page has the rest.
agents-and-tools/tool-use/build-a-tool-using-agent First recorded · 4674 lines, first recorded
## Ring 1: Single tool, single turn ## Ring 2: The agentic loop ## Ring 3: Multiple tools, parallel calls ## Ring 4: Error handling ## Ring 5: The Tool Runner SDK abstraction ## What you built ## Next steps
The first capture of this source. The page was already there, and this is what it said.
---
title: "Tutorial: Build a tool-using agent"
url: https://platform.claude.com/docs/en/agents-and-tools/tool-use/build-a-tool-using-agent
description: A guided walkthrough from a single tool call to a production-ready agentic loop.
---
This tutorial builds a calendar-management agent in five concentric rings. Each ring is a complete, runnable program that adds exactly one concept to the ring before it. By the end you will have written the agentic loop by hand and then replaced it with the Tool Runner SDK abstraction.
The example tool is `create_calendar_event`. Its schema uses nested objects, arrays, and optional fields, so you will see how Claude handles realistic input shapes rather than a single flat string.
<Note>
Every ring runs standalone. Copy any ring into a fresh file and it will run without the code from earlier rings.
</Note>
## Ring 1: Single tool, single turn
The smallest possible tool-using program: one tool, one user message, one tool call, one result. The code is heavily commented so you can map each line to the [tool use lifecycle](https://platform.claude.com/docs/en/agents-and-tools/tool-use/how-tool-use-works).
The request sends a `tools` array alongside the user message. When Claude determines that a tool call is needed, the response comes back with `stop_reason: "tool_use"` and a `tool_use` content block containing the tool name, a unique `id`, and the structured `input`. Your code runs the tool, then sends the result back in a `tool_result` block whose `tool_use_id` matches the `id` from the call.
<CodeGroup>
```bash cURL
#!/bin/bash
# Ring 1: Single tool, single turn.
# Define one tool as a JSON fragment. The input_schema is a JSON Schema
# object describing the arguments Claude should pass when it calls this
# tool. This schema includes nested objects (recurrence), arrays
# (attendees), and optional fields, which is closer to real-world tools
# than a flat string argument.
TOOLS='[
{
"name": "create_calendar_event",
"description": "Create a calendar event with attendees and optional recurrence.",
"input_schema": {
"type": "object",
"properties": {
"title": {"type": "string"},
"start": {"type": "string", "format": "date-time"},
"end": {"type": "string", "format": "date-time"},
"attendees": {
"type": "array",
"items": {"type": "string", "format": "email"}
},
"recurrence": {
"type": "object",
"properties": {
"frequency": {"enum": ["daily", "weekly", "monthly"]},
"count": {"type": "integer", "minimum": 1}
}
}
},
"required": ["title", "start", "end"]
}
}
]'
USER_MSG="Schedule a 30-minute sync with [email protected] and [email protected] on Monday, March 30, 2026 at 10am."
# Send the user's request along with the tool definition. Claude decides
# whether to call the tool based on the request and the tool description.
RESPONSE=$(curl -s https://api.anthropic.com/v1/messages \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "content-type: application/json" \
-d "$(jq -n \
--argjson tools "$TOOLS" \
--arg msg "$USER_MSG" \
'{
model: "claude-opus-5",
max_tokens: 1024,
tools: $tools,
tool_choice: {type: "auto", disable_parallel_tool_use: true},
messages: [{role: "user", content: $msg}]
}')")
# When Claude calls a tool, the response has stop_reason "tool_use"
# and the content array contains a tool_use block alongside any text.
echo "stop_reason: $(echo "$RESPONSE" | jq -r '.stop_reason')"
# Find the tool_use block. A response may contain text blocks before the
# tool_use block, so filter by type rather than assuming position.
TOOL_USE=$(echo "$RESPONSE" | jq '.content[] | select(.type == "tool_use")')
TOOL_USE_ID=$(echo "$TOOL_USE" | jq -r '.id')
echo "Tool: $(echo "$TOOL_USE" | jq -r '.name')"
echo "Input: $(echo "$TOOL_USE" | jq -c '.input')"
# Execute the tool. In a real system this would call your calendar API.
# Here the result is hardcoded to keep the example self-contained.
RESULT='{"event_id": "evt_123", "status": "created"}'
# Send the result back. The tool_result block goes in a user message and
# its tool_use_id must match the id from the tool_use block above. The
# assistant's previous response is included so Claude has the full history.
ASSISTANT_CONTENT=$(echo "$RESPONSE" | jq '.content')
FOLLOWUP=$(curl -s https://api.anthropic.com/v1/messages \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "content-type: application/json" \
-d "$(jq -n \
--argjson tools "$TOOLS" \
--arg msg "$USER_MSG" \
--argjson assistant "$ASSISTANT_CONTENT" \
--arg tool_use_id "$TOOL_USE_ID" \
--arg result "$RESULT" \
'{
model: "claude-opus-5",
max_tokens: 1024,
tools: $tools,
tool_choice: {type: "auto", disable_parallel_tool_use: true},
messages: [
{role: "user", content: $msg},
{role: "assistant", content: $assistant},
{role: "user", content: [
{type: "tool_result", tool_use_id: $tool_use_id, content: $result}
]}
]
}')")
# With the tool result in hand, Claude produces a final natural-language
# answer and stop_reason becomes "end_turn".
echo "stop_reason: $(echo "$FOLLOWUP" | jq -r '.stop_reason')"
echo "$FOLLOWUP" | jq -r '.content[] | select(.type == "text") | .text'
```
```bash CLI
#!/usr/bin/env bash
# Ring 1: Single tool, single turn.
# Uses jq for cross-turn message-array state — building an agentic loop in shell
# requires JSON manipulation beyond ant's single-call --transform scope.
set -euo pipefail
USER_MSG="Schedule a 30-minute sync with [email protected] and [email protected] on Monday, March 30, 2026 at 10am."
MESSAGES=$(jq -n --arg msg "$USER_MSG" '[{role: "user", content: $msg}]')
# Define one tool. The input_schema is a JSON Schema object describing
# the arguments Claude should pass when it calls this tool. This schema
# includes nested objects (recurrence), arrays (attendees), and optional
# fields, which is closer to real-world tools than a flat string argument.
call_api() {
# ant reads the request body as YAML on stdin: no auth headers, no
# hand-built JSON envelope. The static keys (model, tools, tool_choice)
# live in a quoted heredoc; the growing messages array is appended as
# JSON, which YAML accepts as flow syntax.
{
cat <<'YAML'
model: claude-opus-5
max_tokens: 1024
tool_choice: {type: auto, disable_parallel_tool_use: true}
tools:
- name: create_calendar_event
description: Create a calendar event with attendees and optional recurrence.
input_schema:
type: object
properties:
title: {type: string}
start: {type: string, format: date-time}
end: {type: string, format: date-time}
attendees:
type: array
items: {type: string, format: email}
recurrence:
type: object
properties:
frequency: {enum: [daily, weekly, monthly]}
count: {type: integer, minimum: 1}
required: [title, start, end]
YAML
printf 'messages: %s\n' "$MESSAGES"
} | ant messages create --format json
}
# Send the user's request along with the tool definition. Claude decides
# whether to call the tool based on the request and the tool description.
RESPONSE=$(call_api)
# When Claude calls a tool, the response has stop_reason "tool_use"
# and the content array contains a tool_use block alongside any text.
echo "stop_reason: $(jq -r '.stop_reason' <<<"$RESPONSE")"
# Find the tool_use block. A response may contain text blocks before the
# tool_use block, so filter by type rather than assuming position.
TOOL_USE=$(jq '.content[] | select(.type == "tool_use")' <<<"$RESPONSE")
TOOL_USE_ID=$(jq -r '.id' <<<"$TOOL_USE")
echo "Tool: $(jq -r '.name' <<<"$TOOL_USE")"
echo "Input: $(jq -c '.input' <<<"$TOOL_USE")"
# Execute the tool. In a real system this would call your calendar API.
# Here the result is hardcoded to keep the example self-contained.
RESULT='{"event_id": "evt_123", "status": "created"}'
# Send the result back. The tool_result block goes in a user message and
# its tool_use_id must match the id from the tool_use block above. The
# assistant's previous response is included so Claude has the full history.
MESSAGES=$(jq \
--argjson assistant "$(jq '.content' <<<"$RESPONSE")" \
--arg tool_use_id "$TOOL_USE_ID" \
--arg result "$RESULT" \
'. + [
{role: "assistant", content: $assistant},
{role: "user", content: [
{type: "tool_result", tool_use_id: $tool_use_id, content: $result}
]}
]' <<<"$MESSAGES")
FOLLOWUP=$(call_api)
# With the tool result in hand, Claude produces a final natural-language
# answer and stop_reason becomes "end_turn".
echo "stop_reason: $(jq -r '.stop_reason' <<<"$FOLLOWUP")"
jq -r '.content[] | select(.type == "text") | .text' <<<"$FOLLOWUP"
```
```python Python
# Ring 1: Single tool, single turn.
import json
import anthropic
# Create a client. It reads ANTHROPIC_API_KEY from the environment.
client = anthropic.Anthropic()
# Define one tool. The input_schema is a JSON Schema object describing
# the arguments Claude should pass when it calls this tool. This schema
# includes nested objects (recurrence), arrays (attendees), and optional
# fields, which is closer to real-world tools than a flat string argument.
tools = [
{
"name": "create_calendar_event",
"description": "Create a calendar event with attendees and optional recurrence.",
"input_schema": {
"type": "object",
"properties": {
"title": {"type": "string"},
"start": {"type": "string", "format": "date-time"},
"end": {"type": "string", "format": "date-time"},
"attendees": {
"type": "array",
"items": {"type": "string", "format": "email"},
},
"recurrence": {
"type": "object",
"properties": {
"frequency": {"enum": ["daily", "weekly", "monthly"]},
"count": {"type": "integer", "minimum": 1},
},
},
},
"required": ["title", "start", "end"],
},
}
]
# Send the user's request along with the tool definition. Claude decides
# whether to call the tool based on the request and the tool description.
response = client.messages.create(
model="claude-opus-5",
max_tokens=1024,
tools=tools,
tool_choice={"type": "auto", "disable_parallel_tool_use": True},
messages=[
{
"role": "user",
"content": "Schedule a 30-minute sync with [email protected] and [email protected] on Monday, March 30, 2026 at 10am.",
}
],
)
# When Claude calls a tool, the response has stop_reason "tool_use"
# and the content array contains a tool_use block alongside any text.
print(f"stop_reason: {response.stop_reason}")
# Find the tool_use block. A response may contain text blocks before the
# tool_use block, so scan the content array rather than assuming position.
tool_use = next(block for block in response.content if block.type == "tool_use")
print(f"Tool: {tool_use.name}")
print(f"Input: {tool_use.input}")
# Execute the tool. In a real system this would call your calendar API.
# Here the result is hardcoded to keep the example self-contained.
result = {"event_id": "evt_123", "status": "created"}
# Send the result back. The tool_result block goes in a user message and
# its tool_use_id must match the id from the tool_use block above. The
# assistant's previous response is included so Claude has the full history.
followup = client.messages.create(
model="claude-opus-5",
max_tokens=1024,
tools=tools,
tool_choice={"type": "auto", "disable_parallel_tool_use": True},
messages=[
{
"role": "user",
"content": "Schedule a 30-minute sync with [email protected] and [email protected] on Monday, March 30, 2026 at 10am.",
},
{"role": "assistant", "content": response.content},
{
"role": "user",
"content": [
Cut at 300 lines. The page has the rest.
agents-and-tools/tool-use/code-execution-tool First recorded · 1562 lines, first recorded
## Model compatibility ## Platform availability ## Quick start ## How code execution works ### When Claude runs code ## Work with files ### Upload and analyze your own files #### Upload and analyze files ### Retrieve generated files ## Tool definition ## Response format ### Bash command response ### File operation responses ### Results ### Errors ### `pause_turn` stop reason ## Containers ### Runtime environment ### Resource limits ### Networking and security ### Pre-installed libraries ## Container reuse ### Example ## Using code execution with other execution tools ## Streaming ## Batch requests ## Usage and pricing ## Upgrade to latest tool version ### What's changed ### Backward compatibility ### Upgrade steps ## Data retention ## Next steps
The first capture of this source. The page was already there, and this is what it said.
---
title: Code execution tool
url: https://platform.claude.com/docs/en/agents-and-tools/tool-use/code-execution-tool
description: Run Python and bash code in a sandboxed container to analyze data, generate files, and iterate on solutions.
---
Claude can analyze data, create visualizations, perform complex calculations, run system commands, create and edit files, and process uploaded files directly within the API conversation. The code execution tool allows Claude to run Bash commands and manipulate files, including writing code, in a secure, sandboxed environment.
**Code execution is free when used with web search or web fetch (`web_search_20260209`, `web_fetch_20260209`, or later).** When one of those tools is in your request, there are no additional charges for code execution in that request beyond standard token costs. This covers both the code execution behind dynamic filtering and any code Claude runs directly. Standard code execution pricing applies when they are not included.
Code execution also powers dynamic filtering in the [web search](https://platform.claude.com/docs/en/agents-and-tools/tool-use/web-search-tool) and [web fetch](https://platform.claude.com/docs/en/agents-and-tools/tool-use/web-fetch-tool) tools: Claude filters results inside the code execution environment before they reach the context window. When dynamic filtering runs, the API provisions the code execution it needs for the request automatically, so you don't add the code execution tool to your request for it.
<Note>
Reach out through the [feedback form](https://forms.gle/LTAU6Xn2puCJMi1n6) to share your feedback on this feature.
</Note>
<Note>
For how zero data retention (ZDR) applies to this feature, see [API and data retention](https://platform.claude.com/docs/en/manage-claude/api-and-data-retention).
</Note>
## Model compatibility
The code execution tool is available on the following models:
| Model | Tool versions |
| ---------------------------------------------- | ------------------------------------------------------------------------------- |
| Claude Opus 5 (claude-opus-5) | `code_execution_20250825`, `code_execution_20260120`, `code_execution_20260521` |
| Claude Fable 5 (claude-fable-5) | `code_execution_20250825`, `code_execution_20260120`, `code_execution_20260521` |
| Claude Mythos 5 (claude-mythos-5) | `code_execution_20250825`, `code_execution_20260120`, `code_execution_20260521` |
| Claude Sonnet 5 (claude-sonnet-5) | `code_execution_20250825`, `code_execution_20260120`, `code_execution_20260521` |
| Claude Opus 4.8 (claude-opus-4-8) | `code_execution_20250825`, `code_execution_20260120`, `code_execution_20260521` |
| Claude Opus 4.7 (claude-opus-4-7) | `code_execution_20250825`, `code_execution_20260120`, `code_execution_20260521` |
| Claude Opus 4.6 (claude-opus-4-6) | `code_execution_20250825`, `code_execution_20260120`, `code_execution_20260521` |
| Claude Sonnet 4.6 (claude-sonnet-4-6) | `code_execution_20250825`, `code_execution_20260120`, `code_execution_20260521` |
| Claude Opus 4.5 (claude-opus-4-5-20251101) | `code_execution_20250825`, `code_execution_20260120`, `code_execution_20260521` |
| Claude Sonnet 4.5 (claude-sonnet-4-5-20250929) | `code_execution_20250825`, `code_execution_20260120`, `code_execution_20260521` |
| Claude Haiku 4.5 (claude-haiku-4-5-20251001) | `code_execution_20250825`, `code_execution_20260120`, `code_execution_20260521` |
Each tool version builds on the previous one:
* `code_execution_20250825` supports Bash commands and file operations.
* `code_execution_20260120` adds REPL state persistence and [programmatic tool calling](https://platform.claude.com/docs/en/agents-and-tools/tool-use/programmatic-tool-calling) from within the sandbox. Claude Haiku 4.5 accepts the `code_execution_20260120` and `code_execution_20260521` tool types, but programmatic tool calling and the REPL state persistence that depends on it aren't available on it, so the newer versions behave like `code_execution_20250825` there.
* `code_execution_20260521` is the same runtime as `code_execution_20260120`. The difference is that the tool description tells Claude about the 90-second wall-clock limit on each Python cell in programmatic tool calling, so Claude can budget long-running cells. A cell that exceeds the limit returns a normal code execution result with a non-zero `return_code` and a `detection_timeout` status message in its output. This is separate from the `execution_time_exceeded` [error code](https://platform.claude.com/docs/en/agents-and-tools/tool-use/code-execution-tool#errors), which the API returns when a whole tool invocation exceeds the maximum execution time.
All three tool versions are generally available and don't require an `anthropic-beta` header. The legacy code execution beta headers remain valid opt-ins.
The examples on this page use `code_execution_20250825`, which covers the Bash and file operations they demonstrate and behaves the same way on every model in the table; use `code_execution_20260120` or later when you need programmatic tool calling or REPL state persistence. The current [web search](https://platform.claude.com/docs/en/agents-and-tools/tool-use/web-search-tool) and [web fetch](https://platform.claude.com/docs/en/agents-and-tools/tool-use/web-fetch-tool) tools (`web_search_20260209`, `web_fetch_20260209`, and later) require `code_execution_20260120` or later as their code execution version.
<Note>
If you're still using the legacy `code_execution_20250522` (Python only), see [Upgrade to latest tool version](https://platform.claude.com/docs/en/agents-and-tools/tool-use/code-execution-tool#upgrade-to-latest-tool-version) to migrate from it.
</Note>
<Warning>
Older tool versions are not guaranteed to stay compatible with newer models. When you adopt a new model, check the [model compatibility table](https://platform.claude.com/docs/en/agents-and-tools/tool-use/code-execution-tool#model-compatibility) and prefer the newest tool version your integration supports.
</Warning>
## Platform availability
Code execution is available on:
* **Claude API** (Anthropic)
* **[Claude Platform on AWS](https://platform.claude.com/docs/en/build-with-claude/claude-platform-on-aws)**
* **[Microsoft Foundry](https://platform.claude.com/docs/en/build-with-claude/claude-in-microsoft-foundry)** (requires a [Hosted on Anthropic deployment](https://platform.claude.com/docs/en/build-with-claude/claude-in-microsoft-foundry#additional-features-not-supported-when-hosted-on-azure))
Code execution is not currently available on Amazon Bedrock or Google Cloud.
<Note>
For [Claude Mythos Preview](https://anthropic.com/glasswing), code execution is supported on the Claude API and Microsoft Foundry only. It is not available for Mythos Preview on Amazon Bedrock, Claude Platform on AWS, or Google Cloud.
</Note>
## Quick start
Here's an example that asks Claude to perform a calculation:
<CodeGroup>
```bash cURL
curl --fail-with-body -sS https://api.anthropic.com/v1/messages \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "content-type: application/json" \
-d '{
"model": "claude-opus-5",
"max_tokens": 4096,
"messages": [
{
"role": "user",
"content": "Use the code execution tool to calculate the mean and standard deviation of [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]"
}
],
"tools": [
{
"type": "code_execution_20250825",
"name": "code_execution"
}
]
}'
```
```bash CLI
ant messages create \
--model claude-opus-5 \
--max-tokens 4096 \
--message '{
role: user,
content: "Use the code execution tool to calculate the mean and standard
deviation of [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]"
}' \
--tool '{type: code_execution_20250825, name: code_execution}'
```
```python Python
client = anthropic.Anthropic()
response = client.messages.create(
model="claude-opus-5",
max_tokens=4096,
messages=[
{
"role": "user",
"content": "Use the code execution tool to calculate the mean and standard deviation of [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]",
}
],
tools=[{"type": "code_execution_20250825", "name": "code_execution"}],
)
print(response.to_json())
```
```typescript TypeScript
const client = new Anthropic();
const response = await client.messages.create({
model: "claude-opus-5",
max_tokens: 4096,
messages: [
{
role: "user",
content:
"Use the code execution tool to calculate the mean and standard deviation of [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]"
}
],
tools: [{ type: "code_execution_20250825", name: "code_execution" }]
});
console.log(JSON.stringify(response));
```
```csharp C#
AnthropicClient client = new();
var message = await client.Messages.Create(new()
{
Model = Model.ClaudeOpus5,
MaxTokens = 4096,
Messages = [new() { Role = Role.User, Content = "Use the code execution tool to calculate the mean and standard deviation of [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]" }],
Tools = [new CodeExecutionTool20250825()]
});
Console.WriteLine(message);
```
```go Go
client := anthropic.NewClient()
response, err := client.Messages.New(context.Background(), anthropic.MessageNewParams{
Model: anthropic.ModelClaudeOpus5,
MaxTokens: 4096,
Messages: []anthropic.MessageParam{
anthropic.NewUserMessage(anthropic.NewTextBlock("Use the code execution tool to calculate the mean and standard deviation of [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]")),
},
Tools: []anthropic.ToolUnionParam{
{OfCodeExecutionTool20250825: &anthropic.CodeExecutionTool20250825Param{}},
},
})
if err != nil {
log.Fatal(err)
}
fmt.Println(response.RawJSON())
```
```java Java
AnthropicClient client = AnthropicOkHttpClient.fromEnv();
MessageCreateParams params = MessageCreateParams.builder()
.model(Model.CLAUDE_OPUS_5)
.maxTokens(4096L)
.addUserMessage("Use the code execution tool to calculate the mean and standard deviation of [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]")
.addTool(CodeExecutionTool20250825.builder().build())
.build();
Message response = client.messages().create(params);
IO.println(ObjectMappers.jsonMapper().valueToTree(response));
```
```php PHP
$client = new Client();
$message = $client->messages->create(
maxTokens: 4096,
messages: [
[
'role' => 'user',
'content' => 'Use the code execution tool to calculate the mean and standard deviation of [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]',
],
],
model: Model::CLAUDE_OPUS_5,
tools: [new CodeExecutionTool20250825()],
);
echo json_encode($message, JSON_PRETTY_PRINT), PHP_EOL;
```
```ruby Ruby
client = Anthropic::Client.new
message = client.messages.create(
model: Anthropic::Model::CLAUDE_OPUS_5,
max_tokens: 4096,
messages: [
{
role: "user",
content: "Use the code execution tool to calculate the mean and standard deviation of [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]"
}
],
tools: [Anthropic::CodeExecutionTool20250825.new]
)
puts message.to_json
```
</CodeGroup>
The response interleaves `server_tool_use` blocks (the commands Claude ran) with their tool result blocks, followed by Claude's text. The top level also includes a `container` object whose `id` you can [reuse across requests](https://platform.claude.com/docs/en/agents-and-tools/tool-use/code-execution-tool#container-reuse). See [Response format](https://platform.claude.com/docs/en/agents-and-tools/tool-use/code-execution-tool#response-format) for the block shapes.
## How code execution works
When you add the code execution tool to your API request:
1. Claude evaluates whether code execution would help answer your question
2. The tool automatically provides Claude with the following capabilities:
* **Bash commands:** Run shell commands for system operations
* **File operations:** Create, view, and edit files directly, including writing code
3. Claude can use any combination of these capabilities in a single request
4. All operations run in a secure, sandboxed container. The container has no internet access, so Claude can't download packages at runtime: only the [pre-installed libraries](https://platform.claude.com/docs/en/agents-and-tools/tool-use/code-execution-tool#pre-installed-libraries) are available
5. The API runs every command server-side and returns the results to Claude within the same request, so you never execute code or send back `tool_result` blocks yourself. One exception is when Claude calls one of your client tools alongside code execution: the API returns the code execution call without its result. The result arrives in a later response, after you send back the `tool_result` blocks for your client tools
6. Each request runs in a new container unless you pass an earlier response's container ID back (see [Container reuse](https://platform.claude.com/docs/en/agents-and-tools/tool-use/code-execution-tool#container-reuse))
7. Claude provides results with any generated charts, calculations, or analysis
The container has Python pre-installed. Claude writes Python with the file operations sub-tool and runs it with a Bash command. With `code_execution_20260120` or later and [programmatic tool calling](https://platform.claude.com/docs/en/agents-and-tools/tool-use/programmatic-tool-calling), the Python interpreter state (such as variable bindings) also persists across requests that reuse the container.
### When Claude runs code
Claude runs code when the request benefits from computation or file handling:
* Non-trivial math (large numbers, many steps, precision-sensitive results)
* Data analysis, file parsing, or visualization
* Algorithm execution or simulation
* Explicit requests to "run", "compute", or "execute"
Claude answers directly without running code for:
* Simple arithmetic and well-known math facts
* Factual, conversational, or creative requests
* Simple unit conversions or translations
If you want Claude to run code for a borderline request, ask explicitly (for example, "run code to verify this").
## Work with files
### Upload and analyze your own files
To analyze your own data files (such as CSV, Excel, or images), upload them through the Files API and reference them in your request:
<Note>
Using the Files API with code execution requires the Files API beta header: `"anthropic-beta": "files-api-2025-04-14"`
</Note>
The Python environment can process various file types uploaded through the Files API, including:
* CSV
* Excel (.xlsx, .xls)
* JSON
* XML
* Images (JPEG, PNG, GIF, WebP)
* Text files (.txt, .md, .py, and others)
#### Upload and analyze files
1. **Upload your file** using the [Files API](https://platform.claude.com/docs/en/build-with-claude/files)
2. **Reference the file** in your message using a `container_upload` content block
3. **Include the code execution tool** in your API request
<CodeGroup>
```bash cURL
Cut at 300 lines. The page has the rest.
agents-and-tools/tool-use/computer-use-tool First recorded · 2181 lines, first recorded
## Compatibility ## Overview ## Security considerations ## Quick start ## How computer use works ### The computing environment ## How to implement computer use ### Start with the reference implementation ### Understand the agent loop ### Optimize model performance with prompting ### System prompts ### Available actions ### Tool parameters ### Combining with thinking ### Augmenting computer use with other tools ### Build a custom computer use environment #### Implement the computer use tool #### Handle errors #### Size screenshots to fit image limits #### Diagnose click issues #### Follow implementation best practices ## Understand computer use limitations ## Data retention ## Pricing ## Next steps
The first capture of this source. The page was already there, and this is what it said.
---
title: Computer use tool
url: https://platform.claude.com/docs/en/agents-and-tools/tool-use/computer-use-tool
description: Give Claude screenshot, mouse, and keyboard control of a desktop environment with the computer use tool.
---
## Compatibility
- Status: Beta
- [Beta header](https://platform.claude.com/docs/en/api/beta-headers): `computer-use-2025-11-24`
- [ZDR](https://platform.claude.com/docs/en/manage-claude/api-and-data-retention): eligible (excludes [Covered Models](https://platform.claude.com/docs/en/manage-claude/api-and-data-retention#model-specific-data-retention-requirements))
- Supported models: `claude-opus-5`, `claude-sonnet-5`, `claude-opus-4-8`, `claude-opus-4-7`, `claude-opus-4-6`, `claude-sonnet-4-6`, `claude-opus-4-5-20251101`
- Platforms: Claude API (beta), Claude Platform on AWS (beta), Amazon Bedrock (beta), Google Cloud (beta), Microsoft Foundry (beta)
Claude can interact with computer environments through the computer use tool, which provides screenshot capabilities and mouse/keyboard control for autonomous desktop interaction.
<Note>
On Claude Sonnet 4.5, Claude Haiku 4.5, Claude Opus 4.1 ([retired, except on Bedrock and Google Cloud](https://platform.claude.com/docs/en/about-claude/model-deprecations)), Claude Sonnet 4 ([retired, except on Bedrock and Google Cloud](https://platform.claude.com/docs/en/about-claude/model-deprecations)), and Claude Opus 4 ([retired, except on Google Cloud](https://platform.claude.com/docs/en/about-claude/model-deprecations)), use the earlier `computer-use-2025-01-24` [beta header](https://platform.claude.com/docs/en/api/beta-headers) instead of `computer-use-2025-11-24`.
Reach out through the [feedback form](https://forms.gle/H6UFuXaaLywri9hz6) to share your feedback on this feature.
</Note>
## Overview
Computer use is a beta feature that enables Claude to interact with desktop environments. This tool provides:
* **Screenshot capture:** See what's currently displayed on screen
* **Mouse control:** Click, drag, and move the cursor
* **Keyboard input:** Type text and use keyboard shortcuts
* **Desktop automation:** Interact with any application or interface
While computer use can be augmented with other tools such as bash and text editor for more comprehensive automation workflows, computer use specifically refers to the computer use tool's capability to see and control desktop environments.
For model support, see the [Tool reference](https://platform.claude.com/docs/en/agents-and-tools/tool-use/tool-reference).
## Security considerations
Computer use is a beta feature with unique risks distinct from standard API features. These risks are heightened when interacting with the internet.
<Warning>
To minimize risks, consider taking precautions such as:
1. Using a dedicated virtual machine or container with minimal privileges to prevent direct system attacks or accidents.
2. Avoiding giving the model access to sensitive data, such as account login information, to prevent information theft.
3. Limiting internet access to an allowlist of domains to reduce exposure to malicious content.
4. Asking a human to confirm decisions that might result in meaningful real-world consequences and any tasks requiring affirmative consent, such as accepting cookies, completing financial transactions, or agreeing to terms of service.
</Warning>
In some circumstances, Claude will follow commands found in content even when they conflict with your instructions. For example, instructions on webpages or contained in images might override your instructions or cause Claude to make mistakes. Take precautions to isolate Claude from sensitive data and actions to avoid risks related to prompt injection.
Anthropic has trained the model to resist these prompt injections and has added an extra layer of defense. If you use the computer use tools, classifiers will automatically run on your prompts to flag potential instances of prompt injections. When these classifiers identify potential prompt injections in screenshots, they will automatically steer the model to ask for user confirmation before proceeding with the next action. This extra protection won't be ideal for every use case (for example, use cases without a human in the loop), so if you'd like to opt out and turn it off, [contact support](https://support.claude.com/en/).
These precautions remain important even with the classifier defense layer in place.
Inform end users of relevant risks and obtain their consent prior to enabling computer use in your own products.
<Card title="Computer use reference implementation" icon="computer" href="https://github.com/anthropics/anthropic-quickstarts/tree/main/computer-use-demo">
Get started with the computer use reference implementation that includes a web interface, Docker container, example tool implementations, and an agent loop.
</Card>
## Quick start
Here's how to get started with computer use:
<CodeGroup>
```bash cURL
curl https://api.anthropic.com/v1/messages \
-H "content-type: application/json" \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "anthropic-beta: computer-use-2025-11-24" \
-d '{
"model": "claude-opus-5",
"max_tokens": 1024,
"tools": [
{
"type": "computer_20251124",
"name": "computer",
"display_width_px": 1024,
"display_height_px": 768,
"display_number": 1
},
{
"type": "text_editor_20250728",
"name": "str_replace_based_edit_tool"
},
{
"type": "bash_20250124",
"name": "bash"
}
],
"messages": [
{
"role": "user",
"content": "Save a picture of a cat to my desktop."
}
]
}'
```
```bash CLI
ant beta:messages create --beta computer-use-2025-11-24 <<'YAML'
model: claude-opus-5
max_tokens: 1024
tools:
- type: computer_20251124
name: computer
display_width_px: 1024
display_height_px: 768
display_number: 1
- type: text_editor_20250728
name: str_replace_based_edit_tool
- type: bash_20250124
name: bash
messages:
- role: user
content: Save a picture of a cat to my desktop.
YAML
```
```python Python
client = anthropic.Anthropic()
response = client.beta.messages.create(
model="claude-opus-5", # or another compatible model
max_tokens=1024,
tools=[
{
"type": "computer_20251124",
"name": "computer",
"display_width_px": 1024,
"display_height_px": 768,
"display_number": 1,
},
{"type": "text_editor_20250728", "name": "str_replace_based_edit_tool"},
{"type": "bash_20250124", "name": "bash"},
],
messages=[{"role": "user", "content": "Save a picture of a cat to my desktop."}],
betas=["computer-use-2025-11-24"],
)
print(response)
```
```typescript TypeScript
const client = new Anthropic();
const response = await client.beta.messages.create({
model: "claude-opus-5",
max_tokens: 1024,
tools: [
{
type: "computer_20251124",
name: "computer",
display_width_px: 1024,
display_height_px: 768,
display_number: 1
},
{
type: "text_editor_20250728",
name: "str_replace_based_edit_tool"
},
{
type: "bash_20250124",
name: "bash"
}
],
messages: [{ role: "user", content: "Save a picture of a cat to my desktop." }],
betas: ["computer-use-2025-11-24"]
});
console.log(response);
```
```csharp C#
using Anthropic.Models.Beta.Messages;
using Messages = Anthropic.Models.Messages;
var client = new AnthropicClient();
var parameters = new MessageCreateParams
{
Model = Messages::Model.ClaudeOpus5,
MaxTokens = 1024,
Tools = new BetaToolUnion[]
{
new BetaToolComputerUse20251124
{
DisplayWidthPx = 1024,
DisplayHeightPx = 768,
DisplayNumber = 1
},
new BetaToolTextEditor20250728(),
new BetaToolBash20250124()
},
Messages =
[
new BetaMessageParam
{
Role = Role.User,
Content = "Save a picture of a cat to my desktop."
}
],
Betas = ["computer-use-2025-11-24"]
};
var response = await client.Beta.Messages.Create(parameters);
Console.WriteLine(response);
```
```go Go
client := anthropic.NewClient()
response, err := client.Beta.Messages.New(context.TODO(), anthropic.BetaMessageNewParams{
Model: anthropic.ModelClaudeOpus5,
MaxTokens: 1024,
Tools: []anthropic.BetaToolUnionParam{
{OfComputerUseTool20251124: &anthropic.BetaToolComputerUse20251124Param{
DisplayWidthPx: 1024,
DisplayHeightPx: 768,
DisplayNumber: anthropic.Int(1),
}},
{OfTextEditor20250728: &anthropic.BetaToolTextEditor20250728Param{}},
{OfBashTool20250124: &anthropic.BetaToolBash20250124Param{}},
},
Messages: []anthropic.BetaMessageParam{
anthropic.NewBetaUserMessage(anthropic.NewBetaTextBlock("Save a picture of a cat to my desktop.")),
},
Betas: []anthropic.AnthropicBeta{
"computer-use-2025-11-24", // no SDK exposes a named constant for this beta yet
},
})
if err != nil {
log.Fatal(err)
}
fmt.Println(response)
```
```java Java
import com.anthropic.models.beta.messages.BetaMessage;
import com.anthropic.models.beta.messages.BetaToolBash20250124;
import com.anthropic.models.beta.messages.BetaToolComputerUse20251124;
import com.anthropic.models.beta.messages.BetaToolTextEditor20250728;
import com.anthropic.models.beta.messages.MessageCreateParams;
import com.anthropic.models.messages.Model;
void main() {
AnthropicClient client = AnthropicOkHttpClient.fromEnv();
MessageCreateParams params = MessageCreateParams.builder()
.model(Model.CLAUDE_OPUS_5)
.maxTokens(1024L)
.addTool(BetaToolComputerUse20251124.builder()
.displayWidthPx(1024L)
.displayHeightPx(768L)
.displayNumber(1L)
.build())
.addTool(BetaToolTextEditor20250728.builder().build())
.addTool(BetaToolBash20250124.builder().build())
.addUserMessage("Save a picture of a cat to my desktop.")
.addBeta("computer-use-2025-11-24")
.build();
BetaMessage response = client.beta().messages().create(params);
IO.println(response);
}
```
```php PHP
$client = new Client();
$response = $client->beta->messages->create(
maxTokens: 1024,
messages: [
['role' => 'user', 'content' => 'Save a picture of a cat to my desktop.'],
],
model: 'claude-opus-5',
tools: [
[
'type' => 'computer_20251124',
'name' => 'computer',
'display_width_px' => 1024,
'display_height_px' => 768,
'display_number' => 1,
],
[
'type' => 'text_editor_20250728',
'name' => 'str_replace_based_edit_tool',
],
[
'type' => 'bash_20250124',
'name' => 'bash',
],
],
betas: ['computer-use-2025-11-24'],
);
echo $response;
```
```ruby Ruby
client = Anthropic::Client.new
Cut at 300 lines. The page has the rest.
agents-and-tools/tool-use/define-tools First recorded · 917 lines, first recorded
## Prerequisites ## Choosing a model ## Specifying client tools ### Tool use system prompt ### Best practices for tool definitions ## Providing tool use examples ### Basic usage ### Requirements and limitations ## Controlling Claude's output ### Forcing tool use ### Model responses with tools ## Next steps
The first capture of this source. The page was already there, and this is what it said.
---
title: Define tools
url: https://platform.claude.com/docs/en/agents-and-tools/tool-use/define-tools
description: Specify tool schemas, write effective descriptions, and control when Claude calls your tools.
---
## Prerequisites
* Familiarity with the [tool use overview](https://platform.claude.com/docs/en/agents-and-tools/tool-use/overview)
* A Claude API key and a working SDK or cURL setup
## Choosing a model
Use the latest Claude Opus model, Claude Opus 5, for complex tools and ambiguous queries; it handles multiple tools better and seeks clarification when needed.
Use Claude Haiku models for straightforward tools, but note they may infer missing parameters.
<Tip>
If using Claude with tool use and thinking, see [Thinking](https://platform.claude.com/docs/en/build-with-claude/thinking) for more information.
</Tip>
## Specifying client tools
Client tools (both Anthropic-schema and user-defined) are specified in the `tools` top-level parameter of the API request. Each tool definition includes:
| Parameter | Description |
| ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `name` | The name of the tool. Must match the regex `^[a-zA-Z0-9_-]{1,64}$`. |
| `description` | A detailed plaintext description of what the tool does, when it should be used, and how it behaves. |
| `input_schema` | A [JSON Schema](https://json-schema.org/) object defining the expected parameters for the tool. |
| `input_examples` | (Optional) An array of example input objects to help Claude understand how to use the tool. See [Providing tool use examples](https://platform.claude.com/docs/en/agents-and-tools/tool-use/define-tools#providing-tool-use-examples). |
For the full set of optional properties available on any tool definition, including `cache_control`, `strict`, `defer_loading`, and `allowed_callers`, see the [Tool reference](https://platform.claude.com/docs/en/agents-and-tools/tool-use/tool-reference#tool-definition-properties).
<Accordion title="Example simple tool definition">
```json JSON
{
"name": "get_weather",
"description": "Get the current weather in a given location",
"input_schema": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "The city and state, e.g. San Francisco, CA"
},
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
"description": "The unit of temperature, either 'celsius' or 'fahrenheit'"
}
},
"required": ["location"]
}
}
```
This tool, named `get_weather`, expects an input object with a required `location` string and an optional `unit` string that must be either "celsius" or "fahrenheit".
</Accordion>
### Tool use system prompt
When you call the Claude API with the `tools` parameter, the API constructs a special system prompt from the tool definitions, tool configuration, and any user-specified system prompt. The constructed prompt is designed to instruct the model to use the specified tool(s) and provide the necessary context for the tool to operate properly:
```text wrap
In this environment you have access to a set of tools you can use to answer the user's question.
{{ FORMATTING INSTRUCTIONS }}
String and scalar parameters should be specified as is, while lists and objects should use JSON format. Note that spaces for string values are not stripped. The output is not expected to be valid XML and is parsed with regular expressions.
Here are the functions available in JSONSchema format:
{{ TOOL DEFINITIONS IN JSON SCHEMA }}
{{ USER SYSTEM PROMPT }}
{{ TOOL CONFIGURATION }}
```
### Best practices for tool definitions
To get the best performance out of Claude when using tools, follow these guidelines:
* **Provide extremely detailed descriptions.** This is by far the most important factor in tool performance. Your descriptions should explain every detail about the tool, including:
* What the tool does
* When it should be used (and when it shouldn't)
* What each parameter means and how it affects the tool's behavior
* Any important caveats or limitations, such as what information the tool does not return if the tool name is unclear. The more context you can give Claude about your tools, the better it will be at deciding when and how to use them. Aim for at least 3–4 sentences for each tool description, more if the tool is complex.
* **Prioritize descriptions, but consider using `input_examples` for complex tools.** Clear descriptions are most important, but for tools with complex inputs, nested objects, or format-sensitive parameters, you can use the `input_examples` field to provide schema-validated examples. See [Providing tool use examples](https://platform.claude.com/docs/en/agents-and-tools/tool-use/define-tools#providing-tool-use-examples) for details.
* **Consolidate related operations into fewer tools.** 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 and make your tool surface easier for Claude to navigate.
* **Use meaningful namespacing in tool names.** When your tools span multiple services or resources, prefix names with the service (for example, `github_list_prs`, `slack_send_message`). This makes tool selection unambiguous as your library grows, and is especially important when using [tool search](https://platform.claude.com/docs/en/agents-and-tools/tool-use/tool-search-tool).
* **Design tool responses to return only high-signal information.** Return semantic, stable identifiers (for example, slugs or UUIDs) rather than opaque internal references, and include only the fields Claude needs to reason about its next step. Bloated responses waste context and make it harder for Claude to extract what matters.
<AccordionGroup>
<Accordion title="Example of a good tool description">
```json JSON
{
"name": "get_stock_price",
"description": "Retrieves the current stock price for a given ticker symbol. The ticker symbol must be a valid symbol for a publicly traded company on a major US stock exchange like NYSE or NASDAQ. The tool will return the latest trade price in USD. It should be used when the user asks about the current or most recent price of a specific stock. It will not provide any other information about the stock or company.",
"input_schema": {
"type": "object",
"properties": {
"ticker": {
"type": "string",
"description": "The stock ticker symbol, e.g. AAPL for Apple Inc."
}
},
"required": ["ticker"]
}
}
```
</Accordion>
<Accordion title="Example poor tool description">
```json JSON
{
"name": "get_stock_price",
"description": "Gets the stock price for a ticker.",
"input_schema": {
"type": "object",
"properties": {
"ticker": {
"type": "string"
}
},
"required": ["ticker"]
}
}
```
</Accordion>
</AccordionGroup>
The good description clearly explains what the tool does, when to use it, what data it returns, and what the `ticker` parameter means. The poor description is too brief and leaves Claude with many open questions about the tool's behavior and usage.
<Tip>
For deeper guidance on tool design (consolidation, naming, and response shaping), see [Writing tools for agents](https://www.anthropic.com/engineering/writing-tools-for-agents).
</Tip>
## Providing tool use examples
You can provide concrete examples of valid tool inputs to help Claude understand how to use your tools more effectively. This is particularly useful for complex tools with nested objects, optional parameters, or format-sensitive inputs.
### Basic usage
Add an optional `input_examples` field to your tool definition with an array of example input objects. Each example must be valid according to the tool's `input_schema`:
<CodeGroup>
```bash cURL
curl -sS https://api.anthropic.com/v1/messages \
-H "content-type: application/json" \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-d @- <<'EOF'
{
"model": "claude-opus-5",
"max_tokens": 1024,
"tools": [
{
"name": "get_weather",
"description": "Get the current weather in a given location",
"input_schema": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "The city and state, e.g. San Francisco, CA"
},
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
"description": "The unit of temperature"
}
},
"required": ["location"]
},
"input_examples": [
{"location": "San Francisco, CA", "unit": "fahrenheit"},
{"location": "Tokyo, Japan", "unit": "celsius"},
{"location": "New York, NY"}
]
}
],
"messages": [
{"role": "user", "content": "What's the weather like in San Francisco?"}
]
}
EOF
```
```bash CLI
ant messages create <<'YAML'
model: claude-opus-5
max_tokens: 1024
tools:
- name: get_weather
description: Get the current weather in a given location
input_schema:
type: object
properties:
location:
type: string
description: The city and state, e.g. San Francisco, CA
unit:
type: string
enum: [celsius, fahrenheit]
description: The unit of temperature
required: [location]
input_examples:
- location: San Francisco, CA
unit: fahrenheit
- location: Tokyo, Japan
unit: celsius
- location: New York, NY # 'unit' is optional
messages:
- role: user
content: What's the weather like in San Francisco?
YAML
```
```python Python
client = anthropic.Anthropic()
response = client.messages.create(
model="claude-opus-5",
max_tokens=1024,
tools=[
{
"name": "get_weather",
"description": "Get the current weather in a given location",
"input_schema": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "The city and state, e.g. San Francisco, CA",
},
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
"description": "The unit of temperature",
},
},
"required": ["location"],
},
"input_examples": [
{"location": "San Francisco, CA", "unit": "fahrenheit"},
{"location": "Tokyo, Japan", "unit": "celsius"},
{
"location": "New York, NY" # 'unit' is optional
},
],
}
],
messages=[{"role": "user", "content": "What's the weather like in San Francisco?"}],
)
print(response)
```
```typescript TypeScript
const client = new Anthropic();
const response = await client.messages.create({
model: "claude-opus-5",
max_tokens: 1024,
tools: [
{
name: "get_weather",
description: "Get the current weather in a given location",
input_schema: {
type: "object",
properties: {
location: {
type: "string",
description: "The city and state, e.g. San Francisco, CA"
},
unit: {
type: "string",
enum: ["celsius", "fahrenheit"],
description: "The unit of temperature"
}
},
required: ["location"]
},
input_examples: [
{
location: "San Francisco, CA",
unit: "fahrenheit"
},
{
location: "Tokyo, Japan",
unit: "celsius"
},
{
location: "New York, NY"
// Demonstrates that 'unit' is optional
}
]
}
],
Cut at 300 lines. The page has the rest.
agents-and-tools/tool-use/fine-grained-tool-streaming First recorded · 928 lines, first recorded
## How to use fine-grained tool streaming ## Accumulating tool input deltas ## Handling invalid JSON in tool responses ## Next steps
The first capture of this source. The page was already there, and this is what it said.
---
title: Fine-grained tool streaming
url: https://platform.claude.com/docs/en/agents-and-tools/tool-use/fine-grained-tool-streaming
description: Stream tool inputs without server-side JSON buffering for latency-sensitive applications.
---
<Note>
For how zero data retention (ZDR) applies to this feature, see [API and data retention](https://platform.claude.com/docs/en/manage-claude/api-and-data-retention).
</Note>
Fine-grained tool streaming delivers a tool's input to your client as Claude generates it, without server-side buffering or JSON validation. Skipping the buffering step reduces the time to the first fragment of a large parameter, such as a document or a block of code, and the fragments arrive through the same [Streaming messages](https://platform.claude.com/docs/en/build-with-claude/streaming) events as standard tool use.
<Warning>
Because the API does not buffer or validate a tool's input before streaming it, you might receive partial or invalid JSON. A response that ends with the [stop reason](https://platform.claude.com/docs/en/build-with-claude/handling-stop-reasons) `max_tokens` can also cut a parameter off midway. Accumulate the fragments, guard the parse, and see [Handling invalid JSON in tool responses](https://platform.claude.com/docs/en/agents-and-tools/tool-use/fine-grained-tool-streaming#handling-invalid-json-in-tool-responses) for how to return unparseable input to Claude.
</Warning>
## How to use fine-grained tool streaming
All models support fine-grained tool streaming on the Claude API, [Amazon Bedrock](https://platform.claude.com/docs/en/build-with-claude/claude-in-amazon-bedrock), [Claude Platform on AWS](https://platform.claude.com/docs/en/build-with-claude/claude-platform-on-aws), [Google Cloud](https://platform.claude.com/docs/en/build-with-claude/claude-on-vertex-ai), and [Microsoft Foundry](https://platform.claude.com/docs/en/build-with-claude/claude-in-microsoft-foundry). To use it, set `eager_input_streaming` to `true` on any user-defined tool where you want fine-grained streaming enabled, and enable streaming on your request.
The `eager_input_streaming` field is optional. Setting it to `true` turns on fine-grained streaming for that tool, and omitting it gives you standard buffered streaming, in which the API buffers and validates each parameter value before streaming it back. The exception is a request that still sends the legacy `fine-grained-tool-streaming-2025-05-14` beta header, which turns fine-grained streaming on for tools that leave the field unset. The per-tool field replaces that header, and an explicit `false` keeps buffered streaming for a tool even when a request still sends it. See [Tool reference](https://platform.claude.com/docs/en/agents-and-tools/tool-use/tool-reference) for the field definition.
The following example turns on fine-grained streaming for a `make_file` tool and asks Claude for a long poem, so the tool input is large enough to watch it stream in:
<CodeGroup>
```bash cURL
curl https://api.anthropic.com/v1/messages \
-H "content-type: application/json" \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-d '{
"model": "claude-opus-5",
"max_tokens": 65536,
"tools": [
{
"name": "make_file",
"description": "Write text to a file",
"eager_input_streaming": true,
"input_schema": {
"type": "object",
"properties": {
"filename": {
"type": "string",
"description": "The filename to write text to"
},
"lines_of_text": {
"type": "array",
"description": "An array of lines of text to write to the file"
}
},
"required": ["filename", "lines_of_text"]
}
}
],
"messages": [
{
"role": "user",
"content": "Can you write a long poem and make a file called poem.txt?"
}
],
"stream": true
}'
```
```bash CLI
ant messages create --stream --format jsonl <<'YAML' |
model: claude-opus-5
max_tokens: 65536
tools:
- name: make_file
description: Write text to a file
eager_input_streaming: true
input_schema:
type: object
properties:
filename:
type: string
description: The filename to write text to
lines_of_text:
type: array
description: An array of lines of text to write to the file
required:
- filename
- lines_of_text
messages:
- role: user
content: Can you write a long poem and make a file called poem.txt?
YAML
jq -rj 'select(.delta.type == "input_json_delta") | .delta.partial_json'
```
```python Python
client = anthropic.Anthropic()
with client.messages.stream(
max_tokens=65536,
model="claude-opus-5",
tools=[
{
"name": "make_file",
"description": "Write text to a file",
"eager_input_streaming": True,
"input_schema": {
"type": "object",
"properties": {
"filename": {
"type": "string",
"description": "The filename to write text to",
},
"lines_of_text": {
"type": "array",
"description": "An array of lines of text to write to the file",
},
},
"required": ["filename", "lines_of_text"],
},
}
],
messages=[
{
"role": "user",
"content": "Can you write a long poem and make a file called poem.txt?",
}
],
) as stream:
for event in stream:
if event.type == "input_json":
print(event.partial_json, end="", flush=True)
final_message = stream.get_final_message()
print()
for block in final_message.content:
if block.type == "tool_use":
print(f"Complete tool input: {block.input}")
```
```typescript TypeScript
const client = new Anthropic();
const stream = client.messages.stream({
model: "claude-opus-5",
max_tokens: 65536,
tools: [
{
name: "make_file",
description: "Write text to a file",
eager_input_streaming: true,
input_schema: {
type: "object",
properties: {
filename: {
type: "string",
description: "The filename to write text to"
},
lines_of_text: {
type: "array",
description: "An array of lines of text to write to the file"
}
},
required: ["filename", "lines_of_text"]
}
}
],
messages: [
{
role: "user",
content: "Can you write a long poem and make a file called poem.txt?"
}
]
});
stream.on("inputJson", (partialJson) => {
process.stdout.write(partialJson);
});
const message = await stream.finalMessage();
console.log();
for (const block of message.content) {
if (block.type === "tool_use") {
console.log("Complete tool input:", block.input);
}
}
```
```csharp C#
AnthropicClient client = new();
MessageCreateParams parameters = new()
{
Model = Model.ClaudeOpus5,
MaxTokens = 65536,
Tools =
[
new Tool
{
Name = "make_file",
Description = "Write text to a file",
EagerInputStreaming = true,
InputSchema = new InputSchema
{
Properties = new Dictionary<string, JsonElement>
{
["filename"] = JsonSerializer.SerializeToElement(
new { type = "string", description = "The filename to write text to" }
),
["lines_of_text"] = JsonSerializer.SerializeToElement(
new { type = "array", description = "An array of lines of text to write to the file" }
),
},
Required = ["filename", "lines_of_text"],
},
},
],
Messages =
[
new()
{
Role = Role.User,
Content = "Can you write a long poem and make a file called poem.txt?",
},
],
};
// The C# example assembles the input itself: content block index -> accumulated JSON
var toolInputs = new Dictionary<long, StringBuilder>();
await foreach (var streamEvent in client.Messages.CreateStreaming(parameters))
{
if (
streamEvent.TryPickContentBlockStart(out var start)
&& start.ContentBlock.TryPickToolUse(out _)
)
{
toolInputs[start.Index] = new StringBuilder();
}
else if (
streamEvent.TryPickContentBlockDelta(out var delta)
&& delta.Delta.TryPickInputJson(out var inputJson)
)
{
Console.Write(inputJson.PartialJson);
toolInputs[delta.Index].Append(inputJson.PartialJson);
}
}
Console.WriteLine();
foreach (var accumulatedInput in toolInputs.Values)
{
Console.WriteLine($"Complete tool input: {accumulatedInput}");
}
```
```go Go
client := anthropic.NewClient()
makeFileTool := anthropic.ToolParam{
Name: "make_file",
Description: anthropic.String("Write text to a file"),
EagerInputStreaming: anthropic.Bool(true),
InputSchema: anthropic.ToolInputSchemaParam{
Properties: map[string]any{
"filename": map[string]any{
"type": "string",
"description": "The filename to write text to",
},
"lines_of_text": map[string]any{
"type": "array",
"description": "An array of lines of text to write to the file",
},
},
Required: []string{"filename", "lines_of_text"},
},
}
stream := client.Messages.NewStreaming(context.Background(), anthropic.MessageNewParams{
Model: anthropic.ModelClaudeOpus5,
MaxTokens: 65536,
Tools: []anthropic.ToolUnionParam{{OfTool: &makeFileTool}},
Messages: []anthropic.MessageParam{
anthropic.NewUserMessage(anthropic.NewTextBlock(
"Can you write a long poem and make a file called poem.txt?",
)),
},
})
message := anthropic.Message{}
for stream.Next() {
event := stream.Current()
if err := message.Accumulate(event); err != nil {
panic(err)
}
if delta, ok := event.AsAny().(anthropic.ContentBlockDeltaEvent); ok {
if inputJSON, ok := delta.Delta.AsAny().(anthropic.InputJSONDelta); ok {
fmt.Print(inputJSON.PartialJSON)
}
}
}
if err := stream.Err(); err != nil {
panic(err)
}
Cut at 300 lines. The page has the rest.
agents-and-tools/tool-use/handle-tool-calls First recorded · 277 lines, first recorded
## Handling results from client tools ## Handling results from server tools ## Handling errors with is\_error ## Next steps
The first capture of this source. The page was already there, and this is what it said.
---
title: Handle tool calls
url: https://platform.claude.com/docs/en/agents-and-tools/tool-use/handle-tool-calls
description: Parse tool_use blocks, format tool_result responses, and handle errors with is_error.
---
This page covers the tool-call lifecycle: reading `tool_use` blocks from Claude's response, formatting `tool_result` blocks in your reply, and signaling errors. For the SDK abstraction that handles this automatically, see [Tool Runner](https://platform.claude.com/docs/en/agents-and-tools/tool-use/tool-runner).
<Note>
**Simpler with Tool Runner:** The manual tool handling described on this page is automatically managed by [Tool Runner](https://platform.claude.com/docs/en/agents-and-tools/tool-use/tool-runner). Use this page when you need custom control over tool execution.
</Note>
Claude's response differs based on whether it uses a [client or server tool](https://platform.claude.com/docs/en/agents-and-tools/tool-use/overview#how-tool-use-works).
## Handling results from client tools
The response will have a `stop_reason` of `tool_use` and one or more `tool_use` content blocks that include:
* `id`: A unique identifier for this particular tool use block. This will be used to match up the tool results later.
* `name`: The name of the tool being used.
* `input`: An object containing the input being passed to the tool, conforming to the tool's `input_schema`.
<Accordion title="Example API response with a `tool_use` content block">
```json JSON
{
"id": "msg_01Aq9w938a90dw8q",
"model": "claude-opus-5",
"stop_reason": "tool_use",
"role": "assistant",
"content": [
{
"type": "text",
"text": "I'll check the current weather in San Francisco for you."
},
{
"type": "tool_use",
"id": "toolu_01A09q90qw90lq917835lq9",
"name": "get_weather",
"input": { "location": "San Francisco, CA", "unit": "celsius" }
}
]
}
```
</Accordion>
When you receive a tool use response for a client tool, you should:
1. Extract the `name`, `id`, and `input` from the `tool_use` block.
2. Run the actual tool in your codebase corresponding to that tool name, passing in the tool `input`.
3. Continue the conversation by sending a new message with the `role` of `user`, and a `content` block containing the `tool_result` type and the following information:
* `tool_use_id`: The `id` of the tool use request this is a result for.
* `content` (optional): The result of the tool, as a string (for example, `"content": "15 degrees"`), a list of nested content blocks (for example, `"content": [{"type": "text", "text": "15 degrees"}]`), or a list of document blocks (for example, `"content": [{"type": "document", "source": {"type": "text", "media_type": "text/plain", "data": "15 degrees"}}]`). These content blocks can use the `text`, `image`, `document`, or [`search_result`](https://platform.claude.com/docs/en/build-with-claude/search-results) types.
* `is_error` (optional): Set to `true` if the tool execution resulted in an error.
<Note>
**Important formatting requirements:**
* Tool result blocks must immediately follow their corresponding tool use blocks in the message history. You cannot include any messages between the assistant's tool use message and the user's tool result message.
* In the user message containing tool results, the tool\_result blocks must come FIRST in the content array. Any text must come AFTER all tool results.
* If the assistant turn also called a [server tool](https://platform.claude.com/docs/en/agents-and-tools/tool-use/server-tools) that has no result block yet, the user message must contain only `tool_result` blocks. Text after the results ends the turn early; for a server tool Claude called directly, the request then fails with a 400 error that names the unresolved server tool. See [Stop reasons and fallback](https://platform.claude.com/docs/en/build-with-claude/handling-stop-reasons#tool-use).
For example, this will cause a 400 error:
```json
{
"role": "user",
"content": [
{ "type": "text", "text": "Here are the results:" }, // ❌ Text before tool_result
{ "type": "tool_result", "tool_use_id": "toolu_01" /* ... */ }
]
}
```
This is correct when the assistant turn calls only client tools:
```json
{
"role": "user",
"content": [
{ "type": "tool_result", "tool_use_id": "toolu_01" /* ... */ },
{ "type": "text", "text": "What should I do next?" } // ✅ Text after tool_result
]
}
```
If you receive an error like "tool\_use ids were found without tool\_result blocks immediately after", check that your tool results are formatted correctly.
</Note>
<Warning>
Tool results often carry content from sources outside your control: web pages, inbound email, user uploads, third-party APIs. Treat that content as untrusted: an attacker who can influence it may embed instructions that try to redirect Claude (indirect prompt injection). Keep untrusted content inside `tool_result` blocks rather than `system` prompts or plain user `text` blocks, and see [Mitigate jailbreaks and prompt injections](https://platform.claude.com/docs/en/test-and-evaluate/strengthen-guardrails/mitigate-jailbreaks#indirect-prompt-injection) for further hardening.
</Warning>
<AccordionGroup>
<Accordion title="Example of successful tool result">
```json JSON
{
"role": "user",
"content": [
{
"type": "tool_result",
"tool_use_id": "toolu_01A09q90qw90lq917835lq9",
"content": "15 degrees"
}
]
}
```
</Accordion>
<Accordion title="Example of tool result with images">
```json JSON
{
"role": "user",
"content": [
{
"type": "tool_result",
"tool_use_id": "toolu_01A09q90qw90lq917835lq9",
"content": [
{ "type": "text", "text": "15 degrees" },
{
"type": "image",
"source": {
"type": "base64",
"media_type": "image/jpeg",
"data": "/9j/4AAQSkZJRg..."
}
}
]
}
]
}
```
</Accordion>
<Accordion title="Example of empty tool result">
```json JSON
{
"role": "user",
"content": [
{
"type": "tool_result",
"tool_use_id": "toolu_01A09q90qw90lq917835lq9"
}
]
}
```
</Accordion>
<Accordion title="Example of tool result with documents">
```json JSON
{
"role": "user",
"content": [
{
"type": "tool_result",
"tool_use_id": "toolu_01A09q90qw90lq917835lq9",
"content": [
{ "type": "text", "text": "The weather is" },
{
"type": "document",
"source": {
"type": "text",
"media_type": "text/plain",
"data": "15 degrees"
}
}
]
}
]
}
```
</Accordion>
</AccordionGroup>
After receiving the tool result, Claude will use that information to continue generating a response to the original user prompt.
## Handling results from server tools
Claude executes the tool internally and incorporates the results directly into its response without requiring additional user interaction.
<Note>
A response can contain both a client `tool_use` block and a `server_tool_use` block that has no result block. That server tool call is not finished yet, and its result block arrives in a later response. Reply with a user message that contains only the `tool_result` blocks for the client tools and keep the same `tools` array; for a server tool Claude called directly, the API runs it on that request and the next response starts with its result block. See [Stop reasons and fallback](https://platform.claude.com/docs/en/build-with-claude/handling-stop-reasons#tool-use).
</Note>
<Tip>
**Differences from other APIs**
Unlike APIs that separate tool use or use special roles like `tool` or `function`, the Claude API integrates tools directly into the `user` and `assistant` message structure.
Messages contain arrays of `text`, `image`, `tool_use`, and `tool_result` blocks. `user` messages include client content and `tool_result`, while `assistant` messages contain AI-generated content and `tool_use`.
</Tip>
## Handling errors with is\_error
There are a few different types of errors that can occur when using tools with Claude:
<AccordionGroup>
<Accordion title="Tool execution error">
If the tool itself throws an error during execution (for example, a network error when fetching weather data), you can return the error message in the `content` along with `"is_error": true`:
```json JSON
{
"role": "user",
"content": [
{
"type": "tool_result",
"tool_use_id": "toolu_01A09q90qw90lq917835lq9",
"content": "ConnectionError: the weather service API is not available (HTTP 500)",
"is_error": true
}
]
}
```
Claude will then incorporate this error into its response to the user. For example: "I'm sorry, I was unable to retrieve the current weather because the weather service API is not available. Please try again later."
<Tip>
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.
</Tip>
</Accordion>
<Accordion title="Invalid tool name">
If Claude's attempted use of a tool is invalid (for example, missing required parameters), it usually means that there wasn't enough information for Claude to use the tool correctly. Your best bet during development is to try the request again with more-detailed `description` values in your tool definitions.
However, you can also continue the conversation forward with a `tool_result` that indicates the error, and Claude will try to use the tool again with the missing information filled in:
```json JSON
{
"role": "user",
"content": [
{
"type": "tool_result",
"tool_use_id": "toolu_01A09q90qw90lq917835lq9",
"content": "Error: Missing required 'location' parameter",
"is_error": true
}
]
}
```
If a tool request is invalid or missing parameters, Claude will retry 2-3 times with corrections before apologizing to the user.
<Tip>
To eliminate invalid tool calls entirely, use [strict tool use](https://platform.claude.com/docs/en/agents-and-tools/tool-use/strict-tool-use) with `strict: true` on your tool definitions. This guarantees that tool inputs will always match your schema exactly, preventing missing parameters and type mismatches.
</Tip>
</Accordion>
<Accordion title="Server tool errors">
When server tools encounter errors (for example, network issues with Web Search), Claude will transparently handle these errors and attempt to provide an alternative response or explanation to the user. Unlike client tools, you do not need to handle `is_error` results for server tools.
For web search specifically, possible error codes include:
* `too_many_requests`: Rate limit exceeded
* `invalid_input`: Invalid search query parameter
* `max_uses_exceeded`: Maximum web search tool uses exceeded
* `query_too_long`: Query exceeds maximum length
* `unavailable`: An internal error occurred
</Accordion>
</AccordionGroup>
## Next steps
<CardGroup cols={3}>
<Card title="Parallel tool use" icon="grid" href="https://platform.claude.com/docs/en/agents-and-tools/tool-use/parallel-tool-use">
Handle responses where Claude calls several tools in a single turn.
</Card>
<Card title="Tool Runner (SDK)" icon="wrench" href="https://platform.claude.com/docs/en/agents-and-tools/tool-use/tool-runner">
Let the SDK manage the `tool_use` loop, result formatting, and retries for you.
</Card>
<Card title="Define tools" icon="hammer" href="https://platform.claude.com/docs/en/agents-and-tools/tool-use/define-tools">
Write schemas and descriptions that steer Claude toward the right tool.
</Card>
</CardGroup>
agents-and-tools/tool-use/how-tool-use-works First recorded · 100 lines, first recorded
## The tool-use contract ## Where tools run ### User-defined tools (client-executed) ### Anthropic-schema tools (client-executed) ### Server-executed tools ## The agentic loop (client tools) ## The server-side loop ## When to use tools (and when not to) ## Choosing between approaches ## Next steps
The first capture of this source. The page was already there, and this is what it said.
---
title: How tool use works
url: https://platform.claude.com/docs/en/agents-and-tools/tool-use/how-tool-use-works
description: Understand the tool use loop, where tools execute, and when to use tools instead of prose.
---
This page explains the concepts behind tool use: where tools run, how the agentic loop works, and when tool use is the right approach. For hands-on guidance, start with the [Build a tool-using agent](https://platform.claude.com/docs/en/agents-and-tools/tool-use/build-a-tool-using-agent) tutorial or the [Define tools](https://platform.claude.com/docs/en/agents-and-tools/tool-use/define-tools) guide.
## The tool-use contract
Tool use is a contract between your application and the model. You specify what operations are available and what shape their inputs and outputs take; Claude determines when and how to call them. The model never executes anything on its own. It emits a structured request, your code (or Anthropic's servers) runs the operation, and the result flows back into the conversation.
This contract makes the model behave less like a text generator and more like a function you call. Engineers with classical API experience can integrate tool use the same way they would any other typed interface: define the schema, handle the callback, return a result. The difference is that the caller on the other side is a language model choosing which function to call based on the conversation.
## Where tools run
The primary axis along which tools differ is where the code executes. Every tool falls into one of three buckets, and the bucket determines what your application is responsible for.
### User-defined tools (client-executed)
You write the schema, you execute the code, you return the results. This is the most common case: the vast majority of tool-use traffic is [user-defined tools](https://platform.claude.com/docs/en/agents-and-tools/tool-use/define-tools) calling into application-specific logic.
When Claude calls one of your tools, the API response contains a `tool_use` block with the tool name and a JSON object of arguments. Your application extracts those arguments, runs the operation (a database query, an HTTP call, a file write, whatever the tool does), and sends the output back in a `tool_result` block on the next request. Claude never sees your implementation; it only sees the schema you provided and the result you returned.
### Anthropic-schema tools (client-executed)
For a handful of common operations (managing scratchpad memory, running shell commands, editing files, controlling a browser), Anthropic publishes the tool schema and your application handles execution. The tools in this category are [`memory`](https://platform.claude.com/docs/en/agents-and-tools/tool-use/memory-tool), [`bash`](https://platform.claude.com/docs/en/agents-and-tools/tool-use/bash-tool), [`text_editor`](https://platform.claude.com/docs/en/agents-and-tools/tool-use/text-editor-tool), and [`computer`](https://platform.claude.com/docs/en/agents-and-tools/tool-use/computer-use-tool).
The execution model is identical to user-defined tools: the response contains a `tool_use` block, your code runs the operation, and you send back a `tool_result`. The reason to use an Anthropic-schema tool instead of defining your own equivalent is that these schemas are trained-in. Claude has been optimized on thousands of successful trajectories that use these exact tool signatures, so it calls them more reliably and recovers from errors more gracefully than it would with a custom tool that does the same thing. The schema is the interface the model already expects.
### Server-executed tools
For [`web_search`](https://platform.claude.com/docs/en/agents-and-tools/tool-use/web-search-tool), [`web_fetch`](https://platform.claude.com/docs/en/agents-and-tools/tool-use/web-fetch-tool), [`code_execution`](https://platform.claude.com/docs/en/agents-and-tools/tool-use/code-execution-tool), and [`tool_search`](https://platform.claude.com/docs/en/agents-and-tools/tool-use/tool-search-tool), Anthropic runs the code. You enable the tool in your request and the server handles everything else. You never construct a `tool_result` block for these tools. When a turn calls only [server tools](https://platform.claude.com/docs/en/agents-and-tools/tool-use/server-tools), the server-side loop executes the operation and feeds the output back to the model before the response reaches you, unless the loop stops before it finishes, most often because it pauses.
The response you receive contains `server_tool_use` blocks showing what ran and what came back. In the common case, execution is already complete by the time you see them, and your application's job is to enable the tool and read the final answer rather than to participate in the execution loop; the main exceptions are a paused loop ([`pause_turn`](https://platform.claude.com/docs/en/agents-and-tools/tool-use/how-tool-use-works#the-server-side-loop)) and a turn that also calls a client tool.
## The agentic loop (client tools)
Client-executed tools (both user-defined and Anthropic-schema) require your application to drive a loop. The model can't run your code, so every tool call is a round trip: the model asks, you execute, you report back, the model continues.
The canonical shape is a `while` loop keyed on `stop_reason`:
1. Send a request with your `tools` array and the user message.
2. Claude responds with `stop_reason: "tool_use"` and one or more `tool_use` blocks.
3. Execute each tool. Format the outputs as `tool_result` blocks.
4. Send a new request containing the original messages, the assistant's response, and a user message with the `tool_result` blocks.
5. Repeat from step 2 while `stop_reason` is `"tool_use"`.
In practice this reads as: while `stop_reason == "tool_use"`, execute the tools and continue the conversation. The loop exits on any other stop reason (`"end_turn"`, `"max_tokens"`, `"stop_sequence"`, or `"refusal"`), which means Claude has either produced a final answer or stopped for another reason that your application should handle.
For the mechanics of building requests, handling parallel tool calls, and formatting results, see [Handle tool calls](https://platform.claude.com/docs/en/agents-and-tools/tool-use/handle-tool-calls).
## The server-side loop
Server-executed tools run their own loop inside Anthropic's infrastructure. A single request from your application might trigger several web searches or code executions before a response comes back. The model searches, reads results, determines whether to search again, and iterates until it has what it needs, all without your application participating.
This internal loop has an iteration limit. If the model is still iterating when it hits the cap, the response comes back with `stop_reason: "pause_turn"` instead of `"end_turn"`. A paused turn means the work isn't finished; re-send the conversation (including the paused response) to let the model continue where it left off. See [Server tools](https://platform.claude.com/docs/en/agents-and-tools/tool-use/server-tools) for the continuation pattern.
The loop also hands control back to you before a server tool runs if Claude calls that server tool and a client tool in the same group of parallel tool calls. The response then comes back with `stop_reason: "tool_use"` and a `server_tool_use` block that has no result block yet; the API runs it after you return the client tool results. See [Stop reasons and fallback](https://platform.claude.com/docs/en/build-with-claude/handling-stop-reasons#tool-use) for the exact contract.
## When to use tools (and when not to)
Tool use fits when the task requires something the model can't do from text alone:
* **Actions with side effects.** Sending an email, writing a file, updating a record. The model can describe these actions, but only a tool can perform them.
* **Fresh or external data.** Current prices, today's weather, the contents of a database. Anything outside the training data or specific to your system needs a tool to fetch it.
* **Structured, guaranteed-shape outputs.** When you need a JSON object with specific fields rather than prose that happens to contain the information, a tool schema enforces the shape.
* **Calling into existing systems.** Databases, internal APIs, filesystems. Tool use is the bridge between natural-language requests and the systems that fulfill them.
A clear sign that you should be using tools: if you're writing a regex to extract a decision from model output, that decision should have been a tool call. Parsing free-form text to recover structured intent is a sign the structure belongs in the schema.
Tool use doesn't fit when:
* The model can answer from training alone. Summarization, translation, and general-knowledge questions don't need a tool round trip.
* The interaction is one-shot Q\&A with no side effects. If there's nothing to execute, there's nothing for a tool to do.
* Tool-calling latency would dominate a trivial response. Every tool call is at least one extra round trip; for lightweight tasks the overhead can exceed the work.
## Choosing between approaches
| Approach | When to use it | What to expect | Learn more |
| ----------------------------- | ------------------------------------------------------------- | ------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
| User-defined client tools | Custom business logic, internal APIs, proprietary data | You handle execution and the agentic loop | [Define tools](https://platform.claude.com/docs/en/agents-and-tools/tool-use/define-tools) |
| Anthropic-schema client tools | Standard dev operations (bash, file editing, browser control) | You handle execution; Claude calls the tool reliably because the schema is trained-in | [Tool reference](https://platform.claude.com/docs/en/agents-and-tools/tool-use/tool-reference) |
| Server-executed tools | Web search, code sandbox, web fetch | Anthropic handles execution; you read the results instead of producing them | [Server tools](https://platform.claude.com/docs/en/agents-and-tools/tool-use/server-tools) |
## Next steps
<CardGroup>
<Card href="https://platform.claude.com/docs/en/agents-and-tools/tool-use/build-a-tool-using-agent" title="Tutorial: Build a tool-using agent">
Build an agent step by step from a single tool call to production.
</Card>
<Card href="https://platform.claude.com/docs/en/agents-and-tools/tool-use/define-tools" title="Define tools">
Schema specification, descriptions, and `tool_choice`.
</Card>
<Card href="https://platform.claude.com/docs/en/agents-and-tools/tool-use/tool-reference" title="Tool reference">
Directory of Anthropic-provided tools.
</Card>
</CardGroup>
agents-and-tools/tool-use/manage-tool-context First recorded · 65 lines, first recorded
## The four approaches ### Tool search ### Programmatic tool calling ### Prompt caching ### Context editing ## Combining approaches ## Next steps
The first capture of this source. The page was already there, and this is what it said.
---
title: Manage tool context
url: https://platform.claude.com/docs/en/agents-and-tools/tool-use/manage-tool-context
description: Choose between tool search, programmatic tool calling, prompt caching, and context editing to manage context bloat.
---
Tool definitions and accumulated `tool_result` blocks consume your context window. Long-running agents with many tools or many turns can exhaust available context before the task is finished. Four approaches address this at different points in the pipeline.
## The four approaches
Each approach targets a different source of context pressure. Pick the one that matches where your tokens are going.
| Approach | What it reduces | When it fits | Learn more |
| ------------------------- | --------------------------------------- | -------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
| Tool search | Tool definitions loaded upfront | Large toolsets (20+ tools) where most tools aren't needed every turn | [Tool search tool](https://platform.claude.com/docs/en/agents-and-tools/tool-use/tool-search-tool) |
| Programmatic tool calling | `tool_result` roundtrips | Chains of tool calls that can execute as a single script | [Programmatic tool calling](https://platform.claude.com/docs/en/agents-and-tools/tool-use/programmatic-tool-calling) |
| Prompt caching | Token cost of repeated tool definitions | Stable toolsets across many requests | [Tool use with prompt caching](https://platform.claude.com/docs/en/agents-and-tools/tool-use/tool-use-with-prompt-caching) |
| Context editing | Old `tool_result` blocks in history | Long conversations where early results are no longer relevant | [Context editing](https://platform.claude.com/docs/en/build-with-claude/context-editing) |
### Tool search
Tool search keeps tool definitions out of the context window until Claude asks for them. Instead of sending 50 tool schemas upfront, you send a single `tool_search` tool and let Claude discover the rest on demand. This trades a small amount of latency (one extra turn to look up a tool) for a large reduction in baseline context usage.
### Programmatic tool calling
Programmatic tool calling collapses a sequence of tool calls into a single code block that Claude writes and Anthropic's code execution sandbox runs. Rather than five roundtrips of `tool_use` and `tool_result`, Claude emits one script that calls all five functions from within the sandbox. The intermediate results never enter the conversation history.
### Prompt caching
Prompt caching doesn't reduce the number of tokens in context, but it reduces what you pay for them on subsequent requests. If your tool definitions are stable, cache them once and reuse the cached prefix across thousands of requests. This is the right choice when the toolset is large but fixed.
### Context editing
Context editing removes old `tool_result` blocks from the conversation history once they've served their purpose. A long agent loop might produce hundreds of intermediate results that were useful at the time but are now dead weight. Context editing lets you trim them without restarting the conversation.
## Combining approaches
These approaches compose. A long-running agent might use tool search to keep the toolset lean, prompt caching to amortize the cost of the remaining definitions, and context editing to trim stale results as the conversation grows. Each solves a different part of the problem, so there's no conflict in using them together.
A reasonable starting point for a high-volume agent:
1. Enable prompt caching on your tool definitions from day one. Cache writes carry a 25% markup over base input pricing, which pays back on the second request that hits the cache.
2. Add tool search once your toolset grows past roughly 20 tools or your baseline context usage becomes noticeable.
3. Add context editing once individual conversations start running long enough that early results become irrelevant.
4. Consider programmatic tool calling if you notice repetitive chains of small tool calls that could run as a single batch.
## Next steps
<CardGroup cols={2}>
<Card title="Tool search tool" icon="magnifying-glass" href="https://platform.claude.com/docs/en/agents-and-tools/tool-use/tool-search-tool">
Load tool definitions on demand instead of upfront.
</Card>
<Card title="Programmatic tool calling" icon="code" href="https://platform.claude.com/docs/en/agents-and-tools/tool-use/programmatic-tool-calling">
Collapse tool-call chains into a single executable script.
</Card>
<Card title="Tool use with prompt caching" icon="database" href="https://platform.claude.com/docs/en/agents-and-tools/tool-use/tool-use-with-prompt-caching">
Cache tool definitions across requests to cut token costs.
</Card>
<Card title="Context editing" icon="scissors" href="https://platform.claude.com/docs/en/build-with-claude/context-editing">
Trim stale tool results from long-running conversations.
</Card>
</CardGroup>
agents-and-tools/tool-use/memory-tool First recorded · 1025 lines, first recorded
## Use cases ## How it works ### Example: How memory tool calls work ## Getting started ## Basic usage ## Implement the memory handler ## Tool commands ### view #### Return values #### Error handling ### create #### Return values #### Error handling ### str\_replace #### Return values #### Error handling #### Directory handling ### insert #### Return values #### Error handling #### Directory handling ### delete #### Return values #### Error handling #### Directory handling ### rename #### Return values #### Error handling #### Directory handling ## Prompting guidance ## Security considerations ### Sensitive information ### File storage size ### Memory expiration ### Path traversal protection ## Error handling ## Context editing integration ## Using with compaction ## Multisession software development pattern ### How the pattern works ### Key principle ## Next steps
The first capture of this source. The page was already there, and this is what it said.
---
title: Memory tool
url: https://platform.claude.com/docs/en/agents-and-tools/tool-use/memory-tool
description: Let Claude store and retrieve information across conversations by implementing the memory tool's file operations in your application.
---
The memory tool lets Claude store and retrieve information across conversations in a directory of memory files. Claude can create, read, update, and delete files that persist between sessions, building up knowledge over time without keeping everything in the context window.
Memory supports just-in-time context retrieval. Rather than loading all relevant information up front, an agent records what it learns in memory files and reads them back on demand. This keeps the active context focused on the current task, which matters for long-running sessions that would otherwise overwhelm the context window. See [Effective context engineering](https://www.anthropic.com/engineering/effective-context-engineering-for-ai-agents) for the broader pattern.
The memory tool operates client-side: Claude requests file operations, and your application executes them. You control where and how the data is stored through your own infrastructure.
<Note>
Reach out through the [feedback form](https://forms.gle/YXC2EKGMhjN1c4L88) to share your feedback on this feature.
</Note>
<Note>
For how zero data retention (ZDR) applies to this feature, see [API and data retention](https://platform.claude.com/docs/en/manage-claude/api-and-data-retention).
</Note>
## Use cases
* Maintain project context across multiple agent sessions
* Apply lessons from past interactions, decisions, and feedback to new tasks
* Build up a knowledge base over time
## How it works
When the memory tool is enabled, Claude automatically checks its memory directory before starting a task. As it works, Claude stores what it learns in files under `/memories` and reads them back in later conversations to continue earlier work.
Because the memory tool is client-side, Claude only requests memory operations. Your application executes each request against storage you control and returns the result in a `tool_result` block (see [Handle tool calls](https://platform.claude.com/docs/en/agents-and-tools/tool-use/handle-tool-calls)). The `/memories` path is a prefix that your handler maps onto real storage, such as a per-user directory or keys in a database. Memory lives entirely in your application. A later conversation continues from the same memory when it sends the same `tools` entry and your handler serves the same store. For security, restrict all memory operations to the `/memories` directory (see [Path traversal protection](https://platform.claude.com/docs/en/agents-and-tools/tool-use/memory-tool#path-traversal-protection)).
### Example: How memory tool calls work
A typical interaction looks like this:
**1. User request:**
```text wrap
"Help me respond to this customer service ticket."
```
**2. Claude checks the memory directory:**
```text wrap
"I'll help you respond to the customer service ticket. Let me check my memory for any previous context."
```
Claude calls the memory tool:
```json
{
"type": "tool_use",
"id": "toolu_01C4D5E6F7G8H9I0J1K2L3M4",
"name": "memory",
"input": {
"command": "view",
"path": "/memories"
}
}
```
**3. Your application returns the directory contents:**
```json
{
"type": "tool_result",
"tool_use_id": "toolu_01C4D5E6F7G8H9I0J1K2L3M4",
"content": "Here're the files and directories up to 2 levels deep in /memories, excluding hidden items and node_modules:\n4.0K\t/memories\n1.5K\t/memories/customer_service_guidelines.xml\n2.0K\t/memories/refund_policies.xml"
}
```
**4. Claude reads relevant files:**
```json
{
"type": "tool_use",
"id": "toolu_01D5E6F7G8H9I0J1K2L3M4N5",
"name": "memory",
"input": {
"command": "view",
"path": "/memories/customer_service_guidelines.xml"
}
}
```
**5. Your application returns the file contents:**
```json
{
"type": "tool_result",
"tool_use_id": "toolu_01D5E6F7G8H9I0J1K2L3M4N5",
"content": "Here's the content of /memories/customer_service_guidelines.xml with line numbers:\n 1\t<guidelines>\n 2\t<addressing_customers>\n 3\t- Always address customers by their first name\n 4\t- Use empathetic language\n..."
}
```
**6. Claude uses the memory to help:**
```text wrap
"Based on your customer service guidelines, I can help you craft a response. Please share the ticket details..."
```
The memory tool is available on all Claude 4 and later models. For the full list of Anthropic-provided tools, see the [Tool reference](https://platform.claude.com/docs/en/agents-and-tools/tool-use/tool-reference).
## Getting started
The memory tool is generally available on the Messages API: no beta header is required. Using it takes two steps:
1. Add the memory tool to your request. The `tools` entry `{"type": "memory_20250818", "name": "memory"}` is the entire configuration: the `name` must be `memory`, and you don't define an input schema for an Anthropic-provided tool.
2. Implement a client-side handler for each memory command. Your handler must reject paths outside `/memories`, so read [Path traversal protection](https://platform.claude.com/docs/en/agents-and-tools/tool-use/memory-tool#path-traversal-protection) before you write it.
## Basic usage
<CodeGroup>
```bash cURL
curl https://api.anthropic.com/v1/messages \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "content-type: application/json" \
-d '{
"model": "claude-opus-5",
"max_tokens": 2048,
"messages": [
{
"role": "user",
"content": "Help me respond to this customer service ticket."
}
],
"tools": [{
"type": "memory_20250818",
"name": "memory"
}]
}'
```
```bash CLI
ant messages create <<'YAML'
model: claude-opus-5
max_tokens: 2048
tools:
- type: memory_20250818
name: memory
messages:
- role: user
content: Help me respond to this customer service ticket.
YAML
```
```python Python
client = anthropic.Anthropic()
message = client.messages.create(
model="claude-opus-5",
max_tokens=2048,
messages=[
{
"role": "user",
"content": "Help me respond to this customer service ticket.",
}
],
tools=[{"type": "memory_20250818", "name": "memory"}],
)
print(message)
```
```typescript TypeScript
const anthropic = new Anthropic();
const message = await anthropic.messages.create({
model: "claude-opus-5",
max_tokens: 2048,
messages: [
{
role: "user",
content: "Help me respond to this customer service ticket."
}
],
tools: [{ type: "memory_20250818", name: "memory" }]
});
console.log(message);
```
```csharp C#
var client = new AnthropicClient();
var message = await client.Messages.Create(
new()
{
Model = Model.ClaudeOpus5,
MaxTokens = 2048,
Messages =
[
new()
{
Role = Role.User,
Content = "Help me respond to this customer service ticket.",
},
],
Tools = [new MemoryTool20250818()],
}
);
Console.WriteLine(message);
```
```go Go
client := anthropic.NewClient()
message, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{
Model: anthropic.ModelClaudeOpus5,
MaxTokens: 2048,
Messages: []anthropic.MessageParam{
anthropic.NewUserMessage(anthropic.NewTextBlock("Help me respond to this customer service ticket.")),
},
Tools: []anthropic.ToolUnionParam{
{OfMemoryTool20250818: &anthropic.MemoryTool20250818Param{}},
},
})
if err != nil {
log.Fatal(err)
}
fmt.Println(message)
```
```java Java
import com.anthropic.models.messages.MemoryTool20250818;
// ...
AnthropicClient client = AnthropicOkHttpClient.fromEnv();
MessageCreateParams params = MessageCreateParams.builder()
.model(Model.CLAUDE_OPUS_5)
.maxTokens(2048L)
.addTool(MemoryTool20250818.builder().build())
.addUserMessage("Help me respond to this customer service ticket.")
.build();
Message message = client.messages().create(params);
IO.println(message);
```
```php PHP
$client = new Client();
$message = $client->messages->create(
model: Model::CLAUDE_OPUS_5,
maxTokens: 2048,
messages: [
[
'role' => 'user',
'content' => 'Help me respond to this customer service ticket.',
],
],
tools: [new MemoryTool20250818],
);
echo $message;
```
```ruby Ruby
client = Anthropic::Client.new
message = client.messages.create(
model: Anthropic::Model::CLAUDE_OPUS_5,
max_tokens: 2048,
messages: [
{
role: "user",
content: "Help me respond to this customer service ticket."
}
],
tools: [
{
type: "memory_20250818",
name: "memory"
}
]
)
puts message
```
</CodeGroup>
## Implement the memory handler
Claude's reply to a request like the previous one ends with a `tool_use` block that requests a memory operation, such as `view /memories`. Your application executes the operation and returns the result in a `tool_result` block, then sends the conversation back so Claude can continue: the standard [tool-use loop](https://platform.claude.com/docs/en/agents-and-tools/tool-use/handle-tool-calls).
Four SDKs provide memory tool helpers that handle the tool interface and the loop. Subclass `BetaAbstractMemoryTool` (Python and C#), use `betaMemoryTool` (TypeScript), or implement `BetaMemoryToolHandler` (Java) to back memory with your own storage, such as files on disk, a database, cloud storage, or encrypted files. Python and TypeScript also ship a ready-made local-filesystem implementation, `BetaLocalFilesystemMemoryTool`. The helper and tool-runner surfaces live in each SDK's beta namespace even though the memory tool itself is generally available. The Go and Ruby SDKs have no memory helper, so those examples run the tool-use loop themselves, and PHP wraps your handler closure in its generic `BetaRunnableTool`. All three use an in-memory store that you replace with your own storage.
<CodeGroup exclude="shell">
```python Python
import anthropic
from anthropic.tools import BetaLocalFilesystemMemoryTool
client = anthropic.Anthropic()
memory = BetaLocalFilesystemMemoryTool(base_path="./memory")
runner = client.beta.messages.tool_runner(
model="claude-opus-5",
max_tokens=1024,
Cut at 300 lines. The page has the rest.
agents-and-tools/tool-use/overview First recorded · 904 lines, first recorded
## How tool use works ## When Claude uses tools ## Choose a tool ### Your own tools ### Anthropic-schema client tools ### Server tools ## Pricing ## Next steps
The first capture of this source. The page was already there, and this is what it said.
---
title: Tool use with Claude
url: https://platform.claude.com/docs/en/agents-and-tools/tool-use/overview
description: Connect Claude to external tools and APIs. See where tools execute, when Claude calls them, and which tool fits your task.
---
Tool use lets Claude call functions that you define or that Anthropic provides. Claude determines when to call a tool based on the user's request and the tool's description. It then returns a structured call that your application executes (client tools) or that Anthropic executes (server tools).
Here's a minimal example using a server tool, the [Web search tool](https://platform.claude.com/docs/en/agents-and-tools/tool-use/web-search-tool), which Anthropic executes for you:
<CodeGroup>
```bash cURL
curl https://api.anthropic.com/v1/messages \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "content-type: application/json" \
-d '{
"model": "claude-opus-5",
"max_tokens": 1024,
"tools": [{"type": "web_search_20260209", "name": "web_search"}],
"messages": [{"role": "user", "content": "What'\''s the latest on the Mars rover?"}]
}'
```
```bash CLI
ant messages create --transform content --format yaml \
--model claude-opus-5 \
--max-tokens 1024 \
--tool '{type: web_search_20260209, name: web_search}' \
--message '{role: user, content: "What is the latest on the Mars rover?"}'
```
```python Python
client = anthropic.Anthropic()
response = client.messages.create(
model="claude-opus-5",
max_tokens=1024,
tools=[{"type": "web_search_20260209", "name": "web_search"}],
messages=[{"role": "user", "content": "What's the latest on the Mars rover?"}],
)
print(response.content)
```
```typescript TypeScript
const client = new Anthropic();
const response = await client.messages.create({
model: "claude-opus-5",
max_tokens: 1024,
tools: [{ type: "web_search_20260209", name: "web_search" }],
messages: [{ role: "user", content: "What's the latest on the Mars rover?" }]
});
console.log(response.content);
```
```csharp C#
AnthropicClient client = new();
var parameters = new MessageCreateParams
{
Model = Model.ClaudeOpus5,
MaxTokens = 1024,
Tools = [new ToolUnion(new WebSearchTool20260209())],
Messages = [new() { Role = Role.User, Content = "What's the latest on the Mars rover?" }]
};
var message = await client.Messages.Create(parameters);
Console.WriteLine(message.Content);
```
```go Go
client := anthropic.NewClient()
response, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{
Model: anthropic.ModelClaudeOpus5,
MaxTokens: 1024,
Tools: []anthropic.ToolUnionParam{
{OfWebSearchTool20260209: &anthropic.WebSearchTool20260209Param{}},
},
Messages: []anthropic.MessageParam{
anthropic.NewUserMessage(anthropic.NewTextBlock("What's the latest on the Mars rover?")),
},
})
if err != nil {
log.Fatal(err)
}
fmt.Println(response.Content)
```
```java Java
import com.anthropic.models.messages.WebSearchTool20260209;
void main() {
AnthropicClient client = AnthropicOkHttpClient.fromEnv();
MessageCreateParams params = MessageCreateParams.builder()
.model(Model.CLAUDE_OPUS_5)
.maxTokens(1024L)
.addTool(WebSearchTool20260209.builder().build())
.addUserMessage("What's the latest on the Mars rover?")
.build();
Message response = client.messages().create(params);
IO.println(response.content());
}
```
```php PHP
$client = new Client();
$message = $client->messages->create(
model: 'claude-opus-5',
maxTokens: 1024,
tools: [
['type' => 'web_search_20260209', 'name' => 'web_search'],
],
messages: [
['role' => 'user', 'content' => "What's the latest on the Mars rover?"],
],
);
echo $message;
```
```ruby Ruby
client = Anthropic::Client.new
message = client.messages.create(
model: "claude-opus-5",
max_tokens: 1024,
tools: [{ type: "web_search_20260209", name: "web_search" }],
messages: [{ role: "user", content: "What's the latest on the Mars rover?" }]
)
puts message.content
```
</CodeGroup>
Claude runs the search on Anthropic's infrastructure and returns the cited results in the same response. To have Claude call a function that you define, pass a tool with an `input_schema`, then execute the call when Claude returns a `tool_use` block. [How tool use works](https://platform.claude.com/docs/en/agents-and-tools/tool-use/overview#how-tool-use-works) shows that round trip end to end. Learn more about [defining tools](https://platform.claude.com/docs/en/agents-and-tools/tool-use/define-tools) and [handling tool calls](https://platform.claude.com/docs/en/agents-and-tools/tool-use/handle-tool-calls).
## How tool use works
Tools differ primarily by where the code executes. **Client tools** (including user-defined tools and tools with Anthropic-defined schemas, such as `bash` and `text_editor`) run in your application. Claude responds with `stop_reason: "tool_use"` and one or more `tool_use` blocks. Your code executes the operation and sends back a `tool_result`. **Server tools** (such as `web_search`, `web_fetch`, `code_execution`, and `tool_search`) run on Anthropic's infrastructure: you see the results directly without handling execution, unless Claude calls the tool in the same group of parallel tool calls as one of your client tools (see [Stop reasons and fallback](https://platform.claude.com/docs/en/build-with-claude/handling-stop-reasons#tool-use)).
Here's that round trip in full for a client tool. The first request defines a `get_weather` tool, and Claude answers the question by calling it: the response carries a `tool_use` block, your code runs the lookup, and a second request sends the result back in a `tool_result` block so Claude can reply with the answer.
<CodeGroup>
```bash cURL
# Claude replies with a tool_use block naming the tool and its arguments.
TOOLS='[
{
"name": "get_weather",
"description": "Get the current weather for a given location.",
"input_schema": {
"type": "object",
"properties": {
"location": {"type": "string", "description": "City and state, e.g. San Francisco, CA"}
},
"required": ["location"]
}
}
]'
USER_MSG="What's the weather in San Francisco?"
RESPONSE=$(curl -s https://api.anthropic.com/v1/messages \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "content-type: application/json" \
-d "$(jq -n --argjson tools "$TOOLS" --arg msg "$USER_MSG" '{
model: "claude-opus-5",
max_tokens: 1024,
tools: $tools,
# Ask for at most one tool call per turn.
tool_choice: {type: "auto", disable_parallel_tool_use: true},
messages: [{role: "user", content: $msg}]
}')")
TOOL_USE=$(echo "$RESPONSE" | jq '.content[] | select(.type == "tool_use")')
echo "Claude called $(echo "$TOOL_USE" | jq -r '.name') with $(echo "$TOOL_USE" | jq -c '.input')"
# Run the tool, then send the result back in a tool_result block.
WEATHER="15 degrees Celsius, partly cloudy"
FOLLOWUP=$(curl -s https://api.anthropic.com/v1/messages \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "content-type: application/json" \
-d "$(jq -n \
--argjson tools "$TOOLS" \
--arg msg "$USER_MSG" \
--argjson assistant "$(echo "$RESPONSE" | jq '.content')" \
--arg tool_use_id "$(echo "$TOOL_USE" | jq -r '.id')" \
--arg weather "$WEATHER" \
'{
model: "claude-opus-5",
max_tokens: 1024,
tools: $tools,
tool_choice: {type: "auto", disable_parallel_tool_use: true},
messages: [
{role: "user", content: $msg},
{role: "assistant", content: $assistant},
{role: "user", content: [
{type: "tool_result", tool_use_id: $tool_use_id, content: $weather}
]}
]
}')")
# Claude uses the result to answer the original question.
echo "$FOLLOWUP" | jq -r '.content[] | select(.type == "text") | .text'
```
```bash CLI
# ant reads the request body as YAML on stdin; jq carries the conversation
# state into the second request.
USER_MSG="What's the weather in San Francisco?"
MESSAGES=$(jq -n --arg msg "$USER_MSG" '[{role: "user", content: $msg}]')
call_api() {
{
cat <<'YAML'
model: claude-opus-5
max_tokens: 1024
# Ask for at most one tool call per turn.
tool_choice: {type: auto, disable_parallel_tool_use: true}
tools:
- name: get_weather
description: Get the current weather for a given location.
input_schema:
type: object
properties:
location: {type: string, description: "City and state, e.g. San Francisco, CA"}
required: [location]
YAML
printf 'messages: %s\n' "$MESSAGES"
} | ant messages create --format json
}
# Claude replies with a tool_use block naming the tool and its arguments.
RESPONSE=$(call_api)
TOOL_USE=$(jq '.content[] | select(.type == "tool_use")' <<<"$RESPONSE")
echo "Claude called $(jq -r '.name' <<<"$TOOL_USE") with $(jq -c '.input' <<<"$TOOL_USE")"
# Run the tool, then send the result back in a tool_result block.
WEATHER="15 degrees Celsius, partly cloudy"
MESSAGES=$(jq \
--argjson assistant "$(jq '.content' <<<"$RESPONSE")" \
--arg tool_use_id "$(jq -r '.id' <<<"$TOOL_USE")" \
--arg weather "$WEATHER" \
'. + [
{role: "assistant", content: $assistant},
{role: "user", content: [
{type: "tool_result", tool_use_id: $tool_use_id, content: $weather}
]}
]' <<<"$MESSAGES")
FOLLOWUP=$(call_api)
# Claude uses the result to answer the original question.
jq -r '.content[] | select(.type == "text") | .text' <<<"$FOLLOWUP"
```
```python Python
client = anthropic.Anthropic()
tools = [
{
"name": "get_weather",
"description": "Get the current weather for a given location.",
"input_schema": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "City and state, e.g. San Francisco, CA",
}
},
"required": ["location"],
},
}
]
messages = [{"role": "user", "content": "What's the weather in San Francisco?"}]
# Claude replies with a tool_use block naming the tool and its arguments.
response = client.messages.create(
model="claude-opus-5",
max_tokens=1024,
tools=tools,
# Ask for at most one tool call per turn.
tool_choice={"type": "auto", "disable_parallel_tool_use": True},
messages=messages,
)
tool_use = next(block for block in response.content if block.type == "tool_use")
print(f"Claude called {tool_use.name} with {json.dumps(tool_use.input)}")
# Run the tool, then send the result back in a tool_result block.
weather = "15 degrees Celsius, partly cloudy" # your weather lookup goes here
messages += [
{"role": "assistant", "content": response.content},
{
"role": "user",
"content": [
{"type": "tool_result", "tool_use_id": tool_use.id, "content": weather}
],
},
]
followup = client.messages.create(
model="claude-opus-5",
Cut at 300 lines. The page has the rest.
agents-and-tools/tool-use/parallel-tool-use First recorded · 1586 lines, first recorded
## Execution semantics ## Test parallel tool calls ## Maximizing parallel tool use ## Disable parallel tool use ### At most one tool call ### Exactly one tool call ## Troubleshooting ## Next steps
The first capture of this source. The page was already there, and this is what it said.
---
title: Parallel tool use
url: https://platform.claude.com/docs/en/agents-and-tools/tool-use/parallel-tool-use
description: Enable, format, and disable parallel tool calls, with message-history guidance and troubleshooting.
---
By default, Claude may call multiple tools in a single response. This page covers how to run those calls, how to format the message history so parallelism keeps working, and how to disable parallel tool use when you need to. For the single-call flow, see [Handle tool calls](https://platform.claude.com/docs/en/agents-and-tools/tool-use/handle-tool-calls).
## Execution semantics
When Claude calls tools, the response has a `stop_reason` of `tool_use` and can contain several `tool_use` blocks in a single assistant turn. How you run those calls is your decision. The API doesn't prescribe an execution order: you can run the calls concurrently (`Promise.all`, `asyncio.gather`), sequentially in the order they appear, or in any combination that suits your tools.
Choose the strategy based on what your tools do. Independent, read-only operations are usually safe to run in parallel for lower latency. Tools with side effects, shared state, or ordering requirements might be better run sequentially.
Whichever strategy you use, return one `tool_result` for each `tool_use` block, all together in the next user message. Match each result to its call with `tool_use_id`, and put every `tool_result` block before any text content in that message. See [Handle tool calls](https://platform.claude.com/docs/en/agents-and-tools/tool-use/handle-tool-calls) for the full formatting rules. If you choose not to run a particular call (for example, because you ran the batch sequentially and an earlier call failed), still return a `tool_result` for it with `is_error: true` and a brief explanation.
```json
{
"type": "tool_result",
"tool_use_id": "toolu_02",
"is_error": true,
"content": "Not executed: the preceding write_file call failed."
}
```
## Test parallel tool calls
<Note>
**Use the Tool Runner for most applications:** the SDK [Tool Runner](https://platform.claude.com/docs/en/agents-and-tools/tool-use/tool-runner) handles responses with multiple tool calls and formats the results for you, so you don't write this handling yourself. Use the manual pattern on this page when you need direct control over how the calls run, such as custom batching, ordering, or error handling.
</Note>
The following script sends a request that should trigger parallel tool calls, verifies the response contains them, and formats the tool results so parallelism keeps working. Run it with `ANTHROPIC_API_KEY` set in your environment:
<CodeGroup>
```bash cURL
# This end-to-end test flow doesn't translate well to a one-off shell command.
# See the SDK tabs for the full flow. The underlying HTTP request is a standard
# tool use request with multiple tools defined.
```
```bash CLI
# This end-to-end test flow doesn't translate well to a one-off shell command.
# See the SDK tabs for the full flow.
```
```python Python
client = Anthropic()
# Define tools
tools = [
{
"name": "get_weather",
"description": "Get the current weather in a given location",
"input_schema": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "The city and state, e.g. San Francisco, CA",
}
},
"required": ["location"],
},
},
{
"name": "get_time",
"description": "Get the current time in a given timezone",
"input_schema": {
"type": "object",
"properties": {
"timezone": {
"type": "string",
"description": "The timezone, e.g. America/New_York",
}
},
"required": ["timezone"],
},
},
]
# Test conversation with parallel tool calls
messages = [
{
"role": "user",
"content": "What's the weather in SF and NYC, and what time is it there?",
}
]
# Make initial request
print("Requesting parallel tool calls...")
response = client.messages.create(
model="claude-opus-5", max_tokens=1024, messages=messages, tools=tools
)
# Check for parallel tool calls
tool_uses = [block for block in response.content if block.type == "tool_use"]
print(f"\n✓ Claude made {len(tool_uses)} tool calls")
if len(tool_uses) > 1:
print("✓ Parallel tool calls detected!")
for tool in tool_uses:
print(f" - {tool.name}: {tool.input}")
else:
print("✗ No parallel tool calls detected")
# Simulate tool execution and format results correctly
tool_results = []
for tool_use in tool_uses:
if tool_use.name == "get_weather":
if "San Francisco" in str(tool_use.input):
result = "San Francisco: 68°F, partly cloudy"
else:
result = "New York: 45°F, clear skies"
else: # get_time
if "Los_Angeles" in str(tool_use.input):
result = "2:30 PM PST"
else:
result = "5:30 PM EST"
tool_results.append(
{"type": "tool_result", "tool_use_id": tool_use.id, "content": result}
)
# Continue conversation with tool results
messages.extend(
[
{"role": "assistant", "content": response.content},
{"role": "user", "content": tool_results}, # All results in one message!
]
)
# Get final response
print("\nGetting final response...")
final_response = client.messages.create(
model="claude-opus-5", max_tokens=1024, messages=messages, tools=tools
)
final_text = next(
block.text for block in final_response.content if block.type == "text"
)
print(f"\nClaude's response:\n{final_text}")
# Verify formatting
print("\n--- Verification ---")
print(f"✓ Tool results sent in single user message: {len(tool_results)} results")
print("✓ No text before tool results in content array")
print("✓ Conversation formatted correctly for future parallel tool use")
```
```typescript TypeScript
const client = new Anthropic();
// Define tools
const tools: Anthropic.Tool[] = [
{
name: "get_weather",
description: "Get the current weather in a given location",
input_schema: {
type: "object" as const,
properties: {
location: {
type: "string",
description: "The city and state, e.g. San Francisco, CA"
}
},
required: ["location"]
}
},
{
name: "get_time",
description: "Get the current time in a given timezone",
input_schema: {
type: "object" as const,
properties: {
timezone: {
type: "string",
description: "The timezone, e.g. America/New_York"
}
},
required: ["timezone"]
}
}
];
// Make initial request
console.log("Requesting parallel tool calls...");
const response = await client.messages.create({
model: "claude-opus-5",
max_tokens: 1024,
messages: [
{
role: "user",
content: "What's the weather in SF and NYC, and what time is it there?"
}
],
tools: tools
});
// Check for parallel tool calls
const toolUses = response.content.filter((block) => block.type === "tool_use");
console.log(`\n✓ Claude made ${toolUses.length} tool calls`);
if (toolUses.length > 1) {
console.log("✓ Parallel tool calls detected!");
for (const tool of toolUses) {
if (tool.type === "tool_use") {
console.log(` - ${tool.name}: ${JSON.stringify(tool.input)}`);
}
}
} else {
console.log("✗ No parallel tool calls detected");
}
// Simulate tool execution and format results correctly
const toolResults: Anthropic.ToolResultBlockParam[] = toolUses
.filter((block): block is Anthropic.ToolUseBlock => block.type === "tool_use")
.map((toolUse) => {
const input = toolUse.input as Record<string, string>;
let result: string;
if (toolUse.name === "get_weather") {
result = input.location?.includes("San Francisco")
? "San Francisco: 68F, partly cloudy"
: "New York: 45F, clear skies";
} else {
result = input.timezone?.includes("Los_Angeles") ? "2:30 PM PST" : "5:30 PM EST";
}
return {
type: "tool_result" as const,
tool_use_id: toolUse.id,
content: result
};
});
// Get final response with correct formatting
console.log("\nGetting final response...");
const finalResponse = await client.messages.create({
model: "claude-opus-5",
max_tokens: 1024,
messages: [
{
role: "user",
content: "What's the weather in SF and NYC, and what time is it there?"
},
{ role: "assistant", content: response.content },
{ role: "user", content: toolResults }
],
tools: tools
});
for (const block of finalResponse.content) {
if (block.type === "text") {
console.log(`\nClaude's response:\n${block.text}`);
}
}
// Verify formatting
console.log("\n--- Verification ---");
console.log(`✓ Tool results sent in single user message: ${toolResults.length} results`);
console.log("✓ No text before tool results in content array");
console.log("✓ Conversation formatted correctly for future parallel tool use");
```
```csharp C#
AnthropicClient client = new();
var tools = new List<ToolUnion>
{
new ToolUnion(new Tool()
{
Name = "get_weather",
Description = "Get the current weather in a given location",
InputSchema = new InputSchema()
{
Properties = new Dictionary<string, JsonElement>
{
["location"] = JsonSerializer.SerializeToElement(new { type = "string", description = "The city and state, e.g. San Francisco, CA" }),
},
Required = ["location"],
},
}),
new ToolUnion(new Tool()
{
Name = "get_time",
Description = "Get the current time in a given timezone",
InputSchema = new InputSchema()
{
Properties = new Dictionary<string, JsonElement>
{
["timezone"] = JsonSerializer.SerializeToElement(new { type = "string", description = "The timezone, e.g. America/New_York" }),
},
Required = ["timezone"],
},
}),
};
Console.WriteLine("Requesting parallel tool calls...");
var parameters = new MessageCreateParams
{
Model = Model.ClaudeOpus5,
Cut at 300 lines. The page has the rest.
agents-and-tools/tool-use/programmatic-tool-calling First recorded · 1585 lines, first recorded
## Model compatibility ## Quick start ## How programmatic tool calling works ## Core concepts ### The `allowed_callers` field ### The `caller` field in responses ### Container lifecycle ## Example workflow ### Step 1: Initial request ### Step 2: API response with tool call ### Step 3: Provide tool result ### Step 4: Next tool call or completion ### Step 5: Final response ## Advanced patterns ### Batch processing with loops ### Early termination ### Conditional tool selection ### Data filtering ## Response format ### Programmatic tool call ### Tool result handling ### Code execution completion ## Error handling ### Common errors ### Container expiration during tool call ### Tool execution errors ## Constraints and limitations ### Feature incompatibilities ### Input schema limitations ### Tool restrictions ### Message formatting restrictions ### Rate limits ### Validate tool results before use ## Token efficiency ## Usage and pricing ## Best practices ### Tool design ### When to use programmatic calling ### Performance optimization ## Troubleshooting ### Common issues ### Debugging tips ## Why programmatic tool calling works ## Alternative implementations ### Client-side direct execution ### Self-managed sandboxed execution ### Anthropic-managed execution ## Data retention ## Next steps
The first capture of this source. The page was already there, and this is what it said.
---
title: Programmatic tool calling
url: https://platform.claude.com/docs/en/agents-and-tools/tool-use/programmatic-tool-calling
description: Let Claude call your tools from code in the code execution container, cutting model round trips and token use in multi-tool workflows.
---
Programmatic tool calling allows Claude to write code that calls your tools programmatically within a [code execution](https://platform.claude.com/docs/en/agents-and-tools/tool-use/code-execution-tool) container, rather than requiring round trips through the model for each tool invocation. This reduces latency for multi-tool workflows and decreases token consumption by allowing Claude to filter or process data before it reaches the model's context window. On agentic search benchmarks like [BrowseComp](https://arxiv.org/abs/2504.12516) and [DeepSearchQA](https://github.com/google-deepmind/deepsearchqa), which test multistep web research and complex information retrieval, adding programmatic tool calling on top of basic search tools improved performance by an average of 11% while using 24% fewer input tokens (see [Improved web search with dynamic filtering](https://claude.com/blog/improved-web-search-with-dynamic-filtering)).
Consider checking budget compliance across 20 employees: the traditional approach requires 20 separate model round-trips, pulling thousands of expense line items into the context along the way. With programmatic tool calling, a single script runs all 20 lookups, filters the results, and returns only the employees who exceeded their limits, shrinking what Claude needs to reason over from hundreds of kilobytes down to a handful of lines.
<Tip>
For a deeper look at the inference and context costs that programmatic tool calling addresses, see [Advanced tool use](https://www.anthropic.com/engineering/advanced-tool-use).
</Tip>
<Note>
This feature requires the code execution tool to be enabled.
</Note>
<Note>
For how zero data retention (ZDR) applies to this feature, see [API and data retention](https://platform.claude.com/docs/en/manage-claude/api-and-data-retention).
</Note>
## Model compatibility
Programmatic tool calling requires `code_execution_20260120` or later, which is supported on the following models:
| Model |
| ---------------------------------------------- |
| Claude Fable 5 (claude-fable-5) |
| Claude Mythos 5 (claude-mythos-5) |
| Claude Opus 5 (claude-opus-5) |
| Claude Opus 4.8 (claude-opus-4-8) |
| Claude Opus 4.7 (claude-opus-4-7) |
| Claude Opus 4.6 (claude-opus-4-6) |
| Claude Sonnet 5 (claude-sonnet-5) |
| Claude Sonnet 4.6 (claude-sonnet-4-6) |
| Claude Opus 4.5 (claude-opus-4-5-20251101) |
| Claude Sonnet 4.5 (claude-sonnet-4-5-20250929) |
For the full code execution tool version matrix, see the [code execution tool model compatibility table](https://platform.claude.com/docs/en/agents-and-tools/tool-use/code-execution-tool#model-compatibility). Programmatic tool calling is available on the Claude API, [Claude Platform on AWS](https://platform.claude.com/docs/en/build-with-claude/claude-platform-on-aws), and [Microsoft Foundry](https://platform.claude.com/docs/en/build-with-claude/claude-in-microsoft-foundry). On Microsoft Foundry, programmatic tool calling requires a [Hosted on Anthropic deployment](https://platform.claude.com/docs/en/build-with-claude/claude-in-microsoft-foundry#additional-features-not-supported-when-hosted-on-azure). It is not currently available on Amazon Bedrock or Google Cloud.
## Quick start
Here's an example where Claude programmatically queries a database multiple times and aggregates results. Adding `allowed_callers: ["code_execution_20260120"]` to a tool definition is what makes that tool callable from within code execution (see [The `allowed_callers` field](https://platform.claude.com/docs/en/agents-and-tools/tool-use/programmatic-tool-calling#the-allowed-callers-field)):
<CodeGroup>
```bash cURL
curl https://api.anthropic.com/v1/messages \
--header "x-api-key: $ANTHROPIC_API_KEY" \
--header "anthropic-version: 2023-06-01" \
--header "content-type: application/json" \
--data '{
"model": "claude-opus-5",
"max_tokens": 4096,
"messages": [
{
"role": "user",
"content": "Query sales data for the West, East, and Central regions, then tell me which region had the highest revenue"
}
],
"tools": [
{
"type": "code_execution_20260120",
"name": "code_execution"
},
{
"name": "query_database",
"description": "Execute a SQL query against the sales database. Returns a list of rows as JSON objects.",
"input_schema": {
"type": "object",
"properties": {
"sql": {
"type": "string",
"description": "SQL query to execute"
}
},
"required": ["sql"]
},
"allowed_callers": ["code_execution_20260120"]
}
]
}'
```
```bash CLI
ant messages create <<'YAML'
model: claude-opus-5
max_tokens: 4096
messages:
- role: user
content: >-
Query sales data for the West, East, and Central regions, then
tell me which region had the highest revenue
tools:
- type: code_execution_20260120
name: code_execution
- name: query_database
description: >-
Execute a SQL query against the sales database. Returns a list
of rows as JSON objects.
input_schema:
type: object
properties:
sql:
type: string
description: SQL query to execute
required:
- sql
allowed_callers:
- code_execution_20260120
YAML
```
```python Python
client = anthropic.Anthropic()
response = client.messages.create(
model="claude-opus-5",
max_tokens=4096,
messages=[
{
"role": "user",
"content": "Query sales data for the West, East, and Central regions, then tell me which region had the highest revenue",
}
],
tools=[
{"type": "code_execution_20260120", "name": "code_execution"},
{
"name": "query_database",
"description": "Execute a SQL query against the sales database. Returns a list of rows as JSON objects.",
"input_schema": {
"type": "object",
"properties": {
"sql": {"type": "string", "description": "SQL query to execute"}
},
"required": ["sql"],
},
"allowed_callers": ["code_execution_20260120"],
},
],
)
print(response)
```
```typescript TypeScript
const client = new Anthropic();
const response = await client.messages.create({
model: "claude-opus-5",
max_tokens: 4096,
messages: [
{
role: "user",
content:
"Query sales data for the West, East, and Central regions, then tell me which region had the highest revenue"
}
],
tools: [
{
type: "code_execution_20260120",
name: "code_execution"
},
{
name: "query_database",
description:
"Execute a SQL query against the sales database. Returns a list of rows as JSON objects.",
input_schema: {
type: "object" as const,
properties: {
sql: {
type: "string",
description: "SQL query to execute"
}
},
required: ["sql"]
},
allowed_callers: ["code_execution_20260120"]
}
]
});
console.log(response);
```
```csharp C#
AnthropicClient client = new();
var parameters = new MessageCreateParams
{
Model = Model.ClaudeOpus5,
MaxTokens = 4096,
Messages = [
new() {
Role = Role.User,
Content = "Query sales data for the West, East, and Central regions, then tell me which region had the highest revenue"
}
],
Tools = [
new CodeExecutionTool20260120(),
new ToolUnion(new Tool()
{
Name = "query_database",
Description = "Execute a SQL query against the sales database. Returns a list of rows as JSON objects.",
InputSchema = new InputSchema()
{
Properties = new Dictionary<string, JsonElement>
{
["sql"] = JsonSerializer.SerializeToElement(new { type = "string", description = "SQL query to execute" }),
},
Required = ["sql"],
},
AllowedCallers = ["code_execution_20260120"]
}),
]
};
var message = await client.Messages.Create(parameters);
Console.WriteLine(message);
```
```go Go
client := anthropic.NewClient()
response, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{
Model: anthropic.ModelClaudeOpus5,
MaxTokens: 4096,
Messages: []anthropic.MessageParam{
anthropic.NewUserMessage(anthropic.NewTextBlock("Query sales data for the West, East, and Central regions, then tell me which region had the highest revenue")),
},
Tools: []anthropic.ToolUnionParam{
{OfCodeExecutionTool20260120: &anthropic.CodeExecutionTool20260120Param{}},
{OfTool: &anthropic.ToolParam{
Name: "query_database",
Description: anthropic.String("Execute a SQL query against the sales database. Returns a list of rows as JSON objects."),
InputSchema: anthropic.ToolInputSchemaParam{
Properties: map[string]any{
"sql": map[string]any{
"type": "string",
"description": "SQL query to execute",
},
},
Required: []string{"sql"},
},
AllowedCallers: []string{"code_execution_20260120"},
}},
},
})
if err != nil {
log.Fatal(err)
}
fmt.Println(response)
```
```java Java
import com.anthropic.models.messages.CodeExecutionTool20260120;
// ...
void main() {
AnthropicClient client = AnthropicOkHttpClient.fromEnv();
MessageCreateParams params = MessageCreateParams.builder()
.model(Model.CLAUDE_OPUS_5)
.maxTokens(4096L)
.addUserMessage("Query sales data for the West, East, and Central regions, then tell me which region had the highest revenue")
.addTool(CodeExecutionTool20260120.builder().build())
.addTool(Tool.builder()
.name("query_database")
.description("Execute a SQL query against the sales database. Returns a list of rows as JSON objects.")
.inputSchema(InputSchema.builder()
.properties(JsonValue.from(Map.of(
"sql", Map.of(
"type", "string",
"description", "SQL query to execute"
)
)))
.putAdditionalProperty("required", JsonValue.from(List.of("sql")))
.build())
.allowedCallers(List.of(Tool.AllowedCaller.of("code_execution_20260120")))
.build())
.build();
Message response = client.messages().create(params);
IO.println(response);
}
```
```php PHP
$client = new Client();
$message = $client->messages->create(
maxTokens: 4096,
messages: [
['role' => 'user', 'content' => 'Query sales data for the West, East, and Central regions, then tell me which region had the highest revenue'],
],
model: 'claude-opus-5',
tools: [
[
'type' => 'code_execution_20260120',
'name' => 'code_execution',
Cut at 300 lines. The page has the rest.
agents-and-tools/tool-use/server-tools First recorded · 1116 lines, first recorded
## The server\_tool\_use block ## The server-side loop and pause\_turn ## Mixing server tools and client tools in one turn ## ZDR and allowed\_callers ## Domain filtering ## Dynamic filtering with code execution ## Streaming server-tool events ## Batch requests ## Next steps
The first capture of this source. The page was already there, and this is what it said.
---
title: Server tools
url: https://platform.claude.com/docs/en/agents-and-tools/tool-use/server-tools
description: "Work with Anthropic-executed tools: server_tool_use blocks, pause_turn continuation, mixed server and client tool turns, and domain filtering."
---
Server-executed tools share these mechanics: the `server_tool_use` block, `pause_turn` continuation, turns that mix server and client tools, Zero Data Retention (ZDR) eligibility, and domain filtering. For individual tools, see the [tool reference](https://platform.claude.com/docs/en/agents-and-tools/tool-use/tool-reference).
## The server\_tool\_use block
The `server_tool_use` block appears in Claude's response when a server-executed tool runs. Its `id` field uses the `srvtoolu_` prefix to distinguish it from client tool calls:
```json
{
"type": "server_tool_use",
"id": "srvtoolu_01A2B3C4D5E6F7G8H9",
"name": "web_search",
"input": { "query": "latest quantum computing breakthroughs" }
}
```
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`. The tool's result block (for example, `web_search_tool_result` for web search) follows the `server_tool_use` block in the same assistant turn, paired by `tool_use_id`. If Claude calls one of your client tools at the same time, the `server_tool_use` block appears without its result, and the response ends with `stop_reason: "tool_use"`. The API runs the tool when you return the client `tool_result` blocks in your next request.
## The server-side loop and pause\_turn
When using server tools such as web search, the API executes tool calls in a server-side agentic loop. On a long-running turn, the API might pause that loop and return a `pause_turn` stop reason.
Here's how to handle the `pause_turn` stop reason:
<CodeGroup>
```bash cURL
# Initial request. If "stop_reason" in the response is "pause_turn", continue
# the turn by re-sending the request with the assistant content appended to messages.
curl https://api.anthropic.com/v1/messages \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "content-type: application/json" \
-d '{
"model": "claude-opus-5",
"max_tokens": 1024,
"messages": [
{
"role": "user",
"content": "Search for comprehensive information about quantum computing breakthroughs in 2025"
}
],
"tools": [{"type": "web_search_20250305", "name": "web_search", "max_uses": 10}]
}' | jq '{stop_reason, content}'
```
```bash CLI
# Initial request. If "stop_reason" in the output is "pause_turn", re-run with
# the assistant content appended to messages (see the SDK tabs).
ant messages create --format json <<'YAML' | jq '{stop_reason, content}'
model: claude-opus-5
max_tokens: 1024
tools:
- {type: web_search_20250305, name: web_search, max_uses: 10}
messages:
- {role: user, content: "Search for comprehensive information about quantum computing breakthroughs in 2025"}
YAML
```
```python Python
client = anthropic.Anthropic()
# Initial request with web search
response = client.messages.create(
model="claude-opus-5",
max_tokens=1024,
messages=[
{
"role": "user",
"content": "Search for comprehensive information about quantum computing breakthroughs in 2025",
}
],
tools=[{"type": "web_search_20250305", "name": "web_search", "max_uses": 10}],
)
# Check if the response has pause_turn stop reason
if response.stop_reason == "pause_turn":
# Continue the conversation with the paused content
messages = [
{
"role": "user",
"content": "Search for comprehensive information about quantum computing breakthroughs in 2025",
},
{"role": "assistant", "content": response.content},
]
# Send the continuation request
continuation = client.messages.create(
model="claude-opus-5",
max_tokens=1024,
messages=messages,
tools=[{"type": "web_search_20250305", "name": "web_search", "max_uses": 10}],
)
print(continuation)
else:
print(response)
```
```typescript TypeScript
const client = new Anthropic();
// Initial request with web search
const response = await client.messages.create({
model: "claude-opus-5",
max_tokens: 1024,
messages: [
{
role: "user",
content:
"Search for comprehensive information about quantum computing breakthroughs in 2025"
}
],
tools: [
{
type: "web_search_20250305",
name: "web_search",
max_uses: 10
}
]
});
// Check if the response has pause_turn stop reason
if (response.stop_reason === "pause_turn") {
// Continue the conversation with the paused content
const messages: Anthropic.MessageParam[] = [
{
role: "user",
content:
"Search for comprehensive information about quantum computing breakthroughs in 2025"
},
{ role: "assistant", content: response.content }
];
// Send the continuation request
const continuation = await client.messages.create({
model: "claude-opus-5",
max_tokens: 1024,
messages,
tools: [
{
type: "web_search_20250305",
name: "web_search",
max_uses: 10
}
]
});
console.log(continuation);
} else {
console.log(response);
}
```
```csharp C#
AnthropicClient client = new();
var parameters = new MessageCreateParams
{
Model = Model.ClaudeOpus5,
MaxTokens = 1024,
Messages = [
new() {
Role = Role.User,
Content = "Search for comprehensive information about quantum computing breakthroughs in 2025"
}
],
Tools = [new ToolUnion(new WebSearchTool20250305 { MaxUses = 10 })]
};
var response = await client.Messages.Create(parameters);
if (response.StopReason?.Value() == StopReason.PauseTurn)
{
// Continue the conversation with the paused content
var continuationParams = new MessageCreateParams
{
Model = Model.ClaudeOpus5,
MaxTokens = 1024,
Messages = [
new() {
Role = Role.User,
Content = "Search for comprehensive information about quantum computing breakthroughs in 2025"
},
new() {
Role = Role.Assistant,
Content = response.Content.Select(block => new ContentBlockParam(block.Json)).ToList()
}
],
Tools = [new ToolUnion(new WebSearchTool20250305 { MaxUses = 10 })]
};
var continuation = await client.Messages.Create(continuationParams);
Console.WriteLine(continuation);
}
else
{
Console.WriteLine(response);
}
```
```go Go
client := anthropic.NewClient()
webSearchTool := []anthropic.ToolUnionParam{
{OfWebSearchTool20250305: &anthropic.WebSearchTool20250305Param{
MaxUses: anthropic.Int(10),
}},
}
response, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{
Model: anthropic.ModelClaudeOpus5,
MaxTokens: 1024,
Messages: []anthropic.MessageParam{
anthropic.NewUserMessage(anthropic.NewTextBlock("Search for comprehensive information about quantum computing breakthroughs in 2025")),
},
Tools: webSearchTool,
})
if err != nil {
log.Fatal(err)
}
if response.StopReason == anthropic.StopReasonPauseTurn {
// Pass the paused response back as-is so Claude can continue the turn
continuation, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{
Model: anthropic.ModelClaudeOpus5,
MaxTokens: 1024,
Messages: []anthropic.MessageParam{
anthropic.NewUserMessage(anthropic.NewTextBlock("Search for comprehensive information about quantum computing breakthroughs in 2025")),
response.ToParam(),
},
Tools: webSearchTool,
})
if err != nil {
log.Fatal(err)
}
fmt.Println(continuation)
} else {
fmt.Println(response)
}
```
```java Java
import com.anthropic.models.messages.StopReason;
import com.anthropic.models.messages.WebSearchTool20250305;
void main() {
AnthropicClient client = AnthropicOkHttpClient.fromEnv();
MessageCreateParams params = MessageCreateParams.builder()
.model(Model.CLAUDE_OPUS_5)
.maxTokens(1024L)
.addUserMessage("Search for comprehensive information about quantum computing breakthroughs in 2025")
.addTool(WebSearchTool20250305.builder()
.maxUses(10L)
.build())
.build();
Message response = client.messages().create(params);
if (response.stopReason().isPresent()
&& response.stopReason().get().equals(StopReason.PAUSE_TURN)) {
MessageCreateParams continuationParams = MessageCreateParams.builder()
.model(Model.CLAUDE_OPUS_5)
.maxTokens(1024L)
.addUserMessage("Search for comprehensive information about quantum computing breakthroughs in 2025")
.addMessage(response)
.addTool(WebSearchTool20250305.builder()
.maxUses(10L)
.build())
.build();
Message continuation = client.messages().create(continuationParams);
IO.println(continuation);
} else {
IO.println(response);
}
}
```
```php PHP
$client = new Client();
$response = $client->messages->create(
maxTokens: 1024,
messages: [
[
'role' => 'user',
'content' => 'Search for comprehensive information about quantum computing breakthroughs in 2025'
]
],
model: 'claude-opus-5',
tools: [
[
'type' => 'web_search_20250305',
'name' => 'web_search',
Cut at 300 lines. The page has the rest.
agents-and-tools/tool-use/strict-tool-use First recorded · 1142 lines, first recorded
## Why strict tool use matters for agents ## Quick start ## How it works ## Common use cases ## Data retention ## Next steps
The first capture of this source. The page was already there, and this is what it said.
---
title: Strict tool use
url: https://platform.claude.com/docs/en/agents-and-tools/tool-use/strict-tool-use
description: Enforce JSON Schema compliance on Claude's tool inputs with grammar-constrained sampling.
---
Setting `strict: true` on a tool definition guarantees Claude's tool inputs match your JSON Schema by constraining the model's token sampling to schema-valid outputs (a technique called grammar-constrained sampling). This page covers why strict mode matters for agents, how to enable it, and common use cases. For the supported JSON Schema subset, see [JSON Schema limitations](https://platform.claude.com/docs/en/build-with-claude/structured-outputs#json-schema-limitations). For non-strict schema guidance, see [Define tools](https://platform.claude.com/docs/en/agents-and-tools/tool-use/define-tools).
Strict tool use validates tool parameters, ensuring Claude calls your functions with correctly-typed arguments. Use strict tool use when you need to:
* Validate tool parameters
* Build agentic workflows
* Ensure type-safe function calls
* Handle complex tools with nested properties
## Why strict tool use matters for agents
Building reliable agentic systems requires guaranteed schema conformance. Without strict mode, Claude might return incompatible types (`"2"` instead of `2`) or omit required fields, breaking your functions and causing runtime errors.
Strict tool use guarantees type-safe parameters:
* Functions receive correctly-typed arguments every time
* No need to validate and retry tool calls
* Production-ready agents that work consistently at scale
For example, suppose a booking system needs `passengers: int`. Without strict mode, Claude might provide `passengers: "two"` or `passengers: "2"`. With `strict: true`, the response always contains `passengers: 2`.
## Quick start
<CodeGroup>
```bash cURL
curl https://api.anthropic.com/v1/messages \
-H "content-type: application/json" \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-d '{
"model": "claude-opus-5",
"max_tokens": 1024,
"messages": [
{"role": "user", "content": "What is the weather in San Francisco?"}
],
"tools": [{
"name": "get_weather",
"description": "Get the current weather in a given location",
"strict": true,
"input_schema": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "The city and state, e.g. San Francisco, CA"
},
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"]
}
},
"required": ["location"],
"additionalProperties": false
}
}]
}'
```
```bash CLI
ant messages create --transform content <<'YAML'
model: claude-opus-5
max_tokens: 1024
messages:
- role: user
content: What is the weather in San Francisco?
tools:
- name: get_weather
description: Get the current weather in a given location
strict: true
input_schema:
type: object
properties:
location:
type: string
description: The city and state, e.g. San Francisco, CA
unit:
type: string
enum: [celsius, fahrenheit]
required: [location]
additionalProperties: false
YAML
```
```python Python
client = anthropic.Anthropic()
response = client.messages.create(
model="claude-opus-5",
max_tokens=1024,
messages=[{"role": "user", "content": "What's the weather like in San Francisco?"}],
tools=[
{
"name": "get_weather",
"description": "Get the current weather in a given location",
"strict": True, # Enable strict mode
"input_schema": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "The city and state, e.g. San Francisco, CA",
},
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
"description": "The unit of temperature, either 'celsius' or 'fahrenheit'",
},
},
"required": ["location"],
"additionalProperties": False,
},
}
],
)
print(response.content)
```
```typescript TypeScript
const client = new Anthropic({
apiKey: process.env.ANTHROPIC_API_KEY
});
const response = await client.messages.create({
model: "claude-opus-5",
max_tokens: 1024,
messages: [
{
role: "user",
content: "What's the weather like in San Francisco?"
}
],
tools: [
{
name: "get_weather",
description: "Get the current weather in a given location",
strict: true, // Enable strict mode
input_schema: {
type: "object",
properties: {
location: {
type: "string",
description: "The city and state, e.g. San Francisco, CA"
},
unit: {
type: "string",
enum: ["celsius", "fahrenheit"]
}
},
required: ["location"],
additionalProperties: false
}
}
]
});
console.log(response.content);
```
```csharp C#
using System.Text.Json;
using Anthropic;
using Anthropic.Models.Messages;
AnthropicClient client = new();
var parameters = new MessageCreateParams
{
Model = Model.ClaudeOpus5,
MaxTokens = 1024,
Messages = [new() { Role = Role.User, Content = "What's the weather like in San Francisco?" }],
Tools = [
new ToolUnion(new Tool()
{
Name = "get_weather",
Description = "Get the current weather in a given location",
Strict = true,
InputSchema = new InputSchema(new Dictionary<string, JsonElement>
{
["properties"] = JsonSerializer.SerializeToElement(new Dictionary<string, object>
{
["location"] = new { type = "string", description = "The city and state, e.g. San Francisco, CA" },
["unit"] = new { type = "string", @enum = new[] { "celsius", "fahrenheit" } },
}),
["required"] = JsonSerializer.SerializeToElement(new[] { "location" }),
["additionalProperties"] = JsonSerializer.SerializeToElement(false),
}),
}),
]
};
var message = await client.Messages.Create(parameters);
Console.WriteLine(message);
```
```go Go
client := anthropic.NewClient()
response, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{
Model: anthropic.ModelClaudeOpus5,
MaxTokens: 1024,
Messages: []anthropic.MessageParam{
anthropic.NewUserMessage(anthropic.NewTextBlock("What's the weather like in San Francisco?")),
},
Tools: []anthropic.ToolUnionParam{
{OfTool: &anthropic.ToolParam{
Name: "get_weather",
Description: anthropic.String("Get the current weather in a given location"),
Strict: anthropic.Bool(true),
InputSchema: anthropic.ToolInputSchemaParam{
Properties: map[string]any{
"location": map[string]any{
"type": "string",
"description": "The city and state, e.g. San Francisco, CA",
},
"unit": map[string]any{
"type": "string",
"enum": []string{"celsius", "fahrenheit"},
},
},
Required: []string{"location"},
ExtraFields: map[string]any{
"additionalProperties": false,
},
}}},
},
})
if err != nil {
log.Fatal(err)
}
fmt.Println(response.Content)
```
```java Java
AnthropicClient client = AnthropicOkHttpClient.fromEnv();
InputSchema schema = InputSchema.builder()
.properties(
JsonValue.from(
Map.of(
"location", Map.of(
"type", "string",
"description", "The city and state, e.g. San Francisco, CA"
),
"unit", Map.of(
"type", "string",
"enum", List.of("celsius", "fahrenheit")
)
)
)
)
.putAdditionalProperty("required", JsonValue.from(List.of("location")))
.putAdditionalProperty("additionalProperties", JsonValue.from(false))
.build();
MessageCreateParams params = MessageCreateParams.builder()
.model(Model.CLAUDE_OPUS_5)
.maxTokens(1024L)
.addUserMessage("What's the weather like in San Francisco?")
.addTool(
Tool.builder()
.name("get_weather")
.description("Get the current weather in a given location")
.strict(true)
.inputSchema(schema)
.build()
)
.build();
Message response = client.messages().create(params);
IO.println(response.content());
```
```php PHP
$client = new Client();
$message = $client->messages->create(
maxTokens: 1024,
messages: [
['role' => 'user', 'content' => "What's the weather like in San Francisco?"]
],
model: 'claude-opus-5',
tools: [
[
'name' => 'get_weather',
'description' => 'Get the current weather in a given location',
'strict' => true,
'input_schema' => [
'type' => 'object',
'properties' => [
'location' => [
'type' => 'string',
'description' => 'The city and state, e.g. San Francisco, CA'
],
'unit' => [
'type' => 'string',
Cut at 300 lines. The page has the rest.
agents-and-tools/tool-use/text-editor-tool First recorded · 2277 lines, first recorded
## When to use the text editor tool ## Use the text editor tool ### Text editor tool commands #### view #### str\_replace #### create #### insert ### Example: Fixing a syntax error with the text editor tool ## Implement the text editor tool ### Handle errors ### Follow implementation best practices ## Pricing and token usage ## Integrate the text editor tool with other tools ## Change log ## Next steps
The first capture of this source. The page was already there, and this is what it said.
---
title: Text editor tool
url: https://platform.claude.com/docs/en/agents-and-tools/tool-use/text-editor-tool
description: Give Claude the Anthropic-defined text editor tool to view, create, and edit files, and handle its view, str_replace, create, and insert commands.
---
<Note>
For how zero data retention (ZDR) applies to this feature, see [API and data retention](https://platform.claude.com/docs/en/manage-claude/api-and-data-retention).
</Note>
Claude can use an Anthropic-schema text editor tool to view and modify text files, helping you debug, fix, and improve your code or other text documents. This allows Claude to directly interact with your files, providing hands-on assistance rather than just suggesting changes.
For model support, see the [Tool reference](https://platform.claude.com/docs/en/agents-and-tools/tool-use/tool-reference).
## When to use the text editor tool
Some examples of when to use the text editor tool are:
* **Code debugging:** Have Claude identify and fix bugs in your code, from syntax errors to logic issues.
* **Code refactoring:** Let Claude improve your code structure, readability, and performance through targeted edits.
* **Documentation generation:** Ask Claude to add docstrings, comments, or README files to your code base.
* **Test creation:** Have Claude create unit tests for your code based on its analysis of the implementation.
## Use the text editor tool
Provide the text editor tool (named `str_replace_based_edit_tool`) to Claude using the Messages API.
You can optionally specify a `max_characters` parameter to control truncation when viewing large files.
<Note>
`max_characters` is only compatible with `text_editor_20250728` and later versions of the text editor tool.
</Note>
<CodeGroup>
```bash cURL
curl https://api.anthropic.com/v1/messages \
-H "content-type: application/json" \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-d '{
"model": "claude-opus-5",
"max_tokens": 1024,
"tools": [
{
"type": "text_editor_20250728",
"name": "str_replace_based_edit_tool",
"max_characters": 10000
}
],
"messages": [
{
"role": "user",
"content": "There'\''s a syntax error in my primes.py file. Can you help me fix it?"
}
]
}'
```
```bash CLI
ant messages create \
--model claude-opus-5 \
--max-tokens 1024 \
--tool '{type: text_editor_20250728, name: str_replace_based_edit_tool, max_characters: 10000}' \
--message '{role: user, content: There is a syntax error in my primes.py file. Can you help me fix it?}'
```
```python Python
client = anthropic.Anthropic()
response = client.messages.create(
model="claude-opus-5",
max_tokens=1024,
tools=[
{
"type": "text_editor_20250728",
"name": "str_replace_based_edit_tool",
"max_characters": 10000,
}
],
messages=[
{
"role": "user",
"content": "There's a syntax error in my primes.py file. Can you help me fix it?",
}
],
)
print(response)
```
```typescript TypeScript
const anthropic = new Anthropic();
const response = await anthropic.messages.create({
model: "claude-opus-5",
max_tokens: 1024,
tools: [
{
type: "text_editor_20250728",
name: "str_replace_based_edit_tool",
max_characters: 10000
}
],
messages: [
{
role: "user",
content: "There's a syntax error in my primes.py file. Can you help me fix it?"
}
]
});
console.log(response);
```
```csharp C#
var client = new AnthropicClient();
var response = await client.Messages.Create(
new()
{
Model = Model.ClaudeOpus5,
MaxTokens = 1024,
Tools = [new ToolTextEditor20250728 { MaxCharacters = 10000 }],
Messages =
[
new()
{
Role = Role.User,
Content = "There's a syntax error in my primes.py file. Can you help me fix it?",
},
],
}
);
Console.WriteLine(response);
```
```go Go
client := anthropic.NewClient()
response, err := client.Messages.New(context.TODO(), anthropic.MessageNewParams{
Model: anthropic.ModelClaudeOpus5,
MaxTokens: 1024,
Tools: []anthropic.ToolUnionParam{
{OfTextEditor20250728: &anthropic.ToolTextEditor20250728Param{
MaxCharacters: anthropic.Int(10000),
}},
},
Messages: []anthropic.MessageParam{
anthropic.NewUserMessage(anthropic.NewTextBlock("There's a syntax error in my primes.py file. Can you help me fix it?")),
},
})
if err != nil {
log.Fatal(err)
}
fmt.Println(response)
```
```java Java
import com.anthropic.models.messages.ToolTextEditor20250728;
// ...
void main() {
AnthropicClient client = AnthropicOkHttpClient.fromEnv();
ToolTextEditor20250728 editorTool =
ToolTextEditor20250728.builder()
.maxCharacters(10000L)
.build();
MessageCreateParams params = MessageCreateParams.builder()
.model(Model.CLAUDE_OPUS_5)
.maxTokens(1024)
.addTool(editorTool)
.addUserMessage("There's a syntax error in my primes.py file. Can you help me fix it?")
.build();
Message message = client.messages().create(params);
IO.println(message);
}
```
```php PHP
$client = new Client();
$response = $client->messages->create(
model: 'claude-opus-5',
maxTokens: 1024,
tools: [ToolTextEditor20250728::with(maxCharacters: 10000)],
messages: [
[
'role' => 'user',
'content' => "There's a syntax error in my primes.py file. Can you help me fix it?",
],
],
);
echo $response;
```
```ruby Ruby
client = Anthropic::Client.new
response = client.messages.create(
model: "claude-opus-5",
max_tokens: 1024,
tools: [
{
type: "text_editor_20250728",
name: "str_replace_based_edit_tool",
max_characters: 10000
}
],
messages: [
{
role: "user",
content: "There's a syntax error in my primes.py file. Can you help me fix it?"
}
]
)
puts response
```
</CodeGroup>
Use the text editor tool in the following way:
<Steps>
<Step title="Provide Claude with the text editor tool and a user prompt">
* Include the text editor tool in your API request
* Provide a user prompt that may require examining or modifying files, such as "Can you fix the syntax error in my code?"
</Step>
<Step title="Claude uses the tool to examine files or directories">
* Claude assesses what it needs to look at and uses the `view` command to examine file contents or list directory contents
* The API response will contain a `tool_use` content block with the `view` command
</Step>
<Step title="Execute the view command and return results">
* Extract the file or directory path from Claude's tool use request
* Read the file's contents or list the directory contents
* If a `max_characters` parameter was specified in the tool configuration, truncate the file contents to that length
* Return the results to Claude by continuing the conversation with a new `user` message containing a `tool_result` content block
</Step>
<Step title="Claude uses the tool to modify files">
* After examining the file or directory, Claude may use a command such as `str_replace` to make changes or `insert` to add text at a specific line number.
* If Claude uses the `str_replace` command, Claude constructs a properly formatted tool use request with the old text and new text to replace it with
</Step>
<Step title="Execute the edit and return results">
* Extract the file path, old text, and new text from Claude's tool use request
* Perform the text replacement in the file
* Return the results to Claude
</Step>
<Step title="Claude provides its analysis and explanation">
* After examining and possibly editing the files, Claude provides a complete explanation of what it found and what changes it made
</Step>
</Steps>
### Text editor tool commands
The text editor tool supports several commands for viewing and modifying files:
#### view
The `view` command allows Claude to examine the contents of a file or list the contents of a directory. It can read the entire file or a specific range of lines.
Parameters:
* `command`: Must be "view"
* `path`: The path to the file or directory to view
* `view_range` (optional): An array of two integers specifying the start and end line numbers to view. Line numbers are 1-indexed, and -1 for the end line means read to the end of the file. This parameter only applies when viewing files, not directories.
<Accordion title="Example view commands">
Example for viewing a file:
```json
{
"type": "tool_use",
"id": "toolu_01A09q90qw90lq917835lq9",
"name": "str_replace_based_edit_tool",
"input": {
"command": "view",
"path": "primes.py"
}
}
```
Example for viewing a directory:
```json
{
"type": "tool_use",
"id": "toolu_02B19r91rw91mr917835mr9",
"name": "str_replace_based_edit_tool",
"input": {
"command": "view",
"path": "src/"
}
Cut at 300 lines. The page has the rest.
agents-and-tools/tool-use/tool-combinations First recorded · 99 lines, first recorded
## Research agent: web\_search + code\_execution ## Coding agent: text\_editor + bash ## Cite-then-fetch: web\_search + web\_fetch ## Long-running agent: memory + any toolset ## All-in-one: computer\_use ## Next steps
The first capture of this source. The page was already there, and this is what it said.
---
title: Tool combinations
url: https://platform.claude.com/docs/en/agents-and-tools/tool-use/tool-combinations
description: Common Anthropic tool pairings for research agents, coding agents, and long-running agents.
---
Anthropic-provided tools are designed to work together. Common agent patterns pair tools that cover complementary stages of a workflow: one tool gathers or discovers, another processes or acts. The combinations below are starting points, not prescriptions. Mix them to fit your task.
Each snippet shows only the `tools` array. See [Handle tool calls](https://platform.claude.com/docs/en/agents-and-tools/tool-use/handle-tool-calls) for the full request shape.
## Research agent: web\_search + code\_execution
Search finds sources; code execution analyzes and synthesizes. Claude searches for data, then writes Python to process, tabulate, or visualize it. This pairing is a good fit for questions that require both up-to-date information and nontrivial computation over that information, such as "compare this quarter's earnings across the top five cloud providers."
```json
{
"tools": [
{ "type": "web_search_20260209", "name": "web_search" },
{ "type": "code_execution_20260521", "name": "code_execution" }
]
}
```
The flow is typically search, then execute, then optionally search again if the first pass surfaced a gap. Code execution runs server-side, so there's no client-side sandbox to manage.
## Coding agent: text\_editor + bash
The text editor reads and modifies files; bash runs tests and build commands. This is the canonical software-development loop: inspect the code, make an edit, run the tests, repeat. Both tools are client-executed, so your application controls which files and commands are accessible.
```json
{
"tools": [
{ "type": "text_editor_20250728", "name": "str_replace_based_edit_tool" },
{ "type": "bash_20250124", "name": "bash" }
]
}
```
Pair this with a constrained working directory and a command allowlist if the agent operates on untrusted code. See [Text editor tool](https://platform.claude.com/docs/en/agents-and-tools/tool-use/text-editor-tool) and [Bash tool](https://platform.claude.com/docs/en/agents-and-tools/tool-use/bash-tool) for the execution contracts.
## Cite-then-fetch: web\_search + web\_fetch
Search surfaces candidate URLs; fetch retrieves full page content for the relevant ones. This avoids fetching everything upfront. Claude runs a search, inspects the snippets, picks the two or three results that actually look relevant, and fetches only those.
```json
{
"tools": [
{ "type": "web_search_20260209", "name": "web_search" },
{ "type": "web_fetch_20260209", "name": "web_fetch" }
]
}
```
This pairing is useful when the answer lives in long-form content (documentation pages, articles, specifications) that a search snippet can't fully capture. Fetch pulls the complete page so Claude can cite specific passages.
## Long-running agent: memory + any toolset
Memory persists state across conversations; the other tools do the work. Add memory to any agent that needs to remember prior sessions, such as a support agent that recalls a customer's earlier issues or a project assistant that tracks decisions made last week.
```json
{
"tools": [{ "type": "memory_20250818", "name": "memory" }]
}
```
Add your other tools alongside `memory` in the same array.
Memory is orthogonal to the rest of your toolset. It doesn't change how other tools behave; it gives Claude a place to write down and later retrieve facts that would otherwise be lost when the context window resets. See [Memory tool](https://platform.claude.com/docs/en/agents-and-tools/tool-use/memory-tool) for the storage model.
## All-in-one: computer\_use
The computer use tool subsumes most others by operating a full desktop. Claude sees screenshots and issues mouse and keyboard actions, which means it can drive any application a human can. Use this when the task requires arbitrary GUI interaction that more specific tools can't reach: legacy software without an API, visual verification steps, or workflows that span multiple desktop apps.
```json
{
"tools": [
{
"type": "computer_20250124",
"name": "computer",
"display_width_px": 1280,
"display_height_px": 800
}
]
}
```
Computer use is the most general option and also the slowest, because every action requires a screenshot roundtrip. Prefer narrower tools when they cover your use case, and reach for computer use when nothing else fits. See [Computer use tool](https://platform.claude.com/docs/en/agents-and-tools/tool-use/computer-use-tool) for the sandbox setup.
## Next steps
<CardGroup cols={2}>
<Card title="Tool reference" icon="book" href="https://platform.claude.com/docs/en/agents-and-tools/tool-use/tool-reference">
Full catalog of Anthropic-provided tools with type strings and parameters.
</Card>
<Card title="Tool use overview" icon="map" href="https://platform.claude.com/docs/en/agents-and-tools/tool-use/overview">
How tool use works and when to use Anthropic tools versus defining your own.
</Card>
</CardGroup>
agents-and-tools/tool-use/tool-reference First recorded · 77 lines, first recorded
## Anthropic-provided tools ### Tool versioning ## Tool definition properties ### `allowed_callers` values ### `defer_loading` and prompt caching
The first capture of this source. The page was already there, and this is what it said.
--- title: Tool reference url: https://platform.claude.com/docs/en/agents-and-tools/tool-use/tool-reference description: Directory of Anthropic-provided tools and reference for optional tool definition properties. --- This page is a reference for the tools Anthropic provides and the optional properties you can set on any tool definition. For a conceptual introduction to tool use, see [Tool use with Claude](https://platform.claude.com/docs/en/agents-and-tools/tool-use/overview). For guidance on implementing tool use in your application, see [Define tools](https://platform.claude.com/docs/en/agents-and-tools/tool-use/define-tools). ## Anthropic-provided tools Anthropic provides two kinds of tools: **server tools** that execute on Anthropic's infrastructure, and **client tools** where Anthropic defines the schema but your application handles execution. Both kinds appear in your request's `tools` array alongside any user-defined tools. | Tool | `type` | Execution | Status | | -------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------- | --------- | --------------------------------------------------------- | | [Web search tool](https://platform.claude.com/docs/en/agents-and-tools/tool-use/web-search-tool) | `web_search_20260318` `web_search_20260209` `web_search_20250305` | Server | GA | | [Web fetch tool](https://platform.claude.com/docs/en/agents-and-tools/tool-use/web-fetch-tool) | `web_fetch_20260318` `web_fetch_20260309` `web_fetch_20260209` `web_fetch_20250910` | Server | GA | | [Code execution tool](https://platform.claude.com/docs/en/agents-and-tools/tool-use/code-execution-tool) | `code_execution_20260521` `code_execution_20260120` `code_execution_20250825` | Server | GA | | [Advisor tool](https://platform.claude.com/docs/en/agents-and-tools/tool-use/advisor-tool) | `advisor_20260301` | Server | Beta: `advisor-tool-2026-03-01` | | [Tool search tool](https://platform.claude.com/docs/en/agents-and-tools/tool-use/tool-search-tool) | `tool_search_tool_regex_20251119` `tool_search_tool_bm25_20251119` | Server | GA | | [MCP connector](https://platform.claude.com/docs/en/agents-and-tools/mcp-connector) | `mcp_toolset` | Server | Beta: `mcp-client-2025-11-20` | | [Memory tool](https://platform.claude.com/docs/en/agents-and-tools/tool-use/memory-tool) | `memory_20250818` | Client | GA | | [Bash tool](https://platform.claude.com/docs/en/agents-and-tools/tool-use/bash-tool) | `bash_20250124` | Client | GA | | [Text editor tool](https://platform.claude.com/docs/en/agents-and-tools/tool-use/text-editor-tool) | `text_editor_20250728` `text_editor_20250124` | Client | GA | | [Computer use tool](https://platform.claude.com/docs/en/agents-and-tools/tool-use/computer-use-tool) | `computer_20251124` `computer_20250124` | Client | Beta: `computer-use-2025-11-24` `computer-use-2025-01-24` | For model compatibility, see each tool's page. Supported models vary by tool and by tool version. <Note> The tool search `type` values also accept undated aliases: `tool_search_tool_regex` and `tool_search_tool_bm25`. These resolve to the latest dated version. </Note> ### Tool versioning Most Anthropic-provided tools carry a `_YYYYMMDD` suffix in the `type` string. A new version is released when the tool's behavior, schema, or model support changes. Older versions remain available so that existing integrations continue to work. When a tool has multiple active versions, the relationship between them varies: * **Capability-keyed:** `web_search_20260209` and `web_fetch_20260209` add dynamic content filtering over their predecessors; `web_fetch_20260309` adds a cache-bypass option; `web_search_20260318` and `web_fetch_20260318` add response-inclusion control. `code_execution_20260120` adds [programmatic tool calling](https://platform.claude.com/docs/en/agents-and-tools/tool-use/programmatic-tool-calling) from within the sandbox; `code_execution_20260521` discloses the per-cell time limit in the tool description. In each case, both the new and old versions are current; which one you use depends on whether you need the new capability. * **Model-keyed:** `text_editor_20250728` is for Claude 4 and later models and `text_editor_20250124` is for earlier models. The version you use depends on the model you target. * **Variant, not version:** `tool_search_tool_regex_20251119` and `tool_search_tool_bm25_20251119` are two search algorithms released together. Neither supersedes the other. * **Legacy:** `code_execution_20250522` supports only Python. `code_execution_20250825` adds Bash and file operations. The `mcp_toolset` type is not date-versioned; versioning is carried in the `anthropic-beta` header instead. ## Tool definition properties Every tool in the `tools` array, including user-defined tools, accepts optional properties that control how the tool is loaded, who can call it, and how its inputs are validated. These properties compose: you can set `defer_loading` and `cache_control` and `strict` on the same tool. | Property | Purpose | Available on | Detailed guide | | ----------------------- | --------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | | `cache_control` | Set a prompt-cache breakpoint at this tool definition | All tools | [Prompt caching](https://platform.claude.com/docs/en/build-with-claude/prompt-caching) | | `strict` | Guarantee schema validation on tool names and inputs | All tools except `mcp_toolset` | [Strict tool use](https://platform.claude.com/docs/en/agents-and-tools/tool-use/strict-tool-use) | | `defer_loading` | Exclude the tool from the initial system prompt; load it on demand when tool search returns a `tool_reference` for it | All tools (for `mcp_toolset`, see [tool configuration](https://platform.claude.com/docs/en/agents-and-tools/mcp-connector#mcp-toolset-configuration)) | [Tool search tool](https://platform.claude.com/docs/en/agents-and-tools/tool-use/tool-search-tool) | | `allowed_callers` | Restrict which callers can call the tool | All tools except `mcp_toolset` | [Programmatic tool calling](https://platform.claude.com/docs/en/agents-and-tools/tool-use/programmatic-tool-calling#the-allowed-callers-field) | | `input_examples` | Provide example input objects to help Claude understand how to call the tool | User-defined and Anthropic-schema client tools. Not available on server tools. | [Define tools](https://platform.claude.com/docs/en/agents-and-tools/tool-use/define-tools#providing-tool-use-examples) | | `eager_input_streaming` | Enable fine-grained input streaming (`true`) or keep standard buffered streaming (`false`) for this tool | User-defined tools only | [Fine-grained tool streaming](https://platform.claude.com/docs/en/agents-and-tools/tool-use/fine-grained-tool-streaming) | ### `allowed_callers` values `allowed_callers` is an array that accepts any combination of: | Value | Meaning | | --------------------------- | ----------------------------------------------------------------------------------------------------------------- | | `"direct"` | The model can call this tool directly in a `tool_use` block. This is the default if `allowed_callers` is omitted. | | `"code_execution_20260120"` | Code running inside a `code_execution_20260120` or later sandbox can call this tool. | Both `"code_execution_20260120"` and `"code_execution_20260521"` are accepted in `allowed_callers` and are interchangeable: a request using either code-execution tool version satisfies tools that list either caller. Response blocks always tag the caller as `code_execution_20260120` regardless of which version the request declared. Omitting `"direct"` from the array (for example, `"allowed_callers": ["code_execution_20260120"]`) guides Claude to call the tool only from within code execution. The response's `tool_use` block includes a `caller` field that identifies which caller called the tool. See [Programmatic tool calling](https://platform.claude.com/docs/en/agents-and-tools/tool-use/programmatic-tool-calling#the-allowed-callers-field) for the full treatment, including the `caller` response shape and error behavior. ### `defer_loading` and prompt caching Tools with `defer_loading: true` are stripped from the rendered tools section before the cache key is computed. They don't appear in the system-prompt prefix at all. When tool search discovers a deferred tool and returns a `tool_reference` for it, the tool's full definition is expanded inline at that point in the conversation body, not in the prefix. This means `defer_loading: true` preserves your prompt cache. You can add deferred tools to a request without invalidating an existing cache entry, and the cache remains valid across the turn where the tool is discovered and the turn where it's called. For how to combine `defer_loading` with `cache_control` breakpoints, see the [Tool search tool prompt caching guidance](https://platform.claude.com/docs/en/agents-and-tools/tool-use/tool-search-tool#prompt-caching).