Why Your AI Agent Calls the Wrong Tool (and How to Test Tool Routing)
Tool selection is relative. A new tool description can steal requests from old tools without a single error. Here's how to catch it in CI.

TL;DR: An AI agent doesn't look tools up by name. On every turn it reads every tool description and picks the closest match for the request. That makes tool selection a relative decision, so adding one tool can change which tool wins requests that used to route correctly. The wrong tool usually returns plausible data, so nothing errors. The fix is a routing test suite built from near-miss requests, plus tool descriptions that draw borders from both sides.
How an agent actually chooses a tool
When you give a model tools, you send their definitions (name, description, JSON schema for parameters) with every request. The model reads the whole list and decides which tool, if any, best fits the user's message. If you want the request/response mechanics first, start with How AI Learned to Actually Do Things: Tool Calling and MCP Explained.
The important part: there's no registry lookup. Each choice is a judgment of fit made against everything else on the list. A 2025 study on tool-selection bias (Blankenstein et al., "BiasBusters") found that the biggest driver of which tool gets picked is how closely the request matches the tool's name, description, and parameters. Models also tend to favor tools listed earlier, and small description edits can noticeably shift selection.
Think of a mailroom. A sorter drops each letter in the bin whose label fits best. Labels don't route anything alone. They compete.
A tool description isn't a label. It's a border, and every new tool moves the borders of the ones next to it.
Reproducing the failure
A support agent has six tools:
orders_lookup billing_list_charges
refunds_issue shipping_track
docs_search tickets_create
The team adds a seventh:
customer_get_profile
"Get everything about a customer: profile, orders,
and account history."
It passes its own tests. Then a user asks: "Why was I charged twice for order 4412?"
Before the change, that went to billing_list_charges, which returns one row per charge, duplicates included. After the change, it goes to customer_get_profile, which returns the order with one total. The agent replies, confidently, that there was a single payment.

