# Programmatic tool calling (`allowed_callers`, `caller`) **Status:** DOCUMENTED · LIVE_VERIFIED 2026-09-18 (g1 pause + g2 resume on claude-sonnet-4-6 ≈ $0.021; g3 haiku 400; g4 `tool_choice` 400; k28 `strict` 400). GA since 2026-02-17 (beta 2025-11-24 with `advanced-tool-use-2025-11-20`). **Sources:** [Programmatic tool calling](https://platform.claude.com/docs/en/agents-and-tools/tool-use/programmatic-tool-calling) · [Tool reference — allowed_callers](https://platform.claude.com/docs/en/agents-and-tools/tool-use/tool-reference#allowed-callers-values) · [Server tools — mixed turns](https://platform.claude.com/docs/en/agents-and-tools/tool-use/server-tools) · [Code execution](https://platform.claude.com/docs/en/agents-and-tools/tool-use/code-execution-tool). **Last verified:** 2026-09-18. ## Setup ```json "tools": [ {"type": "code_execution_20260120", "name": "code_execution"}, // or code_execution_20260521 (interchangeable for callers) {"name": "query_database", "description": "Execute a SQL query. Returns a JSON array of row objects.", "input_schema": {"type":"object","properties":{"sql":{"type":"string"}},"required":["sql"]}, "allowed_callers": ["code_execution_20260120"]} // ["direct"] default | ["direct","code_execution_20260120"] both ] ``` Inside the sandbox each such tool is an **async Python function** taking one dict and returning the `tool_result` string: `rows = json.loads(await query_database({"sql": "…"}))`; parallel via `asyncio.gather`. Describe the output format precisely in `description`. Models: Fable 5.1, Mythos 5.1, Fable 5, Mythos 5, Opus 5, 4.8, 4.7, 4.6, 4.5, Sonnet 5, 4.6, 4.5. **Not Haiku 4.5** — live g3: `400 'claude-haiku-4-5-20251001' does not support programmatic tool calling. The following tools have allowed_callers that require it: query_database. Explicitly set allowed_callers=["direct"]…` (same message for `web_search_20260209`). Platforms: Claude API, Claude Platform on AWS, Foundry Hosted-on-Anthropic. Not ZDR. ## Flow (live g1 → g2) 1. Request as above (sonnet 4.6, prompt "Use code to call query_database with sql 'SELECT 1 AS one'…"). 2. Response `stop_reason: tool_use`, top-level `container: {id, expires_at}`, content: ```json {"type":"server_tool_use","id":"srvtoolu_01XRkMSA5mTTPpoGGMHeLMyb","name":"code_execution","input":{"code":"\nimport json\nresult = await query_database({\"sql\": \"SELECT 1 AS one\"})\nparsed = json.loads(result)\nprint(parsed)\n"}} {"type":"tool_use","id":"toolu_013BmH2TKXHokC4S8i6XKJ5V","name":"query_database","input":{"sql":"SELECT 1 AS one"},"caller":{"type":"code_execution_20260120","tool_id":"srvtoolu_01XRkMSA5mTTPpoGGMHeLMyb"}} ``` `caller.tool_id` = the cell that made the call. Direct calls carry `caller: {"type":"direct"}`. The caller type is always tagged `code_execution_20260120` even if you declared `_20260521`. 3. Reply: same `tools`, top-level `"container": ""` (**required**, 400 otherwise), user message containing **only** `tool_result` blocks (string or `text` content; no image/document, no trailing text): ```json {"role":"user","content":[{"type":"tool_result","tool_use_id":"toolu_013BmH2…","content":"[{\"one\": 1}]"}]} ``` 4. The cell resumes; either another pause (repeat step 3 for every pending programmatic call in one message) or completion: ```json {"type":"code_execution_tool_result","tool_use_id":"srvtoolu_01XRk…","content":{"type":"code_execution_result","stdout":"[{'one': 1}]\n","stderr":"","return_code":0,"content":[],"abort_reason":null}} {"type":"text","text":"The value of `one` is **1**."} ``` Timeouts: your result must arrive within ≈4 minutes (`TimeoutError: Calling tool ['query_database'] timed out (no response after 270s)` in stderr, Claude usually retries); idle containers reclaimed after ≈5 min; watch `expires_at`. Each REPL cell has a 90 s wall-clock limit (disclosed by `_20260521`). ## Restrictions (live 400 messages) | Case | Message | |---|---| | `tool_choice` naming a code-only tool (g4) | `tool_choice.name 'query_database' cannot be used because this tool only allows calls from ['code_execution_20260120']. Tools specified in tool_choice must allow 'direct' calls from the model.` | | `strict: true` on such a tool (k28) | `tools.1.custom: Tools with strict=true cannot have code_execution callers in allowed_callers.` | | model without PTC (g3, c2) | see above | | recursive `$ref` schema (docs) | `Circular $ref detected` | Also: `disable_parallel_tool_use: true` unsupported; MCP tools and the computer/browser toolsets cannot be called programmatically (toolsets accept only `["direct"]`); `allowed_callers` is guidance, not a hard block — still handle a direct `tool_use` for every tool; validate tool results (code may `exec`/parse them). ## Token economics Programmatic tool results never enter Claude's context (not billed as input); only the final cell output and text count. Anthropic reports ≈38 % fewer billed input tokens on a 75-tool agent benchmark, 20–40 % typical savings for 10–49 tool definitions, ≈8 % *more* cost on strictly sequential single-call workflows (τ²-bench). Pricing = code execution pricing (free with `web_*_20260209+`). Live: g1 3,147 + g2 3,262 input tokens. Examples: `examples/anthropic/tools/programmatic-tool-calling/basic.{sh,py,ts}` (sh performs both steps); test `test_programmatic_tool_calling_caller` (expensive).