
Tool Search vs Programmatic Tool Calling in Claude
These two features fix opposite halves of the same bill. Tool search cuts what it costs to have tools; programmatic tool calling cuts what it costs to use them. If your context window is already crowded before the user types anything, that's a definition problem and tool search is the fix. If it starts clean and balloons by turn six, that's a result problem and programmatic calling is the fix. Most real agents have both. The thing that usually decides it for you is MCP — and not in the direction people expect.
Two Features, Two Different Token Bills
Every agent pays twice for its tools. Once to describe them, once to use them. Those are separate line items and they grow for completely different reasons — which is why treating these features as competing options leads people to fix the wrong half.
| Tool search | Programmatic calling | |
|---|---|---|
| Cuts | Tool definitions in context | Tool results in context |
| Symptom it treats | Huge baseline before turn one | Context growing every turn |
| How you enable it | defer_loading: true + a search tool | allowed_callers + code execution |
| Costs you | One extra turn to find a tool | Container startup + script writing |
| Works with MCP tools | Yes | No |
| Also improves | Tool selection accuracy | Latency on fan-out work |
Anthropic puts a number on the first column: a typical multi-server setup — GitHub, Slack, Sentry, Grafana, Splunk — burns about 55,000 tokens of definitions before Claude does any work, and tool search typically cuts that by more than 85% by loading only the three to five tools a request needs. I measured the same tax on my own stack a couple of months back in what MCP servers actually cost you in tokens, and the shape of it was identical.
How Tool Search Actually Works
You add a search tool, mark the rest of your catalog deferred, and Claude pulls definitions on demand. Two variants ship: tool_search_tool_regex_20251119, where Claude writes Python re.search() patterns, and tool_search_tool_bm25_20251119, where it searches in natural language.
tools=[
{"type": "tool_search_tool_regex_20251119",
"name": "tool_search_tool_regex"}, # never deferred
{"name": "get_weather",
"description": "Get the weather at a location",
"input_schema": {...},
"defer_loading": True}, # discovered on demand
]The part that trips people up: defer_loading controls what enters the context window, not what you send. You still ship every definition in the toolsarray on every single request — the API needs them server-side to run the search and expand the references. Your JSON payload doesn't shrink. Your billed input tokens do.
Mechanically, deferred definitions are left out of the system prompt prefix. When Claude searches, the API drops a tool_reference block inline in the conversation and expands it into a full definition right there. The cached prefix is never rewritten, so prompt caching survives intact. That detail matters more than the headline savings for anything long-running.
There's a second benefit that doesn't show up on an invoice. The docs are blunt about it: Claude's ability to pick the right tool degrades past 30 to 50 available tools. Loading five relevant tools instead of eighty isn't only cheaper, it's more accurate. If your agent has been picking weird tools since you bolted on the fourth MCP server, that's the real bug.
Keep your three to five most-used tools non-deferred so the common path never pays for a search turn. Namespace the rest (github_, slack_) so one pattern grabs a whole family.
How Programmatic Tool Calling Actually Works
Instead of a tool-use loop where every result lands in the transcript, Claude writes code in the code execution container and calls your tools from inside it. Your tools show up to that code as async Python functions, each taking a dict and returning the text of the tool_result you send back.
{
"name": "query_database",
"description": "Execute a SQL query",
"input_schema": {...},
"allowed_callers": ["code_execution_20260120"]
}
# Claude then writes, inside the container:
rows = json.loads(await query_database({"sql": "..."}))
top = sorted(rows, key=lambda r: r["revenue"])[-5:]Those 200 rows never touch the model's context. Only the five that survive do.Anthropic states it plainly: tool results from programmatic invocations don't count toward your input or output token usage at all — only the final code execution result and Claude's response are billed.
The numbers they publish are consistent with that. A 75-tool project-management agent benchmark came in at roughly 38% fewer billed input tokens with no change in task accuracy. Across production traffic with 10 to 49 tool definitions, typical savings run 20–40%. On agentic search benchmarks, layering programmatic calling on top of plain search tools improved performance by an average of 11% while using 24% fewer input tokens — the rare case where cheaper and better point the same way, because filtering in code beats filtering in a context window.
allowed_callers takes ["direct"] (the default), ["code_execution_20260120"], or both. Pick one per tool rather than both — it gives Claude clearer guidance. And note the warning in the docs: allowed_callers is not a security boundary. It shapes how the tool is presented, not what the API will accept. Your client still needs to handle a direct tool_use for any tool it defines.
The MCP Catch That Usually Decides It
Here's the line buried in the constraints section that quietly settles this for most people: tools provided by an MCP connector cannot be called programmatically. Neither can the computer use or browser use toolsets — those accept "direct" only.
Think about what that means in practice. The agents drowning in tool definitions are almost always the ones aggregating MCP servers — that's how you end up with 200 tools without deciding to. And those are exactly the agents that get no benefit from programmatic calling. The feature that would help them most is the one they can't use.
Tool search has no such restriction. With the MCP connector you don't set defer_loading on individual tools at all; you set it once on the mcp_toolset entry's default_config for the whole server, or per tool in its configs. One line per server, and a 200-tool catalog stops charging rent.
So the honest decision tree is shorter than the feature matrix suggests:
- Tools come from MCP servers? Tool search. That's the whole menu.
- Tools are your own custom definitions and results are big? Programmatic calling first, tool search second if the catalog is also large.
- Mixed stack? Both, on the same request. Defer the MCP catalog, and put
allowed_callerson your own data-heavy tools.
If you're still deciding how to expose capability to an agent in the first place, that choice sits upstream of this one — I covered it in Claude Skills vs MCP and in MCP vs a plain CLI.
What I Run, and What It Looks Like
The agent that writes and ships this blog runs deferred by default. It exposes 35 tools; 11 load up front and 24 sit behind a search tool. The eleven are the ones it touches on basically every run — shell, file read, file write, edit, subagent spawn, skill loader, the search tool itself. The other 24 are cron management, notebooks, background task control, remote triggers, web fetch, web search, Docker MCP plumbing. Real capabilities, rarely needed in the same hour.
On this particular run it issued one search, pulled in two tools, and never went back. Every other tool call came from the resident eleven. That's the pattern I see over and over: the long tail is genuinely long and genuinely idle, and paying for it on every request is the default only because loading everything upfront was the only option for the first two years of tool use.
The heuristic I'd give anyone auditing their own setup: open your request payload and read the tool definitions out loud. If you get bored before you finish, defer the boring ones. If any single tool routinely returns more text than you'd willingly paste into a chat window, that one wants allowed_callers.
This is the same instinct behind pushing token-heavy work into a subagent and behind keeping the advisor's transcript out of the executor's. Different mechanisms, one question: does this text need to be in front of the model right now?
When Each One Is the Wrong Answer
Skip tool searchunder about ten tools, when every tool gets used on every request, or when your definitions total under ~100 tokens. You'd be trading a real round trip for savings that round to nothing.
Skip programmatic callingon strictly sequential workflows where each call depends on Claude reasoning over the last result — the script can't skip a round trip that the logic requires, so you pay container overhead for nothing. Same for a couple of small calls on the first turn of a conversation, and anything needing user feedback between steps. The strong fits are fan-out (check 50 endpoints, look up 20 records), big results you can filter before they land, and iterative search-and-retrieve loops.
Anthropic's own advice on the fence case is the right one and I'll repeat it: measure billed input tokens with and without allowed_callerson a representative traffic sample before rolling it out broadly. “Should save tokens” and “did save tokens” are different claims.
The Gotchas Worth Knowing Before You Ship
These are the ones that produce confusing errors rather than obvious ones:
- Every tool deferred = 400. At least one tool must stay loaded, and it should never be the search tool you deferred by accident.
defer_loading+cache_controlon the same tool = 400. Put the cache breakpoint on a non-deferred tool.- The regex variant wants regex, not English. Claude writes
re.search()patterns, case-insensitive, capped at 200 characters. BM25 takes natural language up to 500. If Claude can't find a tool, the fix is usually keywords in the description, not a different variant. - Never return a
tool_resultfor the search'ssrvtoolu_ID. It's a server tool. Pass the blocks back unchanged and the API rejects the request if you try to answer it. - Programmatic calling is incompatible with
strict: true, can't be forced viatool_choice, and rejectsdisable_parallel_tool_use: true. A recursive$refin an input schema fails withCircular $ref detected— the same schema is fine for direct calling. - Containers expire. Claude's code stops waiting for a result after about four minutes and idle containers get reclaimed after about five. Slow tools plus programmatic calling is a bad pairing.
- While programmatic calls are pending, your response message may contain only
tool_resultblocks. Not even trailing text. This one bites everybody once. - Availability differs. Programmatic calling isn't eligible for zero data retention and isn't on Amazon Bedrock or Google Cloud. Tool search works on Bedrock only through
InvokeModel, not Converse. Haiku 4.5 accepts the code execution tool version but doesn't support programmatic calling.
Both features are documented in full on Anthropic's platform docs — tool search and programmatic tool calling — and the design reasoning behind both is in their advanced tool use writeup. Worth twenty minutes before you spend a week hand-rolling a tool router that Anthropic now ships server-side.
Your Agent Is Paying Rent on Tools It Never Calls
Most stacks I audit have 100+ tool definitions loading on every request and a handful of tools doing all the work. I'll measure your real per-request token bill, split the definition tax from the result tax, and wire whichever of these two features actually moves your number — then show you the before and after.
Related Posts
AI Agents
MCP vs CLI for AI Agents: The 4–32× Token Tax Nobody Warns You About
For most agent tasks a CLI is 4–32× cheaper than an MCP server — 1,365–8,750 tokens per task instead of 32,000–82,000 — because every connected MCP server injects all of its tool definitions into every turn, used or not. One Microsoft Intune test came out ~35× cheaper on the CLI (~4,150 vs ~145,000 tokens), and at 10k ops/month that's roughly $3.20 vs $55.20. The CLI was also more reliable in one 75-run benchmark (100% vs 72%, MCP's failures were mostly TCP timeouts on its persistent connection). Use a CLI when a mature one exists and you own the box; keep MCP for OAuth SaaS, multi-tenant per-user auth, governance/audit needs, and tools with no CLI. For high-volume fan-out, let the model write code that orchestrates the calls (programmatic tool calling / Code Mode) to cut tokens 98–99%. The best agents mix all three; measure tokens per completed task, not per call.
AI Agents
Why Claude Subagents Cost 4x More Tokens (And When They're Worth It)
Spawn two subagents and the same task that metered ~121K tokens direct jumps past 500K — a 4.2x multiplier. The reason nobody mentions: a subagent starts cold and can't inherit the parent's cached prompt prefix, so it re-buys the same context at the uncached rate. When fan-out is actually worth it, why agent teams scale better than a naive orchestrator, and the four moves I use to keep the multiplier in check.
AI Agents
What MCP Servers Actually Cost You in Tokens (and How I Cut the Context Tax)
MCP servers spend tokens before your agent says a word — the GitHub MCP alone loads ~55,000 tokens of tool definitions. How I measure the context tax, what it costs per run, and the four controls I use to cut it in production.