@mcp-b/chrome-devtools-mcp 1.3.1 → 1.5.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.
@@ -13,7 +13,7 @@
13
13
  * Communication flow:
14
14
  *
15
15
  * CDP → Page (requests):
16
- * 1. CDP calls Runtime.evaluate with __mcpBridge.toServer(messageJson)
16
+ * 1. CDP calls Runtime.evaluate with __mcpCdpBridge.toServer(messageJson)
17
17
  * 2. Bridge parses the message and posts it via window.postMessage
18
18
  * 3. TabServerTransport receives it and processes the MCP request
19
19
  *
@@ -38,8 +38,8 @@ export const WEB_MCP_BRIDGE_SCRIPT = `
38
38
  var CHANNEL_ID = 'mcp-default';
39
39
  var BRIDGE_VERSION = '1.1.0';
40
40
 
41
- // Prevent double injection
42
- if (window.__mcpBridge && window.__mcpBridge.version === BRIDGE_VERSION) {
41
+ // Prevent double injection - use __mcpCdpBridge to avoid collision with polyfill's __mcpBridge
42
+ if (window.__mcpCdpBridge && window.__mcpCdpBridge.version === BRIDGE_VERSION) {
43
43
  return { alreadyInjected: true };
44
44
  }
45
45
 
@@ -68,8 +68,13 @@ export const WEB_MCP_BRIDGE_SCRIPT = `
68
68
  serverReady = false;
69
69
  }
70
70
 
