MCP Server Discovery
- Entity ID:
ent-20260410-a1a4325ff3b6 - Type:
mechanism - Scope:
shared - Status:
active - Aliases: MCP config, MCP server configuration, .mcp.json
Description
MCP server discovery (src/services/mcp/config.ts) loads server definitions from 7 configuration scopes, merges them with a defined precedence order, deduplicates across sources, enforces deny/allow policies, and provides the final server list for connection. It supports 8 server config types (stdio, SSE, SSE-IDE, HTTP, WebSocket, WS-IDE, SDK, and claude.ai proxy).
Configuration scopes
Servers are discovered from 7 scopes (later wins on name conflicts):
| Priority | Scope | Source | Notes |
|---|---|---|---|
| 1 (lowest) | claudeai |
fetchClaudeAIMcpConfigsIfEligible() |
Claude.ai hosted connectors |
| 2 | plugins | getPluginMcpServers() |
From enabled plugins |
| 3 | user |
~/.claude/settings.json |
User-level config |
| 4 | project |
.mcp.json in project root |
Only approved servers |
| 5 | local |
Local settings | Per-machine overrides |
| 6 | dynamic |
Runtime arguments | Passed programmatically |
| 7 (highest) | enterprise |
${managedPath}/managed-mcp.json |
Takes exclusive control |
When enterprise config exists, it takes exclusive control — all other scopes are ignored.
Config file format
.mcp.json is validated against McpJsonConfigSchema:
{
"mcpServers": {
"server-name": {
"type": "stdio",
"command": "node",
"args": ["server.js"],
"env": { "KEY": "value" }
}
}
}
Written atomically: writeMcpjsonFile() uses temp file, datasync(), then rename(), preserving original file permissions.
Server config types
| Type | Required fields | Optional |
|---|---|---|
stdio |
command |
args, env, type (defaults to stdio) |
sse |
type: 'sse', url |
headers, headersHelper, oauth |
sse-ide |
type: 'sse-ide', url, ideName |
ideRunningInWindows |
http |
type: 'http', url |
headers, headersHelper, oauth |
ws |
type: 'ws', url |
headers, headersHelper |
ws-ide |
type: 'ws-ide', url, ideName |
authToken, ideRunningInWindows |
sdk |
type: 'sdk', name |
— |
claudeai-proxy |
type: 'claudeai-proxy', url, id |
— |
Deduplication strategies
Three strategies prevent duplicate registrations:
- Plugin dedup (
dedupPluginMcpServers) — matches by signature (stdio command array or URL). Manual config wins over plugin; first plugin wins ties. - Claude.ai dedup (
dedupClaudeAiMcpServers) — only enabled manual servers count as dedup targets. - CCR proxy unwrap (
unwrapCcrProxyUrl) — extracts the original vendor URL from CCR proxy path markers (/v2/session_ingress/shttp/mcp/,/v2/ccr-sessions/) so a proxied server and its direct URL are recognized as the same server.
Policy enforcement
- Deny list:
isMcpServerDenied()checksdeniedMcpServersin settings by name, command, or URL pattern. Supports wildcard*matching. - Allow list:
isMcpServerAllowedByPolicy()checksallowedMcpServersallowlist. - Plugin-only mode:
isRestrictedToPluginOnly('mcp')blocks all non-plugin servers. - Enterprise managed: when
managed-mcp.jsonexists at the enterprise managed path, it takes exclusive control of all MCP configuration.
Connection concurrency
getMcpToolsCommandsAndResources() splits servers into local (stdio/sdk) and remote groups, processing each with different concurrency limits via processBatched(). Local servers get lower concurrency (process spawning overhead); remote servers get higher concurrency (network I/O only).
Platform handling
Config validation detects Windows + npx command without cmd /c wrapper and emits a warning with a fix suggestion. This is a common gotcha on Windows where npx requires a shell wrapper.
Trade-offs
- 7 scopes — very flexible for different deployment scenarios (personal, team, enterprise) but complex precedence. Enterprise override simplifies managed environments at the cost of user freedom.
- Atomic writes —
datasync()+rename()prevents corrupt.mcp.jsonbut is slower than a direct write. - Approval required for project servers —
.mcp.jsonservers only connect if markedapproved, preventing untrusted repos from auto-connecting to arbitrary servers. Adds friction for legitimate use. - CCR proxy unwrap — deduplicates proxied servers correctly but depends on specific URL path patterns that could break if the proxy format changes.
Depends on
- config-schemas — Zod validation schemas for server configs
- permission-pipeline — deny/allow rule enforcement
Key claims
- Enterprise MCP config takes exclusive control, disabling all other config scopes
- Project-level
.mcp.jsonservers require explicit approval before connecting - Config files are written atomically (temp file + datasync + rename)
- Windows users are warned when using
npxwithoutcmd /cwrapper
Relations
part_ofmcp-subsystemdepends_onconfig-schemasdepends_onpermission-pipeline
Sources
src-20260409-a5fc157bc756, source code analysis of src/services/mcp/config.ts