Runtype
GuidesGuide

How many tools is too many, and what to do when you pass the limit

Why tool selection degrades past twenty tools, and how to fix it: measuring accuracy, merging duplicates, namespacing, tool search and per-task pools.

Last updated 6 min read

There is no hard number, but selection accuracy starts sliding somewhere between fifteen and thirty tools, and most agents that feel unreliable are carrying tools they never call. Treat the tool surface as a budget: measure which calls go wrong, merge the near-duplicates, and give each task a small pool instead of the whole catalogue.

My AI agent has too many tools and picks the wrong one

The symptom is specific. A task that passed last month starts failing after someone shipped an unrelated tool, and the trace shows the model calling search_documents when the request plainly wanted search_tickets. Nothing about the failing task changed, which is why the prompt and the model version both come back clean.

Two things are happening at once. The catalogue is consuming context that used to hold the conversation, and the descriptions the model reads to choose between tools have grown similar enough that the request no longer separates them. Both get worse with every tool added, so an agent can pass its own tests the week it ships and drift into wrong-tool calls three sprints later.

Why tool count degrades selection

Three mechanisms account for most of it, and they compound.

Schemas are input tokens on every request

Each tool definition ships with the request: a name, a description, and a JSON Schema for its parameters. Serialize the definitions your client sends and tokenize them, because the total is usually larger than people guess once nested objects, enums and per-field descriptions are counted. That total is paid on every turn of a multi-turn loop, and it displaces the conversation history and retrieved context the model needs to make the choice correctly.

Near-duplicate descriptions collapse the decision

Selection is a similarity judgement over short strings. When find_order, get_order_history and search_orders all open with "Look up orders for a customer", the request does not separate them, and the model resolves the tie on whichever phrasing overlaps more words with the user's wording. Descriptions written to describe a tool, rather than to distinguish it from its neighbours, are the cheapest of these problems to fix and the one most often skipped. The failure and its repair are covered in more depth at why agents pick the wrong tool.

Position in the list is not neutral

The same tool gets chosen at different rates depending on where it sits in the list. The 2025 BiasBusters study measured this across seven models, including GPT, Claude, Gemini and Qwen releases, by rotating the order of functionally equivalent tools and recording which one each model picked. Selection skewed toward whichever tool appeared earlier in the context, and the skew was largest when no single tool clearly dominated the others. A refactor that reorders your registry can change behaviour with no other edit, so pin the order, keep related tools adjacent, and treat a reordering as a change worth re-running your selection tests over.

Step 1: measure selection accuracy before you cut anything

Cutting by intuition removes the wrong tools. Build a fixed set of 30 to 50 real requests, each labelled with the tool that should be called first, including the requests where the correct answer is no tool at all. Score three numbers on every prompt, catalogue or model change:

  • First-call accuracy. The share of requests whose first tool call matches the label.
  • Wrong-tool rate, broken down by the tool called instead. The confusion pairs name the exact descriptions to rewrite, which a single accuracy number hides.
  • No-call rate. Requests where the agent answered from memory rather than calling anything. This one rises quietly as the catalogue grows.

Pair the fixture set with production counts. Group tool calls by name over the last 30 days and list the tools with zero calls, the tools whose call is immediately followed by a call to a different tool (a retry after a wrong choice), and the tools whose results never appear in the final answer. Testing agent tool calls covers building that fixture set and keeping it in CI.

Step 2: collapse near-duplicate tools

Most painful catalogues are thirty tools doing twelve jobs. Three lookups over the same resource become one tool with a mode parameter, which moves the ambiguity out of a choice between tools and into a field the model fills in, where an enum constrains it:

{
  "name": "orders_lookup",
  "description": "Read order data for one customer. Use for order status, full order history, or a single order by id. Do not use to create, cancel or refund an order: that is orders_write.",
  "parameters": {
    "type": "object",
    "properties": {
      "mode": { "type": "string", "enum": ["by_id", "history", "status"] },
      "customer_id": { "type": "string", "description": "Required for history and status" },
      "order_id": { "type": "string", "description": "Required when mode is by_id" }
    },
    "required": ["mode"]
  }
}

Merge only where the arguments genuinely overlap. Two tools with disjoint parameters become one schema full of conditionally required fields, and that trades a selection error for an argument error, which is harder to spot in a trace. Keep the split whenever one side reads and the other writes.

