Skip to main content
BVDNET
Arnhem · websites & automationBVDNET
Tools & Frameworks

Programmatic tool calling

With programmatic tool calling, an AI model writes a short program that runs many lookups in your systems and hands back only the result, which saves time and cost.

Also known as: PTC, Code Mode

Programmatic tool calling lets an AI model use your software through a short program it writes itself. Normally the model requests each action separately and reads every result, which gets slow and expensive when a task needs many lookups. Here it writes code that runs the lookups, keeps what matters and passes back only the outcome, such as the three invoices that are overdue.

Where does it come from?

Anthropic launched programmatic tool calling for its Claude API in public beta on 24 November 2025, in a post on advanced tool use. Since 17 February 2026 it needs no beta header (release notes). The idea is older than the name. In September 2025 Cloudflare described Code Mode, which turns MCP tools into a TypeScript API that the model writes code against, and in November 2025 Anthropic made the same case in Code execution with MCP.

Illustration: a chef laying out a chart of connected steps on a counter while a small robot waits beside it
The model writes the steps down once as code, and the code calls the tools.

How does it work?

In ordinary function calling the model requests a tool call, your application runs it, and the result goes back into the model's context window. Each call means another pass through the model. Anthropic's documentation describes the programmatic route in five steps:

  1. You add Anthropic's code execution tool to the request and mark your own tools with allowed_callers, so that code may call them.
  2. Claude writes Python that calls those tools as functions, in a loop or with conditions if needed.
  3. The code runs in a sandboxed container. At each tool call it pauses, and the API returns a tool_use block to your application.
  4. You run the tool and send back the result. The result goes to the code, not into Claude's context window.
  5. When the code finishes, Claude receives only its output and continues.

Your application still runs every tool. What disappears is a model pass between the calls: "The model is not re-sampled between the tool calls inside one code execution." It is opt-in per tool, and ordinary tool calls keep working next to it.

What does it save?

Anthropic reports its own internal measurements. At the launch in November 2025, average token use on complex research tasks fell from 43,588 to 27,297, a 37% reduction. In September 2026 the documentation adds two results. On a benchmark with a 75-tool project-management agent, billed input tokens fell by roughly 38% with unchanged accuracy. On τ²-bench, where each turn makes one or two sequential tool calls, scores stayed the same and the cost rose by about 8%.

Where does it go wrong?

  • Step-by-step work. If every call depends on the model thinking about the previous result, the script cannot skip the model. With a few calls and small results, starting the container and writing the script can cost more than it saves.
  • Restrictions. In September 2026 tools from Anthropic's MCP connector cannot be called from code, and neither can tools with strict: true. If your application takes longer than about 4 minutes to return a result, the waiting code gets a timeout error.
  • Untrusted output. Tool results reach the code as strings, and the documentation warns of code injection when a result from an external source is interpreted as code.

Example

Anthropic's launch post asks: which team members exceeded their Q3 travel budget? With ordinary tool calls, the expenses of 20 people arrive as 20 tool results with more than 2,000 line items, all in Claude's context. With programmatic tool calling Claude writes a script like this one (shortened from the post):

Python
1team = await get_team_members("engineering")
2levels = list(set(m["level"] for m in team))
3budget_results = await asyncio.gather(*[get_budget_by_level(level) for level in levels])
4budgets = dict(zip(levels, budget_results))
5expenses = await asyncio.gather(*[get_expenses(m["id"], "Q3") for m in team])
6
7exceeded = []
8for member, items in zip(team, expenses):
9 total = sum(e["amount"] for e in items)
10 limit = budgets[member["level"]]["travel_limit"]
11 if total > limit:
12 exceeded.append({"name": member["name"], "spent": total, "limit": limit})
13
14print(json.dumps(exceeded))

Claude sees only the printed list of the two or three people over budget. Anthropic puts the difference at 200 KB of raw expense data against 1 KB of results.