If you maintain an MCP server, at some point you ask this. You've got twenty tools, you're about to add five more, and something feels wrong about it — but you can't say what, and there's no guidance anywhere.
I read the tool schemas of 4,951 public MCP servers to answer it. 87,146 tools, 270,487 parameters.
The short answer: around thirty. Past that, the thing that breaks isn't your server. It's whether a model can tell your tools apart.
The measurement
For every tool, I took the content words in its description and counted how many appear in no other tool's description on the same server. Call it the tool's distinctive share.
If that number is zero, every word in the description is a word its siblings also use. A model choosing between your tools has nothing in the descriptions to choose on — the names are doing all the work.
Then I split the corpus by how many tools each server publishes.
| Tools on the server | Servers | Zero-distinctive tools | Parameters with no description |
|---|---|---|---|
| 1–3 | 1,256 | 0.5% | 14.8% |
| 4–7 | 1,292 | 1.6% | 22.4% |
| 8–15 | 1,044 | 4.3% | 21.9% |
| 16–30 | 767 | 7.7% | 24.8% |
| 31–60 | 377 | 16.3% | 22.2% |
| 61+ | 215 | 31.3% | 20.5% |
A factor of sixty, rising monotonically. On servers with more than sixty tools, nearly one tool in three has no distinguishing word at all.
Why it gets worse
Part of it is arithmetic. More tools means more chances that two of them collide, and that would happen even if every author wrote carefully.
But the curve steepens around thirty, and arithmetic alone doesn't explain that. What happens around thirty is that authors stop writing descriptions one at a time and start generating them from a pattern. A template is a machine for producing tools that read alike.
The most extreme case in the corpus: one server appends the same 51-word context block to all 275 of its tools.
3land_createCollection "Create a new NFT collection on 3.Land marketplace.
SAP MCP context: Protocol 3land; operation class
write. Use for 3.Land NFT collection, minting,
listing, cancellation, and purchase flows…"
3land_buyNFT "Purchase an NFT from a 3.Land listing.
SAP MCP context: Protocol 3land; operation class
write. Use for 3.Land NFT collection, minting,
listing, cancellation, and purchase flows…"
Creating a collection and buying one are different operations, and the opening sentence says so — in 8 words out of 59. The other 51 are identical across both, and across all 275.
That block was added deliberately, to help.
Zero doesn't mean badly written
This is the part worth internalising, because it's counterintuitive.
Four tools from a widely-installed Gmail server:
Gmail_DeleteDraftEmail "Delete a draft email using the Gmail API."
Gmail_SendDraftEmail "Send a draft email using the Gmail API."
Gmail_ListLabels "List all the labels in the user's mailbox."
Gmail_SearchThreads "Search for threads in the user's mailbox."
Every one of those is clear, correct English. No reviewer would flag them. Every one is also built entirely out of words the other tools use — delete, draft, email, gmail, api, list, search, threads, mailbox all recur across the set.
The description tells you what the tool does. It doesn't tell you what this tool does and the others don't. That second thing is what a model needs at the moment it's choosing, and it's a different question from "is this description good."
The column that argues against splitting
Look at the right-hand column again. Parameters with no description at all sit between 20% and 25% at every size above the smallest bucket. It doesn't improve as servers get smaller.
A two-tool server has the habit about as much as a two-hundred-tool one.
So the two failures are independent. Splitting a large server reduces your description collisions and does nothing whatsoever for your undescribed parameters. They need separate fixes, and the split only buys you one of them.
So should you split?
Split when the collisions are real, not because you crossed a number.
The threshold in the data is around thirty, but that's a population average and your server isn't the population. A server with forty tools that all do genuinely different things to genuinely different objects may be fine. A server with twelve tools where four of them are variations on "search" is not.
The test that actually tells you: read your tool list as one block, the way a model receives it. Nothing else. No README, no repo, no memory of what you meant. Then ask which tool you'd pick for a request that could plausibly go to two of them.
Three options when the answer is "I can't tell":
Rewrite for contrast rather than clarity. Not "is this description clear" but "is it clear which of my tools this is." If you have a shared preamble on every tool, it's costing more than it's buying — the distinguishing sentence shouldn't be a seventh of the text.
Collapse near-identical tools into one with a mode parameter. Four search variants become one search with a scope enum. Fewer things to choose between, and the choice the model has to make moves from "which tool" to "which value," which an enum can constrain and a description can't.
Split the server. Fewer tools per connection, more servers to maintain. Worth it when the tools genuinely belong to different domains, less so when you're just cutting an arbitrary list in half.
The limits of this
This is static analysis. I never ran a model against any of these servers, so I can't tell you how often collisions actually cost anything. A zero-distinctive description might be harmless when the tool name is unambiguous, and expensive when it isn't. Ranking these signals by how well they predict a real mistake needs a model in the loop, which is the next study.
One more hole, found by a server author after I published: the undescribed-parameter figure measures absence only. A parameter described as "query: The query" counts as described and passes. Restating the parameter name is arguably the more common failure and it passes every linter, so 21.8% is a floor.
Data and analysis scripts: https://github.com/getmcpulse/mcp-schema-study
If you want your own numbers rather than the corpus averages, there's a free checker at https://getmcpulse.com/check — paste your tools/list JSON and it scores your distinctive share, undescribed parameters, and token cost against all 4,951 servers. Browser only, nothing uploaded.
I'm building MCPulse, an SDK that reports what models actually do with your tools once real traffic arrives. Everything above came from outside the server, which is exactly its limit: a schema can tell you a model has nothing to choose on, but only traffic tells you whether it chose wrong.
Originally published at getmcpulse.com.
Top comments (0)