Step 3: namespace what survives, and say when not to use each tool

Give every remaining tool a prefix that matches its domain: billing_, crm_, docs_. Prefixes group related tools in the list, keep position stable when the catalogue changes, and let you scope a pool by prefix later. For tools that arrive from an MCP server, keep the server name inside the prefix so two servers that both expose search do not collide in the model's view.

Then rewrite each description to three sentences with fixed jobs:

  • What it returns, in the vocabulary the user would use, not the vocabulary of the underlying API.
  • Which sibling tool it is confused with, and when to call that one instead.
  • Where the required arguments come from, so the model asks for an id rather than inventing one.

Step 4: search the catalogue instead of shipping it

Past roughly twenty tools, sending every schema on every request stops paying for itself. Replace the flat list with a retrieval step: expose one search tool that takes a task description and returns matching tool names with their full schemas, then let the model call what it found. The prompt carries one schema rather than two hundred, and the catalogue grows without moving per-request cost.

Retrieval becomes the new failure surface. A tool that never matches a query is invisible rather than merely unlikely, so the index needs the same description discipline the prompt needed, plus a test asserting every tool is reachable from at least one realistic request. The extra round trip per turn matters on latency-sensitive surfaces and very little elsewhere.

Step 5: give each task its own tool pool

One agent holding every tool is a design choice rather than a requirement. Split by phase, so a research phase gets read-only tools and an execution phase gets the writes. Split by request class, so a cheap classifier routes an incoming request to a configuration whose pool is six tools instead of sixty.

Delegation is the same idea with a process boundary. A parent keeps a handful of coordination tools and hands a subtask to a child with its own small pool, its own model and its own budget, and the child returns a result rather than a transcript the parent has to carry. The tradeoffs around latency and context handoff are set out in multi-agent systems.

Step 6: let the agent write code over the pool

When the catalogue is genuinely large and the work is data-shaped, sequential tool calls are an awkward instruction set for it. Code mode hands the model a sandbox with the tool pool exposed as callable functions, so a task that would have been eleven round trips becomes one script it writes and runs. Intermediate results stay in the sandbox rather than passing through the context window, which is frequently the larger saving.

The cost is control. Per-call approval gates and per-call logging are harder to place inside a script than around a discrete tool call, so keep any write path that needs human sign-off as an explicit tool. A sandbox with a network policy you trust is also a precondition: an interpreter with open egress is a different risk than ten tools with fixed endpoints.

Where this gets easier

Runtype treats the tool surface as a budget the runtime enforces rather than a convention someone has to remember: tool search activates automatically once an agent passes twenty tools, and a single request carries at most fifty runtime tools, so a large catalogue does not have to mean a large prompt. Each agent, and each subagent it delegates to, has its own tool pool with its own model and budget, and code mode runs over that same pool when a task is better written as a script than as a sequence of calls. The per-turn call limit (maxToolCalls, default 10, maximum 100) bounds the retry loop a wrong selection sets off before it burns a turn. Built-in tools, HTTP tools you register, and tools consumed from an MCP server all land in the same pool, so the consolidation and namespacing work above applies to every source at once.

Frequently asked questions

Is there a hard limit on how many tools an agent can have?
Model providers cap the size of the request, not the number of tools, so the ceiling you actually hit is the one your accuracy tests find. Most teams see first-call accuracy start to slide somewhere between fifteen and thirty tools, earlier if the descriptions overlap. Many agent platforms also impose their own per-request tool cap, so check the number for the runtime you use before designing a catalogue of hundreds.
Does tool search fix a large catalogue on its own?
It fixes the prompt-size half of the problem and moves the selection problem into retrieval. The model now picks from whatever the search step returned, so a tool with a vague description is not merely ranked low, it is absent from the choice entirely. Keep a test that every tool is retrievable from at least one realistic request, and keep rewriting descriptions after you add search.
Should I split into several agents or keep one agent with fewer tools?
Start with per-task tool pools inside one agent, since that costs nothing but configuration. Split into a parent and subagents when the subtask has its own budget, its own model, or a long transcript the parent does not need to read. A separate agent per tool group is usually more coordination overhead than the selection gain is worth.