[Bug] MCP `navigate` to localhost auto-denied by 30s popup; `bypassLocalhostForMcp` flag never enabled

Status Open
Reported on v2.1.220
Maintainer reply None cached
Activity 0 comments · opened Aug 1, 2026

Bug Description

# Claude-in-Chrome — MCP navigate on localhost is gated by a 30s auto-deny popup; the bypassLocalhostForMcp flag exists but is never enabled ## Summary When driving a local dev server over MCP (Claude Code → extension), navigate to a localhost origin frequently returns Permission denied by user and only succeeds after a manual retry. The extension already contains a bypassLocalhostForMcp code path that would fix this, but it is never enabled in the shipping build, there is no setting to turn it on, and grants are not persisted — so the friction repeats on every fresh tab. ## Environment - Extension: "Claude" fcoeoabgfenejglbffodgkkbkcdhcgfn, v1.0.84 (latest on the Web Store as of 2026-08 — update endpoint returns 1.0.84). - Chrome 151, macOS. - Client: Claude Code CLI over the MCP bridge / native messaging. - Chrome host access: <all_urls> granted, active, withholding_permissions:false ("On all sites"). Claude Code allowlist: mcp__claude-in-chrome__navigate allowed. So neither Chrome host permissions nor the client allowlist is the gate. ## Root cause (from the shipped bundle, assets/mcpPermissions-*.js + service-worker.ts-*.js) 1. PermissionManager.checkPermission(url) exempts passive reads (screenshot / read_page / find) but gates navigate (and click / execute_javascript) to a new origin as a DOMAIN_TRANSITIONpermission_required. Observed effect: screenshot succeeds while navigate is denied on the same tab. 2. A bypassLocalhostForMcp flag exists: ``js if (!forcePrompt && bypassLocalhostForMcp && isLocalhostUrl(url)) return { allowed: true, needsPrompt: false }; ` but every PermissionManager is constructed as new kD(() => false, { trackEvent }) — the option is never passed, so it defaults to false. Grep of the whole bundle shows bypassLocalhostForMcp is **never set to true anywhere**. It is effectively dead code in 1.0.84. There is also no user setting to enable it. 3. The approval handler opens a 600×600 focused:true popup and **auto-denies after 30s**, and denies immediately if the window can't be created/focused: `js chrome.windows.create({ url: "sidepanel.html?...&mcpPermissionOnly=true", type:"popup", width:600, height:600, focused:true }, w => { w ? ... : a(false) }); setTimeout(() => a(false), 30000); ` When an agent navigates a freshly-created/background tab, the focused popup races with the agent's next step and the 30s deadline elapses → Permission denied by user (errorCode: permission_denied_user). 4. On approval the grant is lA.ONCE (netloc scope), **not** ALWAYS: `js grantPermission({ type:"netloc", netloc: url.host }, lA.ONCE, toolUseId, url.origin) ` so nothing is persisted — every fresh tab / new turn re-prompts. ## Reproduction 1. Run any local dev server (e.g. http://localhost:5937). 2. From Claude Code, tabs_create_mcp (new blank tab) then navigate to the localhost URL. 3. Observe intermittent Permission denied by user; a manual retry (with the popup approved in time) succeeds. Repeat on another fresh tab → prompts again. ## Impact Automated browser testing against local dev servers is unreliable: navigations to localhost stall on a focus-racing popup with a hard 30s deadline, and approvals don't stick. This is the single most common failure mode in local /tf-style testing. ## Suggested fixes (any one helps; combined is ideal) 1. **Enable bypassLocalhostForMcp by default** (or expose a setting), since 127.0.0.1 / localhost / *.localhost are inherently local and low-risk. 2. **Persist an "always" grant per origin** from the approval popup (offer Once / Always), so repeated navigations to an approved dev origin don't re-prompt. 3. **Don't hard auto-deny on the 30s timeout for a still-open popup** — leave it pending, or surface a distinct timeout/no_response errorCode instead of permission_denied_user, which misleadingly reads as an explicit user denial. 4. Avoid denying when the focused popup can't grab focus; retry/reshow instead. ## Workarounds we use today (v1.0.84) - Reuse an already-loaded localhost tab and drive via in-page clicks (no DOMAIN_TRANSITION) instead of tabs_create_mcp + navigate`. - Approve the popup within 30s, and expect to repeat it (grants are ONCE).

Environment Info

  • Platform: darwin
  • Terminal: iTerm.app
  • Version: 2.1.220
  • Feedback ID: fce6ac9d-662a-4bd7-af3c-ddf197f4bd13

Errors

[]

View original on GitHub ↗