71
- // Forward to CDP client via the binding
72
- // The binding '__mcpBridgeToClient' is set up by CDPClientTransport before injection
71
+ // Forward to CDP client via the binding.
72
+ // NOTE: We use '__mcpBridgeToClient' (not '__mcpCdpBridgeToClient') for the CDP binding
73
+ // because this name is set up by WebMCPClientTransport.connect() via Runtime.addBinding,
74
+ // which is separate from the window property '__mcpCdpBridge'. The window property was
75
+ // renamed to avoid collision with the polyfill's internal '__mcpBridge', but the CDP
76
+ // binding name doesn't conflict with anything and changing it would require coordinated
77
+ // updates to both the bridge script and WebMCPClientTransport.
73
78
  if (typeof window.__mcpBridgeToClient === 'function') {
74
79
  try {
75
80
  var messageJson = typeof payload === 'string' ? JSON.stringify(payload) : JSON.stringify(payload);
@@ -99,7 +104,8 @@ export const WEB_MCP_BRIDGE_SCRIPT = `
99
104
  checkWebMCPAvailable();
100
105
 
101
106
  // Expose the bridge API for CDP to call
102
- Object.defineProperty(window, '__mcpBridge', {
107
+ // Use __mcpCdpBridge to avoid collision with polyfill's __mcpBridge
108
+ Object.defineProperty(window, '__mcpCdpBridge', {
103
109
  value: {
104
110
  version: BRIDGE_VERSION,
105
111
 
@@ -179,7 +185,7 @@ export const WEB_MCP_BRIDGE_SCRIPT = `
179
185
  */
180
186
  dispose: function() {
181
187
  window.removeEventListener('message', handleServerMessage);
182
- delete window.__mcpBridge;
188
+ delete window.__mcpCdpBridge;
183
189
  }
184
190
  },
185
191
  writable: true,
@@ -4,6 +4,7 @@
4
4
  * SPDX-License-Identifier: Apache-2.0
5
5
  */
6
6
  import { WEB_MCP_BRIDGE_SCRIPT, CHECK_WEBMCP_AVAILABLE_SCRIPT } from './WebMCPBridgeScript.js';
7
+ import { CDP_BRIDGE_WINDOW_PROPERTY, CDP_BRIDGE_BINDING } from './bridgeConstants.js';
7
8
  /**
8
9
  * MCP Client Transport that connects to a WebMCP server running in a browser tab.
9
10
  *
@@ -162,21 +163,29 @@ export class WebMCPClientTransport {
162
163
  // Set up binding for receiving messages from the bridge
163
164
  // When the bridge calls window.__mcpBridgeToClient(msg), we receive it here
164
165
  await this._cdpSession.send('Runtime.addBinding', {
165
- name: '__mcpBridgeToClient',
166
+ name: CDP_BRIDGE_BINDING,
166
167
  });
167
168
  // Create bound handlers so we can remove them later
168
169
  this._bindingCalledHandler = (event) => {
169
- if (event.name !== '__mcpBridgeToClient')
170
+ if (event.name !== CDP_BRIDGE_BINDING)
170
171
  return;
171
172
  // Guard against processing messages after close
172
173
  if (this._closed)
173
174
  return;
175
+ let payload;
176
+ try {
177
+ payload = JSON.parse(event.payload);
178
+ }
179
+ catch (err) {
180
+ this.onerror?.(new Error(`Failed to parse JSON message from bridge: ${err instanceof Error ? err.message : String(err)}. ` +
181
+ `Raw payload: ${event.payload.substring(0, 200)}`));
182
+ return;
183
+ }
174
184
  try {
175
- const payload = JSON.parse(event.payload);
176
185
  this._handlePayload(payload);
177
186
  }
178
187
  catch (err) {
179
- this.onerror?.(new Error(`Failed to parse message from bridge: ${err}`));
188
+ this.onerror?.(new Error(`Failed to handle message from bridge: ${err instanceof Error ? err.message : String(err)}`));
180
189
  }
181
190
  };
182
191
  this._frameNavigatedHandler = (frame) => {
@@ -196,9 +205,9 @@ export class WebMCPClientTransport {
196
205
  }
197
206
  this._started = true;
198
207
  // Initiate server-ready handshake
199
- await this._page.evaluate(() => {
200
- window.__mcpBridge?.checkReady();
201
- });
208
+ await this._page.evaluate((bridgeProp) => {
209
+ window[bridgeProp]?.checkReady();
210
+ }, CDP_BRIDGE_WINDOW_PROPERTY);
202
211
  // Wait for server ready with timeout
203
212
  const timeoutPromise = new Promise((_, reject) => {
204
213
  const timer = setTimeout(() => {
@@ -280,8 +289,11 @@ export class WebMCPClientTransport {
280
289
  this._serverReadyReject(new Error('Page navigated, connection lost'));
281
290
  }
282
291
  // Full teardown - detach CDP session and remove listeners
283
- this._cleanup().catch(() => {
284
- // Ignore cleanup errors during navigation
292
+ this._cleanup().catch((err) => {
293
+ // Cleanup errors during navigation are expected and non-critical
294
+ const message = err instanceof Error ? err.message : String(err);
295
+ const logFn = console.debug || console.log;
296
+ logFn('[WebMCPClientTransport] Cleanup error during navigation (non-critical):', message);
285
297
  });
286
298
  // Signal clean disconnection (not an error)
287
299
  this.onclose?.();
@@ -311,8 +323,8 @@ export class WebMCPClientTransport {
311
323
  try {
312
324
  // JSON.stringify inside try block to catch non-serializable messages
313
325
  const messageJson = JSON.stringify(message);
314
- await this._page.evaluate((msg) => {
315
- const bridge = window.__mcpBridge;
326
+ await this._page.evaluate((msg, bridgeProp) => {
327
+ const bridge = window[bridgeProp];
316
328
  if (!bridge) {
317
329
  throw new Error('WebMCP bridge not found');
318
330
  }
@@ -320,7 +332,7 @@ export class WebMCPClientTransport {
320
332
  if (!sent) {
321
333
  throw new Error('Bridge failed to send message');
322
334
  }
323
- }, messageJson);
335
+ }, messageJson, CDP_BRIDGE_WINDOW_PROPERTY);
324
336
  }
325
337
  catch (err) {
326
338
  const error = new Error(`Failed to send message: ${err}`);
@@ -345,20 +357,26 @@ export class WebMCPClientTransport {
345
357
  }
346
358
  // Dispose the bridge script if possible
347
359
  try {
348
- await this._page.evaluate(() => {
349
- window.__mcpBridge?.dispose();
350
- });
360
+ await this._page.evaluate((bridgeProp) => {
361
+ window[bridgeProp]?.dispose();
362
+ }, CDP_BRIDGE_WINDOW_PROPERTY);
351
363
  }
352
- catch {
353
- // Ignore errors during cleanup (page might be closed/navigated)
364
+ catch (err) {
365
+ // Cleanup errors during navigation are expected and non-critical
366
+ const message = err instanceof Error ? err.message : String(err);
367
+ const logFn = console.debug || console.log;
368
+ logFn('[WebMCPClientTransport] Bridge dispose skipped:', message);
354
369
  }
355
370
  // Detach CDP session
356
371
  if (this._cdpSession) {
357
372
  try {
358
373
  await this._cdpSession.detach();
359
374
  }
360
- catch {
361
- // Ignore detach errors
375
+ catch (err) {
376
+ // Expected when session already detached - log for debugging only
377
+ const message = err instanceof Error ? err.message : String(err);
378
+ const logFn = console.debug || console.log;
379
+ logFn('[WebMCPClientTransport] CDP detach skipped:', message);
362
380
  }
363
381
  this._cdpSession = null;
364
382
  }
@@ -0,0 +1,22 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ /**
7
+ * Constants for WebMCP CDP bridge communication.
8
+ * These names are used to coordinate between the bridge script injected into the page
9
+ * and the WebMCPClientTransport running in Node.js via CDP.
10
+ */
11
+ /**
12
+ * Window property name for the CDP bridge object.
13
+ * Renamed from `__mcpBridge` to `__mcpCdpBridge` to avoid collision with
14
+ * the polyfill's internal `__mcpBridge` property.
15
+ */
16
+ export const CDP_BRIDGE_WINDOW_PROPERTY = '__mcpCdpBridge';
17
+ /**
18
+ * CDP binding name for receiving messages from the bridge.
19
+ * This is the function name registered via Runtime.addBinding that the
20
+ * bridge calls to send messages back to the CDP transport.
21
+ */
22
+ export const CDP_BRIDGE_BINDING = '__mcpBridgeToClient';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mcp-b/chrome-devtools-mcp",
3
- "version": "1.3.1",
3
+ "version": "1.5.0",
4
4
  "description": "MCP server for Chrome DevTools with WebMCP integration for connecting to website MCP tools",
5
5
  "keywords": [
6
6
  "mcp",
@@ -47,19 +47,40 @@
47
47
  "LICENSE",
48
48
  "!*.tsbuildinfo"
49
49
  ],
50
+ "scripts": {
51
+ "build": "tsc && node --experimental-strip-types --no-warnings=ExperimentalWarning scripts/post-build.ts",
52
+ "bundle": "npm run clean && npm run build && rollup -c rollup.config.mjs",
53
+ "check-format": "eslint --cache . && prettier --check --cache .;",
54
+ "clean": "node -e \"require('fs').rmSync('build', {recursive: true, force: true})\"",
55
+ "docs": "npm run build && npm run docs:generate && npm run format",
56
+ "docs:generate": "node --experimental-strip-types scripts/generate-docs.ts",
57
+ "format": "eslint --cache --fix . && prettier --write --cache .",
58
+ "prepare": "node --experimental-strip-types scripts/prepare.ts",
59
+ "start": "npm run build && node build/src/index.js",
60
+ "start-debug": "DEBUG=mcp:* DEBUG_COLORS=false npm run build && node build/src/index.js",
61
+ "test": "npm run build && node --require ./build/tests/setup.js --no-warnings=ExperimentalWarning --test-reporter spec --test-force-exit --test \"build/tests/**/*.test.js\"",
62
+ "test:node20": "node --require ./build/tests/setup.js --test-reporter spec --test-force-exit --test build/tests",
63
+ "test:only": "npm run build && node --require ./build/tests/setup.js --no-warnings=ExperimentalWarning --test-reporter spec --test-force-exit --test --test-only \"build/tests/**/*.test.js\"",
64
+ "test:only:no-build": "node --require ./build/tests/setup.js --no-warnings=ExperimentalWarning --test-reporter spec --test-force-exit --test --test-only \"build/tests/**/*.test.js\"",
65
+ "test:update-snapshots": "npm run build && node --require ./build/tests/setup.js --no-warnings=ExperimentalWarning --test-force-exit --test --test-update-snapshots \"build/tests/**/*.test.js\"",
66
+ "typecheck": "tsc --noEmit",
67
+ "verify-server-json-version": "node --experimental-strip-types scripts/verify-server-json-version.ts"
68
+ },
50
69
  "dependencies": {
51
- "@composio/json-schema-to-zod": "^0.1.17",
52
- "@modelcontextprotocol/sdk": "1.24.1",
70
+ "@composio/json-schema-to-zod": "catalog:",
71
+ "@mcp-b/global": "workspace:*",
72
+ "@modelcontextprotocol/sdk": "catalog:",
53
73
  "core-js": "3.47.0",
54
74
  "debug": "4.4.3",
75
+ "esbuild": "^0.24.2",
55
76
  "puppeteer": "24.32.0",
56
77
  "puppeteer-core": "24.32.0",
57
78
  "yargs": "18.0.0",
58
- "zod": "3.25.76"
79
+ "zod": "catalog:"
59
80
  },
60
81
  "devDependencies": {
61
82
  "@eslint/js": "^9.35.0",
62
- "@modelcontextprotocol/sdk": "1.24.1",
83
+ "@modelcontextprotocol/sdk": "catalog:",
63
84
  "@rollup/plugin-commonjs": "^29.0.0",
64
85
  "@rollup/plugin-json": "^6.1.0",
65
86
  "@rollup/plugin-node-resolve": "^16.0.3",
@@ -88,7 +109,7 @@
88
109
  "typescript": "^5.9.2",
89
110
  "typescript-eslint": "^8.43.0",
90
111
  "yargs": "18.0.0",
91
- "zod": "3.25.76"
112
+ "zod": "catalog:"
92
113
  },
93
114
  "engines": {
94
115
  "node": "^20.19.0 || ^22.12.0 || >=23"
@@ -100,23 +121,5 @@
100
121
  "upstream": {
101
122
  "repository": "https://github.com/ChromeDevTools/chrome-devtools-mcp",
102
123
  "mcpName": "io.github.ChromeDevTools/chrome-devtools-mcp"
103
- },
104
- "scripts": {
105
- "build": "tsc && node --experimental-strip-types --no-warnings=ExperimentalWarning scripts/post-build.ts",
106
- "bundle": "npm run clean && npm run build && rollup -c rollup.config.mjs",
107
- "check-format": "eslint --cache . && prettier --check --cache .;",
108
- "clean": "node -e \"require('fs').rmSync('build', {recursive: true, force: true})\"",
109
- "docs": "npm run build && npm run docs:generate && npm run format",
110
- "docs:generate": "node --experimental-strip-types scripts/generate-docs.ts",
111
- "format": "eslint --cache --fix . && prettier --write --cache .",
112
- "start": "npm run build && node build/src/index.js",
113
- "start-debug": "DEBUG=mcp:* DEBUG_COLORS=false npm run build && node build/src/index.js",
114
- "test": "npm run build && node --require ./build/tests/setup.js --no-warnings=ExperimentalWarning --test-reporter spec --test-force-exit --test \"build/tests/**/*.test.js\"",
115
- "test:node20": "node --require ./build/tests/setup.js --test-reporter spec --test-force-exit --test build/tests",
116
- "test:only": "npm run build && node --require ./build/tests/setup.js --no-warnings=ExperimentalWarning --test-reporter spec --test-force-exit --test --test-only \"build/tests/**/*.test.js\"",
117
- "test:only:no-build": "node --require ./build/tests/setup.js --no-warnings=ExperimentalWarning --test-reporter spec --test-force-exit --test --test-only \"build/tests/**/*.test.js\"",
118
- "test:update-snapshots": "npm run build && node --require ./build/tests/setup.js --no-warnings=ExperimentalWarning --test-force-exit --test --test-update-snapshots \"build/tests/**/*.test.js\"",
119
- "typecheck": "tsc --noEmit",
120
- "verify-server-json-version": "node --experimental-strip-types scripts/verify-server-json-version.ts"
121
124
  }
122
- }
125
+ }