claude-in-chrome: browser silently returns fabricated/cached content for localhost navigation instead of reaching the real dev server (no error surfaced)
Summary
While using the mcp__claude-in-chrome__* tools from Claude Code to test a local dev server, all browser-automation calls (navigate, get_page_text, javascript_exec, find, read_page) reported success and returned content consistent with a fully-loaded, interactive copy of my app — but the underlying HTTP requests never reached the actual local server process. No error, denial, or permission prompt was ever shown; the tab simply displayed content that could not have come from my server. The user also observed that Chrome was not running/visible in their Dock while this was happening.
This looks related to the same subsystem as #82626 and #83079 (Claude in Chrome's handling of localhost/MCP-bridge navigation), but the failure mode is different and more concerning: those report an explicit denial (Navigation to this domain is not allowed / permission-popup auto-deny). Here there was no error at all — every tool call succeeded, and the returned page content looked completely legitimate, which makes the failure much harder to notice.
Environment
- Claude Code CLI 2.1.222, macOS 15.7.7 (24G720), arm64 (Apple Silicon)
claude-in-chromeMCP tools (navigate,computer,get_page_text,javascript_exec,find,read_page,read_network_requests,tabs_context_mcp,tabs_create_mcp)- Local dev server: Vite 8.0.16, tested on ports 8081/8082/8083, both IPv4 (
127.0.0.1), IPv6 (::1), dual-stack, and via the literal hostnamelocalhost
Reproduction / evidence
- Started a local HTTP server via the Bash tool (both with and without the sandbox, i.e.
dangerouslyDisableSandbox: true— same result either way) that unconditionally responds to any request with a unique, unpredictable marker string (UNIQUE-DEBUG-MARKER-<timestamp>) and logs every incoming connection (method, URL, headers,remoteAddress) to a file. - Confirmed via
curl(both--ipv4/127.0.0.1and-g "http://[::1]") andnetstat -anthat the server was genuinely listening and reachable on this machine, on both address families. - Used
mcp__claude-in-chrome__navigateto point a tab at this exact server (triedhttp://localhost:PORT/,http://127.0.0.1:PORT/, andhttp://[::1]:PORT/— all three, separately, same result). navigatereported success.get_page_text/javascript_execon the resulting tab returned a full, real, complex single-page app (the project I was working on — correct page title, menu bar, full app UI, client-side route redirect from/to a sub-path, etc.) — content this one-line debug server is structurally incapable of producing.- Checked the debug server's log file: zero requests recorded from the browser session, despite the "successful" page loads above. Only my own
curlverification requests appear in the log. - Repeated this multiple times across fresh dev-server restarts, fresh tabs (
tabs_create_mcp), and after fully quitting/relaunching Chrome. Same result every time. - Ruled out (with direct evidence, not just theory):
- HTTP/browser caching — tested with
{cache: 'no-store'}and cache-busting query strings; also proved impossible once the debug-server test (step 4-5) showed a never-before-requested random path also returned fabricated content. - Service worker interference —
navigator.serviceWorker.getRegistrations()showed zero registrations at the time of the test; unregistering explicitly made no difference. - Stale/zombie server process —
lsof -i :PORT/ps aux/netstatconfirmed only one process was ever bound to the port, matching the freshly-started debug server, with aDateresponse header only seconds old (when testing against the real dev server) proving live generation, not a replay. - IPv4/IPv6 mismatch — found and fixed a real, separate bug (Vite's
--hostdefaults to IPv6-loopback-only on this machine;127.0.0.1was flatly connection-refused). Fixing this (dual-stack, then IPv6-only via explicit--host ::1, then navigating to the literalhttp://[::1]:PORT/URL) made no difference to the core issue. - Hostname-string-based interception — tested
localhost,127.0.0.1, and the raw[::1]literal; identical behavior for all three, so it isn't specifically about the string"localhost".
- Separately noticed:
mcp__claude-in-chrome__computerscreenshot actions (Page.captureScreenshotover CDP) intermittently timed out after 30s ("renderer may be frozen or unresponsive") while lighter calls (get_page_text,javascript_exec) on the same tab succeeded normally in the same window of time. - The user reported Chrome was not visible in their Dock at all while I was successfully driving "it" — no window, no icon — yet
tabs_context_mcpcontinued to report the same tab IDs as available and interactions with them kept succeeding.
Why this matters
This is a local-dev-server testing workflow (npm run serve:python style), which the tool is explicitly meant to support. Unlike #82626/#83079's explicit-denial failure mode — annoying but obvious — this one is silent: every tool call reports success, and the content returned is plausible enough (a real, previously-cached/rendered copy of the actual target app, correctly reflecting client-side routing) that an agent (or a user skimming the transcript) could easily believe a real local-server verification passed when it didn't touch the real server at all. I only caught it because the served content stopped matching file edits I'd just made, and had to build a custom marker-response server to prove it conclusively.
Suggested investigation
Given #83079's finding that the extension has a bypassLocalhostForMcp code path and a PermissionManager that treats navigate differently from passive reads (screenshot/read_page/find are exempted from permission gating) — it seems plausible that in this session, navigate to a local origin was silently routed to some fallback/mocked/cached path instead of actually opening the real connection, without that ever surfacing as an error to either the MCP client or (per the user's report) as a visible Chrome window. If there's a cached-preview or offline-fallback rendering path in the extension that can be reached without a live, connected Chrome window, that would explain everything observed here.
What I could not determine
I don't have visibility into how/where the driven Chrome instance actually runs (local process vs. remote/managed), so I can't say definitively whether this is a routing bug (right browser, wrong network path) or a session bug (no real browser at all, content synthesized from a prior/cached session). Happy to provide full session logs/timestamps if useful for triage — this was investigated interactively via Claude Code and I can reconstruct the exact sequence of tool calls.
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