- Published on
Too Many Tools: Testing Agent Tool Discovery
- Authors

- Name
- Jared Chung
Introduction
Giving an agent more tools sounds like giving it more capability. At some point the opposite happens: tool definitions consume context, similar names compete for selection, and large intermediate results bury the information the model needs.
Agents are increasingly connected to company context, tools, and repeatable workflows. The engineering question is no longer only how to call a tool. It is how to make the right tool discoverable without loading an entire software catalog into every prompt.
The comparison below uses a controlled experiment instead of assuming that deferred loading or code-based orchestration always helps.
Tool-context laboratory
Tool Catalogs Compete for Attention
Each tool usually contributes a name, description, and input schema to the model's context. Ten compact tools may be easy to distinguish. Fifty tools from several services can overlap:
slack_send_message
slack_send_channel_message
teams_send_message
email_send_message
email_create_draft
crm_create_note
crm_create_task
project_create_task
The problem has three parts:
- Context cost: definitions consume tokens before work begins.
- Selection ambiguity: similar tools can be confused.
- Result pollution: large results accumulate in the conversation.
Start by measuring the serialized size of the catalog and grouping tools by purpose. Tool count alone is misleading: one complex schema may cost more context than several small tools.
Design Tools for Agent Decisions
A tool schema is an interface for a probabilistic caller. Names should expose the service and action. Descriptions should explain when to use the tool and any important boundary.
{
"name": "github_issues_search",
"description": "Search issues in repositories the user can access. Use for issue titles, bodies, labels, and status; do not use for code search.",
"input_schema": {
"type": "object",
"properties": {
"query": {"type": "string"},
"repository": {"type": "string"},
"state": {"enum": ["open", "closed", "all"]}
},
"required": ["query", "repository"]
}
}
Avoid exposing every underlying API endpoint. A higher-level get_customer_context tool may serve an agent better than separate tools that return transactions, notes, and contact details in incompatible shapes. The right boundary depends on representative tasks and evaluation.
Compare Three Loading Strategies
Build a harmless catalog of roughly 40 mock tools across source control, messaging, documents, and project tracking. Include intentional naming collisions and irrelevant tools.
Evaluate three policies:
| Policy | Initial context | Extra step | Main risk |
|---|---|---|---|
| Always loaded | Every definition | None | Context cost and confusion |
| Deferred discovery | Search tool plus common tools | Search before uncommon use | Search miss or latency |
| Curated bundles | Task-specific subset | Bundle selection | Wrong bundle or maintenance |
Keep three to five common tools loaded in the deferred condition. Index the remaining names and descriptions for search.
def load_tools(task, catalog, common_tools, search):
selected = list(common_tools)
selected.extend(search(task, catalog, limit=5))
return deduplicate_by_name(selected)
For a real OpenAI Responses API baseline, pass the selected function definitions to the Python client. The repository pins openai==2.24.0 so the example remains reproducible. It accepts the model ID through configuration so the experiment records the exact model used:
import os
from openai import OpenAI
client = OpenAI()
issue_search_tool = {
"type": "function",
"name": "github_issues_search",
"description": (
"Search issue titles, bodies, labels, and status in one repository. "
"Do not use this function to search source code."
),
"parameters": {
"type": "object",
"properties": {
"query": {"type": "string"},
"repository": {"type": "string"},
"state": {"type": "string", "enum": ["open", "closed", "all"]},
},
"required": ["query", "repository", "state"],
"additionalProperties": False,
},
"strict": True,
}
response = client.responses.create(
model=os.environ["OPENAI_TOOL_MODEL"],
input="Find open payment bugs in example/support-api.",
tools=[issue_search_tool],
tool_choice="auto",
metadata={"tool_policy": "always-loaded-baseline"},
)
for item in response.output:
if item.type == "function_call":
print(item.name, item.arguments)
The application must parse and validate item.arguments before executing the function, then return the tool result using the call identifier. The snippet stops before execution so that the experiment can score selection and arguments independently from the mock tool's behavior.
Use the same task set and model settings for every policy. Tasks should require one tool, several tools, no tool, and a deliberate choice between similarly named tools.
Keep Intermediate Work Out of Context
Tool discovery controls definitions. Programmatic orchestration controls results. When an agent needs to search many records, code can filter and aggregate them before returning a small result to the model.
async def summarize_failed_jobs(api, project_id):
jobs = await api.list_jobs(project_id=project_id, limit=500)
failed = [job for job in jobs if job.status == "failed"]
return {
"total_jobs": len(jobs),
"failed_count": len(failed),
"top_error_codes": count_top(job.error_code for job in failed),
"sample_job_ids": [job.id for job in failed[:5]],
}
This reduces context usage and inference round trips, but it also moves logic into code. Test that filters and aggregation do not hide cases the agent needs. Preserve links or identifiers that allow deeper inspection.
Evaluate Selection, Arguments, and Outcomes
End-to-end task success is the primary measure. Add diagnostic metrics to show why a policy succeeds or fails:
- correct tool selected;
- required tool discovered in the top results;
- arguments valid and semantically correct;
- unnecessary calls;
- input and output tokens;
- time to final answer;
- task rubric passed.
task: find_open_payment_bug
expected_tools:
- github_issues_search
forbidden_tools:
- github_code_search
checks:
- repository argument is correct
- state equals open
- final answer cites returned issue IDs
Run multiple trials. Tool choice is probabilistic, and small differences may disappear across repetitions. Report the task set, model, catalog revision, search method, and full distribution.
Provider-reported tool-search improvements are useful motivation, not results for your catalog. Deferred discovery adds latency and may offer little value for fewer than ten compact, frequently used tools.
Conclusion
An agent's tool catalog should grow only when evaluation shows that new capability outweighs context and selection costs. Clear interfaces, deferred discovery, curated bundles, and code-based aggregation address different bottlenecks.
Measure the smallest useful configuration first. More tools expand what an agent could do; good discovery determines what it can do reliably.
References
- Writing effective tools for AI agents, Anthropic, 11 September 2025
- Introducing advanced tool use, Anthropic, 24 November 2025
- From assistance to execution: How enterprises put AI to work, OpenAI, 12 August 2026