# Why Your AI Agent Calls the Wrong Tool (and How to Test Tool Routing)

**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](https://simplyexplained.hashnode.dev/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:

```text
orders_lookup          billing_list_charges
refunds_issue          shipping_track
docs_search            tickets_create
```

The team adds a seventh:

```text
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.

![Before and after: the same request routes to billing_list_charges with six tools, then to customer_get_profile once the seventh tool is added.](https://cdn.hashnode.com/uploads/covers/6a44b5d24b41ab0145e5cf63/7653c65b-b040-4c42-aa16-9796f50ebb2e.png)

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](https://simplyexplained.hashnode.dev/why-ai-agents-fail).

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`, `list` with 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):

```python
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:

1. **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.](https://simplyexplained.hashnode.dev/temperature-zero-hides-doubt)). Three out of five is a border the model is guessing at, not a pass.
2. **Shuffle tool order at least once.** Position can sway the pick.
3. **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.

![The Border Test: list the neighbors, write near-miss requests, check the route not the answer, fix the border not the model, keep the suite forever.](https://cdn.hashnode.com/uploads/covers/6a44b5d24b41ab0145e5cf63/3e2d1ca7-1051-44dd-812c-078e9c3e6378.png)

## Writing tool descriptions with borders

Say what the tool is for, what it's not for, and who owns the rest:

```text
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_charges` vs `get_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](https://simplyexplained.hashnode.dev/structured-outputs-still-hallucinate-designing-llm-schemas-that-can-say-i-don-t-know).

"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

![Three levers: scope (cost: routing logic you own), consolidate (cost: mistakes move into arguments), defer (cost: the search is one more route).](https://cdn.hashnode.com/uploads/covers/6a44b5d24b41ab0145e5cf63/d6687978-d177-49cb-8d18-6efbfbb879ad.png)

**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](https://simplyexplained.hashnode.dev/trimming-prompt-raises-ai-bill)). 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](https://simplyexplained.hashnode.dev/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"](https://simplyexplained.hashnode.dev/structured-outputs-still-hallucinate-designing-llm-schemas-that-can-say-i-don-t-know)
- [Prompt Caching for AI Agents: Stop Breaking Your Own Cache](https://simplyexplained.hashnode.dev/trimming-prompt-raises-ai-bill)
- [Why AI Agents Fail in the Real World (It's Not What You Think)](https://simplyexplained.hashnode.dev/why-ai-agents-fail)

## 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](https://simplyexplained.hashnode.dev/start-here-understand-ai). Get one of these in your inbox each week: [subscribe to the newsletter](https://simplyexplainedbyaj.substack.com/subscribe).

*Adam Jaber is a software engineer who writes Simply Explained: complex topics, made simple. No jargon, no hype.*
