chrome-bridge-mcp 1.32.0 → 1.32.2

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
@@ -38,17 +38,19 @@ and find the Sign Up button"*: Claude Code calls `navigate`, `audit` and
38
38
  `find_text`. Because `navigate` already returns element refs,
39
39
  `click(ref="n1")` follows with no discovery turn in between.
40
40
 
41
- For `execute_js`, enable **Allow user scripts** in `chrome://extensions` →
42
- Chrome Bridge → Details.
43
-
44
41
  The plugin registers the MCP server from npm together with the recipes skill.
45
- Requires Node.js 18+ and Chrome 135+.
42
+
43
+ | Requirement | For | Notes |
44
+ |---|---|---|
45
+ | Node.js 18+ | the MCP server | |
46
+ | Chrome 135+ | the extension | `execute_js` needs **Allow user scripts** in `chrome://extensions` → Chrome Bridge → Details; on Chrome 135-137, Developer Mode instead |
47
+ | Claude Code 2.1.275+ in a session, 2.1.292+ from a shell | the one-command install | Older versions: see below |
48
+ | Python 3.8+ (optional) | the plugin's error log hooks | As `python3`, `python` or `py` (the Microsoft Store `python3` alias is skipped). Without it they stay silent and the tools work the same |
46
49
 
47
50
  <details>
48
- <summary>Older Claude Code, from source, Windows, Chrome 135-137, one path only</summary>
51
+ <summary>Older Claude Code, from source, Windows, one path only</summary>
49
52
 
50
- **Older Claude Code.** The one-command install needs Claude Code 2.1.275+ in a
51
- session and 2.1.292+ from a shell. Before that, add the marketplace first:
53
+ **Older Claude Code.** Add the marketplace first:
52
54
  `/plugin marketplace add frsorrentino/chrome-bridge`, then
