claude-code-chrome-wsl-windows-host.md — 17 min read

2026-09-1217 min readManuel ParraInvestigation

Fix Claude Code’s “not supported in WSL” Chrome integration on a Windows host

Claude Code in WSL refused Chrome integration in its settings panel. The browser tools use an account-keyed WebSocket bridge, not the local native-host socket, so a WSL session drives the Windows Chrome with a startup flag and one pairing step. This note traces the relay that was never used.

  • Claude Code
  • WSL
  • Windows
  • Chrome
  • MCP
  • Browser Automation
On this page · 13 sections
  1. 01What I was trying to do
  2. 02The panel refused before any connection
  3. 03The startup flag skipped the panel
  4. 04The native host looked like the transport
  5. 05A relay connected to the Windows pipe
  6. 06The session did not connect to the relay
  7. 07The binary always selects the bridge
  8. 08The Windows Chrome ran the task with no relay
  9. 09The working configuration
  10. 10What others found before
  11. 11What this establishes
  12. 12What I will check first next time
  13. 13Sources

I run Claude Code in WSL2. My browser logins are in the Windows Chrome. I wanted the Claude Code browser tools to use that Chrome. /chrome is a command that you type inside the Claude Code terminal. It opens the settings screen for the Chrome integration. In this note I call that screen the panel. In the WSL session, the panel showed one line:

Error

Claude in Chrome is not supported in WSL at this time.

Where it fails

The line is a display condition inside the settings panel. The browser tools never check it.

Cause

The panel skips its menu and prints the line when isWSL || !isClaudeAISubscriber is true. It opens no socket, contacts no bridge, and looks for no extension.

Fix, in this order

  1. Start the session with claude --chrome.

    The startup flag skipped the panel

  2. On the first run, answer the pairing prompt in the extension, or set CLAUDE_CHROME_PAIRED_DEVICE_ID=<device id>.

    The working configuration

The Windows Chrome extension must be signed in to the same claude.ai account as Claude Code.

Tried first

The documentation says the same. I did not believe it. After the weeks I spent repairing the Codex browser integration on WSL, I know how these companies work. They ship a feature, and the documentation and the error messages do not describe it correctly. A line like this often describes the settings screen, not the tools behind it. So I started to test whether the same thing was possible with Claude Code from the CLI in WSL. It was. The line is a display condition inside the settings panel. The browser tools never check it.

I expected a harder problem than that. Chrome starts a small program for the extension, called the native host. On Linux, that program listens for connections from the session on a socket file in /tmp. A socket file is a file that two programs on one Linux machine use to exchange data. On Windows, the same program listens on a named pipe. A named pipe is the Windows equivalent of a socket file. I assumed that the session connects to that socket file. So my plan was a relay between the WSL socket file and the Windows named pipe. A relay is a program that reads data from one connection and writes it to another connection. I built two relays, one in WSL and one in Windows. The Windows relay connected to the named pipe. Then three real runs completed, and the relays received no data. Claude Code 2.1.269 does not use that socket. The browser tools connect to a server at wss://bridge.claudeusercontent.com. In this note I call that server the bridge. The bridge identifies each user by the claude.ai account. So each extension that is signed in to that account is reachable from any operating system. The WSL session opened a page in Windows Chrome. It saved a screenshot in the WSL filesystem. It read console messages. It uploaded a file from a WSL path. No relay was running.

This note is shorter, because the repair is one startup flag and one pairing step. I keep the relay work in the note for two reasons. It is the evidence for where the data does not go. And I would have skipped it if I had read the transport code first.

What I was trying to do

Agent

Claude Code 2.1.269 in WSL2 Ubuntu.

WSL

The binary is compiled with Bun, and its JavaScript is stored inside the executable as plain text. That detail mattered: grep -a on the executable answered the questions that the documentation did not.

Browser

Google Chrome on Windows 11, Default profile, with the Claude in Chrome extension fcoeoabgfenejglbffodgkkbkcdhcgfn.

