handling tool bloat - hundreds of tools #2036
Replies: 11 comments 6 replies
|
MCP actually has a mechanism for this, though it doesn't fully solve the token bloat problem yet. What MCP supports todayDynamic tool lists via Servers can declare
This maps well to your "dynamic tooling" approach. Your MCP server could expose a lightweight discovery tool (e.g., Pagination on The Namespaced tool names Tool names support dots (e.g., What MCP doesn't have (yet)
Practical patterns for 300+ tools
The dynamic loading pattern (#2) is probably closest to what you're already doing with your "briefs + fetch_tool" approach, and it works within MCP today via |
|
I run 10+ MCP servers simultaneously in Claude Code (GitHub, Jira, Mural, Playwright, custom auth servers, etc.) with well over 100 tools total. Some practical observations after months of this setup: What actually works: many small servers, not one big one @chasewhughes's suggestion #1 (agent-per-domain servers) is the pattern I'd recommend. Each server owns one integration and exposes 10-30 tools. The benefits beyond token reduction:
At ~100 tools across 10 servers, I don't see meaningful latency. The token overhead is real but manageable because each server's tool schemas are focused and tight. Practical tips for keeping tool schemas lean Most of the token bloat comes from
The dynamic loading pattern is powerful but has UX friction I've experimented with the For 300-400 tools, I'd split into 15-20 domain servers with 20-25 tools each. That's the sweet spot where tool lists are small enough for the context window but rich enough to be useful without dynamic loading. |
|
I run a production setup with 60+ tools across 5 MCP servers (WhatsApp, Email, Calendar, Database, AWS CLI, smart home, investments, etc.) connected to a single AI assistant. Here is what actually works in practice: 1. Split by domain into separate MCP servers
This keeps each server focused and maintainable. The AI model sees all tools but the context overhead is manageable because tool descriptions are concise. 2. Keep tool descriptions short and precise 3. Use 4. Practical numbers The key insight: MCP does not force you to load everything at once. Use multiple servers + |
This comment was marked as spam.
This comment was marked as spam.
|
A pattern that scales better than putting 300-400 schemas in the prompt is capability discovery + activation. What I would do:
A couple of nuances:
So yes: MCP can support your dynamic-tooling direction, but the win comes from just-in-time exposure, not from one huge server with every concrete tool always visible. |
|
honestly we dealt with this exact thing building relay (https://github.com/valtors/relay) which has like 40 tools and it got messy fast the biggest thing that helped was just grouping tools into categories and letting users enable/disable them per session. like if you're not doing image stuff, why even expose those tools to the model. we prefix tool names with the category too (fileread, webfetch etc) and weirdly that alone made the model call the right tool more often also dont sleep on MCP prompts. if you got 100+ tools the model is gonna guess wrong half the time. prompts let you wrap common workflows into one call so the model doesnt need to figure out which 5 tools to chain together another thing - if a tool needs an api key or a binary that isnt there, just dont register it server side. no point showing it to the client if it cant work if you really have hundreds of tools i'd probably just split into multiple smaller focused servers tbh. one for db stuff, one for web, one for files. only load what you need for that session |
|
The token cost problem is real and gets worse fast. We have 40+ tools on our own MCP server (Relay) and saw the same issue. One approach that works without changing your server architecture: put a proxy between the client and the MCP server that can filter tools before they reach the model. We built Observer (github.com/valtors/observer) as a transparent stdio proxy that wraps any MCP server command. It has OBSERVER_FILTER (hide specific tools from the client) and OBSERVER_MAX_TOOLS (cap the total tools exposed) to reduce the schema tokens sent to the model. The proxy also logs every tool call to SQLite, so you can see which of your 300 tools are actually being used and which are dead weight. That data helps you decide what to prune or group. It's not a full solution to dynamic tool discovery (which the spec needs to address), but it's a practical layer you can drop in today without changing your server code. |
|
A useful way to structure this is to separate the MCP catalog from the model-visible working set.
For the stale-history problem, I would make the working set monotonic within a conversation: In practice, pin a tool as soon as it is called or becomes part of an unresolved plan. Do not evict it merely because the relevance ranking changed on the next turn. Only reset the pinned set at a real conversation boundary or after compaction has preserved the required tool state explicitly. If a pinned capability truly becomes unavailable because of auth, deployment, or configuration changes, keep a small tombstone adapter under the same model-visible name. It can return a structured I would therefore keep these layers separate:
If you use a generic One forward-compatibility note: the current draft direction moves away from connection/session-specific mutation of list operations and toward explicit server-minted handles for cross-call state. So I would implement per-conversation selection in the host/gateway, not by mutating the server's canonical References:
|
|
On the specific worry — "if we do it with MCPs, we lose the [dynamic tooling] flexibility" — you don't have to. The move is to decouple the catalog (every tool your servers expose) from the model-visible working set (what goes into the request), and do that decoupling in a gateway/proxy that sits between the client and your MCP servers. All servers stay connected; the gateway just decides which tools appear in the Two things worth separating in this thread, since they get conflated:
Tradeoff vs. your The |
You do not lose that flexibility on MCP. Earlier replies already cover Ship a static small surface, and only enable the rest behind an explicit switch. The server still has every tool in code. On patchloom we do this with
That is complementary to the gateway pattern, not a replacement. Use a gateway when many third-party servers are already connected and you cannot change them. Use a core/full (or domain) switch when you own the server and can keep 80% of calls on a short list. Practical split that has worked for us:
|
|
One empirical data point for the catalog-vs-working-set pattern, and specifically for the stale-history problem mentioned above: I implemented the host/gateway variant in DeepSeek Harness. The model-facing names never change: it always sees I tested the two paths with the same Harness
The cost uses a dated 2026-08-15 DeepSeek pricing snapshot. This is only a three-task functional pilot, not a general quality or latency benchmark. The gateway adds a search call, and output tokens were higher in this run (491 → 794). The important safety tradeoff with a generic call interface is that it can erase the policy boundary of typed tools if implemented naively. In this implementation, search and call share the same default-deny allow/deny policy; the selected exact schema is returned before execution; the original server/tool identity stays in routing and logs; and catalog, discovery, cursor, metadata, timeout, and HTTP-response sizes are bounded. Source, method, and raw aggregate fields are public here: https://github.com/labmimors/dsh-mcp-lens/blob/main/docs/LIVE_DEEPSEEK_PILOT.md Disclosure: I am the plugin author. The design is host-specific today, but the stale-name tradeoff and measurement method should transfer to other MCP hosts. |
Uh oh!
There was an error while loading. Please reload this page.
we are working with a robust ai assistant, and we currently have 70 diff manually created tools.
We want to build an MCP for each service integration, but this can easily go to 300-400 tools.
We already see latency with 70 tools, and high costs with the token bloat.
One approach we have in mind is having one agent per domain (ie: crm).
But this will get bloated as well if the domain is big enough.
The other approach we have is dynamic tooling, so we only load tools on request.
We give the llm tools "briefs" and "fetch_tool" tool, and it will include the requested tool in the tools on the next request.
If we do it with mcps, we are losing this flexibility.
All reactions