Claude in Chrome: 'Browser extension is not connected' despite correct setup (v2.1.39 + ext v1.0.47)
Resolved 💬 19 comments Opened Feb 11, 2026 by danielmstoffel Closed Jun 2, 2026
Description
The mcp__claude-in-chrome__tabs_context_mcp tool consistently returns "Browser extension is not connected" despite all infrastructure being correctly configured.
Environment
- Claude Code: v2.1.39
- Chrome Extension: Claude in Chrome (Beta) v1.0.47 (ID:
fcoeoabgfenejglbffodgkkbkcdhcgfn) - macOS: Darwin 25.2.0 (Apple Silicon)
- Chrome: Latest stable, Developer mode ON
Setup verified
- Native messaging hosts exist at
~/Library/Application Support/Google/Chrome/NativeMessagingHosts/:
com.anthropic.claude_code_browser_extension.json→~/.claude/chrome/chrome-native-hostcom.anthropic.claude_browser_extension.json→/Applications/Claude.app/Contents/Helpers/chrome-native-host
- Socket file exists:
/tmp/claude-mcp-browser-bridge-daniel/<pid>.sock
- Native host script at
~/.claude/chrome/chrome-native-hostcorrectly points to~/.local/share/claude/versions/2.1.39 --chrome-native-host
- Extension is enabled, has "On all sites" access, "Communicate with cooperating native applications" permission, pinned to toolbar, sidepanel shows "Opus 4.6"
Extension errors (from chrome://extensions errors page)
Uncaught (in promise) Error: Could not establish connection. Receiving end does not exist.
Error: A listener indicated an asynchronous response by returning true, but the message channel closed before a response was received
Plus CSP violations for inline scripts in options.html and blocked Segment analytics scripts (likely cosmetic).
Steps to reproduce
- Start Claude Code v2.1.39 in terminal
- Claude in Chrome extension v1.0.47 installed and enabled
- Call
mcp__claude-in-chrome__tabs_context_mcpwithcreateIfEmpty: true - Returns "Browser extension is not connected"
What was tried (all failed)
- Restarting Chrome (Cmd+Q, reopen)
- Toggling extension OFF/ON
- Removing and reinstalling extension from Chrome Web Store
- Closing Claude Desktop app (to avoid native host conflict)
- Running
/chromecommand in Claude Code - Multiple attempts across ~30 minutes
Possible contributing factors
- Claude Desktop was updated to a new version the same day, which refreshed
com.anthropic.claude_browser_extension.json(timestamp changed). Both native hosts claim the same extension ID. - Antigravity Browser Extension (v1.11.3, ID:
eeijfnjmjelapkebgockoeaadonbchdd) was also installed the same day — unlikely to conflict but noting for completeness.
Expected behavior
tabs_context_mcp should return tab information or create a new tab group.
Actual behavior
Always returns "Browser extension is not connected" error message.
19 Comments
Found 3 possible duplicate issues:
This issue will be automatically closed as a duplicate in 3 days.
🤖 Generated with Claude Code
Fix: disable cloud bridge, use local socket
The root cause of the
"Browser extension is not connected"/"Invalid token or user mismatch"error is a GrowthBook feature flag that forces the MCP server to use a cloud WebSocket bridge (wss://bridge.claudeusercontent.com) instead of the local Unix socket.Quick fix — edit
~/.claude.jsonand set:Then restart:
claude --chromeThis bypasses the cloud bridge OAuth entirely and connects directly via the local socket. Full technical explanation here: https://github.com/anthropics/claude-code/issues/24593#issuecomment-3902208001
that fixed it for me!
Confirmed this fix works on macOS 26.2 (Darwin 25.2.0, Apple Silicon), Claude Code v2.1.42, Chrome extension beta.
Setting
"tengu_copper_bridge": falsein~/.claude.jsonresolved the "Browser extension is not connected" error immediately. Extension connected on first try after restarting Claude Code with--chromeflag.Thanks @Nachx639 for the workaround.
Confirmed fix on Windows 11, Claude Code 2.1.44, Chrome Extension 1.0.52
Confirmed fix on Windows 11, Claude Code 2.1.44, Chrome Extension 1.0.52
The
tengu_copper_bridgeGrowthBook feature flag is the primary root cause of "Browser extension is not connected" on Windows. When set totrue(the server default), Claude Code tries to connect viawss://bridge.claudeusercontent.cominstead of the local Windows named pipe (\\.\pipe\claude-mcp-browser-bridge-USERNAME). The websocket bridge fails silently.What I found:
tengu_copper_bridge: falsein~/.claude.json, GrowthBook re-caches from Anthropic's servers and resets it totrue. This means the fix is not permanent -- you have to check and re-apply it before each Chrome session.falseAND Claude Code is restarted, Chrome connects immediately. No other changes needed (assuming native messaging is properly registered).Suggested fix for the Claude Code team:
tengu_copper_bridgetofalseon Windows, or--local-pipeCLI flag to override the feature flag, orQuick diagnostic (Python):
Quick fix (Python):
This gets re-cached on startup. Does not work.
" Yep — it got overwritten back to true again on startup. The server keeps pushing this flag.
At this point I think we should file a bug. We've exhausted every documented troubleshooting step and this is clearly a
server-side issue with the tengu_copper_bridge flag forcing a cloud WebSocket path that doesn't work.
Want me to draft a GitHub issue for you to submit? Or would you rather just park this and revisit after the next Claude Code
update? "
not just at startup. At the moment, the flag gets reset every couple of seconds. It's virtually unworkable.
Still broken with Claude Code 2.1.69 on Windows 11. The quick fix doesn't work at all so I'm using Playwright as a workaround but it's terrible compared to Chrome MCP.
I have to run
fix-claude-chromebefore starting claude code in the terminal every timeMy .zshrc:
Confirmed on Claude Code 2.1.71, macOS 26.3.1 (Apple Silicon)
Adding a data point on the ~6-hour failure cycle some users report: debug logs show the session starts fine, then fails ~6 hours later with
Invalid token or user mismatch. This timing likely corresponds to the GrowthBook cache TTL — the flag gets reset totruefrom the server, and the next bridge reconnect attempt fails.So there are two distinct failure modes, both producing the same misleading
Invalid token or user mismatchmessage:true, fails immediatelytrueduring a GrowthBook cache refresh, fails on next reconnectThe
tengu_copper_bridge: falseworkaround from @Nachx639 works, but only until the next cache cycle. Thefix-claude-chromealias from @KidkArolis is the most practical workaround so far.Confirmed on v2.1.75, macOS Darwin 25.2.0 (Apple Silicon), Chrome Extension v1.0.59
Three findings not previously covered in this thread:
1.
CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1— reliable workaroundThe
sed/ JSON-editing workarounds fail becausewq()(the feature flag reader) checks the in-memory GrowthBook state first, then falls through to the~/.claude.jsoncache only if the in-memory value is unset. Once GrowthBook has fetched from the server (~1 second after startup), the in-memory value istrueand the JSON cache is never consulted — regardless of what you write to the file. This applies to both native (Bun binary) and npm (Node.js) installs since the GrowthBook SDK behavior is the same.Setting
CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1prevents GrowthBook from fetching features from \cdn.growthbook.io\, so the in-memory value is never populated. \wq("tengu_copper_bridge", false)\then falls through to the JSON cache (which we set to \false\), and the MCP uses the native Unix socket instead of the cloud WebSocket bridge.\
\\`bashOne-shot:
sed -i '' 's/"tengu_copper_bridge": true/"tengu_copper_bridge": false/' ~/.claude.json
CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1 claude --chrome
Or as a shell function (~/.zshrc / ~/.bashrc):
claude-chrome() {
sed -i '' 's/"tengu_copper_bridge": true/"tengu_copper_bridge": false/' ~/.claude.json 2>/dev/null
CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1 command claude --chrome "\$@"
}
\
\\`Why \
CLAUDE_CODE_DISABLE_GROWTHBOOK=1\doesn't work: That env var does not exist in the v2.1.75 binary. \CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC\does — it gates error reporting and (critically) prevents the GrowthBook SDK from initializing its server-fetch polling loop.Downside: Disables error reporting to Anthropic and may prevent some server-side feature flag updates from activating. Functionally identical otherwise.
2. Extension native host "first wins" race condition (Desktop vs Code)
Users with both Claude Desktop and Claude Code installed hit an additional issue. The extension's \
service-worker.ts\iterates native hosts in order and connects to the first that responds with a \pong\:\
\\js\// From extension v1.0.59 service-worker.ts (deobfuscated)
const hosts = [
{ name: "com.anthropic.claude_browser_extension", label: "Desktop" }, // tried FIRST
{ name: "com.anthropic.claude_code_browser_extension", label: "Claude Code" } // tried second
];
for (const host of hosts) {
const port = chrome.runtime.connectNative(host.name);
if (await pingPong(port)) // sends {type:"ping"}, waits for "pong"
return setActiveHost(port); // connected — stop iterating
port.disconnect();
}
\
\Desktop is always tried first. Since Chrome auto-spawns Desktop's native host on extension restore (even if the Desktop app isn't running, per #20316), Desktop always wins and the extension never tries Claude Code's host.
Workaround: Disable Desktop's native messaging manifest:
\
\\`bashmv ~/Library/Application\ Support/Google/Chrome/NativeMessagingHosts/com.anthropic.claude_browser_extension.json{,.disabled}
Then restart Chrome
\
\\`This forces the extension to fall through to Claude Code's native host. Breaks Desktop's browser control, but Code works.
3. Multi-org accounts may worsen bridge token rejection
With two orgs tied to the same email on claude.ai, the cloud bridge's token validation at \
wss://bridge.claudeusercontent.com\appears sensitive to which org is active. When the browser session's \lastActiveOrg\doesn't match Claude Code's \orgId\(from \claude auth status\), the bridge rejects with "Invalid token or user mismatch." This may explain intermittent failures even when the flag is transiently \false\— org context changes when switching between orgs on claude.ai.---
Summary of the two independent failure modes for users with both Desktop + Code:
| Issue | Root cause | Fix |
|---|---|---|
| Bridge vs socket | GrowthBook sets \
tengu_copper_bridge: true\in-memory, JSON edits are ignored | \CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1\|| Wrong native host | Extension connects to Desktop's host first | Disable Desktop's native messaging manifest |
Both must be addressed simultaneously for Claude in Chrome to work with Claude Code.
Solution 1 fixes my problem. Thanks 🤝
Edit
~/.claude/settings.jsonSetting
CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFICto 0 will disable messaging channel with discord botThis should not be the solution, and other features may depend on it
The
tengu_copper_bridgeworkaround no longer works on v2.1.108.Disk cache is ignored. Setting
tengu_copper_bridgetofalsein~/.claude.jsongets overwritten on startup. Blockingcdn.growthbook.iovia/etc/hostsdoesn't help either — the CDN block is confirmed (curl, Python, and Bun's WebFetch all get ECONNREFUSED) but the flag still resets totrue. The GrowthBook SDK appears to initialize from a source other than the disk cache.CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1kills Chrome MCP. It prevents GrowthBook from initializing, but also disables the Chrome integration entirely — both the/claude-in-chromeskill and direct MCP tool calls fail.The local socket path works. Native host runs, bridge socket listens, manual connections via Python succeed end-to-end.
ss -xpshows zero connections from Claude Code to the bridge socket — it never attempts the local path.Environment: v2.1.108, Arch Linux 6.19.11, Chromium, team/org account.
(Opus 4.7 investigated the situation on my machine and claims to have found more detailed info not yet present in the thread, sharing here)
Confirmed on v2.1.121, macOS Darwin 25.2.0, Chrome 147.0.7727.103, extension v1.0.69
Adding fresh debug-log evidence + a few observations not yet in this thread.
Symptom
/chromereports MCP connected./mcpshowsclaude-in-chromeas connected. Every tool call returns "Browser extension is not connected." Reproducible across 22+ concurrent Claude Code sessions and from a fresh--print --chromeinvocation.Debug log (
--debug --debug-file)The bridge rejects with
{"type":"error","error":"Invalid OAuth token"}and closes WS with code 1008 within 200ms of connect. Reconnect attempts get the same response.What does NOT fix it
/login(re-runs full OAuth flow, fresh access token written to keychain) — bridge still rejects on next connect, immediately.chrome://extensions/(toggle disable/enable) — bridge returns the exact sameInvalid OAuth tokenerror on the next connect, no SW reset effect visible.Token freshness is not the issue (claude investigated himself)
The bridge connect uses the same OAuth token as the rest of Claude Code — verified empirically by running
/login, observing a fresh token gets minted (the rest of Claude Code keeps working with no interruption), and watching the very next bridge connect get rejected with the sameInvalid OAuth tokenerror in the same ~250ms window. So the rejection isn't about local token freshness; the bridge server is rejecting an otherwise-valid token specifically for the/chrome/<accountUuid>channel.tengu_copper_bridgeworkaround no longer works on v2.1.121The local-socket fallback documented earlier in this thread (toggling
tengu_copper_bridge: false+CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1) does not work on v2.1.121 — users appear to be forced through the cloud WebSocket bridge with no escape hatch. The flag is still being written to~/.claude.json, but flipping it has no observable effect on which transport--chromeuses.Account / identity check (matches, not a misconfig)
mimkorn@gmail.comoauthAccount.accountUuidin~/.claude.json:<UUID>— same UUID appears in the bridge URL/chrome/<UUID>organizationType: claude_max,organizationRole: adminchrome://extensions/shows it as enabled, signed in to the same accountPattern
Worked yesterday after disabling Desktop's native messaging manifest (per bbopen's fix in this thread). Stopped working ~24 hours later. Re-tested all the obvious client-side resets today (extension reload, Chrome restart, fresh
claude --print --chromeprocess,/login) — bridge keeps returningInvalid OAuth tokenwith identical timing (~250ms after WS connect). Since none of those touch the server-side channel state for/chrome/<accountUuid>, this strongly suggests the rejection is independent of any state the client can manipulate.Suggested investigation
The bridge server's per-
<accountUuid>channel state appears to outlive both the client OAuth token (refreshable via/login) and the extension service worker (kept alive bychrome.alarms). The "Invalid OAuth token" error is misleading because the token itself is freshly issued and works for every other Claude API call — only the bridge channel rejects it. Reloading the extension forces the bridge to reset that channel state. Bridge server-side TTL on the channel state would be a more durable fix than asking users to manually reload the extension every day.Happy to share the full
--debug --debug-filelog on request.(written by Opus 4.7 xHigh — sharing his additional findings from my local setup, I did not alter his output)
Follow-up on v2.1.123 — two undocumented local state fields gate bridge reconnects
Following up on my comment from yesterday (still extension v1.0.69, now CLI 2.1.123). Two new findings.
1.
bridgeOauthDeadExpiresAtandbridgeOauthDeadFailCountin~/.claude.json(top-level keys, not mentioned in this thread or any other claude-code issue I could find).After the bridge rejects with
Invalid OAuth tokentwice, Claude Code writes:\
\\json\{
"bridgeOauthDeadExpiresAt": <ms-timestamp>,
"bridgeOauthDeadFailCount": 2
}
\
\…and stops attempting the bridge connection until the timestamp passes. On my machine the timestamp was 2026-04-04 (25 days in the past) and \
failCount\stayed pinned at 2 — the retry that's supposed to fire after expiry never did. Anyone who's been silently broken for weeks should check:\
\\sh\jq '{bridgeOauthDeadExpiresAt, bridgeOauthDeadFailCount}' ~/.claude.json
\
\If both are present and the timestamp is in the past, the client has given up. Quick reset:
\
\\sh\jq 'del(.bridgeOauthDeadExpiresAt) | del(.bridgeOauthDeadFailCount)' ~/.claude.json > /tmp/c.json && mv /tmp/c.json ~/.claude.json
\
\2. Clearing those flags is necessary but not sufficient. After clearing + restarting the CLI, I confirmed via \
lsof -iTCP -P | grep 160.79.104.10\that both Claude Code AND Chrome have ESTABLISHED TCP connections to the bridge. The WebSocket layer connects, but tool calls still return "Browser extension is not connected." I then completely uninstalled the extension, reinstalled, re-OAuthed, verified extension storage was fresh, and confirmed the extension's stored \accountUuid\matches Claude Code's \oauthAccount.accountUuid\byte-for-byte. Same symptom.So the fail-count gating is one piece, but there's a second server-side rejection that survives a totally fresh client setup. Combined with @aptinio's report that disk-cache workarounds for \
tengu_copper_bridge\no longer work on v2.1.108+, current versions have effectively no escape hatch.Environment
Problem
Claude in Chrome tools (
mcp__claude-in-chrome__*) always return "Browser extension is not connected" from Claude Code CLI, even though the bridge socket is fully functional.Root Cause Analysis
Issue 1: Desktop always wins the native messaging connection
The Chrome extension's service worker iterates hosts in a fixed order and returns on first success:
Desktop's binary at
/Applications/Claude.app/Contents/Helpers/chrome-native-hostalways exists and responds to ping — even when the Desktop app is not running — because Chrome spawns it as its own child process. CLI host is never tried.Workaround: Renaming Desktop's native messaging host config to
.bakforces the extension to fall through to the CLI host. After this, Chrome correctly spawns the CLI's native host (~/.local/share/claude/versions/2.1.96 --chrome-native-host).Issue 2: CLI MCP client doesn't connect to bridge socket (the real bug)
Even after fixing Issue 1 so that the CLI's native host is running and the bridge socket exists, the CLI process never connects to it.
Diagnostic evidence:
``
``PID 38302: /Users/.../.local/share/claude/versions/2.1.96 --chrome-native-host
``
``/tmp/claude-mcp-browser-bridge-eugene/38302.sock
Directory: 0o700 (correct), Socket: 0o600 (correct), Owner: matches current user
``
python
``sock.connect('/tmp/claude-mcp-browser-bridge-eugene/38302.sock')
# Sends: {"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}
# Receives: {"result":{"content":"Unknown method: tools/list"}}
``
``$ lsof /tmp/claude-mcp-browser-bridge-eugene/38302.sock
COMMAND PID USER FD TYPE
2.1.96 38302 eugene 4u unix ... # only the native host itself
# CLI process is absent — never connected
/chrome→ Reconnect Extension does not establish CLI-to-bridge connection. It appears to only signal the Chrome extension side, not the CLI's MCP client.claudesession (not resume). Same result.os.tmpdir()and/tmp/for.sockfiles inclaude-mcp-browser-bridge-{username}directory. Security validation logic matches the actual file permissions. Butconnect()is never called on the discovered sockets.Steps to Reproduce
/chrome— shows Status: Enabled, Extension: Installedmcp__claude-in-chrome__*tool → "Browser extension is not connected"``
bash
``mv ~/Library/Application\ Support/Google/Chrome/NativeMessagingHosts/com.anthropic.claude_browser_extension.json{,.bak}
connectNative()mcp__claude-in-chrome__*again → still "Browser extension is not connected"lsofconfirms CLI process never connects to the bridge socketExpected Behavior
Related Issues
#20316, #33483, #29057, #20298, #20341, #20943
Closing for now — inactive for too long. Please open a new issue if this is still relevant.