chrome-bridge-mcp 1.8.0 → 1.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,10 +1,10 @@
1
1
  # Chrome Bridge
2
2
 
3
- ![License: MIT](https://img.shields.io/badge/license-MIT-green) ![Node 18+](https://img.shields.io/badge/node-%E2%89%A518-brightgreen) ![Chrome 135+](https://img.shields.io/badge/chrome-%E2%89%A5135-blue) ![Tests](https://img.shields.io/badge/tests-109%20unit%20%2B%20e2e-brightgreen) [![Chrome Web Store](https://img.shields.io/badge/web%20store-published-blue)](https://chromewebstore.google.com/detail/chrome-bridge-for-claude/bioknpaeahidbelaljjohjofiloeodmb)
3
+ ![License: MIT](https://img.shields.io/badge/license-MIT-green) ![Node 18+](https://img.shields.io/badge/node-%E2%89%A518-brightgreen) ![Chrome 135+](https://img.shields.io/badge/chrome-%E2%89%A5135-blue) ![Tests](https://img.shields.io/badge/tests-131%20unit%20%2B%20e2e-brightgreen) [![Chrome Web Store](https://img.shields.io/badge/web%20store-published-blue)](https://chromewebstore.google.com/detail/chrome-bridge-for-claude/bioknpaeahidbelaljjohjofiloeodmb)
4
4
 
5
5
  **Chrome Bridge is an MCP server that connects Claude Code to your real, logged-in Chrome browser — 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.**
6
6
 
7
- By using a local WebSocket bridge and a specialized Chrome extension, Chrome Bridge provides 59 web-development tools (navigation, DOM inspection, visual regression, audits, network mocking) and a dedicated headless instance for CI. It is self-hosted, local-only, and requires no paid plan.
7
+ By using a local WebSocket bridge and a specialized Chrome extension, Chrome Bridge provides 60 web-development tools (navigation, DOM inspection, visual regression, audits, network mocking) and a dedicated headless instance for CI. It is self-hosted, local-only, and requires no paid plan.
8
8
 
9
9
  ## Why Chrome Bridge?
10
10
 
@@ -31,12 +31,12 @@ unfavourable ones) and the harness limits are in
31
31
  - **Honest caveat:** per *turn*, Chrome Bridge costs slightly more than Claude in Chrome (41,985 vs 36,613 cache-read tokens; $0.0373 vs $0.0337). The win is in the number of turns.
32
32
 
33
33
  ### 2. Feature Comparison
34
- Chrome Bridge ships 59 specialized tools (30 core by default) compared to ~20 in Claude in Chrome. It is also the only automation that drives the real host Chrome on ChromeOS/Crostini.
34
+ Chrome Bridge ships 60 specialized tools (31 core by default) compared to ~20 in Claude in Chrome. It is also the only automation that drives the real host Chrome on ChromeOS/Crostini.
35
35
 
36
36
  | | Chrome Bridge | Claude in Chrome | Chrome DevTools MCP | Playwright MCP |
37
37
  |---|---|---|---|---|
38
38
  | **ChromeOS / Crostini** | **Yes** (real host) | No | Container only | Container only |
39
- | **Tools** | **59** (30 core) | ~20 | ~50 | 23 core (71 total) |
39
+ | **Tools** | **60** (31 core) | ~20 | ~50 | 23 core (71 total) |
40
40
  | **Requires Paid Plan** | **No** | Yes (Pro+) | No | No |
41
41
  | **Network Mocking** | **Yes** (stub/headers) | No | No | Yes |
42
42
  | **Visual Regression** | **Yes** (`screenshot_diff`) | No | No | No |
@@ -63,7 +63,7 @@ Claude Code calls `navigate` (returning clickable element refs), `accessibility_
63
63
 
64
64
  ## Token-Efficient Design
65
65
 
66
- - **Compact Schemas**: 30 core tools use ≈3.9k tokens of schemas (vs Playwright MCP's ~4.6k). Specialized groups (audits, visual, network, storage, dom, files) are opt-in via `--caps`.
66
+ - **Opt-in Surface**: 31 core tools cost ≈7.4k tokens of `tools/list`, all 60 cost ≈14.1k; specialized groups (audits, visual, network, storage, dom, files) are opt-in via `--caps`. That core figure was ≈3.8k in 1.8.0 and roughly doubled in 1.10.0: MCP annotations on every tool, rewritten descriptions, and a documented `.describe()` on all 254 parameters (coverage went from 35% to 100%). The trade is deliberate and it is not free — on the form benchmark it adds ≈8% to the cache-read tokens per turn — but the measured advantage was never prefix size, it was round trips: 2.75× fewer turns. Playwright MCP's 23 core tools measured ≈4.6k in July 2026, so on prefix size alone it is the leaner one. Measure ours with `npm run measure`.
67
67
  - **Act-from-Result**: Tools attach a capped preview of interactive elements with short refs. Actions only report a `page_changed` delta when the URL or title actually changes.
68
68
  - **Optimized Media**: Screenshots are downscaled to ≤1568px. Full-page captures are sliced into readable segments.
69
69
  - **Zero-Token Escape Hatches**: The [CLI](#cli) skips MCP schemas entirely, and recorded flows [replay](#launch-mode-headless--ci) without any model in the loop.
@@ -84,7 +84,7 @@ Claude Code <--stdio--> MCP Server <--WebSocket :8765--> Chrome Extension
84
84
 
85
85
  The **MCP Server** (Node.js) handles the protocol and tool logic. The **Chrome Extension** (MV3) executes commands via Chrome APIs. User scripts (`execute_js`) run via `chrome.userScripts.execute()`, requiring the "Allow user scripts" toggle in extension settings.
86
86
 
87
- ## Tools (59 total)
87
+ ## Tools (60 total)
88
88
 
89
89
  ### Core & Navigation (7)
90
90
  `get_status`, `get_tabs`, `create_tab`, `navigate`, `tab_action`, `get_frames`, `screenshot`.
@@ -95,8 +95,8 @@ The **MCP Server** (Node.js) handles the protocol and tool logic. The **Chrome E
95
95
  ### DOM & Inspection (11)
96
96
  `read_page`, `extract`, `get_page_info`, `query_dom`, `modify_dom`, `find_text`, `get_interactives`, `inject_css`, `highlight_elements`, `watch_dom`, `measure_spacing`.
97
97
 
98
- ### Debugging & Network (8)
99
- `execute_js`, `read_console`, `monitor_network`, `monitor_websocket`, `network_rules` (block/redirect/stub/headers), `get_performance`, `web_vitals`, `list_event_listeners`.
98
+ ### Debugging & Network (9)
99
+ `execute_js`, `read_console`, `monitor_network`, `monitor_websocket`, `network_rules` (block/redirect/stub/headers), `http_request` (sent with the user's session cookies), `get_performance`, `web_vitals`, `list_event_listeners`.
100
100
 
101
101
  ### Visual & Responsive (7)
102
102
  `element_screenshot`, `full_page_screenshot`, `screenshot_diff`, `viewport_resize`, `set_zoom`, `emulate_media`, `set_geolocation`.
@@ -132,7 +132,7 @@ Configure the server via environment variables (or the matching CLI flags):
132
132
  - `CHROME_BRIDGE_PORT`: Default `8765`.
133
133
  - `CHROME_BRIDGE_HOST` / `--host`: Bind address, default `127.0.0.1`. Set to `0.0.0.0` **only** where the browser lives outside the container (ChromeOS/Crostini port-forward) — and pair it with a token.
134
134
  - `CHROME_BRIDGE_TOKEN`: Shared secret required on both `ext_init` and `relay_init`. Strongly recommended whenever the bind is not loopback.
135
- - `CHROME_BRIDGE_CAPS` / `--caps`: Tool groups to load (`core`, `audits`, `visual`, `network`, `storage`, `dom`, `files`, or `all`). `install.sh` registers with `all`; the bare server defaults to `core` (30 tools). `get_status` reports `caps_active` and `caps_available`.
135
+ - `CHROME_BRIDGE_CAPS` / `--caps`: Tool groups to load (`core`, `audits`, `visual`, `network`, `storage`, `dom`, `files`, or `all`). `install.sh` registers with `all`; the bare server defaults to `core` (31 tools). `get_status` reports `caps_active` and `caps_available`.
136
136
 
137
137
  Run `node tools/measure-schema.mjs` to see the exact schema cost of the active set.
138
138
 
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "manifest_version": 3,
3
3
  "name": "Chrome Bridge for Claude Code",
4
- "version": "1.8.0",
5
- "description": "Bridge your browser to Claude Code: 59 token-efficient web-dev automation tools over a local WebSocket. ChromeOS included.",
4
+ "version": "1.10.0",
5
+ "description": "Bridge your browser to Claude Code: 60 token-efficient web-dev automation tools over a local WebSocket. ChromeOS included.",
6
6
  "homepage_url": "https://github.com/frsorrentino/chrome-bridge",
7
7
  "minimum_chrome_version": "135",
8
8
  "permissions": [
@@ -423,6 +423,8 @@ async function executeCommand(msg) {
423
423
  return await cmdManageDownloads(params);
424
424
  case 'save_page':
425
425
  return await cmdSavePage(params);
426
+ case 'http_request':
427
+ return await cmdHttpRequest(params);
426
428
  case 'set_zoom':
427
429
  return await cmdSetZoom(params);
428
430
  case 'http_auth':
@@ -2177,12 +2179,12 @@ async function cmdWatchDom({ selector = 'body', attributes = true, childList = t
2177
2179
 
2178
2180
  // --- emulate_media ---
2179
2181
 
2180
- async function cmdEmulateMedia({ colorScheme, reducedMotion, printMode = false, reset = false, tab_id }) {
2182
+ async function cmdEmulateMedia({ colorScheme, reducedMotion, printMode = false, user_agent = null, reset = false, tab_id }) {
2181
2183
  const tabId = await resolveTabId(tab_id);
2182
2184
 
2183
2185
  const results = await chrome.scripting.executeScript({
2184
2186
  target: { tabId },
2185
- func: (cs, rm, print, doReset) => {
2187
+ func: (cs, rm, print, ua, doReset) => {
2186
2188
  const STYLE_ID = '__chromeBridge_media';
2187
2189
  const META_ID = '__chromeBridge_colorScheme';
2188
2190
 
@@ -2196,6 +2198,15 @@ async function cmdEmulateMedia({ colorScheme, reducedMotion, printMode = false,
2196
2198
  window.matchMedia = window.__chromeBridge_origMatchMedia;
2197
2199
  delete window.__chromeBridge_origMatchMedia;
2198
2200
  }
2201
+ // navigator.userAgent è stato ridefinito con un getter: per tornare
2202
+ // all'originale va rimossa la proprietà own, così riemerge quella del
2203
+ // prototipo.
2204
+ if (window.__chromeBridge_uaPatched) {
2205
+ for (const prop of ['userAgent', 'appVersion', 'platform']) {
2206
+ try { delete navigator[prop]; } catch { /* proprietà non configurabile: si prosegue */ }
2207
+ }
2208
+ delete window.__chromeBridge_uaPatched;
2209
+ }
2199
2210
  return { reset: true };
2200
2211
  }
2201
2212
 
@@ -2261,9 +2272,26 @@ async function cmdEmulateMedia({ colorScheme, reducedMotion, printMode = false,
2261
2272
  meta.remove();
2262
2273
  }
2263
2274
 
2275
+ // navigator.userAgent non è scrivibile: va ridefinita come getter sull'
2276
+ // istanza, che precede quella del prototipo. Vale per il JS della pagina,
2277
+ // non per l'header della richiesta (quello è network_rules).
2278
+ if (ua) {
2279
+ for (const [prop, value] of [
2280
+ ['userAgent', ua],
2281
+ ['appVersion', ua.replace(/^Mozilla\//, '')],
2282
+ ['platform', /iPhone|iPad|Android/i.test(ua) ? 'iPhone' : navigator.platform],
2283
+ ]) {
2284
+ try {
2285
+ Object.defineProperty(navigator, prop, { get: () => value, configurable: true });
2286
+ } catch { /* alcune pagine congelano navigator: si prosegue */ }
2287
+ }
2288
+ window.__chromeBridge_uaPatched = true;
2289
+ overrides.userAgent = ua;
2290
+ }
2291
+
2264
2292
  return { emulated: overrides };
2265
2293
  },
2266
- args: [colorScheme || null, reducedMotion || null, printMode, reset],
2294
+ args: [colorScheme || null, reducedMotion || null, printMode, user_agent, reset],
2267
2295
  world: 'MAIN',
2268
2296
  });
2269
2297
  return results?.[0]?.result ?? {};
@@ -3440,6 +3468,46 @@ async function cmdManageDownloads({ action, timeout = 30000, limit = 10 }) {
3440
3468
 
3441
3469
  // --- save_page (MHTML via pageCapture) ---
3442
3470
 
3471
+ // --- http_request ---
3472
+
3473
+ /**
3474
+ * La fetch parte dal service worker, non dal server Node: con host permission
3475
+ * `<all_urls>` e `credentials: 'include'` Chrome allega i cookie di sessione
3476
+ * dell'utente. È l'unica differenza che conta — la stessa URL richiesta da Node
3477
+ * restituisce la pagina di login.
3478
+ */
3479
+ async function cmdHttpRequest({ url, method = 'GET', headers, body, binary = false }) {
3480
+ if (!/^https?:\/\//i.test(String(url || ''))) {
3481
+ throw new Error(`http_request needs an absolute http(s) URL, got "${url}"`);
3482
+ }
3483
+ const res = await fetch(url, {
3484
+ method,
3485
+ headers: headers && Object.keys(headers).length ? headers : undefined,
3486
+ body: body ?? undefined,
3487
+ credentials: 'include',
3488
+ redirect: 'follow',
3489
+ });
3490
+
3491
+ const outHeaders = {};
3492
+ for (const [k, v] of res.headers.entries()) outHeaders[k] = v;
3493
+ const common = {
3494
+ status: res.status,
3495
+ ok: res.ok,
3496
+ // res.url riflette i redirect seguiti: senza, un 302 verso il login
3497
+ // sembrerebbe una risposta della URL chiesta.
3498
+ url: res.url,
3499
+ content_type: res.headers.get('content-type') || '',
3500
+ headers: outHeaders,
3501
+ };
3502
+
3503
+ if (binary) {
3504
+ const blob = await res.blob();
3505
+ return { ...common, body_b64: await blobToBase64(blob), size: blob.size };
3506
+ }
3507
+ const text = await res.text();
3508
+ return { ...common, body: text, size: text.length };
3509
+ }
3510
+
3443
3511
  async function cmdSavePage({ tab_id }) {
3444
3512
  const tabId = await resolveTabId(tab_id);
3445
3513
  const blob = await new Promise((resolve, reject) => {
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "chrome-bridge-mcp",
3
3
  "mcpName": "io.github.frsorrentino/chrome-bridge",
4
- "version": "1.8.0",
4
+ "version": "1.10.0",
5
5
  "type": "module",
6
- "description": "MCP server with 59 tools for browser automation via Chrome extension. Works on ChromeOS/Crostini.",
6
+ "description": "MCP server with 60 tools for browser automation via Chrome extension. Works on ChromeOS/Crostini.",
7
7
  "main": "server/index.js",
8
8
  "bin": {
9
9
  "chrome-bridge-mcp": "server/index.js",
@@ -58,6 +58,7 @@ export const MessageType = Object.freeze({
58
58
  SET_GEOLOCATION: 'set_geolocation',
59
59
  MANAGE_DOWNLOADS: 'manage_downloads',
60
60
  SAVE_PAGE: 'save_page',
61
+ HTTP_REQUEST: 'http_request',
61
62
  SET_ZOOM: 'set_zoom',
62
63
  HTTP_AUTH: 'http_auth',
63
64
  GET_RESPONSE_HEADERS: 'get_response_headers',
@@ -80,7 +81,7 @@ export const MessageType = Object.freeze({
80
81
  });
81
82
 
82
83
  // Versione
83
- export const VERSION = '1.8.0';
84
+ export const VERSION = '1.10.0';
84
85
 
85
86
  // Configurazione
86
87
  export const DEFAULT_PORT = 8765;
@@ -129,6 +130,7 @@ export function getTimeout(type) {
129
130
  || type === MessageType.UPLOAD_FILE
130
131
  || type === MessageType.MANAGE_DOWNLOADS
131
132
  || type === MessageType.SAVE_PAGE
133
+ || type === MessageType.HTTP_REQUEST
132
134
  || type === MessageType.WAIT_FOR_FUNCTION
133
135
  || type === MessageType.SCROLL_UNTIL) return 60000;
134
136
  return COMMAND_TIMEOUT_MS;