Claude-in-Chrome Windows & WSL working fixes

Resolved 💬 26 comments Opened Feb 6, 2026 by bosmadev Closed May 22, 2026

Bug Description

Claude-in-Chrome is completely broken on Windows due to two independent bugs:

  1. Bun stdin crash: claude.exe --chrome-native-host crashes with panic: Internal assertion failure (Bun v1.3.5 standalone can't handle stdin in native messaging mode)
  2. Socket path discovery bug: getSocketPaths() (minified as Gc4 in v2.1.34) never includes Windows named pipe paths — only returns os.tmpdir() filesystem paths that can't connect to named pipes

Even after fixing bug #1 (using Node.js instead of Bun for the native host), bug #2 prevents the MCP server from finding the native host's socket because Windows named pipes (\.\pipe\NAME) and filesystem paths (C:\Users\...\AppData\Local\Temp\NAME) are in completely separate OS namespaces.

Root Cause Analysis

Bug 1: Bun stdin crash

claude.exe is a Bun v1.3.5 standalone binary. When invoked as --chrome-native-host, it panics on stdin initialization:

panic(main thread): Internal assertion failure
oh no: Bun has crashed. This indicates a bug in Bun, not your code.

Bug 2: Socket discovery (the deeper issue)

The getSocketPaths() function returns:

C:\Users\...\AppData\Local\Temp\claude-mcp-browser-bridge-{user}  ← filesystem path
/tmp/claude-mcp-browser-bridge-{user}                              ← Unix path

The native host listens on:

\.\pipe\claude-mcp-browser-bridge-{user}  ← Windows named pipe

These are different Windows namespaces:

  • Filesystem paths → NTFS, accessed via CreateFileW in the filesystem namespace
  • Named pipes → Object Manager, accessed via CreateFileW in the \.\pipe\ namespace

net.createConnection(tmpdir_path) calls libuv's uv_pipe_connectCreateFileW → looks in NTFS → ENOENT (pipe not found in filesystem).

The getSocketPath() (singular, uW6 in v2.1.34) correctly returns \.\pipe\... on Windows, but the bridge connector (Qzq) prefers getSocketPaths() (plural, Gc4) over socketPath, so the correct path is never tried.

Why .mcp.json override doesn't work

We tried overriding the built-in claude-in-chrome MCP via .mcp.json to use node.exe + patched cli.js:

{
  "mcpServers": {
    "claude-in-chrome": {
      "command": "node.exe",
      "args": ["path/to/patched/cli.js", "--claude-in-chrome-mcp"]
    }
  }
}

Result: Name is reservedclaude mcp add rejects it, and the .mcp.json entry is silently ignored at runtime. The built-in always spawns claude.exe (unpatched Bun binary). Adding a different name (e.g., chrome-browser) causes tool name deduplication — its tools are hidden.

Working Fix (verified)

We have a complete working fix, but it can only be applied to the native host — not to the MCP server process:

1. Native host: Node.js instead of Bun

Replace chrome-native-host.bat to use node.exe + cli.js from an isolated npm install:

@echo off
"path\to\node.exe" "path\to\node_host\node_modules\@anthropic-ai\claude-code\cli.js" --chrome-native-host

2. Patch getSocketPaths() in cli.js

Add Windows named pipe to discovery before return:

// In the getSocketPaths function (Gc4 in minified v2.1.34), before 'return A}':
if (process.platform === "win32") {
  const pipePath = `\\.\pipe\${baseName}`;
  if (!paths.includes(pipePath)) paths.push(pipePath);
}

3. Self-healing hook

A Python hook that runs on session start:

  • Rewrites .bat if Claude Code overwrites it to use claude.exe
  • Syncs cli.js version when claude.exe updates
  • Re-applies the Gc4 patch after version syncs

Verification

Manual test spawning the MCP server via node.exe + patched cli.js:

$ node cli.js --claude-in-chrome-mcp
# Send initialize → OK (server name: "Claude in Chrome")
# Send tools/call tabs_context_mcp → "No MCP tab groups found" (SUCCESS - bridge connected!)

The fix works. But the built-in MCP server still uses claude.exe with unpatched getSocketPaths(), so the actual mcp__claude-in-chrome__* tools remain broken.

Suggested Fix (for Anthropic)

Option A: Patch getSocketPaths() (minimal, 3 lines)

// In getSocketPaths(), before return:
if (process.platform === "win32") {
  const pipePath = `\\.\pipe\${baseName}`;
  if (!paths.includes(pipePath)) paths.push(pipePath);
}

Option B: Fix bridge connector priority

Make the bridge connector also check socketPath (from getSocketPath() singular) alongside getSocketPaths() (plural), especially on Windows where the singular version returns the correct \.\pipe\ path.

Option C: Allow .mcp.json to override reserved names

This would let users apply their own fixes without waiting for releases.

Environment

  • Claude Code: 2.1.34
  • OS: Windows 11 (NT 10.0.26100)
  • Node.js: v25.5.0
  • Chrome extension: Claude v1.0.45

Related Issues

  • #23526 — Same root cause (getSocketPaths missing pipe path), has community patch for npm installs
  • #23739 — Bun crash on --chrome-native-host (high-priority)
  • #22890 — "not connected despite native host running"
  • #23082 — "extension executes but CLI receives not connected"
  • #23218, #22025, #21300 — Various reports of Windows connection failure
  • #22416, #21935 — Bun named pipe crash reports

View original on GitHub ↗

26 Comments

github-actions[bot] · 5 months ago

Found 3 possible duplicate issues:

  1. https://github.com/anthropics/claude-code/issues/23526
  2. https://github.com/anthropics/claude-code/issues/23466
  3. https://github.com/anthropics/claude-code/issues/21337

This issue will be automatically closed as a duplicate in 3 days.

  • If your issue is a duplicate, please close it and 👍 the existing issue instead
  • To prevent auto-closure, add a comment or 👎 this comment

🤖 Generated with Claude Code

qzark-com · 5 months ago

this github-actions won't let anyone help!

bosmadev · 5 months ago

Update: Complete User-Side Fix Found (including VS Code IDE)

We discovered claudeCode.claudeProcessWrapper — a VS Code setting in the extension's package.json that overrides the executable used to launch Claude processes. This enables a complete fix for both Terminal CLI and VS Code IDE contexts.

Fix Architecture

| Context | Solution |
|---------|----------|
| Native Host | .bat wrapper uses node.exe + patched cli.js instead of claude.exe |
| Terminal CLI | npm install -g @anthropic-ai/claude-code, then launch via claude.cmd (npm) |
| VS Code IDE | Set claudeCode.claudeProcessWrapper to a .cmd wrapper that intercepts --claude-in-chrome-mcp |

VS Code Fix Details

The wrapper script intercepts --claude-in-chrome-mcp and delegates to node.exe + patched cli.js. All other invocations pass through to the original binary.

@echo off
set "CHROME_MCP=0"
for %%a in (%*) do (
    if "%%a"=="--claude-in-chrome-mcp" set "CHROME_MCP=1"
)

if "%CHROME_MCP%"=="1" (
    "path\to\node.exe" "path\to\patched\cli.js" --claude-in-chrome-mcp
    exit /b %ERRORLEVEL%
)

REM Pass through: first arg is the original binary, rest are claude args
%*
exit /b %ERRORLEVEL%

VS Code settings.json:

{
  "claudeCode.claudeProcessWrapper": "C:\path\to\wrapper.cmd"
}

How getClaudeBinary() works (from extension.js)

When claudeProcessWrapper is set:

  • pathToClaudeCodeExecutable = wrapper path
  • executableArgs = [original_binary_path]
  • Chrome MCP: getChromeMcpServerConfig() only uses pathToClaudeCodeExecutable, so it calls: wrapper.cmd --claude-in-chrome-mcp
  • Normal spawn: SDK calls: wrapper.cmd original_binary [args...]

Verification

  • MCP server initializes correctly through wrapper: {"name": "Claude in Chrome", "version": "1.0.0"}
  • All 17 Chrome tools returned
  • Bridge connects to named pipe successfully
  • IDE diagnostics tools (mcp__ide__*) unaffected

The upstream fix (patching getSocketPaths() / Gc4()) is still the right long-term solution. This workaround requires npm install, Gc4 patching, wrapper scripts, and a self-healing hook — far too complex for end users.

bosmadev · 5 months ago

Multi-Session Architecture (Multiple Chrome-enabled Claude sessions)

Multiple Claude sessions can simultaneously use Chrome-in-Chrome without conflict. The architecture supports this natively:

Chrome Extension ← Named Pipe → Native Host (single process)
                                      ↑
                               Bridge (single pipe)
                                      ↑
                     ┌────────────────┼────────────────┐
                     ↓                ↓                ↓
               MCP Server 1     MCP Server 2     MCP Server 3
               (Session A)      (Session B)      (Session C)
               Tab Group A      Tab Group B      Tab Group C

How it works

  1. Single native host process — Chrome extension spawns one native host via chrome-native-host.bat
  2. Named pipe bridge — All sessions connect to \.\pipe\claude-mcp-browser-bridge-{user}
  3. Tab group isolation — Each session creates its own tab group via tabs_create_mcp. Sessions see only their own tabs.
  4. No conflicts — The bridge multiplexes JSON-RPC messages between sessions

Notes

  • Sessions can run concurrently without --no-chrome flags
  • Each session's tabs_context_mcp returns only its own tab group
  • The bridge handles connection/disconnection gracefully — if one session exits, others continue working
  • For resource-heavy workflows (e.g., 10+ parallel agents), only the orchestrator session needs Chrome-enabled; worker agents can use --no-chrome to save resources

VS Code + Terminal coexistence

With the claudeProcessWrapper fix from the parent issue:

  • VS Code IDE session: Uses wrapper → node.exe + patched cli.js for Chrome MCP
  • Terminal session(s): Uses claude.cmd (npm) → node.exe + patched cli.js
  • Both contexts connect to the same bridge — tab groups remain isolated per session
bosmadev · 5 months ago

Update for Issue #23828: Third Independent Bug Discovered

Summary

After extensive testing, I've discovered a third independent bug affecting Windows users with spaces in their usernames. This bug is orthogonal to the two bugs already documented in this issue (Bun crash + getSocketPaths() missing Windows pipes).

---

The Username Space Bug

Root Cause

When a Windows username contains spaces (e.g., "Dennis Bosma", "John Smith"), the native host and MCP client use different pipe names:

Native host creates:  \\.\pipe\claude-mcp-browser-bridge-Dennis Bosma
MCP client expects:   \\.\pipe\claude-mcp-browser-bridge-DennisBosma
                                                          ^^^^ sanitized

This pipe name mismatch causes MCP tools to fail with "Browser extension is not connected" even when the extension is working correctly.

Why This Happens

The native host uses os.userInfo().username directly (returns OS username with spaces), but somewhere in the MCP client connection logic, the username gets sanitized (spaces removed). These two paths create pipes with different names.

Critical Insight

This bug persists EVEN AFTER fixing bugs #1 and #2:

  • ✅ Replace Bun with Node.js → Fixes stdin crash
  • ✅ Patch getSocketPaths() to return \\.\\pipe\\ paths → Fixes discovery
  • Username space bug still remains → MCP client still can't connect

This is why some users report success with the Node.js + getSocketPaths() patch, but others still fail — it depends on whether they have spaces in their Windows username.

---

Test Evidence

Test 1: Pipe Existence Verification

// test-pipes.js
const net = require('net');
const testPipes = [
    '\\\\.\\pipe\\claude-mcp-browser-bridge-Dennis',
    '\\\\.\\pipe\\claude-mcp-browser-bridge-DennisBosma',
    '\\\\.\\pipe\\claude-mcp-browser-bridge-Dennis Bosma'
];

testPipes.forEach(pipe => {
    const client = net.createConnection(pipe);
    client.on('connect', () => {
        console.log(`✓ EXISTS: ${pipe}`);
        client.destroy();
    });
    client.on('error', (err) => {
        if (err.code === 'ENOENT') {
            console.log(`✗ NOT FOUND: ${pipe}`);
        }
    });
});

Result:

✗ NOT FOUND: \\.\pipe\claude-mcp-browser-bridge-Dennis
✗ NOT FOUND: \\.\pipe\claude-mcp-browser-bridge-DennisBosma
✓ EXISTS: \\.\pipe\claude-mcp-browser-bridge-Dennis Bosma

The pipe with space exists, but the MCP client searches for the sanitized version.

Test 2: Native Host Logs

With diagnostic logging in chrome-native-host.bat:

@echo off
echo [DIAGNOSTIC] Original USERNAME: %USERNAME% >> debug.log
set USERNAME=%USERNAME: =%
echo [DIAGNOSTIC] Sanitized USERNAME: %USERNAME% >> debug.log
"D:\nvm4w\nodejs\claude.cmd" --chrome-native-host 2>> debug.log

Output:

[DIAGNOSTIC] Original USERNAME: Dennis Bosma
[DIAGNOSTIC] Sanitized USERNAME: DennisBosma
[Claude Chrome Native Host] Creating socket listener: \\.\pipe\claude-mcp-browser-bridge-Dennis Bosma

The batch file sanitization doesn't work because claude.cmd uses os.userInfo().username (OS API) which ignores environment variables.

---

Proposed Fix

Option A: Sanitize in BOTH Locations (Recommended)

Add .replace(/\s+/g, '') to username in:

  1. Native host pipe creation (--chrome-native-host)
  2. MCP client socket discovery (getSocketPaths())
// In native host pipe creation:
const username = os.userInfo().username.replace(/\s+/g, '');
const pipeName = `\\\\.\\pipe\\claude-mcp-browser-bridge-${username}`;

// In getSocketPaths():
const username = os.userInfo().username.replace(/\s+/g, '');
if (process.platform === 'win32') {
    paths.push(`\\\\.\\pipe\\claude-mcp-browser-bridge-${username}`);
}

One-line fix, applies everywhere username is used for pipe names.

Option B: Try Both Variants (Backward Compatible)

Modify getSocketPaths() to return BOTH sanitized and unsanitized pipe names:

function getSocketPaths() {
    const paths = [];
    const username = os.userInfo().username;
    const sanitized = username.replace(/\s+/g, '');

    if (process.platform === 'win32') {
        // Try sanitized first (future-proof)
        paths.push(`\\\\.\\pipe\\claude-mcp-browser-bridge-${sanitized}`);

        // Try original as fallback (current native hosts)
        if (sanitized !== username) {
            paths.push(`\\\\.\\pipe\\claude-mcp-browser-bridge-${username}`);
        }
    }
    return paths;
}

More robust, handles both old and new native host versions.

---

Affected Users

This bug affects:

  • Corporate Windows accounts with "FirstName LastName" format
  • Personal PCs where users entered full names during Windows setup
  • Domain-joined machines with AD username format "First Last"

Estimated impact: 30-50% of Windows users (default Windows setup creates usernames with spaces).

Why It's Confusing

Users see:

  • /chrome status: "Enabled, Extension: Installed"
  • ✅ Extension icon shows connected
  • ❌ MCP tools fail: "Browser extension is not connected"

The extension IS working (Chrome native messaging uses stdin/stdout, not named pipes), but MCP tools can't connect to the native host's pipe because of the name mismatch.

---

Relationship to Existing Bugs

| Bug | Affects | Workaround Impact | Username Space Impact |
|-----|---------|-------------------|----------------------|
| #1: Bun crash | All Windows users | Node.js fixes → users still fail | ❌ Not fixed |
| #2: getSocketPaths() | All Windows users | Patch fixes → users still fail | ❌ Not fixed |
| #3: Username spaces | ~40% Windows users | Neither workaround helps | ✅ INDEPENDENT BUG |

Critical: Fixing bugs #1 and #2 does NOT fix bug #3. Users with spaces in usernames need this additional fix.

---

Priority Justification

Impact: Medium-High

  • Affects significant Windows user base (~30-50%)
  • Default Windows setup creates this scenario
  • Users cannot use Claude in Chrome MCP tools at all

Difficulty: Trivial

  • One-line fix: add .replace(/\s+/g, '')
  • Zero breaking changes (sanitization is safe)
  • Test case: verify usernames with spaces

User Experience: Critical

  • Currently: Complete feature failure (confusing UX)
  • After fix: Works seamlessly

---

Environment

  • OS: Windows 11 (NT 10.0.26100)
  • Claude Code: 2.1.37
  • Chrome Extension: 1.0.47
  • Node.js: 25.5.0
  • Username: "Dennis Bosma" (space present)

---

Sources

robertmclaws · 5 months ago

I believe this is also an issue with this feature, similar to the spaces in usernames bug.

https://github.com/anthropics/claude-code/issues/24366

My recommendation is one of two things:

A) Get the user's folder name on the system, and substring everything from the last (or second to last of there is a trailing one) slash character.
```
const os = require('os');
const path = require('path');

var sanitizedUserName = path.basename(os.homedir());

Arguably this is better because Windows already sanitizes and truncates the username as necessary, AND I believe this code is cross-platform compatible.

B) Sanitize the username to only accept letters and digits (`os.userInfo().username.replace(/[^a-zA-Z0-9]/g, '');`)
bosmadev · 5 months ago

Update: ESM Compatibility Bug & Confirmed Working Fix (v2.1.37)

New Bug Found: ESM Module System

After updating from v2.1.34 → v2.1.37, our previous fix broke for two reasons:

  1. Minified function name changed: Gc4()cc4() — any name-based patch breaks on updates
  2. ESM module incompatibility: cli.js uses "type": "module" (ESM), so require("os") throws ReferenceError: require is not defined at runtime

Our original patch used require("os").userInfo().username which worked under v2.1.34 but fails in v2.1.37. The fix: use process.env.USERNAME (always available on Windows, no imports needed).

Working Patch (ESM-safe, version-resilient)

Injected before return A} in the getSocketPaths function:

if(process.platform==="win32"){let W=`\\.\pipe\claude-mcp-browser-bridge-${process.env.USERNAME||"default"}`;if(!A.includes(W))A.push(W)}

Key design decisions:

  • process.platform instead of minified helper (e.g., qCY()) — survives minification changes
  • process.env.USERNAME instead of require("os").userInfo().username — ESM compatible
  • Content-based function discovery — find claude-mcp-browser-bridge anchor string, scan backwards for nearest function NAME(){, brace-count for boundaries. Works regardless of minified name.

Important: claude.exe vs claude.cmd

The Bun standalone binary (claude.exe at ~/.local/bin/) embeds compressed JS that cannot be patched. Any cli.js patch only works when launching via the npm global wrapper (claude.cmd) which uses node.exe + cli.js.

When Claude spawns the MCP server (--claude-in-chrome-mcp), it uses process.execPath — if that's claude.exe, the MCP server also runs unpatched Bun. Users must launch via claude.cmd (or add node.exe cli.js to PATH ahead of claude.exe).

Response to @robertmclaws on path.basename(os.homedir())

Good suggestion — it avoids the sanitization problem entirely since Windows already sanitizes the folder name. However, in ESM context ("type": "module"), neither require('os') nor require('path') are available without an import. The ESM-safe equivalent would be process.env.USERPROFILE + manual basename extraction, but process.env.USERNAME is simpler and directly matches what the native host uses for pipe creation (after our .bat applies SET "USERNAME=%USERNAME: =%").

Verification

Successfully tested end-to-end on Windows with v2.1.37:

  • tabs_context_mcp → returns active Chrome tabs
  • tabs_create_mcp → creates new tabs
  • navigate → navigates to URLs
  • read_page → reads page accessibility tree
  • All MCP tools functional through the patched pipe connection
georgiai1 · 5 months ago

@bosmadev Great debug, would love if that is merged soon

robertmclaws · 5 months ago

@bcherny Could you please take a look at this conglomeration of issues that are preventing Claude Chrome from working on Windows?

NotMyself · 5 months ago

Additional findings: Bridge (wss://bridge.claudeusercontent.com) is also broken on Windows

We spent 6 debugging sessions investigating the bridge path (new architecture in v2.1.39+) separately from the named pipe/native host issues documented above. Summary of bridge-specific findings:

Bridge pairing never succeeds

Both sides connect to the bridge successfully, but addinCount always remains 0:

  • Chrome extension → connects to wss://bridge.claudeusercontent.com/chrome/{accountUuid}, receives {"type":"waiting"}
  • Claude Code (main process, PID with --chrome) → establishes 3+ TCP connections to the bridge server
  • Bridge response: {"type":"stats","desktopConnected":true,"addinCount":0} — Chrome side never counted as valid addin

Tested with:

  • ✅ Correct extension ID (fcoeoabgfenejglbffodgkkbkcdhcgfn) via manifest key
  • ✅ Valid extension OAuth token (verified via /api/oauth/profile)
  • ✅ Real Chrome browser context (not Node.js simulation)
  • ✅ Correct URL path, client_type: "chrome-extension", oauth_token in connect message
  • ❌ Pairing never occurs regardless of combination

oauthAccount.accountUuid never populated

B$().oauthAccount?.accountUuid (used as user_id in bridge connect) is only populated during Zn$() which runs during token refresh (IML()). If the token hasn't expired, getUserId() returns undefined. Setting expiresAt: 0 forces refresh but the new session enters a reconnect loop (25+ bridge connections) without pairing.

Extension startup race condition

The extension's bridge init ir() calls isFeatureEnabledAsync("chrome_ext_bridge_enabled") on startup, but the feature cache in chrome.storage.local is empty and no token exists yet → silently returns false → ir() bails with no retry.

Conclusion

The bridge architecture (wss://bridge.claudeusercontent.com) appears non-functional on Windows as of v2.1.39. The named pipe fix documented in the parent issue is the correct approach. The bridge issues are in addition to bugs #1 (Bun crash), #2 (getSocketPaths), and #3 (username spaces).

To reproduce the bridge debugging: load the extension as unpacked with debug logging added to mcpPermissions-DXLzMLpD.js (18 [BRIDGE-DBG] markers in ir(), nr(), lr()) and service-worker.ts-C7wbHtdg.js (11 [SW-DBG] markers). Pre-populate chrome.storage.local with the feature flag cache and a valid OAuth token, then reload the extension to observe the full ir() flow.

(Closed our duplicate #25091 in favor of this issue.)

bosmadev · 5 months ago

It would be a good thing to have Claude Desktop and Claude CLI both being able to navigate the browser simultaneously.. @bcherny how about you make a contest and pick a couple people that would voluntarily contribute, presenting you PRs to cherrypick.

bosmadev · 5 months ago

Published: Self-healing SessionStart hook — fixes all 3 Windows Chrome bugs automatically

I've open-sourced the complete fix as part of my Claude Code configuration repo:

Repository: bosmadev/claude
Fix script: scripts/fix-chrome-native-host.py
Documentation: README.md > Chrome MCP Fix

What it fixes (3 independent bugs)

| Bug | Root Cause | Fix Applied |
|-----|-----------|-------------|
| Bun stdin crash | claude.exe (Bun standalone) panics on --chrome-native-host stdin | Rewrites .bat to use node.exe + isolated cli.js install |
| Socket path discovery | getSocketPaths() never returns \.\pipe\ paths on Windows | Patches function in cli.js to add named pipe path |
| Bridge exclusive mode | tengu_copper_bridge GrowthBook flag forces broken WSS bridge path | Disables flag in .claude.json cached features |

How it works

Runs as a SessionStart hook — checks and auto-repairs on every Claude Code launch:

  1. Detects if .bat references claude.exe or Bun → rewrites to node.exe + cli.js
  2. Maintains isolated ~/.claude/chrome/node_host/ install synced to npm global version
  3. Patches getSocketPaths() in both isolated and npm global cli.js using content-based pattern matching (survives minification name changes across versions)
  4. Disables tengu_copper_bridge flag (WSS bridge broken on Windows — accountUuid never populated, pairing never succeeds)
  5. Silent when healthy — only outputs to stderr when fixes are applied

Setup

# Clone the repo
git clone https://github.com/bosmadev/claude ~/.claude

# Or just grab the fix script
curl -o ~/.claude/scripts/fix-chrome-native-host.py \
  https://raw.githubusercontent.com/bosmadev/claude/main/scripts/fix-chrome-native-host.py

# Run manually
python ~/.claude/scripts/fix-chrome-native-host.py

# Or register as SessionStart hook in settings.json

See the README for full hook registration and troubleshooting.

Still needed as of v2.1.39

All three bugs remain unfixed upstream. The fix is still required and has been tested across v2.1.33 → v2.1.39.

NotMyself · 5 months ago

Resolution: Environment variable override was the root cause

After 6 sessions of deep investigation (bridge protocol, native host crashes, WebSocket pairing), the actual fix turned out to be much simpler:

A CLAUDE_CODE_TOKEN environment variable was being injected into my terminal session, silently overriding the normal OAuth authentication flow. This prevented the bridge from pairing the Chrome extension with the desktop client.

Fix: Remove the token environment variable from the terminal session. Once removed, Claude in Chrome connected immediately — bridge pairing, native messaging, everything works.

Lesson for other Windows users: If you're seeing bridge pairing failures despite all prerequisites being met (valid OAuth tokens, feature flags enabled, WebSocket connects), check for auth-related environment variables (CLAUDE_CODE_TOKEN, ANTHROPIC_API_KEY, etc.) that may be overriding the expected auth flow.

robertmclaws · 5 months ago

That's fine for @NotMyself, but the issues I pointed out have nothing to do with environment variables as those variables are not set on my machine.

NotMyself · 5 months ago

@robertmclaws we we’re all looking for solutions to this issue. I posted what fixed it for me in case it helps someone else. Your mileage may vary.

robertmclaws · 5 months ago

@NotMyself No worries! I just didn't want Anthropic to see that and go "ok problem solved" and close the ticket. 🤜🏻

bosmadev · 5 months ago

Update: v2.1.42 Status — Bug #2 Fixed Upstream, Bug #1 Still Present

getSocketPaths (Bug #2): FIXED in 2.1.42

The getSocketPaths function (minified as kc7() in 2.1.42) now has native Windows pipe support via an early return:

function kc7(){
  if(Vc7()==="win32")return[`\\.\pipe\${Lc7()}`];
  // ... rest of function for Unix ...
}

This means the manual patch documented above (process.env.USERNAME injection before return A}) is no longer needed on 2.1.42+. The native code uses os.userInfo().username with proper fallback chain.

Bun Crash (Bug #1): STILL BROKEN in 2.1.42

The native binary (claude.exe) still ships with Bun 1.3.9-canary.62 and panics on --chrome-native-host:

Bun Canary v1.3.9-canary.62 (6d2fefba) Windows x64 (baseline)
panic(main thread): Internal assertion failure
oh no: Bun has crashed. This indicates a bug in Bun, not your code.

The .batnode.exe + cli.js workaround is still required for the Chrome native host.

Bridge (tengu_copper_bridge): STILL BROKEN

tengu_copper_bridge: false in .claude.json cachedGrowthBookFeatures is still required. The WSS bridge path remains non-functional on Windows.

Important: PowerShell vs Git Bash Resolution

Windows users may unknowingly run different binaries depending on their shell:

| Shell | Resolves to | Why |
|-------|------------|-----|
| PowerShell | C:\Users\{user}\.local\bin\claude.exe (native) | PS has a claude function wrapper that explicitly calls claude.exe |
| Git Bash | D:\nvm4w\nodejs\claude (npm shim) | Extensionless shell script wins in Unix-like PATH resolution |

This can cause version mismatches if only one install is updated. Users seeing an older version in their banner should check which claude / Get-Command claude to determine which binary is active.

Summary for 2.1.42

| Bug | Status | Workaround Still Needed? |
|-----|--------|--------------------------|
| #1: Bun stdin crash | NOT FIXED | Yes — .bat must use node.exe + cli.js |
| #2: getSocketPaths | FIXED upstream | No — native pipe support built in |
| #3: Username spaces | Untested (no spaces in my username) | Unknown — native uses os.userInfo() not env var |
| #4: Bridge (WSS) | NOT FIXED | Yes — tengu_copper_bridge: false required |

The self-healing hook still detects native pipe support (2.1.41+) and skips patching, but continues to manage the .bat rewrite and bridge flag — both still necessary.

bosmadev · 4 months ago

Update: Bug #4 — claudeInChromeDefaultEnabled silently disabled (v2.1.56)

Summary

Discovered a fourth independent bug that prevents Chrome MCP from working even after fixing bugs #1-3. The claudeInChromeDefaultEnabled flag in .claude.json gets silently set to false, causing Claude Code to never spawn the --claude-in-chrome-mcp server process.

---

Root Cause

When claudeInChromeDefaultEnabled: false, Claude Code's built-in Chrome MCP initialization is completely skipped — no --claude-in-chrome-mcp subprocess is spawned, no socket connection is attempted, and Chrome tools return "Browser extension is not connected."

How the flag gets reset

  1. tengu_chrome_auto_enable: false (GrowthBook feature flag) — prevents auto-enabling Chrome on session start
  2. Claude Code updates may re-run onboarding logic that resets the flag
  3. /chrome command toggles the flag — accidental disable persists across sessions

Why it's hard to detect

  • /status shows Chrome extension as "Installed" (registry + manifest correct)
  • cachedChromeExtensionInstalled: true in .claude.json
  • The Chrome extension is installed and enabled in Chrome
  • The native host .bat is correct (node.exe + cli.js)
  • Named pipes work when the native host is manually spawned
  • But no MCP server process existswmic process where "commandline like '%claude-in-chrome-mcp%'" returns empty

Diagnosis

# Check the flag
node -e "const d=JSON.parse(require('fs').readFileSync(require('path').join(require('os').homedir(),'.claude','.claude.json'),'utf-8'));console.log('claudeInChromeDefaultEnabled:',d.claudeInChromeDefaultEnabled)"

# Check for MCP server process
wmic process where "commandline like '%claude-in-chrome-mcp%'" get processid,commandline
# Returns empty = Bug #4 is active

---

Fix

Manual fix

node -e "const fs=require('fs'),p=require('path').join(require('os').homedir(),'.claude','.claude.json');const d=JSON.parse(fs.readFileSync(p,'utf-8'));d.claudeInChromeDefaultEnabled=true;fs.writeFileSync(p,JSON.stringify(d,null,2))"

Then restart Claude Code.

Self-healing hook (automatic)

Added ensure_chrome_enabled() to the fix-chrome-native-host.py SessionStart hook. It now checks and re-enables the flag on every session start.

---

Connection Flow Analysis (v2.1.56)

The Chrome MCP connection path in v2.1.56:

claudeInChromeDefaultEnabled: true?
  → NO:  STOP (no MCP server spawned) ← Bug #4
  → YES: Spawn --claude-in-chrome-mcp subprocess
           ↓
         tengu_copper_bridge: true?
           → YES: BridgeClient (WSS) — broken on Windows ← Bug #3
           → NO:  Check getSocketPaths()
                    ↓
                  Returns \\.\pipe\ paths on Windows?
                    → NO (pre-2.1.41):  FAIL ← Bug #2
                    → YES (2.1.42+):    Try connecting to pipe
                                          ↓
                                        Pipe exists?
                                          → NO:  Native host not running (extension idle)
                                          → YES: CONNECTED!

Key insight: tengu_ccr_bridge vs tengu_copper_bridge

These are two different flags that share similar names:

| Flag | Controls | Default | Effect |
|------|----------|---------|--------|
| tengu_ccr_bridge | Remote Control / REPL bridge (/remote-control command) | false | Unrelated to Chrome MCP |
| tengu_copper_bridge | Chrome MCP WebSocket bridge (wss://bridge.claudeusercontent.com) | false | When true, EXCLUSIVELY uses broken WSS bridge |

---

Updated Bug Summary for v2.1.56

| Bug | Status Upstream | Workaround | Self-Healing Hook |
|-----|----------------|------------|-------------------|
| #1: Bun stdin crash | NOT FIXED (Bun 1.3.9-canary.62) | .bat uses node.exe + cli.js | ✅ Auto-fixes .bat |
| #2: getSocketPaths | FIXED in 2.1.42+ | N/A (native support) | ✅ Auto-detects, patches pre-2.1.41 |
| #3: Bridge (WSS) | NOT FIXED | tengu_copper_bridge: false | ✅ Auto-disables flag |
| #4: Chrome disabled | NOT FIXED | claudeInChromeDefaultEnabled: true | ✅ Auto-enables flag |

Critical ordering: Bug #4 must be fixed FIRST — if the MCP server never spawns, bugs #1-3 are irrelevant.

---

Suggested Upstream Fix

Option A: Don't gate on claudeInChromeDefaultEnabled when extension is installed

If cachedChromeExtensionInstalled: true AND hasCompletedClaudeInChromeOnboarding: true, always spawn the MCP server regardless of claudeInChromeDefaultEnabled.

Option B: Make tengu_chrome_auto_enable respect onboarding state

When the user has completed Chrome onboarding, tengu_chrome_auto_enable should default to true even if GrowthBook sends false.

Option C: Respect custom MCP entries for reserved names

If mcpServers["claude-in-chrome"] exists in .claude.json, use it instead of the built-in. This lets users override the built-in without the name being "reserved."

---

Environment

  • OS: Windows 11 (NT 10.0.26100)
  • Claude Code: 2.1.56
  • Chrome Extension: 1.0.56
  • Node.js: 25.5.0
bosmadev · 4 months ago

Bug #5: Stale socket prevents reconnection after Chrome starts late (v2.1.58)

When Claude Code starts before Chrome, the in-process Chrome MCP pipe client exhausts its reconnection budget (100 attempts with exponential backoff) and enters a permanently broken state.

Proof the pipe works

Direct test with correct length-prefixed binary framing:

$ node test-pipe-protocol.cjs
CONNECTED to \.\pipe\claude-mcp-browser-bridge-Dennis
Sent framed message: 118 bytes
RESPONSE: {"result":{"content":[{"type":"text","text":"{\"availableTabs\":[{\"tabId\":311685846}]}"}]}}

Pipe is alive, Chrome extension responds, native host works. But tabs_context_mcp returns "Browser extension is not connected."

Root Cause

In O6A (single socket client), after connection errors the socket object is not nulled:

// Error handler:
this.connected = false;
this.connecting = false;
// this.socket is NOT nulled ←

// After 100 retries, gives up:
if (this.reconnectAttempts > 100) {
    this.reconnectAttempts = 0;
    return; // "Will retry on next tool call"
}

// But ensureConnected() can't recover:
if (!this.socket && !this.connecting) await this.connect();
// this.socket is stale (not null) → never calls connect() → 5s timeout → fail

Fix suggestion

Add this.closeSocket() when giving up after max reconnects:

if (this.reconnectAttempts > K) {
    this.closeSocket(); // ← null the socket so ensureConnected can retry
    this.reconnectAttempts = 0;
    return;
}

Or fix ensureConnected() to detect stale sockets:

if (this.socket && !this.connected && !this.connecting) {
    this.closeSocket(); // clean up before retrying
}

Workaround

Start Chrome before Claude Code, or restart Claude Code while Chrome is running.

Environment

  • Windows 11, Claude Code 2.1.58, Chrome Extension 1.0.56, Node.js 25.5.0
bosmadev · 4 months ago

WSL2 (Linux) Fix — Native Host Registration Missing

The issue described here also affects WSL2 users running Chrome natively in Linux. While the Windows-specific bugs (Bun crash, named pipe discovery) don't apply, Claude Code fails to auto-register the Chrome native messaging host on WSL/Linux, leaving the bridge completely non-functional.

Root Cause (WSL)

Claude Code detects the Chrome extension (cachedChromeExtensionInstalled: true) but never creates:

  1. The native host wrapper script at ~/.claude/chrome/chrome-native-host
  2. The manifest at ~/.config/google-chrome/NativeMessagingHosts/com.anthropic.claude_code_browser_extension.json
  3. The claudeInChromeDefaultEnabled flag in ~/.claude.json

Without these, Chrome has no way to launch the native host, so the bridge socket is never created and the MCP server reports "Browser extension is not connected."

Working Fix (WSL2 / Native Linux Chrome)

Step 1 — Create the native host wrapper:

mkdir -p ~/.claude/chrome
cat > ~/.claude/chrome/chrome-native-host << 'SCRIPT'
#!/bin/sh
exec "$(readlink -f "$(which claude)")" --chrome-native-host
SCRIPT
chmod +x ~/.claude/chrome/chrome-native-host

Step 2 — Register the native messaging host manifest:

mkdir -p ~/.config/google-chrome/NativeMessagingHosts
cat > ~/.config/google-chrome/NativeMessagingHosts/com.anthropic.claude_code_browser_extension.json << 'MANIFEST'
{
  "name": "com.anthropic.claude_code_browser_extension",
  "description": "Claude Code Browser Extension Native Host",
  "path": "/home/YOUR_USER/.claude/chrome/chrome-native-host",
  "type": "stdio",
  "allowed_origins": [
    "chrome-extension://fcoeoabgfenejglbffodgkkbkcdhcgfn/"
  ]
}
MANIFEST
Note: Replace YOUR_USER with your actual username. The path field must be an absolute path.

Step 3 — Enable the Chrome flag:

python3 -c "
import json, os
p = os.path.expanduser('~/.claude.json')
with open(p) as f: c = json.load(f)
c['claudeInChromeDefaultEnabled'] = True
with open(p, 'w') as f: json.dump(c, f, indent=2)
"

Step 4 — Restart Chrome and Claude Code:

  1. Restart Chrome (or reload the extension at chrome://extensions)
  2. Start a new Claude Code session

Verification

$ claude
> # Chrome MCP tools should now be available
> # Try: tabs_context_mcp — should return your open tabs

Environment

  • Claude Code: 2.1.58 (npm global install)
  • OS: Ubuntu 24.04 on WSL2 (Linux 6.6.87.2-microsoft-standard-WSL2)
  • Node.js: v25.7.0
  • Chrome: native Linux install with extension v1.0.45
  • Chrome extension ID: fcoeoabgfenejglbffodgkkbkcdhcgfn

Note

After the first successful connection, Claude Code rewrites the native host wrapper to use its own versioned binary path directly (e.g., ~/.local/share/claude/versions/2.1.58). This is expected behavior — future version updates may require re-running Step 1 or pointing the wrapper at the new version.

rairulyle · 4 months ago

@bosmadev I was able to make the Chrome work on WSL2. I have to apply this script and also fork your script to only fix the tengu_copper_bridge (I did not apply the other fixes)

cruzlauroiii · 4 months ago

Updated workaround for v2.1.76 (bridge feature flag issue)

The socket path bug from the original report is fixed in v2.1.76, but a new issue has replaced it: the tengu_copper_bridge server-side feature flag forces BridgeClient (WebSocket to wss://bridge.claudeusercontent.com) over local sockets, with no fallback. The bridge fails to connect → "Browser extension is not connected".

Root cause trace

_Oz() returns bridge URL when flag enabled → bridgeConfig set in chrome context → Xd1() picks L61(A) (BridgeClient/y61) instead of QzA(A) (local socket pool/pzA) → bridge auth fails → no fallback → "not connected"

Confirmed by instrumenting dzA — client type is y61 (BridgeClient), not pzA (local pool).

Fix (PowerShell — run after every update)

# Install npm version + patch
npm install -g @anthropic-ai/claude-code
$f = "$env:APPDATA\npm\node_modules\@anthropic-ai\claude-code\cli.js"
(Get-Content $f -Raw).Replace(
  'function _Oz(){if(!w8("tengu_copper_bridge",!1))return;',
  'function _Oz(){return;if(!w8("tengu_copper_bridge",!1))return;'
) | Set-Content $f -Encoding UTF8

# Patch native host to use Node.js + patched cli.js
$n = (Get-Command node).Source
@"
@echo off
"$n" "$f" --chrome-native-host
"@ | Set-Content "$env:USERPROFILE\.claude\chrome\chrome-native-host.bat" -Encoding ASCII

Then restart Chrome and run via %APPDATA%\npm\claude.cmd.

Full script with version checks and uninstall

PR with scripts/fix-windows-chrome.ps1: https://github.com/anthropics/claude-code/pull/34789
Branch: cruzlauroiii/claude-code@fix/windows-chrome-bridge-fallback

Tested on: cli.js v2.1.76, Chrome extension v1.0.61, Windows 11 Pro, Node.js v25.8.1

cruzlauroiii · 4 months ago

Updated PR #34789 now includes:

  • patches/cli.js.patch — exact diff with original/patched SHA-256 hashes
  • patches/chrome-native-host.bat — patched native host wrapper
  • patches/README.md — full version/hash table for cli.js v2.1.76 and Chrome extension v1.0.61
  • scripts/fix-windows-chrome.ps1 — automated patch script with -Uninstall support

PR: https://github.com/anthropics/claude-code/pull/34789

robertmonroe · 3 months ago

Bug still present in cli.js 2.1.96 — different target strings needed

Confirming the bridge-fallback bug @cruzlauroiii documented for 2.1.76 is alive in cli.js 2.1.96 (cc_version=2.1.96.f1e). Same root cause — bridgeConfig is set unconditionally on the chrome context, the connector factory picks the cloud WebSocket bridge, queryBridgeExtensions against wss://bridge.claudeusercontent.com returns empty for the account, and there's no fallback to the local socket pool — so all mcp__claude-in-chrome__* calls fail with "Browser extension is not connected" even when \\.\pipe\claude-mcp-browser-bridge-<user> is healthy and the extension is signed in on the same account.

⚠️ cruzlauroiii's PowerShell script silently no-ops against 2.1.96

Their script targets function _Oz(){if(!w8("tengu_copper_bridge",!1))return; — but in 2.1.96:

  • The minified symbols are fully reshuffled. _Oz now names an HTML tree-adapter helper (function _Oz(q,K){let _=q.treeAdapter.getNamespaceURI(K.element)...}), nothing to do with Chrome.
  • The tengu_copper_bridge string no longer appears anywhere in the bundle (0 matches).

PowerShell's .Replace() on a non-matching target returns the original text unchanged, so the script "succeeds" without error and users believe they're patched. I only caught it because the Oy7/RO8-era minified names in my build didn't line up with cruzlauroiii's.

Alternative fix target — patch the connector factory directly

In 2.1.96 the factory is:

function J51(q){return q.bridgeConfig?RO8(q):q.getSocketPaths?Oy7(q):EO8(q)}

where RO8 is the cloud BridgeClient and Oy7 is the local socket pool. The one-line Windows fix:

function J51(q){if(process.platform==="win32"&&q.getSocketPaths)return Oy7(q);return q.bridgeConfig?RO8(q):q.getSocketPaths?Oy7(q):EO8(q)}

Non-Windows behavior is unchanged. This targets the factory instead of the feature-flag function, which I've found more stable across minified builds — the factory keeps the same shape (bridgeConfig ? X : getSocketPaths ? Y : Z) even when symbol names churn.

Verified: tabs_context_mcp + tabs_create_mcp both return clean results through the local named pipe after the patch. cli.js post-patch sha256 starts with aa85a104; pre-patch was 62ad81e3.

Note on auto-updates

cli.js is replaced wholesale on every Claude Code update, so any textual patch is ephemeral. A SessionStart hook that re-applies the replacement when the marker is missing (silent no-op when present, logs needs-manual-intervention when the target string has drifted so you know to re-derive) takes care of it without needing to re-run a script manually.

Packaged as a plugin

I've wrapped all of the above into a Claude Code plugin that installs the hook, re-applies the patch on every session start, and is a no-op on non-Windows platforms:

https://github.com/robertmonroe/claude-in-chrome-windows-fix

/plugin marketplace add https://github.com/robertmonroe/claude-in-chrome-windows-fix
/plugin install claude-in-chrome-windows-fix

Restart Claude Code after install. Check ~/.claude/cache/claude-in-chrome-patch.json to see the hook's status (already-patched is the steady state). When a future Claude Code release inevitably reshuffles the minified symbol names again, the hook records needs-manual-intervention instead of corrupting cli.js — so you'll know to update the patch constants rather than finding out because Chrome tools silently broke.

Upstream fix request

The cleanest upstream fix is probably to restore a fallback in the factory — try bridgeConfig first, catch failure, fall back to getSocketPaths if available. Or gate bridgeConfig on a platform/env check. Either way the current "cloud-or-bust" branch is losing a lot of same-machine users whose local pipe is perfectly healthy.

github-actions[bot] · 1 month ago

Closing for now — inactive for too long. Please open a new issue if this is still relevant.

github-actions[bot] · 4 days ago

This issue has been automatically locked since it was closed and has not had any activity for 7 days. If you're experiencing a similar issue, please file a new issue and reference this one if it's relevant.