Do more MCP tools make your agent worse?
Tool schemas eat context and bias selection. Lessons from a 47-tool MCP server: naming, disambiguation, grouping, and when to split into two servers.
Every tool you add to an MCP server has a cost, and it is not the cost you think. It is not RAM or latency on the wire. It is context and attention. Each tool ships a JSON schema (name, description, parameters, types) that gets injected into the model’s context before it does anything. So the question “do too many mcp tools make my agent worse” is really two questions: how much context are you spending on schemas, and how confused does the model get when it has to pick one tool out of fifty. We run PredMCP with 47 tools live in production, and we have hit both walls. Here is what we learned. If you are new to the protocol, start with what is MCP.
How tool schemas consume context and bias selection
When a client connects to your server, it calls tools/list and dumps every schema into the model’s context window. A single tool with three parameters and a decent description runs a few hundred tokens, so a few dozen tools put five figures of schema in front of the conversation before the user has typed a word.
Be careful with the money version of that argument, though, because it is mostly wrong. Tool schemas sit at the very top of the prompt and never change between turns, which makes them the single most cacheable block in the whole request. With prompt caching on, you pay for that block once and then read it at a fraction of the price on every following turn. Anyone telling you that 47 tools burns your budget on every turn is quietly assuming you left caching off.
The cost that caching does not refund is attention. The schema block still occupies real estate in the context window, and the model still has to discriminate between every candidate before it picks one. A cached wrong-tool call costs exactly as much as an uncached one: a wasted round trip, a response full of fields nobody asked for, and a reasoning step built on the wrong data. Optimize for selection accuracy, not for the token bill.
Worse than the raw cost is the bias. The model does not read your tools neutrally. It pattern-matches on names and descriptions. Two tools that sound similar (get_funding_rates and get_top_funding_rates, say) create a coin-flip. A tool with a vague name and a long description will get picked for tasks it should not touch, because the description happens to contain a keyword the model latched onto. More surface area means more chances to route wrong.
Symptoms of tool overload
You know you have too many tools on one server when you see these:
- Wrong-tool calls. The agent grabs
get_market_contextwhen the user asked for a single price, because both mention “market”. It technically works, returns ten fields, and the agent now reasons over noise. - Ignored tools. You built
get_signal_backtest. The model never calls it. It sits deep in the schema dump and loses the attention competition to the tools defined earlier. - Latency and cost creep. Bigger schema payload, more tokens per turn, slower first token. On a chatty agent loop this compounds fast.
- Parameter hallucination. With near-duplicate tools, the model mixes up which one takes
symbolversusmarket_idand sends the wrong shape.
Lessons from a 47-tool server
We did not get to 47 tools cleanly. We refactored twice. Two structural facts about how the server is actually built shape everything below.
First, the tools split into two families that live in separate directories: plain data readers (get_markets, get_odds, get_orderbook, get_funding_rates, get_whale_trades) and derived signal tools (get_signals, get_liquidation_clusters, get_pm_hl_divergences, get_conviction_score). The data readers answer “what is the state of the book right now”. The signal tools answer “is something worth acting on”. A model that confuses the two families produces confident nonsense, so the boundary is enforced in the file layout, not just in the prose.
Second, we cover two venues, Polymarket and Hyperliquid, and that is where naming stops being cosmetic.
Name the venue, or the model will guess it. Our real collision: get_whale_positions and get_whale_trades. Same noun, same intuition, two completely different datasets. get_whale_positions imports fetchUserPositions from the Polymarket source and needs a Polygon wallet address, because the Polymarket /positions API has no public “all holders of this market” endpoint, so you must name the trader you want to inspect. get_whale_trades imports fetchWhaleTrades from the Hyperliquid source and takes a coin ticker plus a notional floor. Ask an agent for “whale activity on BTC” with both tools in context and it will reach for get_whale_positions, then fail on a parameter it cannot invent.
We considered renaming to get_polymarket_whale_positions and get_hyperliquid_whale_trades. We did not, because tool names are a public contract and prefixing every tool with its venue makes the 47-name list unreadable. What we did instead was put the venue in the first three words of every description. The live text reads “Positions of a specific Polymarket wallet…” and “Recent large trades on Hyperliquid perps…”. The venue is now the first thing the model reads about each tool, and the parameter descriptions carry it too (“Polygon wallet address (0x…)” versus “Asset ticker… e.g. “BTC""). Renaming is the heavier hammer, and it is the right call when a name is actively misleading rather than merely ambiguous. Ours were ambiguous, so the description carried the load.
The rest of the rules that stuck:
Verb-first, consistent naming. Every read tool starts with get_ or search_. get_funding_rates, get_whale_positions, search_markets. The verb tells the model the action, the noun tells it the domain. No cute names. The model routes on structure, so give it structure.
Disambiguate aggressively. If two tools could answer the same phrasing, one of them is wrong or the descriptions overlap. get_funding_rates and get_top_funding_rates are the obvious pair on our server, so the descriptions separate them in the opening clause: the first is “Current funding rates for Hyperliquid perpetuals”, the second is “Top Hyperliquid perps ranked by absolute funding rate, with OI and annualized yield”. One is a lookup, the other is a ranking. The distinction lives in the first clause, not buried in paragraph three, because attention decays.
Descriptions are routing instructions, not docs. Write for the model deciding whether to call, not for a human reading API reference. Lead with when to use it and when not to. One line of “do not use this for X, use get_odds instead” saves more wrong calls than any amount of parameter prose.
One job per tool. get_price_summary returns a price summary. It does not secretly also return orderbook depth if you pass a flag. Overloaded tools with mode switches are the hardest for a model to reason about and the easiest to misfire.
Grouping, progressive disclosure and deferred tools
The real unlock is not shipping fewer tools, it is not shipping all of them at once. A few patterns:
Progressive disclosure, concretely. “Load tools on demand” is easy to say and vague enough to be useless, so here are the two mechanisms that actually implement it.
Mechanism one: a catalogue tool. You register a small core set for real (discovery, prices, a search entry point) plus one extra tool whose entire job is to describe the tools you did not register. Call it something like list_capabilities. Its schema is one tool’s worth of tokens. Its return value is a compact index of the deferred tools: name, one-line purpose, and the parameters they take. The agent reads the index, decides it needs the backtest tool, and calls a second tool to pull that full schema into context, or in clients that support it, the host expands the tool list in place. Context cost goes from “all schemas always” to “the core set, plus the two schemas this turn actually needs”. The tradeoff is real and you should price it in: the agent now spends an extra round trip discovering what exists, so anything used in most conversations belongs in the core set, not behind the catalogue.
Mechanism two: several servers, mounted per context. Nothing forces one server per project. Build a data server and a signals server as separate processes, and let the client config decide which ones a given agent mounts. A “what is the price of BTC” assistant mounts the data server alone and never sees a backtest schema. A research agent mounts both. This needs zero protocol support beyond plain MCP, which is why it is the option to reach for first: the deferred-tool patterns depend on client features that not every host implements yet, while mounting two servers works everywhere today.
The rule of thumb behind both: a tool that is used in most conversations belongs in context permanently. A tool used in one conversation out of twenty belongs behind a catalogue or on a separate server.
Group by workflow, not by data source. We cluster PredMCP tools by intent: discovery (search_markets, get_markets), pricing (get_odds, get_price_summary), flow (get_whale_flow, get_liquidation_clusters), signals (get_signals, get_conviction_score). A grouped mental model helps you decide what to load together.
When to split into multiple servers instead
Sometimes the answer is not better naming, it is a second server. Split when:
- Two tool families never co-occur in one task. Read-only market data and account-mutating actions (
create_api_key) rarely belong in the same reasoning step. Different servers, different trust boundaries. - Different auth or rate limits. If half your tools need a signed key and half are public, splitting keeps the schema surface honest about what requires what.
- Different consumers. A trading agent and a research agent want different subsets. Two focused servers beat one 47-tool monster where each agent ignores 30 tools.
Combining venues is not the same as combining everything. We put Polymarket and Hyperliquid in one MCP on purpose, because cross-venue signals need both datasets in the same reasoning step. That is a workflow argument, not a “more is better” argument. The test is always: does the agent need these together to answer one question?
A copy-paste checklist for tool design
[ ] Verb-first names, one convention (get_, search_, create_)
[ ] Each tool does exactly one job, no mode-switch flags
[ ] First sentence of the description says WHEN to use it
[ ] Near-duplicate tools state their difference in clause one
[ ] Add "use X instead for Y" cross-references between rivals
[ ] Measure total schema tokens; know your fixed per-turn cost
[ ] Core tools loaded by default, specialists deferred/searchable
[ ] Group tools by workflow intent, not by data source
[ ] Split servers on trust boundary, auth, or distinct consumers
[ ] After every 5 new tools, re-check for routing collisions
Forty-seven tools is fine. Forty-seven tools with sloppy names, overlapping descriptions and no deferral is a server that quietly makes your agent dumber every turn. The count is not the problem. The routing surface is.