Odel
chrome bridge

chrome bridge

Local
@frsorrentino3JavaScriptMITUpdated 4 days ago

Your real, logged-in Chrome as an MCP server: 59 token-efficient web-dev tools. ChromeOS included.

Chrome Bridge

License: MIT Node 18+ Chrome 135+ Tests Chrome Web Store

An MCP server that gives Claude Code your real, logged-in Chrome — measured 2.75× fewer turns and 2.28× lower cost than the official "Claude in Chrome" extension on a form-filling task, with ~3× the toolset and no paid plan.

59 web-development tools (navigation, DOM inspection, visual regression, audits, network mocking) over a local WebSocket bridge, plus a headless instance for CI. Self-hosted, local-only.

The same form filled in 6 turns instead of 16.5 — 2.75× fewer turns, 2.28× lower cost

Quickstart

Requires Node.js 18+ and Chrome 135+.

git clone git@github.com:frsorrentino/chrome-bridge.git
cd chrome-bridge && ./install.sh
  1. Open chrome://extensions, enable Developer mode, click Load unpacked, select the extension/ folder.
  2. Restart Claude Code.

Then ask for something like "open localhost:3000, run an accessibility audit and find the Sign Up button": Claude Code calls navigate, audit and find_text. Because navigate already returns element refs, click(ref="n1") follows with no discovery turn in between.

On ChromeOS/Crostini install from the Chrome Web Store instead: an unpacked extension is dropped on every reboot, because the container isn't mounted when Chrome starts.

install.sh registers the MCP server with --scope user. To do it by hand: claude mcp add --scope user chrome-bridge node /path/to/server/index.js. For execute_js, enable Allow user scripts in chrome://extensions → Chrome Bridge → Details (on Chrome 135-137, enable Developer Mode instead).

Why Chrome Bridge?

Chrome BridgeClaude in ChromeChrome DevTools MCPPlaywright MCP
ChromeOS / CrostiniYes (real host)NoContainer onlyContainer only
Tools59 (38 core)~20~5023 core (71 total)
Requires paid planNoYes (Pro+)NoNo
Network mockingYes (stub/headers)NoNoYes
Visual regressionYes (screenshot_diff)NoNoNo
Audits (a11y/SEO/sec)Yes (one call, report on disk)NoPartialNo
Headless / CIYesNoYesYes
GIF / videoNoYesPartialNo
Breakpoints / heapNoNoYesNo

It wins on round trips, not payload size: short element refs instead of the screenshot-and-click loop, fill_form filling N fields in one call, table filtering done server-side. Per single turn it actually costs slightly more.

The full benchmark — method, every raw run including the unfavourable ones, and what the harness can't measure — is in docs/EFFICIENCY.md.

Claude checks its own work: console errors, pixel diffs, network mocking and audits

Using it

The skill in skills/chrome-bridge/SKILL.md is what makes the tools discoverable: recipes with the phrase that triggers each one ("verify the email arrives", "test the checkout with a test card", "which plugin slows the page", "what fires before consent"), the tool sequence, and the zero-token CLI commands the model would otherwise never see. install.sh copies it to ~/.claude/skills/chrome-bridge; do the same by hand for other clients.

Beyond the MCP tools, two lanes keep work away from the model entirely.

Some jobs never touch the model: the CLI lane runs the same tools at zero tokens

CLI — batch operations, piped through grep or jq before anything reaches the context:

chrome-bridge navigate --url https://example.com
chrome-bridge read_console --level error | head -20
chrome-bridge assert --selector "#success" --text "Done"
chrome-bridge replay --file ./recordings/login.jsonl

Launch mode — a dedicated Chromium instance with an ephemeral profile, for isolated sessions or CI:

node server/index.js --launch --headless

Pair it with session_record + replay for smoke tests with no model in the loop. In launch mode execute_js falls back to new Function when the user-script toggle isn't available.

Tools