Windows

The extension is signed in to claude.ai.

Also present

Claude Code 2.1.261 native on Windows, Node v24.14.0 on Windows, and a Chrome inside WSL (WSLg) with the same extension.

I kept that Chrome closed during every test, so that any new tab could only come from the Windows Chrome.

Network

WSL2 in NAT mode with localhostForwarding=true.

The Windows side of the WSL network adapter was 172.24.224.1 during this session. WSL selects a new subnet after each restart.

Acceptance test

from the WSL session, open https://example.com in a new tab of the Windows Chrome and read the h1.

The panel refused before any connection

I started with the panel, because I wanted to know what its message checks. Claude Code draws its terminal screens with React. React is a library that builds a screen from components. A component is a piece of code that receives input values and decides what to show. The panel is one component. It receives a true-or-false value named isWSL. That value is true when the file /run/WSL/<pid>_interop exists. WSL creates that file for its Windows integration, so on my machine it exists. A second true-or-false value, isClaudeAISubscriber, is true when the login has a claude.ai subscription. The component then runs this check:

Panel checktext
Ke = isWSL || !isClaudeAISubscriber

Ke is the name of the result in the minified code; the binary keeps no readable names. When Ke is true, the component skips the menu and prints the red line. That is the full check. jckeen documented the same condition in issue #41625 in March 2026, together with the CLAUDE_CODE_ENABLE_CFC bypass. It opens no socket, contacts no bridge, and looks for no extension. The message describes the panel and nothing else.

Two functions I needed are inside that skipped menu. The first is the toggle "Enabled by default", which writes claudeInChromeDefaultEnabled into ~/.claude.json. The second is the extension detection line. Detection scans local Chrome profile folders only. On the wsl platform it scans the Linux paths, ~/.config/google-chrome/<profile>/Extensions/<extension id>. It never scans the Windows Chrome under /mnt/c. So a WSL session without the extension in the WSL Chrome shows "Extension: Not detected". Auto-enable has nothing to detect, and the UI gives no way to enable the tools. That was my situation for weeks.

The startup flag skipped the panel

The usage line at the bottom of the panel says claude --chrome. WSL hides that line too, so I found it in the code. The flag goes through a different function, and that function checks these conditions in this order:

  1. The organization policy allow_claude_browser_extension must permit the tools.

  2. The OAuth token must have the scope user:profile, user:office, or user:ccr_inference. The OAuth token is the login that /login stores. API keys and claude setup-token sessions are refused. The log line is "Disabled: OAuth token has no scope accepted by /api/oauth/validate".

  3. --chrome returns true. --no-chrome returns false.

  4. CLAUDE_CODE_ENABLE_CFC in the environment, true or false.

  5. One more condition. I could not identify it in the minified code.

    unknown

  6. claudeInChromeDefaultEnabled from ~/.claude.json. If it is not set, the tools stay off.

There is no WSL condition in that list, so I tested the flag in a headless run. A headless run is claude -p. It runs one prompt without the interactive terminal, and it prints every event as JSON, which makes the result easy to check.

Headless test of --chromebash
claude --chrome -p "Use the browser tools: open https://example.com in a new tab and reply with the h1 text" --max-turns 6 --output-format stream-json --verbose < /dev/null

The init event listed {'name': 'claude-in-chrome', 'status': 'connected'} and 22 mcp__claude-in-chrome__* tools. The same command without --chrome listed no claude-in-chrome server and zero browser tools. claudeInChromeDefaultEnabled: true was already in ~/.claude.json at that time. So in headless mode the flag is the only switch, and the hidden toggle would not have helped me here.

The native host looked like the transport

Before I read the transport code, I followed the native host, because it was the only visible process. The extension and the native host exchange messages through the program's standard input and output. On Linux, the native host for this extension is the shell script ~/.claude/chrome/chrome-native-host. The script runs claude --chrome-native-host, and the program writes this to stderr:

