This project provides a CLI tool: wsl-chrome-bridge.
From chrome-devtools-mcp's perspective, wsl-chrome-bridge behaves like a Chrome executable.
It provides a DevTools connection entry in WSL and bridges CDP messages to Windows Chrome.
With the original chrome-devtools-mcp setup, you only need to change --executablePath and add a few bridge-specific arguments to let WSL agents control Windows Chrome, without system-level setup (for example netsh interface portproxy forwarding or WSL2 mirrored mode).
This idea came from wsl-chrome-mcp, which uses PowerShell to work around the same limitation. That project provides MCP server capability itself, while this project keeps using the widely maintained chrome-devtools-mcp and adds a bridge layer.
0.2.0 adds playwright-mcp compatibility and includes multiple bridge argument/configuration adjustments.
If you are upgrading from 0.1.0, read the upgrade guide first: UPGRADE.md.
- 👍 Keep your existing MCP tools:
wsl-chrome-bridgedoes not replacechrome-devtools-mcporplaywright-mcp; it lets you keep using your current tools in WSL2 to control Windows Chrome. - ⚡ No system-level setup required: no WSL Mirrored Mode, no portproxy, and no extra workaround tools to install.
- ⚙️ Low setup overhead: in most cases, you only need to adjust
--executablePathand a few arguments in your existing MCP config. - 🛡️ Unified shutdown behavior:
chrome-devtools-mcpandplaywright-mcphave different Chrome shutdown behavior on exit; this project normalizes that behavior.- 🖥️ In headed mode, if MCP exits, the Windows Chrome window stays open.
- 🤖 In headless mode, if MCP exits, the background Windows Chrome process is automatically terminated.
[Chrome DevTools MCP] [Playwright MCP]
| |
stdio pipe remote-debugging-port/stdio pipe
| |
+------[wsl-chrome-bridge]-----+
|
PowerShell websocket relay
|
[Windows Chrome]
wsl-chrome-bridge will:
- Accept Chrome launch arguments from both
chrome-devtools-mcpandplaywright-mcp - Launch Windows Chrome via PowerShell
- In
chrome-devtools-mcpmode, receive/return CDP messages through stdio pipe (OS pipe) - In
playwright-mcpmode, create a local WebSocket proxy on the mapped port and forward CDP traffic through it - Relay CDP messages bidirectionally to Windows Chrome through PowerShell websocket relay
Note
Based on our integration tests, recent playwright-mcp versions may launch Chrome with --remote-debugging-pipe (the official release notes do not clearly mark the exact version transition, and it may be between 0.0.71 and 0.0.72). To preserve backward compatibility, the bridge continues to support both upstream connection styles: remote-debugging-port and pipe.
- Windows 11 with the latest Chrome installed
powershell.exeavailable for WSL2- WSL2 with Node 20+
In WSL, choose one of the following installation methods:
- Install globally with
npm install -g wsl-chrome-bridge. - Or clone source, build it, then install with
npm link:
git clone https://github.com/pigochu/wsl-chrome-bridge.git
cd wsl-chrome-bridge
npm install
npm run build
npm link- Find the actual path of
wsl-chrome-bridge, for example/home/pigochu/.local/share/mise/shims/wsl-chrome-bridge. - Configure
.codex/config.tomllike this.
Minimal working chrome-devtools-mcp setup:
[sandbox_workspace_write]
network_access = true
[mcp_servers.chrome-devtools]
command = "npx"
args = [
"-y",
"chrome-devtools-mcp@latest",
"--executablePath",
"/home/pigochu/.local/share/mise/shims/wsl-chrome-bridge"
]
enabled = trueThis is the minimum working setup. wsl-chrome-bridge launches Windows Chrome and relays CDP traffic for chrome-devtools-mcp without extra bridge-specific arguments.
Minimal working playwright-mcp setup (with sandbox):
[sandbox_workspace_write]
network_access = true
[mcp_servers.playwright]
command = "npx"
args = [
"-y",
"@playwright/mcp@latest",
"--browser",
"chrome",
"--sandbox",
"--executable-path",
"/home/pigochu/.local/share/mise/shims/wsl-chrome-bridge"
]
enabled = true
[mcp_servers.playwright.env]
DISPLAY = ":9999"In bridge + system Chrome usage, --browser chrome helps avoid upstream --no-sandbox warning behavior while keeping Playwright launch behavior stable.
Note
See the FAQ for how to locate --executablePath.
For more configuration patterns, see agent-config-sample/.codex/.
chrome-devtools-mcp expects --executablePath to be a file path, not just a command name (for example wsl-chrome-bridge).
Use command to locate the absolute path:
command -v wsl-chrome-bridgeThen paste that output into --executablePath.
If you use mise and switch Node versions, do not hardcode a versioned path from command -v (for example .../installs/node/<version>/bin/...).
Prefer the mise shim entry:
echo $HOME/.local/share/mise/shims/wsl-chrome-bridgeOr inspect it directly:
ls -la ~/.local/share/mise/shims/wsl-chrome-bridge- Without a version manager,
command -vis a direct and valid way to get an absolute path. - With mise multi-version Node, prefer
/home/<user>/.local/share/mise/shims/wsl-chrome-bridgeso path does not break when Node versions change.
In WSL/Linux, playwright-mcp checks display availability before launching the browser.
If DISPLAY is not set, Playwright often switches to headless behavior (even when you did not explicitly pass --headless).
That decision happens upstream before bridge logic starts, so wsl-chrome-bridge cannot override it later.
If you want to see a visible Chrome window on Windows, set DISPLAY in MCP env (for example DISPLAY=:999).
wsl-chrome-bridge applies the following lifecycle policy when upstream MCP exits (for example chrome-devtools-mcp / playwright-mcp):
- If Chrome was started in headless mode, it is usually for automated testing; after the test finishes, it is probably no longer needed, so bridge always terminates that background Chrome process to avoid leaving one behind on Windows.
- If Chrome was started in headed mode (non-headless), it usually means the user wants to see the result on screen or continue interacting manually; bridge always keeps that Chrome process, so the Chrome window stays open and the next time the AI assistant restarts, it can continue using the original Chrome session.
This behavior is intentional, because native chrome-devtools-mcp and playwright-mcp do not behave the same way:
chrome-devtools-mcpdoes not actively close Chrome, whether it is headed or headless.playwright-mcpactively closes Chrome, whether it is headed or headless.
wsl-chrome-bridge normalizes that mismatch.
When playwright-mcp is launched without an explicit browser channel, it may resolve to launch args that include --no-sandbox. In headed Chrome, this shows the warning banner:
You are using an unsupported command-line flag: --no-sandbox.
For bridge usage with system Chrome, recommend adding:
--browser chrome
This keeps Playwright on the Chrome channel and avoids passing --no-sandbox in the common setup.
wsl-chrome-bridge accepts direct --user-data-dir / --userDataDir values from upstream MCP.
Playwright may resolve a Windows-style path into a Linux path and pre-create a local empty directory before bridge logic starts.
Bridge checks that local path and removes it only when it is an empty directory. Non-empty directories are never removed.
chrome-devtools-mcp typically does not create this local empty-directory artifact.
Bridge only passes Windows-style user-data-dir values to Windows Chrome. Path-resolved forms like /cwd/%TEMP%\\... are restored back to the original Windows-style path when possible.
If upstream does not send --user-data-dir, or the final value cannot be restored as a Windows-style path (and gets filtered), bridge automatically falls back to:
%TEMP%\wsl-chrome-bridge\profile-default
This ensures Windows Chrome always starts with a usable profile directory and avoids launch behavior differences when no profile is provided.
To avoid Playwright-specific differences and keep a unified config style across chrome-devtools-mcp and playwright-mcp, prefer setting:
WSL_CHROME_BRIDGE_USER_DATA_DIR = "%TEMP%\\wsl-chrome-bridge\\chrome-profile-xxx"--user-data-dir still works, but WSL_CHROME_BRIDGE_USER_DATA_DIR is recommended as the default style.
--user-data-dir/--userDataDir: Bridge only forwards this flag when the final value is Windows-style (including restored%TEMP%\\...forms).- If
--user-data-diris missing or filtered (non-Windows-style), bridge falls back to%TEMP%\\wsl-chrome-bridge\\profile-default. playwright-mcp --browser chrome: recommended for bridge usage to avoid upstream--no-sandboxbanner in headed Chrome.
WSL_CHROME_BRIDGE_USER_DATA_DIR=%TEMP%\\...: recommended default profile setting. It forces the Windows Chrome profile path, takes precedence over upstream--user-data-dir, avoids Playwright local empty-dir artifacts, and keeps a unified config style across MCP servers.WSL_CHROME_BRIDGE_EXECUTABLE_PATH=C:\\...: optional environment variable to override the Windows Chrome executable path.WSL_CHROME_BRIDGE_REMOTE_DEBUG_PORT=9222: optional environment variable for Windows Chrome debug port. If no port is specified, bridge uses a random port instead of fixed9222.- Bridge now probes port availability on Windows before launching Chrome. In fixed-port mode, startup fails fast if that port is already occupied.
WSL_CHROME_BRIDGE_DEBUG_FILE=/tmp/xxx.log: optional debug output file in WSL.WSL_CHROME_BRIDGE_DEBUG_LEVEL=all|important: optional debug verbosity level.important(default) logs only important session/navigation/disconnect-related CDP methods and error responses.alllogs all CDP relay traffic.WSL_CHROME_BRIDGE_DEBUG_RAW_DIR=/tmp/wsl-chrome-bridge-raw: optional directory to store full raw CDP payload files. Each request/response/event payload is written as a separateraw-<timestamp>.logfile, andWSL_CHROME_BRIDGE_DEBUG_FILEincludes the correspondingrawPathfor each relay log entry.
The following original chrome-devtools-mcp option is currently known as incompatible in this bridge setup:
--browser-url: this enables remote-connection mode inchrome-devtools-mcpinstead of pipe mode, so it cannot work withwsl-chrome-bridge.
Other original
chrome-devtools-mcparguments are not all fully validated yet. This project is still under development, so only currently tested limitations are listed here.
Developer lifecycle and recovery reference:
- Node 24
- TypeScript v6
- Commander v14
- ws v8.20
- Test framework: Vitest v4.1
npm install
npm run buildnpm testTest layers:
- Unit tests: path conversion, argument normalization, bridge launch planning
- Scenario tests: CLI argument passing integration into bridge runner