VSCode mcp.json gallery and settings.json mcpServers silently launch duplicate MCP server processes for the same name
Summary
In the Claude Code VSCode extension, MCP server configuration can be loaded
from two sources: the VSCode-side mcp.json gallery and the user-level~/.claude/settings.json mcpServers block. The merge priority and
deduplication behavior between these two sources is not documented, and
the result is silent duplication: a server declared in both sources is
launched twice with whichever env block happens to be resolved last.
This was first reported in adjacent scope (plugin vs claude.ai collision,
#58375 and inverse #59470) and as a multi-plugin dedup request (#63749), but
the specific case of "VSCode mcp.json gallery AND user-levelsettings.json mcpServers declaring the same name" is not covered by an
existing issue. This report focuses on that specific case.
Environment
anthropic.claude-codeVSCode extension, panel mode, current latest as of
2026-06-09 (cc_version ≈ 2.1.165)
~/.config/Code - Insiders/User/mcp.json— VSCode gallery, 9 servers
declared
~/.claude/settings.json— user-level, 6 servers declared in
mcpServers, 2 names overlapping with mcp.json (context7, apify)
- Debian forky/sid, VSCode Insiders 1.96+
Repro
- Declare the same MCP server in both config sources with the same name
but different env (e.g., a different APIFY_TOKEN in each):
~/.config/Code - Insiders/User/mcp.json:
``json``
{
"servers": {
"apify": {
"command": "npx",
"args": ["-y", "@apify/mcp-server"],
"env": { "APIFY_TOKEN": "value-from-gallery-mcp-json" }
}
}
}
~/.claude/settings.json:
``json``
{
"mcpServers": {
"apify": {
"command": "npx",
"args": ["-y", "@apify/mcp-server"],
"env": { "APIFY_TOKEN": "value-from-user-settings-json" }
}
}
}
- Open the Claude Code extension panel. Wait for the MCP server list to
stabilize (no pending status indicators).
- Inspect processes:
pgrep -fa "apify-mcp-server"(or the equivalent
regex for the server's binary).
- Inspect tool surface:
/mcpandclaude mcp list.
Observed
pgrepreturns twoapify-mcp-serverprocesses, with different
APIFY_TOKEN values in their cat /proc/<pid>/environ output.
/mcpandclaude mcp listshow the server once — there is no
indication of which config source won, no warning that a duplicate
exists, no introspection that says "this came from mcp.json" or "this
came from settings.json".
- The visible tool set is the union of both, with last-write-wins for
per-server config (env, args, transport). Last-write is not documented
and not introspectable.
- Cost impact: paid MCP servers (e.g.,
apifyis a paid upstream
service) consume double their normal quota per session because both
processes are making real upstream calls when invoked.
- Security impact: an attacker with write access to
mcp.json(e.g.,
a malicious VSCode extension) can shadow the user's settings.json
configuration for any server declared in both — including changing
APIFY_TOKEN to a token the attacker controls. The user sees no warning.
Expected
The extension should:
- Document the merge priority (last-wins is the observed default, but
undocumented).
- Deduplicate by name when both sources declare the same server — and
surface the dedup decision visibly (e.g., "apify: deduped, settings.json
won over mcp.json" in /mcp output).
- Emit a one-time warning the first time a session loads with a name
present in both sources, with the names of the conflicting sources.
- Provide an introspection command (e.g.,
/mcp sourcesor
claude mcp doctor) that lists which loaded server came from which
source.
Why this matters
- Users have no way to detect a duplicate. The
/mcpoutput is a flat
list with no source attribution.
- For paid MCP servers, the duplicate doubles the cost per session with
no user-visible signal. Bill goes up, user does not know why.
- The silent last-wins behavior is exploitable by an attacker who can
write to mcp.json (e.g., a malicious VSCode extension installed via
the marketplace). The attacker can shadow the user's settings.json
config for any server, including swapping auth tokens, redirecting
upstream URLs, or appending extra args. The user would not see any
warning.
- Migration between the two config sources is fragile. Users moving
servers from mcp.json to settings.json (or vice versa) have no tool
to detect "you have duplicates, here's which one wins".
Suggested fix surface (any one of these would be a meaningful first step)
- (a) Document the merge priority in
https://docs.claude.com/en/docs/claude-code/mcp (the page that
currently only covers the user-level mcpServers block).
- (b) Add
/mcp sourcesintrospection in the extension and
claude mcp doctor in the CLI. Each MCP entry should show which file
it came from.
- (c) Emit a one-time warning in the extension panel the first time a
session loads with a server name present in both sources, with the
conflicting source filenames.
- (d) Default to a deterministic resolution (e.g., user-level
settings.json always wins, with the mcp.json entry ignored and
logged) and surface the resolution in /mcp output.
Scope boundary
This issue is strictly about the load-time merge of the two local
file-based config sources (mcp.json and settings.json mcpServers).
It is not about:
- Plugin-provided MCP servers (covered separately by #63749 for
multi-plugin dedup, and #59310 for plugin-MCP tools not exposed).
claude.aiconnector collision detection (covered by #58375 and its
inverse #59470).
- Claude Desktop MCP server leaks (covered by #61564 and #53134).
- MCP server content (the tools, resources, prompts the server provides).
- MCP server install / marketplace / permission systems (separate
concerns, separate config blocks).
The distinguishing axis of this report is: same binary, two local
file-based config sources, two processes spawned, no visible signal.
Related but distinct
- #58375 —
claude.aiconnector vs plugin MCP, URL-based collision
detection. Different surface (server-side catalog vs local files).
- #59470 — Inverse of #58375. Same surface difference.
- #63749 — "Reduce duplicate MCP server processes when multiple plugins
wrap the same binary". Multi-plugin scope, not multi-config-file scope.
- #61564, #53134 — Claude Desktop leaks, not VSCode extension.
- #59310 — Plugin MCPs not exposing tools to AI. Different bug
entirely.
Validation
- Environment:
claude --version2.1.165;~/.config/Code - Insiders/User/mcp.json
with 9 servers; ~/.claude/settings.json with 6 servers in
mcpServers; 2 names (context7 and apify) configured in both.
pgrep -fa "apify-mcp-server"showed 2 processes running with
different APIFY_TOKEN env values (verified via cat /proc/<pid>/environ |).
tr '\0' '\n' | grep APIFY_TOKEN
- The extension's
/mcpoutput showed the server once, with no
indication of which config source won. There is no source attribution
in the visible UI.
- Removing the duplicates from
mcp.json(keeping the entries in
settings.json only) reduced the process count to 1 and left the
available tool surface unchanged — confirming the duplicate was
loaded but shadowed.
- This is reproducible from a clean state on a Debian forky/sid host
with VSCode Insiders 1.96+ and the anthropic.claude-code extension
at the version above.