Linux native host · stderrtext
[Claude Chrome Native Host] Creating socket listener: /tmp/claude-mcp-browser-bridge-<user>/<pid>.sock
[Claude Chrome Native Host] Socket permissions set to 0600

On Windows, the registry key HKCU\Software\Google\Chrome\NativeMessagingHosts\com.anthropic.claude_code_browser_extension points to a manifest. The manifest points to C:\Users\<user>\.claude\chrome\chrome-native-host.bat, and the batch file runs claude.exe --chrome-native-host. The program prints its listener before any handshake. So I ran it manually from WSL with stdin closed, to learn the pipe name:

Run the Windows host from WSLbash
cmd.exe /c "C:\Users\<user>\.local\bin\claude.exe --chrome-native-host" < /dev/null
Windows native host · pipe in usetext
[Claude Chrome Native Host] Initializing...
[Claude Chrome Native Host] Creating socket listener: \\.\pipe\claude-mcp-browser-bridge-<user>
[Claude Chrome Native Host] Socket server error: ...
error: listen EADDRINUSE: address already in use \\.\pipe\claude-mcp-browser-bridge-<user>
    code: "EADDRINUSE"

Windows uses one pipe per Windows user, and Linux uses one socket per native host process. The pipe was already in use, and I did not expect the owner:

Find the pipe ownerpowershell
powershell.exe -NoProfile -c "Get-CimInstance Win32_Process | Where-Object { $_.CommandLine -like '*chrome-native-host*' } | Select-Object ProcessId, ParentProcessId, Name, CreationDate, CommandLine | Format-List"
Process that held the pipetext
ProcessId       : 13992
ParentProcessId : 16804
Name            : chrome-native-host.exe
CreationDate    : 9/11/2026 11:11:10 AM
CommandLine     : "C:\Users\<user>\AppData\Roaming\Claude\ChromeNativeHost\chrome-native-host.exe" chrome-extension://fcoeoabgfenejglbffodgkkbkcdhcgfn/ --parent-window=0

