Direct answer

Too many MCP tools bloat the model context because each tool brings a name, description, annotations and input schema into the selection surface. The fix is to stop exposing every integration at once: group tools by source, reveal toolsets only when the task needs them, and keep client configuration pointed at one controlled endpoint. Treat tool count as an interface budget, not just a server inventory.

The Failure Mode

An MCP server that supports tools answers tools/list with the currently available tools, including each tool's name, description, inputSchema and optional annotations. That is necessary when the model is about to choose a call. It becomes waste when the client imports every tool from every system before the user has asked for a task.

The cost is not only tokens. A long list creates a routing problem. The model must distinguish search, read, fetch, lookup and query across providers that may overlap in vocabulary. OpenAI's MCP guide explicitly calls out that exposing many tools can increase cost and latency, and provides allowed_tools for narrowing what a remote MCP server imports. Anthropic's tool guidance makes the same practical point from the agent side: dozens of servers and hundreds of tools can confuse selection, especially when functions overlap.

The symptom usually appears as hesitation or misrouting. The agent searches GitHub when the user meant Linear, uses a broad workspace search when a precise database lookup exists, or burns a first turn reading tool descriptions instead of doing the work. The sibling guide on tool descriptions helps within one opened source; this article is about reducing the number of choices shown in the first place.

Measure The Tool Surface

Before redesigning anything, count what the client actually sees. Run discovery against the MCP endpoint and record tool count, schema size and overlap by verb. The protocol-level request is ordinary JSON-RPC:

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/list",
  "params": {}
}

A response with 12 tools may be fine. A response with 140 tools from GitHub, Linear, Drive, Stripe, Postgres and an internal API is a prompt-design problem even if the server is fast. For each tool, capture the fields the model will read:

tool.name
length(tool.description)
keys(tool.inputSchema.properties)
tool.annotations.readOnlyHint
sourceProvider

Then sort by ambiguity, not just size. A short list of ambiguous tools can be worse than a longer list of precise tools. Flag repeated names like search, fetch and list; tools whose descriptions do not name the system; and schemas with generic parameters such as query without examples or limits. Also measure first-turn relevance: for ten representative prompts, mark the one or two sources that should be visible. If most prompts need one source but the model sees every source, the integration layer is doing no triage.

Use Source Folders

The clean boundary is source first, tool second. Instead of placing every upstream operation into the initial tool list, expose a small control surface that lets the agent inspect connected systems and open one source when needed:

{
  "tools": [
    {
      "name": "list_sources",
      "description": "List connected systems, their status and access summary."
    },
    {
      "name": "list_toolsets",
      "description": "List source-level toolsets and whether each is enabled."
    },
    {
      "name": "enable_toolset",
      "description": "Open one source and return its tools and argument shapes."
    },
    {
      "name": "disable_toolset",
      "description": "Close one source for this session."
    }
  ]
}

A source folder should correspond to the way a developer or operator names work: github_app, linear_product, stripe_billing, postgres_reporting, drive_board_docs. Do not split a provider into tiny folders if common tasks cross them; an issue triage task may need Linear issues, comments and labels in one opened set. Do not combine unrelated systems into one giant folder either, or the problem returns one level down.

The catalog entry should contain enough routing data to make the first choice. Include provider, workspace, read/write posture, current reachability and a short task summary. Keep it deterministic. The MCP spec recommends deterministic ordering for tool lists because stable ordering improves client caching and prompt reuse; the same logic applies to source catalogs. The sibling guide on lazy tool loading goes deeper on this pattern.

Keep Client Config Boring

The client should not have to know every upstream system. Point it at one MCP endpoint, then manage source selection behind that endpoint. Claude Code can add a remote HTTP MCP server from a URL with claude mcp add --transport http:

claude mcp add --transport http workspace-tools https://api.example.com/mcp \
  --header "Authorization: Bearer $WORKSPACE_MCP_KEY"

Cursor supports project-specific .cursor/mcp.json and global ~/.cursor/mcp.json. A remote server can be configured with a url and optional headers:

{
  "mcpServers": {
    "workspace-tools": {
      "url": "https://api.example.com/mcp",
      "headers": {
        "Authorization": "Bearer ${env:WORKSPACE_MCP_KEY}"
      }
    }
  }
}

The context saving only holds if the client config doesn't add the servers back one by one. In VS Code that means .vscode/mcp.json (keyed by servers) holds a single remote entry and no per-server entries next to it:

{
  "servers": {
    "workspace-tools": {
      "type": "http",
      "url": "https://api.example.com/mcp"
    }
  }
}

