claude-code - 💡(How to fix) Fix [FEATURE] Reduce duplicate MCP server processes when multiple plugins wrap the same binary

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…

Root Cause

The processes aren't deduplicated because — by current behavior — different args means different identity. Per the docs, plugin servers match by endpoint (same command or URL); the v2.1.152 fix corrected env being incorrectly treated as part of identity. There is no current mechanism — for plugin authors or users — to opt into "treat args as a per-plugin filter applied to one shared process."

Fix Action

Fix / Workaround

  • Lazy / on-demand connections (#63251). Defers spawning until a tool is invoked — a substantial mitigation. But once a session actually uses tools from each plugin (the common case for "active" plugins), the steady-state process count is unchanged.

Current workaround: disable plugins until process count is bearable. This works but defeats the purpose of having a plugin ecosystem.

Code Example

{
    "mcpServers": {
      "example": {
        "command": "example-mcp",
        "args": ["--include-tools", "A,B"],
        "shareable": true,
        "argsAreToolFilter": true,
        "allowedTools": ["A", "B"]
      }
    }
  }
RAW_BUFFERClick to expand / collapse

Preflight Checklist

  • I have searched existing requests and this feature hasn't been requested yet
  • This is a single feature request (not multiple features)

Problem Statement

When N plugins each declare a stdio MCP server with the same command but different args, Claude Code spawns N independent subprocesses. In ecosystems where many plugins wrap a single MCP binary and customize each plugin's tool surface via CLI flags (--include-tools, --exclude-tools, capability scopes), the process count and memory footprint scale linearly with plugin count — even though the underlying server is the same.

Concrete observation. A developer machine with ~15 plugins enabled, all wrapping the same MCP server binary with different filter args:

  • 60 subprocesses of the same binary
  • ~5.7 GiB total RSS
  • Args differ only in tool-filter flags; none of them change semantics like --port or --workspace-dir
  • Processes are not culled when idle

The processes aren't deduplicated because — by current behavior — different args means different identity. Per the docs, plugin servers match by endpoint (same command or URL); the v2.1.152 fix corrected env being incorrectly treated as part of identity. There is no current mechanism — for plugin authors or users — to opt into "treat args as a per-plugin filter applied to one shared process."

Architectural note (preempting "you don't understand MCP"). The MCP spec defines stdio as 1 client : 1 subprocess; there's no in-protocol multiplexing for stdio. This is therefore explicitly a host-level orchestration concern: Claude

  • Processes are not culled when idle

The processes aren't deduplicated because — by current behavior — different args means different identity. Per the docs, plugin servers match by endpoint (same command or URL); the v2.1.152 fix corrected env being incorrectly treated as part of identity. There is no current mechanism — for plugin authors or users — to opt into "treat args as a per-plugin filter applied to one shared process."

Architectural note (preempting "you don't understand MCP"). The MCP spec defines stdio as 1 client : 1 subprocess; there's no in-protocol multiplexing for stdio. This is therefore explicitly a host-level orchestration concern: Claude Code would act as a single MCP client to a shared subprocess and apply per-plugin tool visibility at the host layer (not push multiplexing into the protocol or the server).

Proposed Solution

Proposed Solution: A plugin-author opt-in for shareable MCP servers. Let a plugin's mcpServers entry declare:

{
  "mcpServers": {
    "example": {
      "command": "example-mcp",
      "args": ["--include-tools", "A,B"],
      "shareable": true,
      "argsAreToolFilter": true,
      "allowedTools": ["A", "B"]
    }
  }
}

Semantics:

  • When two or more plugins both declare a shareable: true server with the same command, Claude Code spawns one subprocess. It is launched with no filter args (or with a documented merge of argsAreToolFilter: true flags); filter semantics are enforced host-side.
  • Each plugin's allowedTools defines what its agents can see/call from the shared process. Tool calls outside that set are rejected by the host before reaching the server.
  • shareable: true is an explicit assertion by the plugin author that the server is stateless across connections — no per-connection auth, no resources.subscribe, no per-session working directory implied via args. Servers that aren't safe to share simply don't set this flag.

This is opt-in: existing plugins are unaffected; foot-gun cases (--port, --workspace-dir, per-connection auth) are excluded by construction.

Companion: heuristic warning. Independent of the opt-in, when CC detects ≥3 plugin MCP server entries with the same command differing only in args, log a one-line notice surfacing the cost:

note: 12 plugin MCP servers point at the same command with different args. This spawns 12 independent processes. See <docs URL> if those args are tool filters and the server is shareable.

This is cheap, makes the cost discoverable, and is useful even before the opt-in lands.

The ask, more abstractly: acknowledge that the "many plugins wrap the same stdio binary with different filter args" pattern has no escape hatch today, and consider an opt-in mechanism for plugin authors to declare shareable. The specific schema above is a sketch — the maintainers may have a better implementation idea.

Alternative Solutions

Existing mechanisms that partially overlap but don't fully cover this:

  • Plugin dependencies (#9444 / plugin-dependencies docs, shipped in stages around v2.1.110–v2.1.143). Solves plugin file/definition duplication: a dependent plugin can use a base plugin's tools without redeclaring them. But this requires authors across the ecosystem to refactor into a base + dependents pattern. For users consuming many independently authored plugins today, this isn't actionable. It also doesn't help when two plugins both declare the same server with different filter args by design.

  • Streamable HTTP transport. When a server supports it, multiple clients can share one process per spec. This is the cleanest path for servers that can run as HTTP. It doesn't help users whose plugins wrap a stdio-only binary they don't control.

  • Lazy / on-demand connections (#63251). Defers spawning until a tool is invoked — a substantial mitigation. But once a session actually uses tools from each plugin (the common case for "active" plugins), the steady-state process count is unchanged.

  • Existing dedup behavior. Matches plugin servers by command/URL; in observed behavior, different args keep servers distinct. There's no opt-out.

Current workaround: disable plugins until process count is bearable. This works but defeats the purpose of having a plugin ecosystem.

Priority

Medium - Would be very helpful

Feature Category

MCP server integration

Use Case Example

  1. I install ~15 plugins from a marketplace where many of them wrap the same MCP server binary, each plugin tailoring its tool surface via CLI flags like --include-tools X,Y,Z or --agent-sop-filter foo/*.
  2. On claude startup, CC spawns one subprocess per plugin — 60 processes total, ~5.7 GiB RSS, all of the same binary.
  3. With plugin-author opt-in to shareable: true, those 60 processes collapse to 1. Per-plugin tool visibility is preserved via host-side allowedTools filtering.
  4. Result: a developer can keep all their plugins enabled without their machine grinding to a halt, and plugin authors don't need to coordinate ecosystem-wide refactors.

Additional Context

  • CLAUDE_CODE_SESSION_ID (v2.1.154) is injected per stdio server. In the shared model, the host owns one connection — the session ID would be the host's, not per-plugin. Servers using it for attribution would need to read per-call metadata instead.

    • Filter-arg union semantics. When two plugins set --include-tools A,B and --exclude-tools A, is the combined surface {B}? Cleanest answer: launch with no filter args at all, enforce host-side from each plugin's allowedTools.
    • Lifecycle. When one plugin is disabled or reloaded, the shared subprocess must keep running for the others. Refcount-based ownership is the obvious approach but adds coordination.
    • Server-side resource savings from filter args. Some servers use --include-tools/--exclude-tools to skip initializing backends for excluded tools, lowering their own memory. A shared process loses that — but for the workloads this issue targets (many plugins, mostly idle), the duplication cost dominates.

    Related issues already triaged in the Alternative Solutions section: #9444, #63251. Also adjacent: #45880 (multi-session multiplication; orthogonal axis), #62676 / #31682 / #38572 (existing dedup docs).

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

claude-code - 💡(How to fix) Fix [FEATURE] Reduce duplicate MCP server processes when multiple plugins wrap the same binary