You gave your agent a hundred tools and it got worse. That isn't a paradox — it's the documented failure mode. Past 30–50 available tools, Claude's ability to pick the right one degrades, and a typical multi-server setup — GitHub, Slack, Sentry, Grafana, Splunk — spends about 55,000 tokens on tool definitions before the model does any work. The fix is tool search: mark the long tail defer_loading: true, let the model search for what it needs, and cut those definition tokens by over 85% while the full library stays reachable.
But when you turn it on, you have to pick a variant — and the docs give you two that look interchangeable. They aren't. Here's the decision, up front.
The one-line answer: Both variants search the same four fields — tool name, description, argument names, argument descriptions. So the choice isn't about what gets searched. **Usetool_search_tool_regexwhen your tool names carry the taxonomy (consistentgithub_,stripe_charge_namespacing). Usetool_search_tool_bm25when your descriptions do** (discover by intent). Regex fails loud; BM25 fails silent.
The two variants, precisely#
Tool search ships in two generally-available forms, both keyed _20251119:
tool_search_tool_regex_20251119— Claude writes Pythonre.search()patterns, case-insensitive, max 200 characters. Thinkweather,get_._data, ordatabase.query|query.*database.tool_search_tool_bm25_20251119— Claude writes natural-language queries, max 500 characters. Think "send a message to a Slack channel."
The line everyone skims past is this one, straight from the docs: "Both tool search variants (regex and bm25) search tool names, descriptions, argument names, and argument descriptions." Same corpus, same fields. The only thing that differs is how Claude formulates the query — and that maps directly onto how your catalog stores meaning.
The decision rule: where does your taxonomy live?#
Regex — when your names carry the taxonomy
Regex is a structural matcher. It shines when your tools follow a naming convention, because a pattern is a scalpel: github_. sweeps every GitHub tool and nothing else, deterministically. The docs make this the headline optimization tip — "Use consistent namespacing in tool names: prefix by service or resource (for example, github_, slack_) so one search matches the whole group."*
If you own your tool catalog and name it cleanly, regex gives you something BM25 can't: predictability. You can read a pattern and know precisely what it will and won't return. The cost is brittleness — inconsistent names across servers you don't control, and the pattern quietly stops sweeping the group you thought it would.
BM25 — when your descriptions carry the taxonomy
BM25 is an intent matcher. It wins when the natural way to find a tool is by what it does, not what it's called — and when your descriptions are richer than your names. A query like "refund a customer's last charge" finds the payments tool even if it's named stripe_txn_reverse_v2, because BM25 ranks lexical relevance across the description text, tolerant of the vocabulary mismatch between how a task is phrased and how a tool is named.
That tolerance is the whole point when you're aggregating MCP servers you didn't design — 200 tools from a dozen vendors with a dozen naming philosophies. You can't impose github_-style discipline on them, but you can lean on their descriptions. This is the same just-in-time-retrieval instinct behind measuring your MCP tools' context cost before it eats the window.
The asymmetry that should tip close calls: loud vs silent failure#
When two options are close, decide on the failure mode — and here they're opposites.
A bad regex fails loud. A malformed pattern or one over the 200-character limit returns a tool_search_tool_result_error with error_code: "invalid_tool_input" — you see it, you fix it. A bad BM25 query fails silent. It returns its top matches (up to 5 by default) ranked by relevance; if the phrasing was off, you get the wrong five with no error at all. That's the recall ceiling of any retrieval system: a "wrong tool" is recoverable because the model can retry, but an "invisible tool" is a silent miss you only catch by measuring retrieval recall.
If you can't yet instrument tool-retrieval recall, that asymmetry argues for regex on the tools you must not miss, and BM25 for the broad, forgiving long tail.
The setup rules are identical — don't trip on these#
Whichever matcher you choose, the mechanics are the same, and three of them bite first-timers:
- You still send every tool definition on every request — including the deferred ones.
defer_loadingcontrols what enters the context window, not what's in the request body; the API needs the full definitions server-side to run the search and expand matches. - Never defer the search tool itself, and keep at least one tool non-deferred. All-deferred returns a 400: "At least one tool must have defer_loading=false." Keep your 3–5 most-used tools non-deferred so the model can call them without a search round-trip.
- For MCP-connector tools, you don't set
defer_loadingper tool. Set it once on themcp_toolsetentry'sdefault_configfor the whole server (or per tool in itsconfigs).
One more, if you cache: a tool with defer_loading: true can't also carry cache_control (that's a 400). Deferring already preserves your prompt cache — deferred tools are excluded from the system-prompt prefix, and discovered ones are appended inline as tool_reference blocks — so put the cache breakpoint on a non-deferred tool and let the two features compose.
So which do I pick?#
Start with one question: read a task your agent handles, then ask whether you'd find the right tool faster by its name or by its description. If the answer is "the name — they're all prefixed by service," use regex and write patterns like service_.. If it's "the description — the names are cryptic or inconsistent," use BM25 and lean on rich, keyword-dense descriptions. Reach for tool search at all once you cross 10 tools, ~10k tokens of definitions, or 200+ aggregated MCP tools — below that, plain tool calling is simpler and just as accurate. And if your tools chain and pass large payloads*, neither matcher is the endgame; that's when code execution keeps the intermediate data out of context entirely.



