[BUG] `allowedMcpServers` from remote managed settings not enforced when the settings fetch is slow: MCP servers connect first and are never re-checked
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet. Closest: #93727 (same symptom class, closed without reproduction steps) and #70181 (the inverse precedence problem, remote settings overriding local managed rules). This report supplies the log sequence and a deterministic reproduction for the #93727 class.
- [x] This is a single bug report
- [x] I am using the latest version of Claude Code (2.1.280, the version pinned in our managed image; the autoupdater is disabled by policy)
What's Wrong?
When remote managed settings are slow to load, Claude Code connects MCP servers before the policy is applied, then logs Remote settings: Loading promise timed out, resolving anyway. Servers whose URLs are not in allowedMcpServers connect successfully, stay connected for the whole session, and their tools remain callable. When the policy is applied later, existing connections are not re-evaluated.
A server that was explicitly logged as blocked by enterprise policy before prefetch still went on to connect six seconds later.
The same configuration is blocked or allowed depending only on whether the remote-settings load beats MCP startup. Across roughly fifteen first-run sessions on identical container images the same day (same version, same policy), every session whose managed settings resolved within about 6 s of process start connected the unlisted servers, and every session that took 20 s or more blocked them, with one ambiguous case. In the blocked sessions, a server that is on the allowlist connected normally, so the allowlist works whenever it is loaded in time.
What Should Happen?
Any of:
- MCP server startup waits for managed-settings resolution when a remote source is configured; or
- on fetch timeout, security-relevant keys (
allowedMcpServers,deniedMcpServers,permissions.deny) fail closed instead of "resolving anyway"; or - when remote settings are applied after startup, live MCP connections are re-evaluated against
allowedMcpServersand disconnected if no longer permitted.
At minimum, a server logged as blocked by enterprise policy before prefetch must not be allowed to connect.
Error Messages/Logs
# One interactive session, --debug-file, times UTC.
# Server A: plain-http MCP server, user scope, URL NOT on allowlist.
# Server B: https MCP server, local scope, URL NOT on allowlist.
08:52:11.899 [pre-warm claude -p process] Remote settings: Fetched successfully
08:52:11.913 [pre-warm] Remote settings: Applied for this non-interactive run; consent deferred — not persisting the disk cache as consented
08:52:12.424 [interactive] Acquired PID lock on running version: .../2.1.280
08:52:13.397 [STARTUP] MCP configs resolved in 4ms (awaited at +1519ms)
08:52:13.397 [WARN] MCP server blocked by enterprise policy before prefetch: <server A>
08:52:13.408 [MCP] --mcp-config servers running fully async (nonblocking)
08:52:13.408 [3P telemetry] Remote managed settings fetch settled, initializing telemetry
08:52:19.360 Remote settings: Fetched successfully
08:52:19.578 MCP server "<server B>": Starting connection with timeout of 30000ms
08:52:19.583 MCP server "<server A>": Starting connection with timeout of 30000ms
08:52:19.652 MCP server "<server A>": Successfully connected (transport: http) in 69ms # flagged as blocked 6 s earlier
08:52:20.071 MCP server "<server B>": Successfully connected (transport: http) in 494ms # not on allowlist, never flagged
08:52:48.868 Remote settings: Loading promise timed out, resolving anyway (load and fetch barriers)
09:09:07.505 Remote settings: Saved to ~/.claude/remote-settings.json
09:09:07.506 Remote settings: Applied new settings successfully # policy now in force; A and B still connected
# Fresh process in the same directory once the cache existed on disk:
[WARN] MCP servers blocked by enterprise policy before prefetch: <server A>, <server B>
Steps to Reproduce
The race needs two conditions: no consented settings cache on disk, and a slow settings fetch. Both can be forced.
- Use an org with remote managed settings that contain an
allowedMcpServerslist. - Remove any cached copy so the session must fetch:
rm -f ~/.claude/remote-settings.json ~/.claude/remote-settings-consent.json - Add latency to outbound HTTPS so the settings fetch takes longer than MCP startup. Either of:
- in a Linux container:
tc qdisc add dev eth0 root netem delay 5000ms - or route through a delaying proxy (for example toxiproxy with a
latencytoxic of 5000 ms) and setHTTPS_PROXYto it
- Register a server whose URL is not on the allowlist:
claude mcp add --transport http probe https://<any-reachable-host-not-on-the-list>/mcp
- Start
claude --debug-file /tmp/d.logand run/mcp. - Observe:
probeis connected. In the log,MCP server "probe": Successfully connectedappears beforeRemote settings: Loading promise timed out, resolving anyway, and laterRemote settings: Applied new settings successfullyappears withprobestill connected and its tools callable. - Exit and start
claudeagain with the cache now on disk. ObserveMCP servers blocked by enterprise policy before prefetch: probeandprobeabsent from/mcp.
Note for anyone trying to work around it: pre-warming with a non-interactive claude -p does not help. It applies the settings for its own run but defers consent and does not persist the disk cache, so the first interactive session in a fresh environment always races.
Claude Model
N/A
Is this a regression?
Not sure
Last Working Version
Unknown
Claude Code Version
2.1.280 (Claude Code)
Platform
CLI
Operating System
Linux (x86_64 container, Node v26.3.0)
Terminal/Shell
bash, xterm-256color
Additional Information
Impact. An enterprise allowedMcpServers policy is bypassable by timing. A user, or an agent acting on injected instructions with shell access, can claude mcp add an arbitrary HTTP server and it connects whenever the settings fetch is slow. permissions.deny rules that live only in remote settings are absent during the same window (not exercised here, but the same load path). The environment where this is most likely is exactly where enterprise controls are relied on most: fresh ephemeral containers behind a corporate egress proxy, where every first session has no cache and a slow fetch.
Related issues. #93727 reported the same symptom class and was closed for lack of reproduction steps; the steps above reproduce it. #70181 describes the inverse precedence problem, remote settings overriding local managed-settings.json. #95020 reports claude mcp list ignoring allowedMcpServers. #94803 covers plugin-sourced servers under the enterprise policy. They point at the same area: policy resolution and MCP startup are not sequenced.