[Bug] Claude in Chrome: CLI MCP client never connects to bridge socket when Desktop is also installed
Environment
- Claude Code: 2.1.96
- Chrome extension: 1.0.66
- macOS: 15.7.2 (Darwin 24.6.0, Apple Silicon)
- Chrome: 146.0.7680.178
- Claude Desktop: also installed
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:
const s = [
{name: "com.anthropic.claude_browser_extension", label: "Desktop"},
{name: "com.anthropic.claude_code_browser_extension", label: "Claude Code"}
];
for (const a of s) {
const t = chrome.runtime.connectNative(a.name);
if (await pingSucceeds(t)) {
return true; // stops here, never tries CLI host
}
}
Desktop's binary at /Applications/Claude.app/Contents/Helpers/chrome-native-host always 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 .bak forces 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:
- Native host running correctly (CLI binary):
````
PID 38302: /Users/.../.local/share/claude/versions/2.1.96 --chrome-native-host
- Bridge socket exists with correct permissions:
````
/tmp/claude-mcp-browser-bridge-eugene/38302.sock
Directory: 0o700 (correct), Socket: 0o600 (correct), Owner: matches current user
- Socket is functional — Python test connects and gets response:
``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"}}
- CLI process has NO connection to the socket:
````
$ 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.
- Fresh sessions also fail — tested with a brand new
claudesession (not resume). Same result.
- CLI binary contains correct discovery logic — decompiled code shows it scans both
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
- Have both Claude Desktop and Claude Code CLI installed on macOS
- Install Claude in Chrome extension (1.0.66)
- From CLI: run
/chrome— shows Status: Enabled, Extension: Installed - Call any
mcp__claude-in-chrome__*tool → "Browser extension is not connected" - Workaround Issue 1 by renaming Desktop's host config:
``bash``
mv ~/Library/Application\ Support/Google/Chrome/NativeMessagingHosts/com.anthropic.claude_browser_extension.json{,.bak}
- Restart Chrome, click extension icon to trigger
connectNative() - Verify CLI native host is running and bridge socket exists with correct permissions
- Call
mcp__claude-in-chrome__*again → still "Browser extension is not connected" lsofconfirms CLI process never connects to the bridge socket
Expected Behavior
- Extension should support connecting to both Desktop and CLI hosts simultaneously (or provide a user-facing toggle)
- CLI's MCP client should connect to the bridge socket when it exists and passes security validation
Related Issues
#20316, #33483, #29057, #20298, #20341, #20943
This issue has 4 comments on GitHub. Read the full discussion on GitHub ↗