chrome-bridge

mcp
Security Audit
Warn
Health Warn
  • License — License: MIT
  • Description — Repository has a description
  • Active repo — Last push 0 days ago
  • Low visibility — Only 5 GitHub stars
Code Warn
  • fs module — File system access in .github/workflows/retry-store-publish.yml
  • process.env — Environment variable access in bench/latency.mjs
Permissions Pass
  • Permissions — No dangerous permissions requested

No AI report is available for this listing yet.

SUMMARY

Chrome Bridge by frsorrentino — your real, logged-in Chrome as an MCP server for Claude Code: 59 measured, token-efficient tools. ChromeOS included.

README.md

Chrome Bridge

By frsorrentino · npm chrome-bridge-mcp · not affiliated with other projects named "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.

60 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 [email protected]: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).

As a plugin, without a clone: in Claude Code, /plugin marketplace add frsorrentino/chrome-bridge then /plugin install chrome-bridge@chrome-bridge
registers the MCP server from npm (all capabilities) together with the recipes
skill; clients that read Agent Plugins 1.0 get the
same from plugin.json + mcp.json. The extension still comes from the Web
Store or extension/. Pick one path: the plugin and install.sh would
register the same server twice.

Why Chrome Bridge?

Chrome Bridge Claude in Chrome Chrome DevTools MCP Playwright MCP
ChromeOS / Crostini Yes (real host) No Container only Container only
Tools 60 (39 core) 22 29 default (56 with flags) 24 core (71 total)
Requires paid plan No Yes (Pro+) No No
Network mocking Yes (stub/headers) No No Yes
Visual regression Yes (screenshot_diff) No No No
Audits (a11y/SEO/sec) Yes (one call, report on disk) No Lighthouse No
Headless / CI Yes No Yes Yes
GIF / video No Yes Partial No
Breakpoints / heap No No Yes No

Codex for Chrome (OpenAI, May 2026) sits in the Claude in Chrome column: an
official extension with the debugger permission, macOS and Windows only,
the ChatGPT app required. Claude in Chrome documents Linux desktop since
September 2026; ChromeOS and WSL stay out. Competitor figures measured on
2026-09-01 and 2026-09-11 (docs/analisi-2026-09-11-concorrenti.md).

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

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

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

Group N What's in it
Core & Navigation 13 tabs, windows, navigate, screenshot, tile_windows
Interaction 11 click, fill_form, upload_file, dialogs, clipboard
DOM & Inspection 11 read_page, extract, query_dom, get_css_styles, watch_dom
Debugging & Network 8 execute_js, console, network log, mocking, track_events
Visual & Responsive 5 screenshot_diff, viewport and zoom, media emulation
Audits 2 audit (a11y, keyboard, SEO, security, links, vitals, css, resources, cache in one call), cookie_audit
State, Storage & Files 9 storage, 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:

Variable Default Notes
CHROME_BRIDGE_PORT 8765
CHROME_BRIDGE_HOST / --host 127.0.0.1 0.0.0.0 only where the browser lives outside the container (ChromeOS/Crostini port-forward) — and only with a token
CHROME_BRIDGE_TOKEN unset Required on both ext_init and relay_init. Strongly recommended whenever the bind isn't loopback
CHROME_BRIDGE_CAPS / --caps core core, audits, visual, network, storage, dom, files, all. install.sh uses all
CHROME_BRIDGE_NO_JS / --no-js unset No arbitrary JavaScript in the page: execute_js and modify_dom leave the schema, wait_for(condition=function) and javascript:/data: URLs are refused. get_status reports js_evaluation
CHROME_BRIDGE_READ_ROOT / --read-root unset Where upload_file may read from. Unset: any file except keys and credentials (~/.ssh, ~/.aws, ~/.gnupg, .env*, id_*, *.pem, *.key, …), symlinks resolved first. Set: only files under this directory, whatever their name — the way to upload a certificate key on purpose
CHROME_BRIDGE_OBSERVE unset off stops the local error log below. The plugin sets hook: its hooks keep the log and the server stays out of it
CHROME_BRIDGE_WRITE_ROOT / --write-root unset Every path the model chooses (save_to, output_path, exports) must be under this directory, checked before the browser does any work; the server's own state under ~/.config/chrome-bridge stays writable. The CLI is your shell and is not restricted

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.

Threat model