That program belongs to the Claude Desktop app, not to Claude Code. The registry has two native hosts for the same extension id: com.anthropic.claude_browser_extension for Claude Desktop, and com.anthropic.claude_code_browser_extension for Claude Code. Windows Chrome started the Claude Desktop host the day before, and the host stays alive as a child process of chrome.exe. It listens on the same pipe name. [System.IO.Directory]::GetFiles('\\.\pipe\') listed exactly one matching pipe. This was good for the relay plan. The pipe had a live listener, and I did not need a Windows Claude Code session to keep it alive.

The native hosts of the extension: a Linux socket per process, and one Windows pipe name for two registry hosts
Native hostStarted asListens onObserved
Claude Code, Linuxthe shell script ~/.claude/chrome/chrome-native-host, which runs claude --chrome-native-host/tmp/claude-mcp-browser-bridge-<user>/<pid>.sock, mode 0600not given in this note
Claude Code, Windowsthe registry key and its manifest, then chrome-native-host.bat, which runs claude.exe --chrome-native-host\\.\pipe\claude-mcp-browser-bridge-<user>started manually from WSL: the pipe was already in useEADDRINUSE
Claude Desktop, Windowschrome-native-host.exe of Claude Desktop, a child process of chrome.exethe same pipe nameWindows Chrome started it the day before, and it stayed aliveheld the pipe
Windows uses one pipe per Windows user, and Linux uses one socket per native host process. The registry has two native hosts for the same extension id: com.anthropic.claude_browser_extension for Claude Desktop, and com.anthropic.claude_code_browser_extension for Claude Code.

The session side of the protocol, read from the binary, also agreed with the plan. On Linux, discovery lists every *.sock file in /tmp/claude-mcp-browser-bridge-<user>, and it does not check that the file name is a live process id. Before it connects, the session checks four conditions. The directory must be a real directory. Its mode must be 0700. Its owner must be the current user. The socket file must have mode 0600 and the same owner. Both directions use one message format: 4 bytes with the length, then the JSON text, with a maximum of 1 MiB. The native host keeps no state. It wraps the session's {method: "execute_tool", params: {...}} as {type: "tool_request", ...} for Chrome. It forwards Chrome's tool_response and notification messages to every connected client. A relay can copy the bytes without parsing them. Every detail agreed with my plan, and that is the reason I did not question the plan earlier.

A relay connected to the Windows pipe

The first question was network reachability. WSL cannot connect to a Windows program that listens on 127.0.0.1, because the localhostForwarding option works only from Windows to WSL. I started three test listeners on Windows and connected to them from WSL with a Python client:

The reachability test: four connections from WSL to three test listeners on Windows
WSL connected toWindows listenerResult
127.0.0.1:48923bound to 127.0.0.1timeout
172.24.224.1:48923bound to 127.0.0.1timeout
172.24.224.1:48924bound to 172.24.224.1OK
172.24.224.1:48925bound to 0.0.0.0OK

So the Windows relay must bind the WSL adapter address, which the local network cannot reach. The relay, relay.js, is a net.createServer on that address. For each connection it calls net.connect('\\\\.\\pipe\\claude-mcp-browser-bridge-<user>'), copies both streams, and logs each message. I ran it from WSL with the Windows Node, through the UNC path:

Windows relay · started from WSLbash
"/mnt/c/Program Files/nodejs/node.exe" '\\wsl.localhost\Ubuntu\home\<user>\claude-chrome-wsl-bridge\relay.js' --bind 172.24.224.1 --port 48923 --dump
Windows relay · listeningtext
[relay] listening on 172.24.224.1:48923 -> \\.\pipe\claude-mcp-browser-bridge-<user>

The WSL relay is socat, with the permissions that the session checks:

WSL relay · socatbash
mkdir -p -m 700 /tmp/claude-mcp-browser-bridge-<user>
socat -d -d UNIX-LISTEN:/tmp/claude-mcp-browser-bridge-<user>/99999.sock,mode=600,fork TCP:172.24.224.1:48923

ls -lan showed drwx------ on the directory and srw------- on the socket, both with uid 1000. A test connection from WSL made the relay log pipe connected. Every condition that the session checks before it connects was satisfied. I expected the next run to pass through the relay.

The session did not connect to the relay

The first headless run connected, listed the tools, and created a tab in Windows Chrome. That looked like success, until I read the relay log. It had one TCP connection, my test connection. socat accepted no connection, and nothing opened 99999.sock. The tools worked, and my relay had no part in it.

The debug log at ~/.claude/debug/<session id>.txt showed the real path of the data:

Debug log · the real data pathtext
[INFO] Bridge URL: wss://bridge.claudeusercontent.com
[DEBUG] MCP server "claude-in-chrome": In-process Chrome MCP server started
[DEBUG] [Claude in Chrome] Fetching user ID for bridge connection
[DEBUG] [Claude in Chrome] Fetching OAuth token for bridge connection
[INFO] [Claude in Chrome] Connecting to bridge: wss://bridge.claudeusercontent.com/chrome/<account uuid>
[INFO] [Claude in Chrome] WebSocket connected, sending connect message
[DEBUG] [Claude in Chrome] Bridge received: {"type":"paired"}
[DEBUG] [Claude in Chrome] Bridge received: {"type":"extensions_list","extensions":[{"deviceId":"d1175018-...","osPlatform":"Windows","connectedAt":1789193060907,"extensionVersion":"1.0.92"},{"deviceId":"78e170be-...","osPlatform":"Windows","connectedAt":1789182732547,"extensionVersion":"1.0.92"}]}
[INFO] Paired with "WSL HOST!!!!!!" (d1175018)
[DEBUG] [Claude in Chrome] Bridge received: {"type":"routing_ack","tool_use_id":"...","routed_to":"d1175018-...","extension_sockets":2,"mcp_sockets":2}

The session opens a WebSocket to the bridge. A WebSocket is a network connection that stays open, so both sides can send messages at any time. The path after /chrome/ is the claude.ai account uuid, which is the value oauthAccount.accountUuid in ~/.claude.json. The session authenticates with the Claude Code OAuth token. Each extension that is signed in to that account also connects to the bridge. The bridge gives each extension install a device id and lists them in extensions_list. Each tool call names a target device id, and the bridge confirms each call with a routing_ack. WSL is not part of any of this. The bridge sees an account and a device, not an operating system.

Assumed: the relaysno data

  1. Claude Code session in WSL

  2. socat on /tmp/claude-mcp-browser-bridge-<user>/99999.sock

    never opened
  3. relay.js on 172.24.224.1:48923, in the Windows Node

  4. the named pipe \\.\pipe\claude-mcp-browser-bridge-<user>

  5. the native host and the Chrome extension

Actual: the bridgecarried the data

  1. Claude Code session in WSL

  2. a WebSocket, with the OAuth token

    wss://bridge.claudeusercontent.com/chrome/<account uuid>

  3. a tool call to a device id, confirmed by routing_ack

    the Chrome extension in the Windows Chrome, signed in to the same account

    tab created
The bridge sees an account and a device, not an operating system.

I have two opinions about this design. It removed WSL from the problem, and that is what I wanted. It also sends every click and every screenshot from my desk to an Anthropic server and back. That is true even when the browser runs on the same machine. The native host protocol keeps that data local. Both facts are true, and I prefer to know them.

That first run also produced the only error of the experiment. Two extensions were connected to the account, so tabs_context_mcp returned:

tabs_context_mcp · selection errortext
Multiple Chrome browsers are connected to this account and none has been selected for this session. Before any browser action, you MUST call the AskUserQuestion tool ...

AskUserQuestion does not exist in headless mode, so the model used switch_browser instead. The extension showed a pairing prompt in Windows Chrome, and the result was Selected browser "WSL HOST!!!!!!". That is the name I gave that extension install some time ago. The session saves the choice:

Saved pairingjson
"chromeExtension": {"pairedDeviceId": "d1175018-...", "pairedDeviceName": "WSL HOST!!!!!!"}

The binary always selects the bridge

I wanted to know whether any setting selects the socket instead of the bridge, so I read the transport selection. It is one expression, verbatim from 2.1.269:

Transport selection · 2.1.269js
function LXn(e){return e.bridgeConfig?_(e):e.getSocketPaths?I(e):dGt(e)}

_ is the bridge WebSocket client. I is the local socket pool that scans the /tmp directory. dGt is a single local socket client. The context builder always returns bridgeConfig. The URL comes from one small function: wss://bridge.claudeusercontent.com, or the staging host with USE_STAGING_OAUTH, or ws://localhost:8765 with LOCAL_BRIDGE or USE_LOCAL_OAUTH. No environment variable removes bridgeConfig. In this version, a Claude Code session never runs the socket branch. The extension still starts the native host, and the native host still creates its socket or pipe. Nothing connects to it. The log line that started my relay plan describes a listener without a client.

That one line also answered two questions from the start of the experiment. The Windows Claude Code version does not matter, because that binary is not in the data path. The Claude Desktop host that holds the pipe does not matter, for the same reason.

The Windows Chrome ran the task with no relay

To prove that the relay had no part in the result, I removed it. I stopped socat and the Windows Node process, deleted /tmp/claude-mcp-browser-bridge-<user>, and confirmed that TCP port 48923 was closed. I made one mistake during this cleanup. pkill -f 'relay.js --bind ...' also matched the shell that ran the command, and it killed that shell with exit code 144. The pattern pgrep -f 'relay\.js' does not match its own command line.

Then I made two runs. I set the device id in the environment, so the selection error could not return:

Run 1 · device id setbash
CLAUDE_CHROME_PAIRED_DEVICE_ID=<device id> claude --chrome --debug -p "Use the browser tools: open https://example.com in a new tab and reply with the h1 text" --max-turns 10 --output-format stream-json --verbose < /dev/null

The debug log said Auto-connecting to persisted extension: d1175018. The tool results, in order:

Run 1 · tool resultstext
Created new tab. Tab ID: 1220184619
Navigated to https://example.com
Found 1 matching element
- ref_2: heading "Example Domain" (h1)

Result success, 8 turns, 22.7 seconds. The tab titles came back as "Nueva pestaña", which is the language of the Windows Chrome. No Chrome process existed in WSL. So the tab could only be in the Windows Chrome.

The second run tested the three tools that move data between the two operating systems. I chose them because a screenshot, a console log, and a file upload each cross the boundary in a different direction.

Run 2 · screenshottext
Successfully captured screenshot (784x389, jpeg) - ID: ss_08193cwq2
Screenshot saved to: /tmp/claude-chrome-screenshots-72Z52L/screenshot-1789202790264-0.jpg

The file exists in WSL, with 3858 bytes. The directory has mode 0700 and the file has mode 0600. The image shows example.com in the Windows Chrome window.

Example Domain captured in the Windows Chrome from the WSL session, saved into the WSL filesystem

Run 2 · console messagestext
Found 1 console messages:
[1] [4:46:37 a.m.] [LOG] wsl-bridge-test-1789202797809
Run 2 · file uploadtext
Uploaded 1 file(s) to file input: upload-test.txt (0 KB total)
upload-test.txt 36 bytes

The upload used a WSL path, /home/<user>/claude-chrome-wsl-bridge/upload-test.txt. I wanted to know how a Windows browser received a file from a Linux path. The debug log shows it. The session read the file itself and changed the call before it sent the call to the bridge:

Debug log · upload call with the file bytestext
Received tool call: "file_upload" with args: {"tabId":1220184627,"ref":"ref_5","files":[{"data":"aGVsbG8gZnJvbSBXU0wgMjAyNi0wOS0xMlQwODo0NjowNFoK","name":"upload-test.txt","mimeType":"text/plain"}]}

The session resolves the path, the bytes travel as base64 text over the WebSocket, and the extension sets the bytes on the file input. Screenshots travel the same way in the other direction. One tool limit appeared during this run. find cannot select an element by its id attribute, because the accessibility tree does not contain that attribute. find returned the input as ref_5, and that reference worked.

  • success

    Open a tab, read the h1

    The Windows Chrome opened https://example.com in a new tab, and the session read the h1 "Example Domain".

    Direction WSL session → Windows Chrome

  • passed

    Screenshot

    The image was saved in the WSL filesystem: 3858 bytes, file mode 0600.

    Direction Windows Chrome → WSL filesystem

  • passed

    Console messages

    The session read the console output of the page.

    Direction Windows Chrome page → WSL session

  • passed

    File upload

    The session read the WSL file itself. The bytes traveled as base64 text over the WebSocket, and the extension set them on the file input.

    Direction WSL filesystem → Windows Chrome page

The working configuration

  1. Start the session with claude --chrome. In headless mode, this is the only switch that loads the tools. The /chrome panel stays unusable in WSL, and nothing in that panel is necessary.

  2. Sign the Windows Chrome extension in to the same claude.ai account as Claude Code. Log in to Claude Code with /login, not with an API key or a setup token, because the bridge accepts only the /login scopes.

  3. Give the extension install a name in its settings. The name appears in the pairing prompt and in the saved pairing, and a name you recognize prevents a wrong guess later.

  4. On the first run, answer the pairing prompt in the extension, or set CLAUDE_CHROME_PAIRED_DEVICE_ID=<device id>. The device id is in the extensions_list line of a --debug log. After that, the session connects to the saved device automatically.

  5. Keep the WSL Chrome closed, or set the device id. Two extensions on one account cause the selection error above in every new session.

relay.js and the socat command stay in the project folder. They are a working TCP-to-pipe relay, and they are not part of the configuration. I keep them because they took an hour and because they are the evidence.

What others found before

The error has a public history. I read it after my runs, and it changes how to read the older guides.

  1. Dec 2025

    Issue #14367, December 2025: the first report, on version 2.0.71. The message then was "Claude in Chrome Native Host not supported on this platform".

  2. Jan 2026

    Version 2.1.0 changelog: "Fixed Claude in Chrome support for WSL environments". The documentation still says the opposite. Issue #18061 has tracked that contradiction since January 2026.

  3. Feb 2026

    February 2026, in #14367: bgr ran claude --chrome from WSL2 with the real extension. The fix edited the Windows .bat so that Chrome's native messaging ran wsl.exe and the WSL native host. On 2.1.50 it also needed a feature flag.

  4. Mar 2026

    March 2026, issue #41625: jckeen documented that forward as an architecture, with a setup script. He also found the panel condition isWSL || !isSubscriber and the CLAUDE_CODE_ENABLE_CFC bypass. The issue was closed as inactive in May.

  5. no date

    A gist by mosgaragedev repeats the forward as a guide with a registry file.

  6. May 2026

    May 2026, zdayang on Medium: bypasses the extension. Windows Chrome on port 9222 and chrome-devtools-mcp from WSL, sold as an installer.

  7. Jul 2026

    July 2026, Karthik Rathod on dev.to: bypasses the extension with port 9222 and agent-browser. It states that Claude Code "declines before it tries anything".

What changed since then: every native-messaging forward depends on the session connecting to the local socket. In 2.1.269 that branch does not run, so those guides do nothing now, as my relay did nothing. The two DevTools guides work, at the cost of a second Chrome, a separate profile and a different toolset. And the claim that the check stops everything is not true for 2.1.269. The panel refuses; the flag does not.

What this establishes

The /chrome panel refuses WSL with a display condition, and the browser tools do not have that condition. In Claude Code 2.1.269, the tools reach the extension through the bridge, and the bridge identifies the user by account. So the operating system of the session and the operating system of the browser are independent. A WSL session created and closed tabs in Windows Chrome. It navigated and read a page. It saved a screenshot in WSL. It read console output. It uploaded a WSL file into a Windows Chrome page. The local native host socket and the Windows named pipe were checked, relayed, and shown unused.

Some questions stay open. Every run was headless with -p. I did not test an interactive WSL session with --chrome, and I did not test whether claudeInChromeDefaultEnabled works there. The bridge listed a second Windows extension that I could not identify. Only one Chrome profile has the extension, and the WSL Chrome was closed. The transport selection is one line in one version. A later release can make the socket branch active again, and then the relay in this folder becomes useful. I also left the Windows Claude Code at 2.1.261, because it was never in the data path.

The public issue comments for this investigation are collected in my Claude Code contribution log.

What I will check first next time

  1. Which operating system runs the agent, and which one runs the browser? The panel message is about the first one only.

  2. Which transport does this version select? Read the transport selection in the binary before building anything on a socket or a pipe. That would have saved an hour.

  3. Is the OAuth session from /login, with a scope that the bridge accepts? An API key or a setup token turns the tools off without an error message.

  4. How many extensions are connected to the account, and which device id is saved in ~/.claude.json? Two extensions mean a selection step that headless mode cannot complete alone.

  5. Did a real action run in the intended browser? A connected status in the init event means the MCP server started. It does not mean that a tab opened where I expected.

Sources

This note is based on the local configuration and the --debug session logs under ~/.claude/debug/. The code quotes come from the JavaScript inside the Claude Code 2.1.269 binary, read with grep -a. The Windows registry and process listings are quoted above. The documentation source is the Claude Code Chrome page at code.claude.com/docs/en/chrome. The relay script, the socat command, and the run logs are in the project folder next to the notes.