53
55
  `/plugin install chrome-bridge@chrome-bridge` (from a shell:
54
56
  `claude plugin marketplace add frsorrentino/chrome-bridge`, then
@@ -71,14 +73,9 @@ starts.
71
73
 
72
74
  **Windows.** `install.sh` does not run from PowerShell or cmd: use the plugin
73
75
  with the Web Store extension, or run `install.sh` from Git Bash. The plugin's
74
- error log hooks run in Git Bash, as Claude Code runs every hook, and need
75
- Python 3.8+ as `python3`, `python` or `py` (the Microsoft Store `python3` alias
76
- is skipped); without Python they stay silent and the tools work the same. For
77
- launch mode see `CHROME_BRIDGE_BROWSER` below: Google Chrome cannot load the
78
- extension there, Microsoft Edge can.
79
-
80
- **Chrome 135-137.** `execute_js` needs Developer Mode instead of the **Allow
81
- user scripts** toggle.
76
+ error log hooks run in Git Bash, as Claude Code runs every hook. For launch
77
+ mode see `CHROME_BRIDGE_BROWSER` below: Google Chrome cannot load the extension
78
+ there, Microsoft Edge can.
82
79
 
83
80
  **One path only.** The plugin and `install.sh` would register the same server
84
81
  twice, and so would a plugin added from the [Anthropic
@@ -203,10 +200,8 @@ user-script toggle isn't available.
203
200
  ## Tools
204
201
 
205
202
  69 in total. `core` (46 tools: every tool used in 101 real sessions, plus the
206
- two WebMCP tools) loads by default, 22 of them always in context; the other 23
207
- sit in seven optional caps that the agent switches on mid-session with
208
- `get_status({enable: ["visual"]})`, with no restart (`--caps` still sets them
209
- at startup).
203
+ two WebMCP tools) loads by default; the other 23 sit in seven optional caps,
204
+ switched on with `get_status({enable: ["visual"]})` or at startup with `--caps`.
210
205
 
211
206
  ![69 tools in nine groups, from clicking a button to tracing a page](assets/readme/card4-toolbox.png)
212
207
 
@@ -251,7 +246,7 @@ executes commands through Chrome APIs. User scripts (`execute_js`) run via
251
246
  | `CHROME_BRIDGE_ALWAYS_LOAD` / `--always-load` | 22 most used tools | Tools kept in context when the client loads the server whole; the others carry `_meta['anthropic/alwaysLoad'] = false` and wait behind tool search (Claude Code 2.1.285+). `all` loads every tool; a comma list picks them |
252
247
  | `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` |
253
248
  | `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 |
254
- | `CHROME_BRIDGE_BROWSER` | unset | Launch mode only: the browser binary, e.g. `C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe`. Unset: the first of Chromium, Edge, Brave, Google Chrome found in the standard paths (on Linux Chromium, then Google Chrome). Google Chrome 137+ cannot load the extension |
249
+ | `CHROME_BRIDGE_BROWSER` | unset | Launch mode only: the browser binary, e.g. `C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe`. Unset: the first of Chromium, Edge, Brave, Google Chrome found in the standard paths (on Linux Chromium, then Google Chrome) |
255
250
  | `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 |
256
251
  | `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 |
257
252
 
@@ -279,8 +274,6 @@ private page data (`get_storage`, `get_tabs`). Those are the product, not bugs.
279
274
  What matters is who can reach them and what a hostile page can make the agent
280
275
  do with them.
281
276
 
282
- - **Who can connect:** see above — loopback bind, `chrome-extension://` origin
283
- check, `CHROME_BRIDGE_TOKEN` on both handshakes.
284
277
  - **Launch mode also opens a DevTools port** on 127.0.0.1 (chosen by the
285
278
  system), for `lighthouse` and `heap_snapshot`. CDP has no authentication:
286
279
  any local process can drive that dedicated browser while it runs. Its profile
@@ -293,11 +286,9 @@ do with them.
293
286
  JavaScript, send a request with your cookies, submit a form or upload a file.
294
287
  Treat every page you automate as untrusted input, and review what the agent
295
288
  proposes on sites that hold money or credentials.
296
- - **Reduce the surface:** `--caps core` (the default) exposes the smallest set;
297
- `--no-js` removes arbitrary JavaScript; `--write-root` fences every path the
298
- model writes; `upload_file` refuses keys and credentials, and `--read-root`
299
- fences it to one directory. For sessions that matter, automate in a separate
300
- Chrome profile.
289
+ - **Reduce the surface:** `--caps core` (the default) exposes the smallest set,
290
+ and see [Limits you can set](#your-browser-your-login). For sessions that
291
+ matter, automate in a separate Chrome profile.
301
292
  - **`--no-js` is not a sandbox:** `inject_css` stays, and CSS attribute
302
293
  selectors with `url()` can leak attribute values to a remote host.
303
294
  - **What leaves your machine** is what the browser sends on the agent's behalf.
@@ -309,9 +300,9 @@ do with them.
309
300
  When one of its own tools fails, chrome-bridge adds a line to a **local** file,
310
301
  `${XDG_STATE_HOME:-~/.local/state}/claude-observe/chrome-bridge.jsonl` (mode
311
302
  0600, in a 0700 folder), in the [claude-observe](https://github.com/frsorrentino/claude-observe)
312
- format (the plugin's copy is in `observe/`). With the plugin, a `PostToolUseFailure` hook writes it; with the npm
313
- server alone, the server does. The same error seen again is one line with a
314
- count.
303
+ format (the plugin's copy is in `observe/`). A `PostToolUseFailure` hook writes
304
+ it, or the server when used without the plugin. A repeated error is one line
305
+ with a count.
315
306
 
316
307
  - **Kept:** the tool name, the error text after scrubbing (your home folder
317
308
  becomes `~`; emails, URL queries, secrets and typed values are removed), the
@@ -323,12 +314,11 @@ count.
323
314
  content.
324
315
  - **Nothing leaves your computer** unless you say yes. With the plugin, once
325
316
  a few errors have piled up (or one has waited a few days, or one is marked
326
- as a defect), a line at the end of a turn tells you so, and Claude may offer,
327
- at a natural moment, to send them as one GitHub issue: it shows you the
328
- anonymized text first, then asks with buttons (from your GitHub, or not now)
329
- and sends only what you chose. `/chrome-bridge:observe send` does the same
330
- on demand; `list`, `mark` and `add` read and triage the log. The server
331
- alone never sends or offers anything.
317
+ as a defect), a line at the end of a turn tells you so, and Claude may offer
318
+ to send them as one GitHub issue: it shows the anonymized text first and
319
+ sends only what you choose. `/chrome-bridge:observe send` does the same on
320
+ demand; `list`, `mark` and `add` read and triage the log. The server alone
321
+ never sends or offers anything.
332
322
  - **Security observations** (a read or write outside the perimeter, a secret
333
323
  exposed, unwanted code execution, data leaving the computer) never enter a
334
324
  public issue: `/chrome-bridge:observe send --security` prepares a private
@@ -23,6 +23,10 @@ export function buildMarkdown(doc) {
23
23
 
24
24
  const attr = (el, name) => (el.getAttribute ? el.getAttribute(name) : null);
25
25
  const clean = (s) => String(s || '').replace(/\s+/g, ' ').trim();
26
+ // childNodes e querySelectorAll sono NodeList nel browser: niente map,
27
+ // filter né some. Senza la copia in array il markdown tornava vuoto su ogni
28
+ // pagina dalla 1.11.0 (test nodelist_markdown).
29
+ const list = (l) => Array.from(l || []);
26
30
 
27
31
  // Dimensioni: il rect è la verità quando c'è un layout, ma su un'immagine non
28
32
  // ancora disposta (o in test) restano solo gli attributi.
@@ -54,7 +58,7 @@ export function buildMarkdown(doc) {
54
58
  }
55
59
  return '';
56
60
  }
57
- const kids = (node.childNodes || []).map(inline).join('');
61
+ const kids = list(node.childNodes).map(inline).join('');
58
62
  if (tag === 'A') {
59
63
  const href = attr(node, 'href');
60
64
  const label = clean(kids);
@@ -70,19 +74,19 @@ export function buildMarkdown(doc) {
70
74
  return kids;
71
75
  };
72
76
 
73
- const cellsOf = (row) => (row.childNodes || [])
77
+ const cellsOf = (row) => list(row.childNodes)
74
78
  .filter((n) => n.nodeType === 1 && (n.tagName === 'TD' || n.tagName === 'TH'))
75
79
  .map((c) => clean(c.textContent).replace(/\|/g, '\\|'));
76
80
 
77
81
  const renderTable = (table) => {
78
- const rows = (table.querySelectorAll ? table.querySelectorAll('tr') : []) || [];
82
+ const rows = list(table.querySelectorAll ? table.querySelectorAll('tr') : []);
79
83
  if (!rows.length) return;
80
84
  const all = rows.map(cellsOf).filter((r) => r.length);
81
85
  if (!all.length) return;
82
86
 
83
87
  // Gli header veri, non un segnaposto: la prima riga se contiene th,
84
88
  // altrimenti colonne numerate.
85
- const firstHasTh = (rows[0].childNodes || []).some((n) => n.nodeType === 1 && n.tagName === 'TH');
89
+ const firstHasTh = list(rows[0].childNodes).some((n) => n.nodeType === 1 && n.tagName === 'TH');
86
90
  const header = firstHasTh ? all[0] : all[0].map((_, i) => `col${i + 1}`);
87
91
  const body = firstHasTh ? all.slice(1) : all;
88
92
  const width = header.length;
@@ -139,7 +143,7 @@ export function buildMarkdown(doc) {
139
143
 
140
144
  // Sommario delle immagini grandi: dice al modello se vale la pena chiedere
141
145
  // uno screenshot, al costo di una riga.
142
- const imgs = (d.body?.querySelectorAll ? d.body.querySelectorAll('img') : []) || [];
146
+ const imgs = list(d.body?.querySelectorAll ? d.body.querySelectorAll('img') : []);
143
147
  const significant = imgs.filter((img) => { const { w, h } = sizeOf(img); return w >= 100 && h >= 100; });
144
148
  if (significant.length) {
145
149
  out.push(`_${significant.length} image(s) at least 100x100 on this page — take a screenshot if the visual matters._`, '');
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "manifest_version": 3,
3
3
  "name": "Chrome Bridge for Claude Code",
4
- "version": "1.31.1",
4
+ "version": "1.32.2",
5
5
  "description": "Bridge your browser to Claude Code: 69 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",
@@ -3,6 +3,7 @@
3
3
  [hidden] { display: none !important; }
4
4
 
5
5
  :root {
6
+ color-scheme: light dark;
6
7
  --bg: #ffffff;
7
8
  --fg: #1f1f1f;
8
9
  --muted: #5f6368;
@@ -2556,7 +2556,7 @@ function observeContentScript(recordValues) {
2556
2556
  send({ command: 'type_text', params: { selector: selectorOf(el), text: value }, human: { label: labelOf(el), sensitive, placeholder: sensitive || !recordValues } });
2557
2557
  }, true);
2558
2558
  document.addEventListener('keydown', (e) => {
2559
- if (e.key === 'Enter' && e.target && /^(INPUT)$/.test(e.target.tagName)) send({ command: 'press_key', params: { key: 'Enter', selector: selectorOf(e.target) }, human: { label: labelOf(e.target) } });
2559
+ if (e.key === 'Enter' && !e.isComposing && e.keyCode !== 229 && e.target && /^(INPUT)$/.test(e.target.tagName)) send({ command: 'press_key', params: { key: 'Enter', selector: selectorOf(e.target) }, human: { label: labelOf(e.target) } });
2560
2560
  }, true);
2561
2561
  return true;
2562
2562
  }
@@ -2821,7 +2821,7 @@ function handoffBanner(id, message, pick, ask, pickMax) {
2821
2821
  root.querySelector('span').textContent = `${message} — ${picks.length}/${pickMax} picked, click more or Done`;
2822
2822
  };
2823
2823
  root.querySelector('.done').onclick = done;
2824
- if (askInput) { askInput.onkeydown = (e) => { if (e.key === 'Enter') done(); }; setTimeout(() => askInput.focus(), 0); }
2824
+ if (askInput) { askInput.onkeydown = (e) => { if (e.key === 'Enter' && !e.isComposing && e.keyCode !== 229) done(); }; setTimeout(() => askInput.focus(), 0); }
2825
2825
  root.querySelector('.cancel').onclick = () => finish({ done: false, action: 'cancel' });
2826
2826
  let box = null; let onMove = null; let onClick = null;
2827
2827
  function cleanupPick() {
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "chrome-bridge-mcp",
3
- "version": "1.32.0",
3
+ "version": "1.32.2",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "chrome-bridge-mcp",
9
- "version": "1.32.0",
9
+ "version": "1.32.2",
10
10
  "license": "MIT",
11
11
  "dependencies": {
12
12
  "@jridgewell/trace-mapping": "^0.3.31",
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "chrome-bridge-mcp",
3
3
  "mcpName": "io.github.frsorrentino/chrome-bridge",
4
- "version": "1.32.0",
4
+ "version": "1.32.2",
5
5
  "type": "module",
6
6
  "description": "Chrome Bridge by frsorrentino: MCP server that lets Claude Code borrow a tab from your own logged-in Chrome and hand it back — logins, 2FA and CAPTCHAs stay with you (handoff). 69 tools; 2.8-4.3× cheaper and up to 9.3× fewer turns than Claude in Chrome, measured; ChromeOS included.",
7
7
  "main": "server/index.js",
@@ -105,7 +105,7 @@ export const MessageType = Object.freeze({
105
105
  });
106
106
 
107
107
  // Versione
108
- export const VERSION = '1.32.0';
108
+ export const VERSION = '1.32.2';
109
109
 
110
110
  // Configurazione
111
111
  export const DEFAULT_PORT = 8765;