If you are using the OpenAI API directly with an MCP server, allowed_tools is a useful narrow gate for a workflow that needs only a known subset:

{
  "type": "mcp",
  "server_label": "workspace_tools",
  "server_url": "https://api.example.com/mcp",
  "require_approval": "never",
  "allowed_tools": ["list_sources", "list_toolsets", "enable_toolset", "call_tool"]
}

The point is not that every client has identical refresh behavior. It is that every client can start from a short server definition while the endpoint presents a smaller tool surface to the model.

Call Through An Explicit Dispatcher

Some clients refresh discovered tools during a session; others keep the first list for a long time. Design the lazy-loading layer so it does not depend on refresh timing. When the agent opens a source, return the opened tools and their argument shapes as data, then let it call an opened tool through a stable dispatcher:

{
  "jsonrpc": "2.0",
  "id": 2,
  "method": "tools/call",
  "params": {
    "name": "enable_toolset",
    "arguments": {
      "source": "linear_product"
    }
  }
}
{
  "source": "linear_product",
  "tools": [
    {
      "name": "search_issues",
      "arguments": {
        "query": "string",
        "limit": "integer"
      }
    }
  ]
}

The next call can stay inside the original visible tool list:

{
  "jsonrpc": "2.0",
  "id": 3,
  "method": "tools/call",
  "params": {
    "name": "call_tool",
    "arguments": {
      "source": "linear_product",
      "tool": "search_issues",
      "arguments": {
        "query": "label:bug status:triage oauth redirect",
        "limit": 10
      }
    }
  }
}

This shape keeps audit records clean because source, tool and arguments are separate fields. It also keeps policy enforceable. You can reject calls to unopened sources, block writes for read-only keys, and preserve upstream namespacing without making the model choose from every upstream operation at session start.

Test For Misrouting

Use evals that are close enough to expose confusion. A good suite includes prompts such as Find the PR that changed OAuth redirect, Find the Linear issue about OAuth redirect, Find the latest board note about OAuth redirect and Look up the invoice for the OAuth migration customer. The expected first action should be different for each prompt. If the agent opens the same source every time, your source summaries are too broad. If it opens three sources before searching, the first-level catalog is not decisive enough.

Also test negative and permission cases. Give the agent a read-only key and ask it to close a billing dispute. It should see that the billing source exists but fail or ask for a higher grant before any write reaches the upstream system. Disconnect one upstream and confirm it remains visible as unreachable rather than disappearing from the map. Ask for a code search when only issue search is opened; the agent should open the GitHub source or say it lacks that source, not force the Linear tool.

Track three numbers after each change: visible tool count at session start, first correct source rate, and wrong-tool call rate. Lowering visible tools is not a win if the model now opens sources blindly. The goal is a smaller first menu with enough signal to route correctly.

How SPYN3 handles this

A SPYN3 agent's first tool list holds navigation, not every upstream tool. list_toolsets shows each source and how many tools it has, and the agent opens only the sources the task needs. A workspace with five servers and a hundred tools therefore starts the conversation with a handful of tool definitions. disable_toolset closes a source again when the task moves on.

Built for the next step

Open sources only when needed

Use source folders and on-demand toolsets so the agent starts with a small, legible surface.

Lazy loading pattern
About SPYN3

SPYN3 is a consolidated MCP layer: one hosted endpoint in front of every system you connect — MCP servers, Postgres, REST APIs, Google Drive — with tools foldered by source and read/write permissions per key. Switching agents means moving one URL. Free plan available; current capabilities are documented on the product facts page.

Sources

  1. MCP specification: tools — tool discovery, invocation and security considerations. Verified 14 Oct 2026
  2. Anthropic: writing effective tools for agents — namespacing, response design and tool evaluations. Verified 14 Oct 2026
  3. Anthropic: effective context engineering — high-signal context and progressive disclosure. Verified 14 Oct 2026
  4. OpenAI API: MCP servers and connectors — remote MCP servers, deferred loading, approvals and risks. Verified 14 Oct 2026
  5. Anthropic docs: Claude Code MCP setup — Claude Code MCP commands, scopes, transports and JSON configuration. Verified 14 Oct 2026
  6. VS Code documentation: add and manage MCP servers — VS Code MCP configuration, trust and logging behavior. Verified 14 Oct 2026
  7. Cursor documentation: MCP integrations — Cursor MCP configuration locations, remote URL entries, headers and approval behavior. Verified 14 Oct 2026