chrome-bridge gives an AI agent your real, logged-in browser. Scanners that
rate MCP servers by capability flag it, correctly, for code execution
(execute_js), requests that carry your cookies (http_request) and reading
private page data (get_storage, get_tabs). Those are the product, not bugs.
What matters is who can reach them and what a hostile page can make the agent
do with them.

  • Who can connect: see above — loopback bind, chrome-extension:// origin
    check, CHROME_BRIDGE_TOKEN on both handshakes.
  • The real risk is prompt injection. A page can ask the agent to run
    JavaScript, send a request with your cookies, submit a form or upload a file.
    Treat every page you automate as untrusted input, and review what the agent
    proposes on sites that hold money or credentials.
  • Reduce the surface: --caps core (the default) exposes the smallest set;
    --no-js removes arbitrary JavaScript; --write-root fences every path the
    model writes; upload_file refuses keys and credentials, and --read-root
    fences it to one directory. For sessions that matter, automate in a separate
    Chrome profile.
  • --no-js is not a sandbox: inject_css stays, and CSS attribute
    selectors with url() can leak attribute values to a remote host.
  • What leaves your machine is what the browser sends on the agent's behalf.
    chrome-bridge itself has no telemetry; the error log below stays local unless
    you approve an issue.

Local error log

When one of its own tools fails, chrome-bridge adds a line to a local file,
${XDG_STATE_HOME:-~/.local/state}/claude-observe/chrome-bridge.jsonl (mode
0600, in a 0700 folder), in the claude-observe
format (the plugin's copy is in observe/). With the plugin, a PostToolUseFailure hook writes it; with the npm
server alone, the server does. The same error seen again is one line with a
count.

  • Kept: the tool name, the error text after scrubbing (your home folder
    becomes ~; emails, URL queries, secrets and typed values are removed), the
    names of the parameters and the length of text values, the chrome-bridge
    version, the OS, the name of the working folder and of the Claude Code config
    folder. With the plugin, also the Claude Code and model versions, the
    session id and the names of the last three tools called.
  • Never kept: parameter values — no URLs, selectors, typed text or page
    content.
  • Nothing leaves your computer unless you say yes. With the plugin, once
    a few errors have piled up (or one has waited a few days, or one is marked
    as a defect), a line at the end of a turn tells you so, and Claude may offer,
    at a natural moment, to send them as one GitHub issue: it shows you the
    anonymized text first, then asks with buttons (from your GitHub, or not now)
    and sends only what you chose. /chrome-bridge:observe send does the same
    on demand; list, mark and add read and triage the log. The server
    alone never sends or offers anything.
  • Security observations (a read or write outside the perimeter, a secret
    exposed, unwanted code execution, data leaving the computer) never enter a
    public issue: /chrome-bridge:observe send --security prepares a private
    vulnerability report for the maintainers, see SECURITY.md.
  • Turn it off: {"enabled": false} in ~/.config/claude-observe/config.json
    (every plugin that uses claude-observe), or CHROME_BRIDGE_OBSERVE=off for
    the server. Keep the log but stop the offers: {"propose": false}.

Troubleshooting

Symptom Cause / fix
Chrome extension not connected Extension 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 use Expected: 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-bridge Something else owns the port. Free it or change CHROME_BRIDGE_PORT.
execute_js fails Enable Allow user scripts in chrome://extensions → Chrome Bridge → Details (Chrome 138+; on 135-137 enable Developer Mode).
read_console returns note=Instrumentation not loaded The page was opened before the extension, "Capture console & metrics" is off, or the page isn't injectable (chrome://). Reload it.
Screenshot times out, or image readback failed A minimized or fully covered window stops producing frames; captures fail after 10 s. Bring the window forward.
wait_for/scroll return page_hidden: true, pages stop updating Same cause: Chrome does not render a hidden page and slows its timers (after a few minutes, to one wake-up per minute). get_page_info reports visibility. Bring the window on screen, or create_tab with new_window and bounds.
screenshot presets says NOT APPLIED Presets resize the window; they do not emulate a phone (no device pixel ratio, UA or touch). The window manager enforces a minimum width, and no window exceeds the screen (e.g. at most a 1536×686 viewport on a 1536×864 ChromeOS screen). For phone emulation or larger viewports use a headless browser.
type_text returns mismatch: true The field rejected the value. Events from an extension have isTrusted: false, and some widgets (date pickers, search boxes with tokenizers) discard them. Try mode: 'keys', then the site's own controls (the calendar buttons), or handoff.
[media removed: request limit] instead of a screenshot Written by the MCP client, not by the bridge: too many images in one request. Use save_to, or element_screenshot with region and a small scale.
Commands work, then stop The 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 reboot Install from the Web Store instead of Load unpacked.
Tool missing from the list It's in an opt-in group. Check get_status → caps_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) ·
npm run bench:latency (milliseconds per tool, launches its own Chromium,
writes docs/PERFORMANCE.md).

License

MIT

Reviews (0)

No results found