Why Your MCP Server Picks the Wrong Tool, and the Five Patterns That Fix It
Five patterns, four anti-patterns, and a measured tool-count budget from 15 production servers.
In ~7 mins: the ~10-15 tool accuracy budget, the five MCP server patterns, the four anti-patterns that break tool selection, a measured-vs-modeled transport table, and a reproduce-the-study appendix at the end.
Claude Haiku 4.5 picks the right tool 91% of the time when an MCP server shows it 10 tools. Add five more and accuracy falls to 87%.
Claude Sonnet 4 holds the line longer, staying at or above 90% up to 20 tools and dropping below by 30.
The variable that moves those numbers is not the model. It is the count of tools visible in a single context, and that count is a decision the server author makes.
That is the practical core of a new industry experience paper, “MCP Server Architecture Patterns for LLM-Integrated Applications,” by Carson Rodrigues of Celabe and Oysturn Vas of the University of Waterloo.
It names five recurring server patterns, four anti-patterns, and a tool-count ceiling that reframes a question every MCP author eventually asks: how many tools is too many?
Context
The Model Context Protocol shipped from Anthropic in November 2024 as a standard way to connect an LLM to external tools, data, and services. Adoption ran ahead of guidance.
Hundreds of servers appeared within months, and a measurement study the paper cites now counts more than 8,000 public MCP servers, but no software-maintenance literature had described how any of them should be built.
Rodrigues and Vas derive their catalog from 15 servers: 5 production servers running Celabe’s ANSYR voice AI platform since late 2024, and 10 public servers from the official modelcontextprotocol/servers registry. The first author works at Celabe, which supplies both the production half of the corpus and all the telemetry behind the tool-count numbers.
The paper discloses this, and it matters for how far the headline figure travels. More on that in the Take.
Core concept: an API for a reader, not a caller
MCP runs on JSON-RPC 2.0 and exposes three primitives to a client: tools (callable functions with a name, a description, and a JSON schema), resources (URI-addressed data the model can read), and prompts (reusable server-side templates). Two transports carry the traffic: stdio for a local server in the same process, and streamable-http for a remote one.
The twist that makes MCP its own design discipline is the client. An LLM decides which tool to call by reading the natural-language description, not by consulting documentation or inspecting the schema.
REST, GraphQL, and the Language Server Protocol all assume a caller that reads docs. MCP assumes a reader that skims descriptions and guesses.
So the tool count and the wording of each description stop being cosmetic and become runtime behavior, which is exactly where the patterns and the anti-patterns come from.
The tool-count budget, and five patterns to stay under it
Start with the budget, because it constrains everything else. On the paper’s production data, a single context should stay under roughly 10 to 15 visible tools for a Haiku-class model.
Past that, the model starts choosing wrong often enough to matter. The five patterns are the vocabulary for organizing a server so you never blow that budget.
Resource Gateway. Mediates all reads from a backend behind one server, exposing data as resources and unsafe-parameter queries as tools, with a sanitization layer that strips injected content before it reaches the model. The cost is an extra network hop per read and a server that has to track backend schema changes.
It shows up in database connectors like PostgreSQL and MongoDB and document bridges like Notion.
Tool Orchestrator. Wraps a multi-system workflow in a single composite tool that runs the sub-calls internally and returns one summary, so the model reasons about one operation instead of five APIs. The tradeoff is that the sub-tools are harder to reuse when the workflow changes, and partial-failure handling moves onto the server.
Common in CI/CD automation and customer-support action hubs.
Stateful Session Server. Issues a session ID on connection, carries it through every call, and keeps per-session context in memory or Redis, expiring it on inactivity. This makes multi-turn work like open, edit, then save feel natural, at the price of memory leaks if sessions are not reaped and a distributed store once you scale horizontally.
It also depends on the model reliably passing the session ID back, which is not guaranteed. Code-editing agents and database-transaction servers use it.
Proxy Aggregator. Fronts many upstream servers behind one endpoint, namespacing tool names to avoid collisions. This is where the budget bites.
A static-merge aggregator surfaces the union of every upstream tool at once, which is the fastest way to push a context past the ceiling. A scoped aggregator instead retrieves only the tools relevant to the current request, an approach also called retrieval-over-tools, and exposes that subset.
When aggregation would otherwise blow the budget, the scoped variant is the fix and the static merge is the trap.
Domain-Specific Adapter. Wraps an LLM-hostile API and adds what the model needs to use it: human-readable descriptions, input normalization for fuzzy dates and names, output enrichment that resolves IDs to display names, and error translation into plain English. The adapter has to be updated when the underlying API changes, and it is over-engineering when the API was already friendly.
CRM adapters for Salesforce and HubSpot are typical.
Each pattern has a classical ancestor. Resource Gateway is a repository, Tool Orchestrator a facade, Proxy Aggregator an API gateway.
The new part is the LLM-client delta: a Tool Orchestrator has to size its tool set to selection accuracy, a Proxy Aggregator has to partition tools to fit the context, and a Domain-Specific Adapter treats validation as natural-language guardrails rather than schema constraints.
The four anti-patterns are the failure modes the same corpus kept hitting.
The God Tool exposes one do_anything(action, params) entry point and forces the model to reason about what “action” means, so selection collapses. Fix it by decomposing into named tools.
Unsanitized Resource Content returns user text straight from the backend, so a document reading “Ignore previous instructions and...” gets processed as an instruction. Sanitize before the content enters the response.
Synchronous Long-Running Operations expose a video encode or a large file job as a blocking tool, and the client times out because MCP has no async callback. Return a job ID and expose a poll_job(id) tool instead.
Missing or Vague Tool Descriptions ship a tool named send_message with no explanation, and since the model picks by reading, it cannot choose well. Write what the tool does, when to use it, and what it returns.
Evidence
The tool-count study is the paper’s most quotable result and its most hedged. It is observational telemetry from the ANSYR platform in Q1 2025, not a fresh controlled benchmark.
For each tool-count bucket in {1, 3, 5, 10, 15, 20, 30, 50}, the authors pulled 200 production session turns, with ground truth set by the tool a human operator confirmed correct in the post-call quality review. Wilson 95% confidence intervals stay within ±4 points across every bucket.
At 10 tools, Haiku hits 91% accuracy at a median 245 ms and Sonnet hits 95% at 410 ms. Haiku crosses below 90% between 10 and 15 tools, and Sonnet holds to 20 before breaking by 30.
The design reading is direct: keep a single context near 10 tools, and reach for the scoped Proxy Aggregator when a fleet would push you past 15.
Transport latency is the second measurement, and it settles a common worry the opposite way most people expect. Two rows are measured end-to-end on loopback over 100 calls.
Three rows are modeled, built from the measured loopback overhead plus a same-region network round-trip constant, and the paper labels them as estimates rather than measurements.
The protocol layer costs well under a millisecond in-host. Network round-trip dominates everything once a call crosses a host boundary, running two to three orders of magnitude larger than the stdio-versus-streamable-http gap.
So the choices that move latency are whether the server sits next to the client and whether an aggregator adds another hop, not which transport encoding you pick.
The third measurement checks whether the taxonomy is reproducible. Two independent raters, Claude Haiku 4.5 and Claude Sonnet 4 at temperature 0, classified 54 held-out servers from neutral descriptions that named no architecture.
Inter-rater agreement was substantial: Cohen’s kappa of 0.76, with a 95% confidence interval of [0.62, 0.88] and 81.5% raw agreement. Full commands to regenerate these numbers are in the appendix.
Where it sits in the ecosystem
The tool-count ceiling is not an isolated claim. It is the low end of an effect other groups have measured at larger scale.
Gan and Sun’s RAG-MCP reports tool-selection success above 90% only up to about 30 candidate tools, degrading sharply past 100. Kate and colleagues’ LongFuncEval measures a 7% to 85% accuracy drop as a function-calling catalog grows.
This paper locates where the degradation begins for a latency-constrained voice deployment, and RAG-MCP’s retrieval approach is a concrete instance of the scoped aggregation it recommends.
Treat the comparison as architectural rather than a leaderboard. This work contributes the design vocabulary, RAG-MCP and LongFuncEval contribute the tool-count measurements, and the 8,000-server census contributes the scale.
On the security and maintenance side, the paper’s anti-patterns line up with the maintainability smells cataloged by Hasan and colleagues, and its cross-cutting notes on auth, versioning, and observability sit alongside Hou and colleagues’ MCP security landscape.
AlphaSignal Take
The patterns and the budget are the useful parts, and they are useful today. A server author gets a shared name for five structures, a decomposition rule for tools, and a concrete number to design against.
That is more actionable guidance than the MCP ecosystem had a month ago. The caveats are about how far three of the results generalize, not whether the advice is sound.
The famous number is one company’s log. The 10-to-15 ceiling comes from ANSYR’s production telemetry, a single voice-AI platform in one industry, and the raw session logs are not released, so the figure cannot be re-derived from the published code. It is a well-instrumented field observation, not a controlled law.
The number to carry is “around 10 to 15 for a Haiku-class model in a latency-constrained deployment,” with the exact threshold calibrated to your own tool set.
Three of the five transport rows are modeled. The remote rows are loopback overhead plus a same-region round-trip constant, so absolute numbers shift with cross-region or congested paths. The relative ordering holds better than the exact milliseconds.
The taxonomy was derived by one coder. A single author did the open coding, with the second author verifying, and the kappa study runs on held-out servers rather than the derivation set. Both raters are Claude models that may share blind spots, which the authors flag as a limit.
The paper is candid that its 15-server corpus is small against the 8,000 servers in the wild, and a larger replication could surface patterns this set misses.
So the best recommendation is to adopt the vocabulary and the sub-15-tool budget as a design default, then measure your own tool surface before treating any exact number as a hard rule. The framework earns its place.
The specific figures are a starting calibration, not a benchmark you can cite as settled.
Who benefits and who doesn’t
This is built for MCP server authors deciding how to expose tools and whether to aggregate, platform teams fighting tool sprawl across a fleet, and technical leads who want a shared vocabulary and clear maintenance seams across several teams’ servers. Anyone shipping a real MCP server past a handful of tools gets an immediate design check out of it.
It does less for teams with a small tool set well under the budget, where the ceiling never binds, and for anyone who needs a controlled cross-model benchmark, since this is single-org observational data. Deployments dominated by cross-region latency should ignore the modeled transport milliseconds and measure their own paths.
Practitioner implication
You can now size an MCP server’s tool surface to a measured accuracy budget and reach for scoped aggregation instead of a static merge, now that there is a named pattern vocabulary and a roughly 10-to-15-tool ceiling to design against.
Links
MCP Server Architecture Patterns for LLM-Integrated Applications (arXiv paper, ~25 min read)
Replication package (MIT: corpus, classifier, transport benchmark)
modelcontextprotocol/servers (the public servers in the corpus)
Follow @AlphaSignalAI for more content like this.
Subscribe at alphasignal.ai/newsletter for daily AI signals. Read by 300,000+ subscribers.
Appendix: reproduce the study
The replication package is a research reproduction repo under an MIT license, not a tool you deploy into an agent stack. If you want to regenerate the reliability and transport numbers yourself, this is the short version of its README.
Clone the repo and enter it:
git clone https://github.com/rodriguescarson/mcp-patterns-icsme2026
cd mcp-patterns-icsme20262. Install the dependencies:
pip install -r requirements.txt3. Inspect the derivation corpus. corpus.json holds the 15-server corpus with timestamps and primary-pattern assignments, the machine-readable version of the paper’s corpus table.
4. Regenerate the inter-rater reliability. kappa_eval.py runs the two Claude raters over the 54 held-out servers at temperature 0 and writes per-rater predictions, the kappa, and its bootstrap confidence interval to results_kappa.json. This step needs Anthropic API access for the two rater models.
5. Run the transport benchmark. transport_bench.py exercises the stdio and loopback streamable-http echo servers and writes raw samples to results/transport_measured.json, reproducing the two measured rows of the latency table.
6. Note what you cannot reproduce. The tool-count accuracy figures depend on ANSYR production logs that are not released. tool_count_telemetry.csv ships the per-bucket summary only, so you can read the numbers but not rebuild them from raw sessions.
Questions?
Q: How many tools should an MCP server expose before accuracy drops? A: On the paper’s production data, keep a single context under about 10 to 15 tools for a Haiku-class model. Claude Haiku 4.5 goes from 91% tool-selection accuracy at 10 tools to 87% at 15, and Claude Sonnet 4 holds at or above 90% up to 20 before dropping by 30. These are single-org observational numbers, so calibrate to your own tool set.
Q: What are the five MCP server architecture patterns? A: Resource Gateway, Tool Orchestrator, Stateful Session Server, Proxy Aggregator, and Domain-Specific Adapter. Each is described in context-problem-solution-consequences form, and each maps to a classical software pattern with an added constraint for LLM clients.
Q: Why does my LLM keep picking the wrong MCP tool? A: LLMs select tools by reading their descriptions, so too many visible tools or vague descriptions degrade selection accuracy. Cut the visible set toward 10 to 15 per context, scope a large fleet with retrieval-over-tools, and write descriptions that state what each tool does, when to use it, and what it returns.
Q: What is a scoped Proxy Aggregator? A: An aggregator that exposes only the tools relevant to the current request, retrieved per call, instead of statically merging every upstream tool into one large catalog. It is the recommended fix when a merged tool list would push a context past the accuracy budget.
Q: What are the common MCP server anti-patterns? A: The God Tool (one catch-all tool), Unsanitized Resource Content (unfiltered backend text that enables prompt injection), Synchronous Long-Running Operations (blocking tools that time out), and Missing or Vague Tool Descriptions. Each has a documented fix, from tool decomposition to returning a job ID with a poll tool.