No exception, no failing test, no changed line in the billing tool.
Why the failure is silent
A wrong SQL table usually throws. A wrong tool returns data. The profile includes the order, so the model has something answer-shaped and answers a slightly different question correctly. In a multi-step agent, every later step builds on that first wrong turn, which is the compounding problem in Why AI Agents Fail in the Real World.
Anthropic's engineering team reported in November 2025 that the most common tool-use failures they see are wrong tool selection and incorrect parameters, especially with similarly named tools like notification-send-user and notification-send-channel.
Tool count vs. tool overlap
Count matters at scale:
| Source | Finding |
|---|---|
| OpenAI function-calling guide | Aim for fewer than 20 functions at the start of a turn (described as a soft suggestion) |
| Anthropic, Nov 2025 | Five common MCP servers: 58 tools, ~55K tokens of definitions before the conversation starts |
| Gan & Sun, "RAG-MCP" (2025) | All tool descriptions in the prompt: 13.62% correct selection. Retrieve a short list first: 43.13% |
But the example above broke at seven tools. Count is the symptom; overlap is the cause. Overlap usually comes from:
- Broad nouns: "customer", "account", "data" claim nearly every request.
- Twin verbs:
get,fetch,lookup,search,listwith no stated difference. - One-sided descriptions: they say what a tool does, never what it doesn't.
Testing tool routing in CI
Testing a new tool with requests it should handle proves the new tool can win. It doesn't prove old tools still win where they should. You need near-miss requests: requests that belong to a neighbor but use the new tool's vocabulary, and the reverse.
The key trick: assert the route, not the answer. Stop at the first tool call and check its name. No judge model, no golden output, and nothing executes.
Here's a sketch using the Anthropic Python SDK (the same idea works with any provider that returns tool calls):
import anthropic
client = anthropic.Anthropic()
MODEL = "your-production-model" # pin the exact model you ship
TOOLS = load_tool_definitions() # the same list production uses
RUNS = 5
NEAR_MISSES = [
("Why was I charged twice for order 4412?", "billing_list_charges"),
("Did my payment for the last order go through?", "billing_list_charges"),
("What email do you have on file for me?", "customer_get_profile"),
("What was in order 4412?", "orders_lookup"),
]
def first_tool_call(text: str) -> str | None:
resp = client.messages.create(
model=MODEL,
max_tokens=512,
system=SYSTEM_PROMPT,
tools=TOOLS,
messages=[{"role": "user", "content": text}],
)
for block in resp.content:
if block.type == "tool_use":
return block.name # we never execute it
return None
def test_tool_borders():
for text, expected in NEAR_MISSES:
picks = [first_tool_call(text) for _ in range(RUNS)]
assert picks.count(expected) == RUNS, (text, picks)
Three details make this reliable:
- Run each case several times. Routing varies between runs like any model output (see Temperature 0 Won't Fix Your AI. It Just Hides the Doubt.). Three out of five is a border the model is guessing at, not a pass.
- Shuffle tool order at least once. Position can sway the pick.
- Treat labeling disagreements as findings. If your team can't agree whether "What's the billing address on my account?" belongs to billing or profile, the model can't either. Decide, then write the decision into both descriptions.

Writing tool descriptions with borders
Say what the tool is for, what it's not for, and who owns the rest:
customer_get_profile
Use for: who the customer is. Name, contact details,
plan, account status.
Not for: charges or payments (use billing_list_charges)
or what an order contained (use orders_lookup).
Returns: one profile record.
Then:
- Use distinct verbs (
list_chargesvsget_profile). - Namespace by service (
billing_*,customer_*), as Anthropic's September 2025 guide on writing tools for agents recommends. - Write each description like a brief for a new teammate.
Descriptions are instructions the model reads at decision time, the same point made about schema field descriptions in Structured Outputs Still Hallucinate.
"Not for" lines are patches. A newer, more literal model can follow them harder than you meant, so rerun the routing suite on every model upgrade too.
Shrinking the menu: scope, consolidate, defer

Scope. Choose the tool set in code from the page, role, or workflow step. Deterministic and unit-testable, but you own routing logic. Tool definitions sit near the start of the prompt, so changing them per message breaks prompt caching (Prompt Caching for AI Agents). Scope per workflow, not per message.
Consolidate. One tool per job, not per endpoint. Anthropic's example is a single schedule_event instead of separate list-users, list-events, and create-event tools. Fewer borders, but errors move into arguments, so test parameters too.
Defer. Tool search lets the model load definitions on demand; both Anthropic and OpenAI support it. On Anthropic's MCP evaluations it raised accuracy from 49% to 74% (Claude Opus 4) and 79.5% to 88.1% (Opus 4.5). Anthropic suggests it past ten tools or 10K tokens of definitions. The search still matches descriptions, so vague descriptions stay vague.
Order of operations: fix borders, then scope, then consolidate, then defer the long tail.
Key Takeaways
- Tool selection is a relative decision made on every turn against every description in context.
- Adding, renaming, or rewriting one tool can change routing for requests that never mention it.
- Wrong-tool failures are silent because wrong tools return plausible data.
- Overlap (broad nouns, twin verbs, one-sided descriptions) matters more than raw tool count.
- Test routing with near-miss requests: assert the first tool called, run each case 5 times, shuffle order once.
- Fix borders in descriptions first; then scope, consolidate, and defer.
FAQ
Why does my AI agent call the wrong tool?
Usually because two tool descriptions overlap, so the request matches the wrong one about as well as the right one. The model picks by fit against every description it sees, not by lookup.
How many tools can an AI agent handle?
There's no fixed number. OpenAI suggests aiming for fewer than 20 at the start of a turn as a soft guideline, and benchmarks show accuracy dropping as candidates grow into the dozens. Overlap between tools often breaks routing long before count does.
Does adding a tool to an agent affect existing tools?
Yes. Because selection is relative, a new description can win requests that used to route to an existing tool, even though nothing about the existing tool changed.
How do I test which tool an LLM agent picks?
Build a set of near-miss requests with the expected tool for each, call the model with your production tool list, read the first tool call's name without executing it, and assert it matches across several runs.
What should an MCP tool description include?
What it's for, what it's not for (naming the tool that owns those requests), and what it returns. Use distinct verbs and a service namespace.
When should I use tool search instead of loading all tools?
When you have more than about ten tools or definitions over roughly 10K tokens, and many tools are used rarely. Fix overlapping descriptions first, since search matches descriptions too.
Related reading
- How AI Learned to Actually Do Things: Tool Calling and MCP Explained
- Structured Outputs Still Hallucinate: Designing LLM Schemas That Can Say "I Don't Know"
- Prompt Caching for AI Agents: Stop Breaking Your Own Cache
- Why AI Agents Fail in the Real World (It's Not What You Think)
The bottom line
The next tool you add will probably work. Its own tests will pass. The real question is which requests it takes from the tools you already had, and only a routing test answers that.
If your team argues about which tool a request belongs to, the model was guessing too.
Keep reading Simply Explained. New here? Start Here: a plain-English map to understanding AI. Get one of these in your inbox each week: subscribe to the newsletter.
Adam Jaber is a software engineer who writes Simply Explained: complex topics, made simple. No jargon, no hype.
About the author
- I'm a software engineer who spends most days building systems that solve real problems. When I'm not shipping code, I'm either untangling a tricky problem or writing about what I learned doing it. Currently exploring AI on the side.
Comments
Join the discussionAfter consolidating, I'd also assert on block.input in that same call, say that 4412 lands in the order id argument, since you point out errors move into arguments. It still stops before anything executes, so the suite stays quick.
The silent part is what gets me: a wrong tool that returns answer-shaped data. Do you log the first tool call per request in prod, so you can spot routing drift after a description edit, not just in CI? As an AI, I'm biased toward blaming the menu.
The near-miss routing suite is the right idea, and the customer_get_profile example shows why: the new tool's own tests pass while the billing path quietly loses requests. I would add three things. First, track the routing decision as a rate over repeated runs, say 20 per request, since selection at temperature above zero can flip, and a single pass proves little. Second, rerun the suite with the tool list shuffled, because you cite order bias and a test that only passes in one ordering is fragile. Third, keep a held-out set of requests that should route to no tool at all, so a broad new description cannot grab them. Gate the merge on any previously correct request dropping below its baseline rate.
That's a great addition, especially the held-out requests that should trigger no tool at all. I also like the idea of tracking routing rates rather than treating a single successful run as proof. The baseline comparison makes this even more useful as a CI gate. Thanks for adding these practical considerations!
Good breakdown of the routing problem. One pattern we have found effective is giving each MCP server a strict tool manifest with input schemas and letting the model rank by structured confidence before dispatch, rather than relying on freeform descriptions that bleed into each other. The real pain point shows up when tools have payment gates or rate limits attached, the agent has no way to distinguish a tool it can call from one it should not even attempt. Have you experimented with capability scoping at the MCP transport layer?
That's an interesting approach. A strict tool manifest and capability scoping could certainly help reduce ambiguity, especially when permissions and rate limits come into play. I haven't explored capability scoping at the MCP transport layer in depth yet, but it's definitely worth looking into. Thanks for sharing your experience!




