@mcp-b/chrome-devtools-mcp 1.3.1 → 1.4.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
@@ -123,7 +123,7 @@ The AI can see the actual response, fix any bugs, and repeat until it works perf
123
123
  This creates a tight feedback loop where your AI assistant can:
124
124
  - **Write** WebMCP tools in your codebase
125
125
  - **Deploy** them automatically via hot-reload
126
- - **Discover** them through `diff_webmcp_tools`
126
+ - **Discover** them through `list_webmcp_tools`
127
127
  - **Test** them by calling tools directly by their prefixed names (e.g., `webmcp_localhost_3000_page0_search_products`)
128
128
  - **Debug** issues using console messages and snapshots
129
129
  - **Iterate** until the tool works correctly
@@ -476,7 +476,7 @@ If you run into any issues, checkout our [troubleshooting guide](./docs/troubles
476
476
  - [`take_screenshot`](docs/tool-reference.md#take_screenshot)
477
477
  - [`take_snapshot`](docs/tool-reference.md#take_snapshot)
478
478
  - **Website MCP Tools** (1 tool)
479
- - [`diff_webmcp_tools`](docs/tool-reference.md#diff_webmcp_tools) - List available website tools across all pages (with diff)
479
+ - [`list_webmcp_tools`](docs/tool-reference.md#list_webmcp_tools) - List available website tools across all pages (with diff)
480
480
 
481
481
  <!-- END AUTO GENERATED TOOLS -->
482
482
 
@@ -814,7 +814,7 @@ Navigate to https://example.com/app
814
814
  What tools are available on this website?
815
815
  ```
816
816
 
817
- The AI agent will use `diff_webmcp_tools` to show you what functionality the
817
+ The AI agent will use `list_webmcp_tools` to show you what functionality the
818
818
  website exposes. This automatically connects to the page's WebMCP server.
819
819
 
820
820
  **3. Use the tools directly**
@@ -840,13 +840,13 @@ By default, WebMCP tools are automatically registered as first-class MCP tools w
840
840
  | Claude Code | Yes | Full support for `tools/list_changed` |
841
841
  | GitHub Copilot | Yes | Supports list changed notifications |
842
842
  | Gemini CLI | Yes | Recently added support |
843
- | Cursor | No | Use `diff_webmcp_tools` to poll manually |
844
- | Cline | Partial | May need manual polling with `diff_webmcp_tools` |
845
- | Continue | Unknown | Use `diff_webmcp_tools` if tools don't appear |
843
+ | Cursor | No | Use `list_webmcp_tools` to poll manually |
844
+ | Cline | Partial | May need manual polling with `list_webmcp_tools` |
845
+ | Continue | Unknown | Use `list_webmcp_tools` if tools don't appear |
846
846
 
847
847
  **For clients without dynamic tool support:**
848
848
 
849
- If your MCP client doesn't support `tools/list_changed` notifications, use `diff_webmcp_tools` to manually see which tools are available, then call them directly by their prefixed names. The `diff_webmcp_tools` tool is diff-aware to reduce context pollution:
849
+ If your MCP client doesn't support `tools/list_changed` notifications, use `list_webmcp_tools` to manually see which tools are available, then call them directly by their prefixed names. The `list_webmcp_tools` tool is diff-aware to reduce context pollution:
850
850
  - First call returns the full tool list
851
851
  - Subsequent calls return only added/removed tools
852
852
  - Use `full: true` to force the complete list
@@ -872,9 +872,9 @@ Call the website's form submission tool to fill out the contact form
872
872
  - **"WebMCP not detected"**: The current webpage doesn't have `@mcp-b/global`
873
873
  installed or no tools are registered. The page needs the WebMCP polyfill loaded.
874
874
  - **Tool call fails**: Check the tool's input schema matches your parameters.
875
- Use `diff_webmcp_tools` to see the expected input format.
875
+ Use `list_webmcp_tools` to see the expected input format.
876
876
  - **Tools not appearing after navigation**: WebMCP auto-reconnects when you
877
- navigate. If the new page has different tools, call `diff_webmcp_tools` again.
877
+ navigate. If the new page has different tools, call `list_webmcp_tools` again.
878
878
 
879
879
  ## Related Packages
880
880
 
@@ -10,7 +10,7 @@ import { Client } from '@modelcontextprotocol/sdk/client/index.js';
10
10
  import { extractUrlLikeFromDevToolsTitle, urlsEqual } from './DevtoolsUtils.js';
11
11
  import { ToolListChangedNotificationSchema } from './third_party/index.js';
12
12
  import { NetworkCollector, ConsoleCollector } from './PageCollector.js';
13
- import { WEB_MCP_BRIDGE_SCRIPT, CHECK_WEBMCP_AVAILABLE_SCRIPT } from './transports/WebMCPBridgeScript.js';
13
+ import { WEB_MCP_BRIDGE_SCRIPT } from './transports/WebMCPBridgeScript.js';
14
14
  import { WebMCPClientTransport } from './transports/WebMCPClientTransport.js';
15
15
  import { Locator } from './third_party/index.js';
16
16
  import { listPages } from './tools/pages.js';
@@ -219,11 +219,11 @@ export class McpContext {
219
219
  newUrl === 'about:blank') {
220
220
  return;
221
221
  }
222
- // Wait a bit for the page to initialize WebMCP
223
- // The bridge script runs on DOMContentLoaded, and WebMCP may initialize after that
224
- await new Promise(resolve => setTimeout(resolve, 500));
225
- // Proactively check for WebMCP and sync tools
226
- await this.#proactivelyDetectWebMCP(page);
222
+ // Immediately try to connect - no polling needed
223
+ // If no WebMCP, connection will timeout gracefully
224
+ this.#tryConnectWebMCP(page).catch(err => {
225
+ this.logger('WebMCP connection attempt failed (expected if page has no WebMCP):', err);
226
+ });
227
227
  };
228
228
  page.on('framenavigated', onFrameNavigated);
229
229
  // Clean up listener when page closes
@@ -234,33 +234,37 @@ export class McpContext {
234
234
  this.logger(`WebMCP auto-detection listener installed for page: ${url}`);
235
235
  }
236
236
  /**
237
- * Proactively detect WebMCP on a page and sync tools if available.
238
- * This is called after page navigation to automatically discover WebMCP tools.
237
+ * Attempt to connect to WebMCP on a page without pre-checking.
238
+ * Uses the extension's approach: just try to connect, handle failure gracefully.
239
+ *
240
+ * This matches the WebMCP extension's behavior:
241
+ * - No pre-flight detection polling
242
+ * - Immediate connection attempt
243
+ * - Graceful handling if no server exists
244
+ * - Notification-based syncing when tools appear later
245
+ *
246
+ * Reference: /WebMCP/apps/extension/entrypoints/content/connection.ts lines 88-118
239
247
  */
240
- async #proactivelyDetectWebMCP(page) {
248
+ async #tryConnectWebMCP(page) {
241
249
  // Skip if tool hub is not enabled
242
250
  if (!this.#toolHub?.isEnabled()) {
243
251
  return;
244
252
  }
245
253
  try {
246
- // Check if WebMCP is available on the page
247
- const hasWebMCP = await this.#checkWebMCPAvailable(page);
248
- if (!hasWebMCP) {
249
- this.logger(`No WebMCP detected on page: ${page.url()}`);
250
- return;
251
- }
252
- this.logger(`WebMCP detected on page: ${page.url()}, connecting...`);
253
- // Connect and sync tools - this handles everything including sending list_changed
254
+ // Immediately try to get/create WebMCP client
255
+ // Transport is configured with requireWebMCP: false and 30s timeout in getWebMCPClient
254
256
  const result = await this.getWebMCPClient(page);
255
257
  if (result.connected) {
256
- this.logger(`WebMCP tools synced for page: ${page.url()}`);
258
+ this.logger(`WebMCP connected for page: ${page.url()}`);
257
259
  }
258
260
  else {
259
- this.logger(`Failed to connect to WebMCP: ${result.error}`);
261
+ // This is normal for pages without WebMCP
262
+ this.logger(`No WebMCP on page: ${page.url()}`);
260
263
  }
261
264
  }
262
265
  catch (err) {
263
- this.logger('Error during proactive WebMCP detection:', err);
266
+ // Connection timeout or error is expected on pages without WebMCP
267
+ this.logger(`WebMCP connection failed for ${page.url()} (normal if page has no WebMCP):`, err);
264
268
  }
265
269
  }
266
270
  /**
@@ -270,8 +274,10 @@ export class McpContext {
270
274
  async #setupWebMCPAutoDetectionForAllPages() {
271
275
  for (const page of this.#pages) {
272
276
  this.#setupWebMCPAutoDetection(page);
273
- // Also do an initial check for existing pages
274
- await this.#proactivelyDetectWebMCP(page);
277
+ // Try to connect immediately (don't await - run in parallel for all pages)
278
+ this.#tryConnectWebMCP(page).catch(err => {
279
+ this.logger('Initial WebMCP connection attempt failed (expected):', err);
280
+ });
275
281
  }
276
282
  }
277
283
  /**
@@ -547,7 +553,7 @@ export class McpContext {
547
553
  }
548
554
  }
549
555
  getPages() {
550
- return this.#pages;
556
+ return [...this.#pages];
551
557
  }
552
558
  getDevToolsPage(page) {
553
559
  return this.#pageToDevToolsPage.get(page);
@@ -716,17 +722,12 @@ export class McpContext {
716
722
  }
717
723
  this.#webMCPConnections.delete(targetPage);
718
724
  }
719
- // Check if WebMCP is available
720
- const hasWebMCP = await this.#checkWebMCPAvailable(targetPage);
721
- if (!hasWebMCP) {
722
- return { connected: false, error: 'WebMCP not detected on this page' };
723
- }
724
- // Connect
725
+ // Connect - no pre-checking needed (extension approach)
725
726
  try {
726
727
  const transport = new WebMCPClientTransport({
727
728
  page: targetPage,
728
- readyTimeout: 10000,
729
- requireWebMCP: false, // We already checked
729
+ readyTimeout: 30000, // 30s to handle slow React apps (up from 10s)
730
+ requireWebMCP: false, // Don't pre-check, just try to connect
730
731
  });
731
732
  const client = new Client({ name: 'chrome-devtools-mcp', version: '1.0.0' }, { capabilities: {} });
732
733
  // Set up onclose handler to clean up connection state
@@ -775,19 +776,6 @@ export class McpContext {
775
776
  };
776
777
  }
777
778
  }
778
- /**
779
- * Check if WebMCP is available on a page by checking the bridge's hasWebMCP() method.
780
- * The bridge is auto-injected into all pages, so we just need to check if it detected WebMCP.
781
- */
782
- async #checkWebMCPAvailable(page) {
783
- try {
784
- const result = await page.evaluate(CHECK_WEBMCP_AVAILABLE_SCRIPT);
785
- return result.available;
786
- }
787
- catch {
788
- return false;
789
- }
790
- }
791
779
  /**
792
780
  * We need to ignore favicon request as they make our test flaky
793
781
  */
@@ -17,6 +17,7 @@ export class McpResponse {
17
17
  #attachedConsoleMessageId;
18
18
  #textResponseLines = [];
19
19
  #images = [];
20
+ #isError = false;
20
21
  #networkRequestsOptions;
21
22
  #consoleDataOptions;
22
23
  #devToolsData;
@@ -96,6 +97,12 @@ export class McpResponse {
96
97
  appendResponseLine(value) {
97
98
  this.#textResponseLines.push(value);
98
99
  }
100
+ setIsError(value) {
101
+ this.#isError = value;
102
+ }
103
+ get isError() {
104
+ return this.#isError;
105
+ }
99
106
  attachImage(value) {
100
107
  this.#images.push(value);
101
108
  }
package/build/src/cli.js CHANGED
@@ -8,7 +8,7 @@ export const cliOptions = {
8
8
  autoConnect: {
9
9
  type: 'boolean',
10
10
  description: 'If specified, automatically connects to a browser (Chrome 145+) running in the user data directory identified by the channel param. Falls back to launching a new instance if no running browser is found.',
11
- default: true,
11
+ default: false,
12
12
  coerce: (value) => {
13
13
  if (value === false) {
14
14
  return false;
package/build/src/main.js CHANGED
@@ -12,7 +12,7 @@ import { logger, saveLogsToFile } from './logger.js';
12
12
  import { McpContext } from './McpContext.js';
13
13
  import { McpResponse } from './McpResponse.js';
14
14
  import { Mutex } from './Mutex.js';
15
- import { McpServer, StdioServerTransport, SetLevelRequestSchema, } from './third_party/index.js';
15
+ import { McpServer, StdioServerTransport, SetLevelRequestSchema, zod, } from './third_party/index.js';
16
16
  import { registerPrompts } from './prompts/index.js';
17
17
  import { ToolCategory } from './tools/categories.js';
18
18
  import { tools } from './tools/tools.js';
@@ -164,7 +164,10 @@ function registerTool(tool) {
164
164
  }
165
165
  server.registerTool(tool.name, {
166
166
  description: tool.description,
167
- inputSchema: tool.schema,
167
+ // For call_webmcp_tool, use a fully permissive schema to allow any properties
168
+ inputSchema: tool.name === 'call_webmcp_tool'
169
+ ? zod.record(zod.unknown())
170
+ : zod.object(tool.schema).passthrough(),
168
171
  annotations: tool.annotations,
169
172
  }, async (params) => {
170
173
  const guard = await toolMutex.acquire();
@@ -178,9 +181,13 @@ function registerTool(tool) {
178
181
  params,
179
182
  }, response, context);
180
183
  const content = await response.handle(tool.name, context);
181
- return {
184
+ const result = {
182
185
  content,
183
186
  };
187
+ if (response.isError) {
188
+ result.isError = true;
189
+ }
190
+ return result;
184
191
  }
185
192
  catch (err) {
186
193
  logger(`${tool.name} error:`, err, err?.stack);
@@ -0,0 +1,44 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2025 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ import { createRequire } from 'node:module';
7
+ import { readFileSync } from 'node:fs';
8
+ const require = createRequire(import.meta.url);
9
+ let cachedPolyfill = null;
10
+ /**
11
+ * Load the @mcp-b/global IIFE polyfill code.
12
+ * This polyfill provides the navigator.modelContext API for pages that don't have WebMCP.
13
+ * Cached after first load for performance.
14
+ *
15
+ * @returns The IIFE JavaScript code as a string
16
+ * @throws Error if the polyfill file cannot be found or read
17
+ */
18
+ export function getPolyfillCode() {
19
+ if (cachedPolyfill) {
20
+ return cachedPolyfill;
21
+ }
22
+ try {
23
+ // Use Node's module resolution to find the package regardless of
24
+ // workspace structure or installation method (pnpm, npm, yarn).
25
+ // Use the package's exported subpath '@mcp-b/global/iife' which resolves
26
+ // to the IIFE bundle via the exports field in package.json.
27
+ const polyfillPath = require.resolve('@mcp-b/global/iife');
28
+ cachedPolyfill = readFileSync(polyfillPath, 'utf-8');
29
+ return cachedPolyfill;
30
+ }
31
+ catch (err) {
32
+ const message = err instanceof Error ? err.message : String(err);
33
+ // Provide actionable error message
34
+ throw new Error(`Could not find @mcp-b/global polyfill: ${message}\n` +
35
+ 'Run `pnpm build` in the global package or ensure @mcp-b/global is installed.');
36
+ }
37
+ }
38
+ /**
39
+ * Clear the cached polyfill code, forcing the next getPolyfillCode() call
40
+ * to reload from disk. Useful for testing or after @mcp-b/global updates.
41
+ */
42
+ export function clearPolyfillCache() {
43
+ cachedPolyfill = null;
44
+ }
@@ -27,7 +27,7 @@ export function registerPrompts(server) {
27
27
  1. **Write the Tool**: I'll ask you to create a WebMCP tool in my codebase using @mcp-b/global
28
28
  2. **Hot Reload**: My dev server will automatically reload with the new tool
29
29
  3. **Navigate**: Use navigate_page to open my dev server (e.g., http://localhost:3000)
30
- 4. **Discover**: Use diff_webmcp_tools to see registered tools (shown with callable names like webmcp_localhost_3000_page0_my_tool)
30
+ 4. **Discover**: Use list_webmcp_tools to see registered tools (shown with callable names like webmcp_localhost_3000_page0_my_tool)
31
31
  5. **Test**: Call the tool directly by its prefixed name (e.g., webmcp_localhost_3000_page0_my_tool)
32
32
  6. **Iterate**: If something is wrong, fix the code and repeat
33
33
 
@@ -92,7 +92,7 @@ What would you like to build? Describe the tool you need and I'll help you imple
92
92
  ## Test Plan
93
93
 
94
94
  1. Navigate to ${url}
95
- 2. Use diff_webmcp_tools to discover registered tools (shown with callable names like webmcp_localhost_3000_page0_tool_name)
95
+ 2. Use list_webmcp_tools to discover registered tools (shown with callable names like webmcp_localhost_3000_page0_tool_name)
96
96
  3. ${toolNameInstruction}
97
97
  4. Call the tool directly by its prefixed name and test with various inputs:
98
98
  - Valid inputs (happy path)
@@ -138,7 +138,7 @@ ${urlInstruction}
138
138
  - Errors loading @mcp-b/global
139
139
  - Tool registration errors
140
140
  - Any JavaScript errors
141
- 3. **Test WebMCP**: Try diff_webmcp_tools to see if connection works
141
+ 3. **Test WebMCP**: Try list_webmcp_tools to see if connection works
142
142
  4. **Verify registration**: If no tools appear, check if the code properly imports '@mcp-b/global' and calls registerTool
143
143
 
144
144
  ## Common Issues