Scoped MCP Server Discovery: Most-Specific-Wins Resolution¶
Across user, workspace, and project MCP configs, the most-specific scope defining a server name wins, and duplicate definitions are suppressed before the agent sees them.
The Three Scopes¶
MCP server definitions can live at three places. Each scope answers a different question — "what do I always want available?" "what does this team agree on?" "what stays personal to this checkout?"
| Scope | Claude Code | VS Code | Use for |
|---|---|---|---|
| User | ~/.claude.json |
mcp.json in the user profile folder |
Personal utilities used across every project |
| Workspace | .mcp.json at the project root |
.vscode/mcp.json, plus workspace-level .mcp.json since 1.118 |
Team-shared servers checked into version control |
| Local | ~/.claude.json, scoped to the project path |
(not exposed) | Personal experiments and credentials you do not want in VCS |
VS Code 1.118 (April 29 2026) added workspace-level .mcp.json "aligning with other tools such as the Copilot CLI" (VS Code 1.118 release notes); the file paths for Claude Code and VS Code workspace/user scopes come from the Claude Code MCP docs and VS Code MCP servers docs. Cursor follows the same shape with .cursor/mcp.json (project) and ~/.cursor/mcp.json (global) (Cursor MCP docs).
Most-Specific-Wins¶
When the same server name appears at multiple scopes, the host picks one. Claude Code documents the precedence explicitly: "When the same server is defined in more than one place, Claude Code connects to it once, using the definition from the highest-precedence source: 1. Local scope 2. Project scope 3. User scope 4. Plugin-provided servers 5. claude.ai connectors" (Claude Code MCP docs). The three file-based scopes match by name; plugins and connectors match by endpoint.
VS Code 1.118 applies the same rule: "By default, only the most-specific MCP server will be enabled, and enabling a server will disable other servers by the same name" (VS Code 1.118 release notes).
Why Dedup Matters¶
MCP defines no collision resolution at the protocol level — the host decides. Loading two servers under the same name forces either namespace prefixing (extra tokens in every tool description) or arbitrary selection (action-at-a-distance routing). Most-specific-wins resolves the collision deterministically before the tool list reaches the model. This matters because tool-search accuracy degrades as the visible toolset grows (Anthropic: advanced tool use).
Operational Discipline¶
Three habits keep layered configs debuggable:
- Inspect the effective config, not the source files. In Claude Code,
/mcplists what is loaded after dedup; in VS Code, search@mcp @installedin the extensions view (VS Code 1.118 release notes). - Approve project-scope servers explicitly. Claude Code prompts before activating servers from a project
.mcp.json; reset choices withclaude mcp reset-project-choices(Claude Code MCP docs). - Rename when intent differs. If a developer's
githubserver points at a private fork and the team'sgithubserver points at the public registry, the dedup rule silently disables one. Rename one of them — layering is for overrides, not for parallel intents.
When This Backfires¶
Most-specific-wins is the right default, but the same silent determinism that prevents duplicates can hide problems. Treat these as failure conditions, not edge cases:
- Different intents collide under one name. When a developer's
githubserver points at a private fork and the team'sgithubpoints at the public registry, the dedup rule disables one with no error — the agent silently routes to whichever scope won. Shadowing is for overrides of the same intent; parallel intents need distinct names. - The winning scope is invisible at the call site. Because resolution happens before the tool list reaches the model, a tool that misbehaves gives no hint that a different-scope definition shadowed the one you were reading. Debugging starts from the wrong file unless you inspect the effective config first.
- Local scope outlives its usefulness. A local-scope override added for a one-off experiment persists in
~/.claude.jsonand keeps shadowing the team default indefinitely. Nothing prompts you to retire it, so a stale personal entry can mask an updated project server for weeks.
In each case the alternative — explicit per-scope naming, or surfacing every loaded server without dedup — trades token cost and a longer tool list for transparency. Prefer most-specific-wins when scopes genuinely override the same server, and reach for distinct names the moment two definitions mean different things.
Example¶
A team ships a Postgres MCP server in their repo's .mcp.json (project scope). A developer wants a local variant pointing at a staging database without breaking the team default:
Project .mcp.json — checked into version control:
{
"mcpServers": {
"postgres": {
"command": "mcp-server-postgres",
"args": ["--url", "${env:DATABASE_URL}"]
}
}
}
Local override — added with claude mcp add postgres --scope local:
claude mcp add postgres --scope local -- mcp-server-postgres --url postgresql://localhost:5432/staging
Claude Code stores the local entry under the developer's home .claude.json and prefers it for this project. The team default still loads in everyone else's checkouts. Running /mcp in the developer's session shows one postgres server — the local one — confirming the project-scope entry was correctly shadowed.
Key Takeaways¶
- Three canonical scopes (user, workspace/project, local) carry distinct intents — personal utilities, team agreement, private overrides
- Most-specific-wins is the dedup rule: the closest scope defining a name disables matches at outer scopes
- Inspect the effective config (
/mcp,@mcp @installed), not the source files, when debugging shadowed entries - Rename rather than rely on shadowing when servers at different scopes have different intents