chrome-local-mcp
chrome-local-mcp
English | 简体中文
Lightweight MCP server for controlling local Chrome via CDP. Works with any Chrome exposing a remote debugging port, with first-class support for the tricky WSL-to-Windows setup (tested on Windows and WSL). Designed for AI coding agents (Claude Code, Codex, Cursor) that need browser interaction with minimal token overhead and human-in-the-loop support.
Why?
Existing browser MCP tools (Chrome DevTools MCP, Playwright MCP) have three problems:
Problem | This project's solution |
Schema overhead: 13,700-18,000 tokens before any interaction | ~1,000 tokens for 17 tools |
Long page content floods LLM context (accessibility tree up to 124K tokens) | Long content auto-saved to file, only summary returned |
No human intervention mechanism for CAPTCHAs/verification | Built-in auto-detection + |
Prerequisites
Windows Chrome running with remote debugging enabled:
chrome.exe --remote-debugging-port=9222Important: Close all existing Chrome windows before running this command, otherwise the debug port won't open.
Or start an isolated Chrome profile without closing your normal browser:
.\scripts\start-chrome-debug.ps1The startup script supports multiple modes:
# Recommended for screenshots: keeps rendering active when Chrome is covered/backgrounded.
.\scripts\start-chrome-debug.ps1 -Mode stable
# Plain visible Chrome with an isolated profile.
.\scripts\start-chrome-debug.ps1 -Mode interactive
# No visible Chrome window. Best for automation, but weaker for manual CAPTCHA/login flows.
.\scripts\start-chrome-debug.ps1 -Mode headlessEquivalent npm shortcuts are available on Windows:
npm run chrome:stable
npm run chrome:stable:wsl
npm run chrome:interactive
npm run chrome:headlessstable adds Chrome flags that reduce background throttling and native occlusion issues:
--disable-backgrounding-occluded-windows
--disable-renderer-backgrounding
--disable-background-timer-throttling
--disable-features=CalculateNativeWinOcclusionUseful options:
.\scripts\start-chrome-debug.ps1 -Mode stable -Port 9333 -UserDataDir D:\tmp\chrome-mcp-profile
.\scripts\start-chrome-debug.ps1 -Mode stable -WindowWidth 1600 -WindowHeight 1000
.\scripts\start-chrome-debug.ps1 -Mode stable -ExtraArgs "--lang=en-US"
.\scripts\start-chrome-debug.ps1 -Mode stable -ReadyTimeoutSeconds 20
.\scripts\start-chrome-debug.ps1 -Mode stable -ReuseExisting
.\scripts\start-chrome-debug.ps1 -Mode stable -DryRunBy default, the script refuses to launch a second Chrome when the requested debug port is already listening. Use -ReuseExisting only when you intentionally want to attach to the currently running Chrome CDP endpoint.
If the MCP server runs in WSL and cannot reach Windows localhost:9222, bind Chrome to a reachable address and point the MCP server at the Windows host IP:
.\scripts\start-chrome-debug.ps1 -Mode stable -RemoteDebuggingAddress 0.0.0.0Or use the npm shortcut:
npm run chrome:stable:wslThen start the MCP server with the Windows host IP instead of localhost:
node dist/index.js --host <windows-host-ip> --port 92220.0.0.0 exposes Chrome DevTools beyond local loopback. Use it only on a trusted local machine/network and prefer firewall rules that restrict access.
Verify Chrome DevTools Protocol is reachable:
curl http://localhost:9222/json/versionInstall
cd /path/to/chrome-local-mcp
npm install
npm run buildSetup
Windows Codex CLI
codex mcp add chrome-local -- node "D:\repository\chrome-local-mcp\dist\index.js"
codex mcp listIf Chrome uses a non-default host or port:
codex mcp add chrome-local -- node "D:\repository\chrome-local-mcp\dist\index.js" --host localhost --port 9222Windows Claude Code
claude mcp add --scope user chrome-local -- node "D:\repository\chrome-local-mcp\dist\index.js"
claude mcp listClaude Code
claude mcp add --scope user chrome-local -- node /path/to/chrome-local-mcp/dist/index.jsOpenAI Codex CLI
Edit ~/.codex/config.toml:
[mcp_servers.chrome-local]
command = ["node", "/path/to/chrome-local-mcp/dist/index.js"]Cursor / VS Code
Add to .cursor/mcp.json or VS Code MCP settings:
{
"mcpServers": {
"chrome-local": {
"command": "node",
"args": ["/path/to/chrome-local-mcp/dist/index.js"]
}
}
}Custom host/port
If WSL and Windows don't share localhost (WSL2 NAT mode), pass the Windows host IP:
node dist/index.js --host 172.x.x.1 --port 9222You can also configure defaults with environment variables:
Variable | Description | Default |
| Chrome DevTools Protocol host |
|
| Chrome DevTools Protocol port |
|
| Directory for saved content and screenshots | OS temp directory + |
| JSONL log path for human verification events detected during normal MCP use | repo |
| Dedupe window for the same URL/challenge log event |
|
| Set to | unset |
Tools
Navigation
Tool | Description |
| Open a URL. Auto-detects verification challenges. |
| Wait for an element to appear. |
| Browser back button. |
Content
Tool | Description |
| Extract page text. Short text returned directly; long text saved to file with preview. |
| Execute JavaScript in page context. Long results are saved to file with preview. |
| Save screenshot to file (never inlined into context). |
Interaction
Tool | Description |
| Click an element. Auto-detects verification after click. |
| Type into a form field. |
| Press a key (Enter, Tab, Escape, etc.). |
| Scroll up or down. |
Tabs
Tool | Description |
| List all open tabs. |
| Open URL in a new tab. |
| Switch to a tab by ID. |
| Close a tab. |
Search
Tool | Description |
| Google search with structured results. Auto-detects CAPTCHAs. |
Human Intervention
Tool | Description |
| Pause for human action (CAPTCHA, login, etc.). Polls until condition is met. |
| Detect verification challenges on current page. |
Human-in-the-loop
Verification challenges are auto-detected after every navigation-related action. When detected, the tool response includes:
{
"status": "human_verification_needed",
"challenges": ["cloudflare", "turnstile"],
"message": "Page has a verification challenge. Complete it in the browser, then call wait_for_human or retry."
}Detected challenge types: cloudflare, recaptcha, hcaptcha, turnstile, google_sorry, google_consent, age_gate.
Access prompts that do not block readable content, such as article subscription prompts or paid preview banners, should not return human_verification_needed.
When normal MCP usage detects a real human verification challenge, it appends a JSONL event to logs/challenge-events.jsonl by default:
{
"event": "human_verification_detected",
"triggered": true,
"triggered_at": "2026-05-02T02:10:00.000Z",
"webpage": {
"url": "https://example.com/",
"title": "Example"
},
"has_challenge": true,
"challenges": ["cloudflare"]
}Use CHROME_MCP_CHALLENGE_LOG to write this log somewhere else.
Challenge regression log
Run the canary monitor against a Chrome instance to record pages where challenge detection may be too aggressive:
npm run build
npm run monitor:challengesThe monitor writes JSONL entries to logs/challenge-regressions.jsonl. Each entry includes checked_at, triggered_at, and the webpage URL/title. By default it only logs; use -- --fail-on-issue true if you want issues to make the command exit non-zero.
Typical flow
Agent: navigate("https://protected-site.com")
-> {"status": "human_verification_needed", "challenges": ["cloudflare"]}
Agent: wait_for_human(reason="Cloudflare verification", wait_until_gone="#challenge-running")
-> User completes verification in Chrome
-> {"status": "ready", "waited_ms": 8000}
Agent: get_content()
-> Page content extracted normallyToken efficiency
Metric | chrome-local-mcp | Chrome DevTools MCP | Playwright MCP |
Schema overhead | ~1,000 tokens | ~18,000 tokens | ~13,700 tokens |
navigate response | ~60 tokens | varies | ~3,800+ tokens (accessibility tree) |
screenshot | ~30 tokens (file path) | ~4,000 tokens (base64) | ~4,000 tokens |
10-page research task | ~5,000 tokens | ~70,000+ tokens | ~500,000+ tokens |
These numbers are order-of-magnitude estimates. Actual token usage depends on the client, tool schema version, page complexity, and task flow.
Architecture
Claude Code / Codex (WSL)
↓ stdio
chrome-local-mcp (Node.js, WSL)
↓ CDP WebSocket (ws://localhost:9222)
Chrome (Windows, with --remote-debugging-port=9222)Transport: stdio (MCP standard)
CDP client:
chrome-remote-interfaceLong content saved to
/tmp/chrome-mcp/; agents read files on demand
Development
npm run build # Compile TypeScript
npm run dev # Watch mode
npm run test # Run test suiteLicense
MIT