@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.
- package/README.md +9 -9
- package/build/src/McpContext.js +59 -44
- package/build/src/McpResponse.js +7 -0
- package/build/src/index.js +0 -0
- package/build/src/main.js +14 -3
- package/build/src/polyfillLoader.js +44 -0
- package/build/src/prompts/index.js +3 -3
- package/build/src/tools/WebMCPToolHub.js +88 -138
- package/build/src/tools/webmcp.js +714 -3
- package/build/src/transports/WebMCPBridgeScript.js +13 -7
- package/build/src/transports/WebMCPClientTransport.js +37 -19
- package/build/src/transports/bridgeConstants.js +22 -0
- package/package.json +28 -25
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* Communication flow:
|
|
14
14
|
*
|
|
15
15
|
* CDP → Page (requests):
|
|
16
|
-
* 1. CDP calls Runtime.evaluate with
|
|
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.
|
|
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
|
-
//
|
|
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
|
-
|
|
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.
|
|
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:
|
|
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 !==
|
|
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
|
|
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
|
|
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
|
-
//
|
|
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
|
|
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
|
|
350
|
-
});
|
|
360
|
+
await this._page.evaluate((bridgeProp) => {
|
|
361
|
+
window[bridgeProp]?.dispose();
|
|
362
|
+
}, CDP_BRIDGE_WINDOW_PROPERTY);
|
|
351
363
|
}
|
|
352
|
-
catch {
|
|
353
|
-
//
|
|
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
|
-
//
|
|
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
|
+
"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": "
|
|
52
|
-
"@
|
|
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": "
|
|
79
|
+
"zod": "catalog:"
|
|
59
80
|
},
|
|
60
81
|
"devDependencies": {
|
|
61
82
|
"@eslint/js": "^9.35.0",
|
|
62
|
-
"@modelcontextprotocol/sdk": "
|
|
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": "
|
|
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
|
+
}
|