59 in total, in seven groups. Only core (38 tools) loads by default; the rest are opt-in via --caps.

59 tools in seven groups, from clicking a button to auditing a whole page

GroupNWhat's in it
Core & Navigation13tabs, windows, navigate, screenshot, tile_windows
Interaction11click, fill_form, upload_file, dialogs, clipboard
DOM & Inspection10read_page, extract, query_dom, watch_dom
Debugging & Network8execute_js, console, network log, mocking, track_events
Visual & Responsive5screenshot_diff, viewport and zoom, media emulation
Audits2audit (a11y, keyboard, SEO, security, links, vitals, css, resources, cache in one call), cookie_audit
State, Storage & Files9storage, fixtures, MHTML, recording, assert

Every tool, with the notes that matter: docs/TOOLS.md.

How it works

It drives the Chrome you are logged into, over a local WebSocket bridge

Claude Code  <--stdio-->  MCP Server  <--WebSocket :8765-->  Chrome Extension
                          (server/)                          (extension/, MV3)

The Node.js server handles the protocol and tool logic; the MV3 extension executes commands through Chrome APIs. User scripts (execute_js) run via chrome.userScripts.execute().

Configuration and security

Environment variables, each with a matching CLI flag:

VariableDefaultNotes
CHROME_BRIDGE_PORT8765
CHROME_BRIDGE_HOST / --host127.0.0.10.0.0.0 only where the browser lives outside the container (ChromeOS/Crostini port-forward) — and only with a token
CHROME_BRIDGE_TOKENunsetRequired on both ext_init and relay_init. Strongly recommended whenever the bind isn't loopback
CHROME_BRIDGE_CAPS / --capscorecore, audits, visual, network, storage, dom, files, all. install.sh uses all

The bridge binds loopback, accepts extension connections only from a chrome-extension:// origin, and — when a token is set — requires it on both handshakes. Without one, any local process could act as a relay and reach execute_js inside your authenticated browser session. Secondary MCP instances connect via loopback and are acknowledged with relay_init_ok, so a foreign process holding the port fails fast instead of timing out per command.

What is not protected: page content reaches the model unfiltered, so a hostile page's text is untrusted input. get_storage, session_fixture, HAR exports and screenshots are not redacted and may carry cookies, tokens or personal data. Don't point the automation at pages holding secrets you wouldn't paste into a chat.

Troubleshooting

SymptomCause / fix
Chrome extension not connectedExtension disabled, or its port differs from the server's. The error names the actual host/port; check them in the popup (⚙).
Port 8765 already in useExpected: a second MCP session becomes a relay and shares the one bridge. Set CHROME_BRIDGE_PORT for a separate one.
Port N is held by a process that is not chrome-bridgeSomething else owns the port. Free it or change CHROME_BRIDGE_PORT.
execute_js failsEnable Allow user scripts in chrome://extensions → Chrome Bridge → Details (Chrome 138+; on 135-137 enable Developer Mode).
read_console returns note=Instrumentation not loadedThe page was opened before the extension, "Capture console & metrics" is off, or the page isn't injectable (chrome://). Reload it.
Screenshot times outOn ChromeOS a fully occluded window stops producing frames; captures fail after 10s. Bring the window forward.
Commands work, then stopThe MV3 service worker restarted and in-memory state (network log, diff baselines, HTTP auth) was reset. Re-run the monitoring call.
Extension dropped on every ChromeOS rebootInstall from the Web Store instead of Load unpacked.
Tool missing from the listIt's in an opt-in group. Check get_statuscaps_available, then set CHROME_BRIDGE_CAPS=all.

Documentation

Tests

npm test (Chrome-free, ~22s) · npm run test:e2e (needs Chrome and a connected extension; with a bridge already on 8765: CHROME_BRIDGE_PORT=8799 node test/test-devtools.js --launch, which opens its own Chromium with extension/) · npm run measure (schema cost).

License

MIT