chrome-bridgeBLOCK
Chrome Bridge by frsorrentino — your real, logged-in Chrome as an MCP server for Claude Code: 60 measured, token-efficient tools. ChromeOS included.
Overview
From the repository's own README, as read at the audited commit. Badges and raw HTML are left out.
By frsorrentino · npm chrome-bridge-mcp · not affiliated with other projects named "chrome-bridge".[](https://chromewebstore.google.com/detail/chrome-bridge-for-claude/bioknpaeahidbelaljjohjofiloeodmb)
Claude Code borrows a tab from the Chrome you are already using — signed in, with your extensions and your cookies — works in it, and hands it back. What only a person can do (a login, a 2FA code, a CAPTCHA, a choice) comes back to you through `handoff`: a banner in the page, you act, the agent continues.
Measured against the official browser extension (paired runs, 29/09/2026, n=5 per task, same model): filling a form, 4.7× fewer turns and 2.8× lower cost; finding one row in a 1,500-row table, 2.3× fewer turns and 3.4× lower cost; debugging a broken page, 9.3× fewer turns and 4.3× lower cost. 15 correct answers out of 15, against 12 (bench/RESULTS.md). ~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. Works on ChromeOS.
Quickstart
Requires Node.js 18+ and Chrome 135+.
git clone https://github.com/frsorrentino/chrome-bridge.git cd chrome-bridge && ./install.sh
- Open
chrome://extensions, enable Developer mode, click **Load
unpac
3661aac0f719OBSERVED · 2026-10-11Connect
Built from this server's own package name, version and transport as found in its source — not copied from anyone's documentation, so it cannot drift against a page we do not control. Replace the environment placeholders with a token scoped to the least it needs.
claude mcp add chrome-bridge-mcp -- npx -y [email protected]
Exposed tools (66)
48 read · 12 write · 6 destructive. Blast radius: 6 tools can delete or overwrite — an agent that can be talked into calling a tool can be talked into calling this one.
| Tool | Risk | Description |
|---|---|---|
animations | read | List the page animations: CSS animations, transitions, Web Animations and View Transitions, each with selector and ref, |
assert | read | Assert a page condition, polling until timeout: element exists/visible (optionally with count or containing text), text on page, tab url/title. Patterns: substring, or |
audit | write | One-call page audit, pick the kinds: accessibility, keyboard (tab order and focus issues), SEO, security headers, broken links, |
click | read | Click an element by CSS selector or by a ref (n1, n2...) from get_interactives or navigate. A real pointer sequence: it can submit, |
clipboard | write | Read or write the system clipboard (text). Activates the tab first. |
cookie_audit | read | Cookie and consent-banner audit: clears this site\ |
create_tab | write | Open a new tab, optionally at a URL, and make it the implicit target of later commands in this session. |
dismiss_overlays | read | Dismiss cookie banners/modal overlays: OneTrust, Cookiebot, Usercentrics, then generic heuristic. Idempotent. |
drag_and_drop | destructive | Drag an element onto another. html5 = DragEvent+DataTransfer; pointer = pointer/mouse events (sortable libraries). |
element_screenshot | read | Screenshot cropped to one element or to a viewport region (PNG), optionally enlarged. Read-only. |
emulate_media | destructive | Make the page believe it runs elsewhere, until reset or reload: prefers-color-scheme, prefers-reduced-motion, print mode, |
execute_js | write | Run JavaScript in the page (MAIN world). Requires the extension\ |
extract | read | Read repeated non-tabular structures — product cards, list items, search results — as one record per item: |
extract_table | read | Read a real <table> as JSON: thead cells become keys, each row an object. Read-only. |
fill_form | read | Batch fill form fields with React-compatible events. Handles input, select, checkbox, radio, and textarea. |
find_setting | read | Where is a setting in an unknown admin panel: follows the panel\ |
find_text | read | Find text on the page: parent selector, context, visibility, position per match. Attaches nearby interactive elements (with refs for click/type_text/hover) for the first visible match. |
frames | read | Record the page for duration_ms and report how smooth it ran: main-thread frame rate and dropped frames, Long Animation Frames |
full_page_screenshot | read | Full-page capture by scrolling: stitched segments of ~2 viewports (≤1568px), or one image per viewport with stitch=false. |
get_css_styles | read | Why an element looks the way it does: for each CSS property the winning declaration — selector, stylesheet, |
get_frames | read | List frames (main + iframes) with frameId, parent, URL — for the frame_id parameter of DOM tools. |
get_interactives | read | List actionable elements (buttons, links, inputs, [role], [onclick]) with ready-to-use CSS selector, label, position, flags. Prefer this over dumping HTML to discover selectors. |
get_page_info | read | Page metadata: title, url, visibility, doctype, charset, meta tags, scripts, stylesheets, links and forms. visibility=hidden means |
get_status | read | Bridge status: extension connection, server mode (primary/relay), host, port, server and extension versions, js_evaluation |
get_storage | read | Read localStorage, sessionStorage or cookies of the current origin (type=all for every one). Read-only. |
get_tabs | read | List every open tab with id, url, title, active flag and mine (created by this session). Read-only. Find a tab_id when the implicit |
handle_dialogs | read | Auto-accept/dismiss future JS dialogs (alert/confirm/prompt), logging them. reset restores native dialogs and returns the log. |
handoff | read | Hand the browser to the user for what only a person can do — 2FA, CAPTCHA, login, a choice — and wait: a banner in the page shows your |
heap_snapshot | read | Take a JS heap snapshot of the tab (after a garbage collection) and summarize it: object count, total size, detached DOM nodes, the |
hover | read | Hover over an element, by CSS selector or ref. Default: synthetic mouseenter/mouseover/mousemove, which fire JS hover handlers |
http_auth | destructive | Set/clear credentials for HTTP Basic/Digest auth dialogs (browser-wide, in-memory only). |
http_request | read | HTTP request sent from the browser, with the logged-in user\ |
inject_css | read | Inject a CSS rule into the page. Stays until the next navigation or reload, and re-injecting the same id |
lighthouse | write | Run a full Lighthouse audit (performance included, unlike chrome-devtools-mcp, plus accessibility, best practices, SEO) and return |
manage_downloads | write | List downloads, start one, or wait for the newest to finish. Files land in the browser Downloads folder, not on the server. |
measure_spacing | read | Measure the gap, overlap and distance in CSS pixels between two elements, with their margins and paddings. |
modify_dom | write | Change an element in the live DOM: setAttribute, removeAttribute, addClass, removeClass, setStyle, |
monitor_network | read | Monitor network requests. source=page: XHR/fetch hook (installed on first call); source=browser: all requests incl. static assets; source=websocket: connections and messages. format=har exports HAR 1.2. |
move_tab | write | Move an existing tab into another window (chrome.tabs.move), keeping id, history and page state; works on chrome-untrusted:// |
navigate | read | Navigate a Chrome tab to a URL. Returns a preview of interactive elements with refs usable in click/type_text/hover. |
network_rules | read | Network interception, browser-wide, survives reloads until cleared: block, redirect, set/remove headers, stub a synthetic body, or record real API responses and replay them with forced errors/latency (local helper; from HTTPS pages the stub host must be trusted). |
perf_trace | read | Record a DevTools performance trace and return its conclusions, never the trace: LCP with its phases (TTFB, resource load delay and |
press_key | write | Send a key to the focused element (or to selector, focusing it first) as a real keydown/keypress/keyup |
query_dom | read | Query DOM elements by CSS selector, returning structure, attributes, bounding rect, and computed styles. |
read_console | read | Read console messages captured since page load, incl. uncaught errors and unhandled rejections. |
read_form | read | Read a form as the user filled it — label, type, value, checked/selected, required-but-empty, browser validity — to check it against the |
read_page | read | Read the page as text (default), markdown, raw HTML, or accessibility tree. Read-only. markdown keeps headings, links and |
save_page | write | Save the full page (DOM, styles, images) as an MHTML archive file on the server filesystem. |
screencast | read | Record the tab as a video (.mp4 or .webm) to watch an animation or interaction frame by frame; returns the file path and size, never |
screenshot | read | Screenshot of the visible viewport only (PNG), at the current scroll position, or one per viewport preset with presets. Read-only. |
screenshot_diff | read | |
scroll | read | Scroll. action=to: once to element/coordinates; action=until: repeatedly until element visible, network idle, or no new content (infinite scroll). |
session_fixture | read | Snapshot localStorage, sessionStorage and cookies of the current origin into a named fixture (a logged-in state, usually), |
session_record | read | Record the commands of this session as a replayable jsonl (chrome-bridge replay --file <path>); observe what the user does in a tab ( |
set_geolocation | destructive | Override navigator.geolocation with fixed coordinates (page-level patch). reset restores native. |
set_storage | destructive | Write, delete or clear a localStorage/sessionStorage key, or a cookie with its path, domain and expiry. |
tab_action | read | Tab lifecycle: close, activate, reload, back, forward, discard, mute, duplicate, close_session (only tabs this session created). |
tile_windows | read | Tile Chrome windows over one monitor in equal parts with no gap. Only Chrome windows. The monitor is the one of a |
track_events | read | Decode the tracking beacons the page fired (GA4, Meta Pixel, Google Ads, TikTok, LinkedIn, Pinterest, Microsoft Ads, GTM, Hotjar, Clarity) |
type_text | write | Put text into an input, textarea or contenteditable, by selector or ref. Replaces the whole value through the |
upload_file | write | Set a file on input[type=file] from the server filesystem via DataTransfer (max 200 MB; over 6 MB it travels in pieces). Keys and credentials (~/.ssh, ~/.aws, |
viewport_resize | read | Resize the Chrome **window** to a preset (mobile 375x812, tablet 768x1024, desktop 1440x900) or explicit dimensions; |
wait_for | read | Block until a condition holds: element, text, function (JS expression; needs the |
watch | read | Keep watching a page in the background — |
watch_dom | read | Watch DOM mutations (MutationObserver). First call installs the watcher; later calls read accumulated mutations. |
window_layout | destructive | Save the current window arrangement under a name, restore, list or delete. save overwrites silently. Window ids do not survive |
Trust audit
BLOCKgrade D · trust 61/100 Do not install this without reading the findings. The audit found something that could harm you or your machine.
| Layer | What it checks | Result |
|---|---|---|
| L0 | Provenance & inventory | PASS |
| L1 | Static analysis of the code | FAIL |
| L2 | Instruction surface (what it tells the agent) | PASS |
| L3 | Class-specific surface | WARN |
| L4 | Behavioural (sandbox) | SKIPPED |
What the source does
- Filesystem
- declared (5 observation(s))
- Network
- declared (7 observation(s))
- Shell
- declared (2 observation(s))
- Dependencies
- not all pinned
- Secrets in source
- none-found
Findings (25)
fn = new Function(`return (${userCode});`);try { fn = new Function(userCode); } catch (e) {const SECRET_FILES = /(^|[\\/])(\.env(\..*)?|\.netrc|\.npmrc|\.pypirc|\.git-credentials|id_(rsa|dsa|ecdsa|ed25519)(_sk)?|[^\\/]*\.(pem|key|p12|pfx|jks|keystore|kdbx))$/i;
'Set a file on input[type=file] from the server filesystem via DataTransfer (max 200 MB; over 6 MB it travels in pieces). Keys and credentials (~/.ssh, ~/.aws, '
+ '~/.gnupg, .env*, *.pem, *.key, id_*...) are refused, and a --read-root on the server limits the readable tree: the call fails '
path: z.string().describe('Absolute path on the server machine; keys and credentials (~/.ssh, .env, *.pem) refused'),return { server, base: `http://127.0.0.1:${server.address().port}` };const list = await (await fetch(`http://127.0.0.1:${port}/json/list`)).json();drag_and_drop, emulate_media, http_auth, set_geolocation, set_storage, window_layout
return new Function('chrome', `${body}; return windowBoundsReport;`)(chrome);const hash = createHash('sha1').update(data.image).digest('hex');const entry = buildEntry({ name: 'upload_file', args: { path: 'x' }, message: 'Refusing to read ~/.ssh/id_ed25519', env: process.env });import { runAssert, matchPattern } from '../../server/assertions.js';import { runAudit, summarizeAudit, auditReport, DEFAULT_KINDS } from '../../server/audit.js';import { registerTools } from '../../server/tools.js';import { MessageType } from '../../server/protocol.js';import { createPacer } from '../../extension/lib/capture-pacing.js';test('trackingLines senza beacon spiega il probabile motivo', () => {http://127.0.0.1:.../ (tab N shows the browser's error page)`. The same goes
base = `http://127.0.0.1:${server.address().port}`;const results = await checkLinksBatch([{ url: 'http://127.0.0.1:1/x', text: 'dead' }], 1000);u.parts[i] = Uint8Array.from(atob(b64), (c) => c.charCodeAt(0));
const bytes = id ? new Blob(pieces.parts) : Uint8Array.from(atob(b64), (c) => c.charCodeAt(0));
@jridgewell/trace-mapping, @modelcontextprotocol/sdk, node-html-parser, ws
| A second MCP session on the same browser | **Measured** (daily use) | The second server becomes a relay through the first; the two still share tabs and refs, so two agents on the same tab step on ea
Gates applied: no_behavioural_pass.
3661aac0f719full audit observations/trust-audit/mcp-server/frsorrentino__chrome-bridge.json · Report an issue / request a re-scanAudit history
Every audit this server has had. A grade with a past is a grade somebody is still checking.
| Date | Source | Verdict | Grade | Score | Change |
|---|---|---|---|---|---|
| 2026-10-11 | 3661aac0f719 | BLOCK | D | 61 | first audit |
Questions
What is the chrome-bridge MCP server?
Chrome Bridge by frsorrentino — your real, logged-in Chrome as an MCP server for Claude Code: 60 measured, token-efficient tools. ChromeOS included.
What tools does chrome-bridge expose?
66 in total: 48 read-only, 12 that write, and 6 that can delete or overwrite (drag_and_drop, emulate_media, http_auth, set_geolocation, set_storage). Every one is listed on this page with its risk.
Is chrome-bridge safe to connect to an agent?
No — not without reading the findings first. The audit graded it D (61/100) and found 6 critical or high issues in the source. Each one is listed on this page with the file and line it is on. Separately from the audit: 6 of its tools can destroy data, so scope the token you give it to what you actually need.
What credentials does chrome-bridge need?
It reads CHROME_BRIDGE_TOKEN from the environment. Give it a token scoped to the least it needs — an agent that can be talked into calling a tool can be talked into calling it with your credentials.
How does chrome-bridge run?
It speaks stdio, so it runs as a local process your client starts. It is published on npm as chrome-bridge-mcp at 1.29.2.
How current is this page?
The grade is for one exact copy of the source (3661aac0f719), read on 2026-10-11. The repository is watched and re-audited when it changes.