chrome-bridge-mcp 1.31.0 → 1.31.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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "manifest_version": 3,
3
3
  "name": "Chrome Bridge for Claude Code",
4
- "version": "1.31.0",
4
+ "version": "1.31.1",
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",
@@ -283,11 +283,14 @@
283
283
  } else {
284
284
  throw new Error('this WebMCP version has no executeTool and the tool was registered before chrome-bridge could see it');
285
285
  }
286
- const out = await Promise.race([run, timedOut]);
286
+ let out = await Promise.race([run, timedOut]);
287
+ // executeTool di Chrome restituisce il risultato come stringa JSON.
288
+ if (typeof out === 'string') { try { out = JSON.parse(out); } catch {} }
287
289
  settle(entry, t0, out);
288
290
  return { status: 'ok', result: entry.output, ms: entry.ms };
289
291
  } catch (err) {
290
- settle(entry, t0, undefined, err);
292
+ // Chrome rifiuta con l'AbortError del segnale prima del nostro timer.
293
+ settle(entry, t0, undefined, ac.signal.aborted && !err?.timeout ? Object.assign(new Error(`timed out after ${timeout} ms`), { timeout: true }) : err);
291
294
  return { status: entry.status, error: entry.error, ms: entry.ms };
292
295
  } finally {
293
296
  clearTimeout(timer);
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "chrome-bridge-mcp",
3
- "version": "1.31.0",
3
+ "version": "1.31.2",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "chrome-bridge-mcp",
9
- "version": "1.31.0",
9
+ "version": "1.31.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.31.0",
4
+ "version": "1.31.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",
package/server/index.js CHANGED
@@ -44,6 +44,9 @@ function parseSecurity() {
44
44
  };
45
45
  }
46
46
 
47
+ const SHUTDOWN_DEADLINE_MS = 10000;
48
+ const PARENT_CHECK_MS = 1000;
49
+
47
50
  function parseCaps() {
48
51
  const i = process.argv.indexOf('--caps');
49
52
  if (i !== -1 && process.argv[i + 1]) return process.argv[i + 1];
@@ -78,6 +81,9 @@ async function main() {
78
81
  // segnale arrivano insieme.
79
82
  let closing = null;
80
83
  const shutdown = () => (closing ??= (async () => {
84
+ // Uno shutdown bloccato non deve tenere vivo il processo: dopo la
85
+ // scadenza, o al secondo segnale, si esce comunque.
86
+ setTimeout(forceExit, SHUTDOWN_DEADLINE_MS);
81
87
  console.error('[chrome-bridge] Shutting down...');
82
88
  // Il browser di --launch per primo: su Windows Claude Code chiude stdin e
83
89
  // dopo 300 ms uccide l'albero dei processi (2.1.296). Le sue schede
@@ -91,14 +97,38 @@ async function main() {
91
97
  try { await mcpServer.close(); } catch {}
92
98
  process.exit(0);
93
99
  })());
94
- process.on('SIGINT', shutdown);
95
- process.on('SIGTERM', shutdown);
100
+ const forceExit = () => {
101
+ if (browser) { try { process.kill(browser.pid, 'SIGKILL'); } catch {} }
102
+ process.exit(1);
103
+ };
104
+ const onSignal = () => (closing ? forceExit() : shutdown());
105
+ process.on('SIGINT', onSignal);
106
+ process.on('SIGTERM', onSignal);
107
+ // Client sparito: stdout e stderr restano pipe senza lettore e ogni
108
+ // scrittura dà EPIPE. Senza questi ascoltatori l'errore arrivava a
109
+ // uncaughtException, che riscriveva su stderr: un ciclo di errori che non
110
+ // lasciava girare l'event loop, neanche per SIGTERM (28/10/2026: un server
111
+ // --launch orfano da 2 h al 46% di CPU, chiuso solo con SIGKILL).
112
+ process.stdout.on('error', shutdown);
113
+ process.stderr.on('error', shutdown);
96
114
  // Su Windows SIGINT e SIGTERM non arrivano, e un client terminato di colpo
97
115
  // non manda segnali: in entrambi i casi resta solo la fine di stdin. L'SDK
98
116
  // non la ascolta, e il WebSocket teneva vivo il processo.
99
117
  process.stdin.once('end', shutdown);
100
118
  process.stdin.once('close', shutdown);
101
119
  mcpServer.server.onclose = shutdown;
120
+ // Prima di mcpServer.connect() nessuno legge stdin e la sua fine non
121
+ // arriva; un client che muore durante l'avvio a freddo di Chrome lascia un
122
+ // orfano adottato da init (o da systemd, su Crostini). Il cambio di padre
123
+ // lo rivela anche quando stdin resta aperto in un altro processo.
124
+ if (LAUNCH) {
125
+ const parent = process.ppid;
126
+ const parentGone = () => {
127
+ if (process.ppid !== parent) return true;
128
+ try { process.kill(parent, 0); return false; } catch (e) { return e.code !== 'EPERM'; }
129
+ };
130
+ setInterval(() => { if (parentGone()) shutdown(); }, PARENT_CHECK_MS).unref();
131
+ }
102
132
  process.on('uncaughtException', (err) => {
103
133
  console.error('[chrome-bridge] Uncaught exception:', err);
104
134
  shutdown().catch(() => process.exit(1));
@@ -105,7 +105,7 @@ export const MessageType = Object.freeze({
105
105
  });
106
106
 
107
107
  // Versione
108
- export const VERSION = '1.31.0';
108
+ export const VERSION = '1.31.2';
109
109
 
110
110
  // Configurazione
111
111
  export const DEFAULT_PORT = 8765;