chrome-devtools-mcp 0.23.0 → 0.25.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.
Files changed (83) hide show
  1. package/README.md +22 -5
  2. package/build/src/DevToolsConnectionAdapter.js +1 -0
  3. package/build/src/DevtoolsUtils.js +1 -0
  4. package/build/src/HeapSnapshotManager.js +16 -0
  5. package/build/src/McpContext.js +57 -4
  6. package/build/src/McpPage.js +10 -4
  7. package/build/src/McpResponse.js +55 -19
  8. package/build/src/Mutex.js +1 -0
  9. package/build/src/PageCollector.js +1 -0
  10. package/build/src/SlimMcpResponse.js +1 -0
  11. package/build/src/TextSnapshot.js +13 -7
  12. package/build/src/WaitForHelper.js +6 -0
  13. package/build/src/bin/check-latest-version.js +1 -0
  14. package/build/src/bin/chrome-devtools-cli-options.js +229 -46
  15. package/build/src/bin/chrome-devtools-mcp-cli-options.js +10 -5
  16. package/build/src/bin/chrome-devtools-mcp-main.js +1 -5
  17. package/build/src/bin/chrome-devtools-mcp.js +1 -0
  18. package/build/src/bin/chrome-devtools.js +5 -13
  19. package/build/src/browser.js +1 -0
  20. package/build/src/daemon/client.js +4 -2
  21. package/build/src/daemon/daemon.js +1 -0
  22. package/build/src/daemon/types.js +1 -0
  23. package/build/src/daemon/utils.js +1 -0
  24. package/build/src/formatters/ConsoleFormatter.js +48 -1
  25. package/build/src/formatters/HeapSnapshotFormatter.js +18 -2
  26. package/build/src/formatters/IssueFormatter.js +1 -0
  27. package/build/src/formatters/NetworkFormatter.js +1 -0
  28. package/build/src/formatters/SnapshotFormatter.js +2 -1
  29. package/build/src/index.js +114 -51
  30. package/build/src/issue-descriptions.js +1 -0
  31. package/build/src/logger.js +1 -0
  32. package/build/src/polyfill.js +1 -0
  33. package/build/src/telemetry/ClearcutLogger.js +13 -1
  34. package/build/src/telemetry/WatchdogClient.js +1 -0
  35. package/build/src/telemetry/flagUtils.js +1 -0
  36. package/build/src/telemetry/metricUtils.js +1 -0
  37. package/build/src/telemetry/persistence.js +1 -0
  38. package/build/src/telemetry/toolMetricsUtils.js +2 -1
  39. package/build/src/telemetry/types.js +1 -0
  40. package/build/src/telemetry/watchdog/ClearcutSender.js +1 -0
  41. package/build/src/telemetry/watchdog/main.js +1 -0
  42. package/build/src/third_party/THIRD_PARTY_NOTICES +8 -8
  43. package/build/src/third_party/bundled-packages.json +3 -3
  44. package/build/src/third_party/devtools-formatter-worker.js +2469 -2933
  45. package/build/src/third_party/devtools-heap-snapshot-worker.js +50 -26
  46. package/build/src/third_party/index.js +1107 -407
  47. package/build/src/third_party/issue-descriptions/genericFormModelContextMissingToolDescription.md +5 -0
  48. package/build/src/third_party/issue-descriptions/genericFormModelContextMissingToolName.md +5 -0
  49. package/build/src/third_party/issue-descriptions/genericFormModelContextParameterMissingName.md +5 -0
  50. package/build/src/third_party/issue-descriptions/genericFormModelContextParameterMissingTitleAndDescription.md +5 -0
  51. package/build/src/third_party/issue-descriptions/genericFormModelContextRequiredParameterMissingName.md +5 -0
  52. package/build/src/third_party/lighthouse-devtools-mcp-bundle.js +21717 -20261
  53. package/build/src/tools/ToolDefinition.js +1 -0
  54. package/build/src/tools/categories.js +10 -3
  55. package/build/src/tools/console.js +3 -0
  56. package/build/src/tools/emulation.js +2 -0
  57. package/build/src/tools/extensions.js +6 -0
  58. package/build/src/tools/input.js +57 -2
  59. package/build/src/tools/lighthouse.js +17 -9
  60. package/build/src/tools/memory.js +34 -1
  61. package/build/src/tools/network.js +5 -0
  62. package/build/src/tools/pages.js +14 -5
  63. package/build/src/tools/performance.js +6 -0
  64. package/build/src/tools/screencast.js +6 -2
  65. package/build/src/tools/screenshot.js +3 -0
  66. package/build/src/tools/script.js +2 -0
  67. package/build/src/tools/slim/tools.js +4 -0
  68. package/build/src/tools/snapshot.js +5 -1
  69. package/build/src/tools/{inPage.js → thirdPartyDeveloper.js} +17 -16
  70. package/build/src/tools/tools.js +3 -2
  71. package/build/src/tools/webmcp.js +5 -4
  72. package/build/src/trace-processing/parse.js +1 -0
  73. package/build/src/types.js +1 -0
  74. package/build/src/utils/check-for-updates.js +1 -0
  75. package/build/src/utils/files.js +5 -10
  76. package/build/src/utils/id.js +1 -0
  77. package/build/src/utils/keyboard.js +1 -0
  78. package/build/src/utils/pagination.js +1 -0
  79. package/build/src/utils/string.js +1 -0
  80. package/build/src/utils/types.js +1 -0
  81. package/build/src/version.js +2 -1
  82. package/package.json +10 -11
  83. package/build/src/bin/cliDefinitions.js +0 -621
package/README.md CHANGED
@@ -477,7 +477,7 @@ If you run into any issues, checkout our [troubleshooting guide](./docs/troubles
477
477
 
478
478
  <!-- BEGIN AUTO GENERATED TOOLS -->
479
479
 
480
- - **Input automation** (9 tools)
480
+ - **Input automation** (10 tools)
481
481
  - [`click`](docs/tool-reference.md#click)
482
482
  - [`drag`](docs/tool-reference.md#drag)
483
483
  - [`fill`](docs/tool-reference.md#fill)
@@ -487,6 +487,7 @@ If you run into any issues, checkout our [troubleshooting guide](./docs/troubles
487
487
  - [`press_key`](docs/tool-reference.md#press_key)
488
488
  - [`type_text`](docs/tool-reference.md#type_text)
489
489
  - [`upload_file`](docs/tool-reference.md#upload_file)
490
+ - [`click_at`](docs/tool-reference.md#click_at)
490
491
  - **Navigation automation** (6 tools)
491
492
  - [`close_page`](docs/tool-reference.md#close_page)
492
493
  - [`list_pages`](docs/tool-reference.md#list_pages)
@@ -504,21 +505,32 @@ If you run into any issues, checkout our [troubleshooting guide](./docs/troubles
504
505
  - **Network** (2 tools)
505
506
  - [`get_network_request`](docs/tool-reference.md#get_network_request)
506
507
  - [`list_network_requests`](docs/tool-reference.md#list_network_requests)
507
- - **Debugging** (6 tools)
508
+ - **Debugging** (8 tools)
508
509
  - [`evaluate_script`](docs/tool-reference.md#evaluate_script)
509
510
  - [`get_console_message`](docs/tool-reference.md#get_console_message)
510
511
  - [`lighthouse_audit`](docs/tool-reference.md#lighthouse_audit)
511
512
  - [`list_console_messages`](docs/tool-reference.md#list_console_messages)
512
513
  - [`take_screenshot`](docs/tool-reference.md#take_screenshot)
513
514
  - [`take_snapshot`](docs/tool-reference.md#take_snapshot)
515
+ - [`screencast_start`](docs/tool-reference.md#screencast_start)
516
+ - [`screencast_stop`](docs/tool-reference.md#screencast_stop)
517
+ - **Memory** (4 tools)
518
+ - [`take_memory_snapshot`](docs/tool-reference.md#take_memory_snapshot)
519
+ - [`get_memory_snapshot_details`](docs/tool-reference.md#get_memory_snapshot_details)
520
+ - [`get_nodes_by_class`](docs/tool-reference.md#get_nodes_by_class)
521
+ - [`load_memory_snapshot`](docs/tool-reference.md#load_memory_snapshot)
514
522
  - **Extensions** (5 tools)
515
523
  - [`install_extension`](docs/tool-reference.md#install_extension)
516
524
  - [`list_extensions`](docs/tool-reference.md#list_extensions)
517
525
  - [`reload_extension`](docs/tool-reference.md#reload_extension)
518
526
  - [`trigger_extension_action`](docs/tool-reference.md#trigger_extension_action)
519
527
  - [`uninstall_extension`](docs/tool-reference.md#uninstall_extension)
520
- - **Memory** (1 tools)
521
- - [`take_memory_snapshot`](docs/tool-reference.md#take_memory_snapshot)
528
+ - **Third-party** (2 tools)
529
+ - [`execute_3p_developer_tool`](docs/tool-reference.md#execute_3p_developer_tool)
530
+ - [`list_3p_developer_tools`](docs/tool-reference.md#list_3p_developer_tools)
531
+ - **WebMCP** (2 tools)
532
+ - [`execute_webmcp_tool`](docs/tool-reference.md#execute_webmcp_tool)
533
+ - [`list_webmcp_tools`](docs/tool-reference.md#list_webmcp_tools)
522
534
 
523
535
  <!-- END AUTO GENERATED TOOLS -->
524
536
 
@@ -595,7 +607,7 @@ The Chrome DevTools MCP server supports the following configuration option:
595
607
  Path to ffmpeg executable for screencast recording.
596
608
  - **Type:** string
597
609
 
598
- - **`--experimentalWebmcp`/ `--experimental-webmcp`**
610
+ - **`--categoryExperimentalWebmcp`/ `--category-experimental-webmcp`**
599
611
  Set to true to enable debugging WebMCP tools. Requires Chrome 149+ with the following flags: `--enable-features=WebMCPTesting,DevToolsWebMCPSupport`
600
612
  - **Type:** boolean
601
613
 
@@ -627,6 +639,11 @@ The Chrome DevTools MCP server supports the following configuration option:
627
639
  - **Type:** boolean
628
640
  - **Default:** `false`
629
641
 
642
+ - **`--categoryExperimentalThirdParty`/ `--category-experimental-third-party`**
643
+ Set to true to enable third-party developer tools exposed by the inspected page itself
644
+ - **Type:** boolean
645
+ - **Default:** `false`
646
+
630
647
  - **`--performanceCrux`/ `--performance-crux`**
631
648
  Set to false to disable sending URLs from performance traces to CrUX API to get field performance data.
632
649
  - **Type:** boolean
@@ -67,3 +67,4 @@ export class PuppeteerDevToolsConnection {
67
67
  }
68
68
  }
69
69
  }
70
+ //# sourceMappingURL=DevToolsConnectionAdapter.js.map
@@ -292,3 +292,4 @@ async function waitForScript(model, scriptId, signal) {
292
292
  });
293
293
  }
294
294
  }
295
+ //# sourceMappingURL=DevtoolsUtils.js.map
@@ -56,6 +56,17 @@ export class HeapSnapshotManager {
56
56
  }
57
57
  return uid;
58
58
  }
59
+ async getNodesByUid(filePath, uid) {
60
+ const snapshot = await this.getSnapshot(filePath);
61
+ const filter = new DevTools.HeapSnapshotModel.HeapSnapshotModel.NodeFilter();
62
+ const className = await this.resolveClassKeyFromUid(filePath, uid);
63
+ if (!className) {
64
+ throw new Error(`Class with UID ${uid} not found in heap snapshot`);
65
+ }
66
+ const provider = snapshot.createNodesProviderForClass(className, filter);
67
+ const range = await provider.serializeItemsRange(0, 1);
68
+ return await provider.serializeItemsRange(0, range.totalLength);
69
+ }
59
70
  #getCachedSnapshot(filePath) {
60
71
  const absolutePath = path.resolve(filePath);
61
72
  const cached = this.#snapshots.get(absolutePath);
@@ -64,6 +75,10 @@ export class HeapSnapshotManager {
64
75
  }
65
76
  return cached;
66
77
  }
78
+ async resolveClassKeyFromUid(filePath, uid) {
79
+ const cached = this.#getCachedSnapshot(filePath);
80
+ return cached.uidToClassKey.get(uid);
81
+ }
67
82
  async #loadSnapshot(absolutePath) {
68
83
  const workerProxy = new DevTools.HeapSnapshotModel.HeapSnapshotProxy.HeapSnapshotWorkerProxy(() => {
69
84
  /* noop */
@@ -92,3 +107,4 @@ export class HeapSnapshotManager {
92
107
  }
93
108
  }
94
109
  }
110
+ //# sourceMappingURL=HeapSnapshotManager.js.map
@@ -4,16 +4,17 @@
4
4
  * SPDX-License-Identifier: Apache-2.0
5
5
  */
6
6
  import fs from 'node:fs/promises';
7
+ import os from 'node:os';
7
8
  import path from 'node:path';
9
+ import { fileURLToPath, pathToFileURL } from 'node:url';
8
10
  import { UniverseManager } from './DevtoolsUtils.js';
9
11
  import { HeapSnapshotManager } from './HeapSnapshotManager.js';
10
12
  import { McpPage } from './McpPage.js';
11
13
  import { NetworkCollector, ConsoleCollector, } from './PageCollector.js';
12
- import { Locator } from './third_party/index.js';
13
- import { PredefinedNetworkConditions } from './third_party/index.js';
14
+ import { Locator, PredefinedNetworkConditions, } from './third_party/index.js';
14
15
  import { listPages } from './tools/pages.js';
15
16
  import { CLOSE_PAGE_ERROR } from './tools/ToolDefinition.js';
16
- import { ensureExtension, saveTemporaryFile } from './utils/files.js';
17
+ import { ensureExtension, getTempFilePath } from './utils/files.js';
17
18
  import { getNetworkMultiplierFromString } from './WaitForHelper.js';
18
19
  const DEFAULT_TIMEOUT = 5_000;
19
20
  const NAVIGATION_TIMEOUT = 10_000;
@@ -41,6 +42,7 @@ export class McpContext {
41
42
  #locatorClass;
42
43
  #options;
43
44
  #heapSnapshotManager = new HeapSnapshotManager();
45
+ #roots = undefined;
44
46
  constructor(browser, logger, options, locatorClass) {
45
47
  this.browser = browser;
46
48
  this.logger = logger;
@@ -89,6 +91,39 @@ export class McpContext {
89
91
  await context.#init();
90
92
  return context;
91
93
  }
94
+ roots() {
95
+ if (this.#roots === undefined) {
96
+ return undefined;
97
+ }
98
+ return [
99
+ ...this.#roots,
100
+ {
101
+ uri: pathToFileURL(os.tmpdir()).href,
102
+ name: 'temp',
103
+ },
104
+ ];
105
+ }
106
+ setRoots(roots) {
107
+ this.#roots = roots;
108
+ }
109
+ validatePath(filePath) {
110
+ if (filePath === undefined) {
111
+ return;
112
+ }
113
+ const roots = this.roots();
114
+ if (roots === undefined) {
115
+ return;
116
+ }
117
+ const absolutePath = path.resolve(filePath);
118
+ for (const root of roots) {
119
+ const rootPath = path.resolve(fileURLToPath(root.uri));
120
+ if (absolutePath === rootPath ||
121
+ absolutePath.startsWith(rootPath + path.sep)) {
122
+ return;
123
+ }
124
+ }
125
+ throw new Error(`Access denied: path ${filePath} is not within any of the workspace roots ${JSON.stringify(roots)}.`);
126
+ }
92
127
  resolveCdpRequestId(page, cdpRequestId) {
93
128
  if (!cdpRequestId) {
94
129
  this.logger('no network request');
@@ -463,9 +498,18 @@ export class McpContext {
463
498
  return this.#mcpPages.get(page)?.isolatedContextName;
464
499
  }
465
500
  async saveTemporaryFile(data, filename) {
466
- return await saveTemporaryFile(data, filename);
501
+ const filepath = await getTempFilePath(filename);
502
+ this.validatePath(filepath);
503
+ try {
504
+ await fs.writeFile(filepath, data);
505
+ }
506
+ catch (err) {
507
+ throw new Error('Could not save a file', { cause: err });
508
+ }
509
+ return { filepath };
467
510
  }
468
511
  async saveFile(data, clientProvidedFilePath, extension) {
512
+ this.validatePath(clientProvidedFilePath);
469
513
  try {
470
514
  const filePath = ensureExtension(path.resolve(clientProvidedFilePath), extension);
471
515
  await fs.mkdir(path.dirname(filePath), { recursive: true });
@@ -518,6 +562,7 @@ export class McpContext {
518
562
  await this.#networkCollector.init(pages);
519
563
  }
520
564
  async installExtension(extensionPath) {
565
+ this.validatePath(extensionPath);
521
566
  const id = await this.browser.installExtension(extensionPath);
522
567
  return id;
523
568
  }
@@ -541,12 +586,20 @@ export class McpContext {
541
586
  return pptrExtensions.get(id);
542
587
  }
543
588
  async getHeapSnapshotAggregates(filePath) {
589
+ this.validatePath(filePath);
544
590
  return await this.#heapSnapshotManager.getAggregates(filePath);
545
591
  }
546
592
  async getHeapSnapshotStats(filePath) {
593
+ this.validatePath(filePath);
547
594
  return await this.#heapSnapshotManager.getStats(filePath);
548
595
  }
549
596
  async getHeapSnapshotStaticData(filePath) {
597
+ this.validatePath(filePath);
550
598
  return await this.#heapSnapshotManager.getStaticData(filePath);
551
599
  }
600
+ async getHeapSnapshotNodesByUid(filePath, uid) {
601
+ this.validatePath(filePath);
602
+ return await this.#heapSnapshotManager.getNodesByUid(filePath, uid);
603
+ }
552
604
  }
605
+ //# sourceMappingURL=McpContext.js.map
@@ -30,7 +30,7 @@ export class McpPage {
30
30
  // Dialog
31
31
  #dialog;
32
32
  #dialogHandler;
33
- inPageTools;
33
+ thirdPartyDeveloperTools;
34
34
  constructor(page, id) {
35
35
  this.pptrPage = page;
36
36
  this.id = id;
@@ -48,8 +48,13 @@ export class McpPage {
48
48
  clearDialog() {
49
49
  this.#dialog = undefined;
50
50
  }
51
- getInPageTools() {
52
- return this.inPageTools;
51
+ throwIfDialogOpen() {
52
+ if (this.#dialog) {
53
+ throw new Error(`A dialog is open (${this.#dialog.type()}: ${this.#dialog.message()}).`);
54
+ }
55
+ }
56
+ getThirdPartyDeveloperTools() {
57
+ return this.thirdPartyDeveloperTools;
53
58
  }
54
59
  getWebMcpTools() {
55
60
  return this.pptrPage.webmcp.tools();
@@ -83,7 +88,7 @@ export class McpPage {
83
88
  dispose() {
84
89
  this.pptrPage.off('dialog', this.#dialogHandler);
85
90
  }
86
- async executeInPageTool(toolName, params, response) {
91
+ async executeThirdPartyDeveloperTool(toolName, params, response) {
87
92
  // Creates array of ElementHandles from the UIDs in the params.
88
93
  // We do not replace the uids with the ElementsHandles yet, because
89
94
  // the `evaluate` function only turns them into DOM elements if they
@@ -307,3 +312,4 @@ export class McpPage {
307
312
  return {};
308
313
  }
309
314
  }
315
+ //# sourceMappingURL=McpPage.js.map
@@ -5,6 +5,7 @@
5
5
  */
6
6
  import { ConsoleFormatter } from './formatters/ConsoleFormatter.js';
7
7
  import { HeapSnapshotFormatter } from './formatters/HeapSnapshotFormatter.js';
8
+ import { isNodeLike } from './formatters/HeapSnapshotFormatter.js';
8
9
  import { IssueFormatter } from './formatters/IssueFormatter.js';
9
10
  import { NetworkFormatter } from './formatters/NetworkFormatter.js';
10
11
  import { SnapshotFormatter } from './formatters/SnapshotFormatter.js';
@@ -124,13 +125,14 @@ export class McpResponse {
124
125
  #networkRequestsOptions;
125
126
  #consoleDataOptions;
126
127
  #listExtensions;
127
- #listInPageTools;
128
+ #listThirdPartyDeveloperTools;
128
129
  #listWebMcpTools;
129
130
  #devToolsData;
130
131
  #tabId;
131
132
  #args;
132
133
  #page;
133
134
  #redactNetworkHeaders = true;
135
+ #error;
134
136
  constructor(args) {
135
137
  this.#args = args;
136
138
  }
@@ -161,9 +163,9 @@ export class McpResponse {
161
163
  setListExtensions() {
162
164
  this.#listExtensions = true;
163
165
  }
164
- setListInPageTools() {
165
- if (this.#args.categoryInPageTools) {
166
- this.#listInPageTools = true;
166
+ setListThirdPartyDeveloperTools() {
167
+ if (this.#args.categoryExperimentalThirdParty) {
168
+ this.#listThirdPartyDeveloperTools = true;
167
169
  }
168
170
  }
169
171
  setListWebMcpTools() {
@@ -204,6 +206,9 @@ export class McpResponse {
204
206
  includePreservedMessages: options?.includePreservedMessages,
205
207
  };
206
208
  }
209
+ setError(error) {
210
+ this.#error = error;
211
+ }
207
212
  attachNetworkRequest(reqId, options) {
208
213
  this.#attachedNetworkRequestId = reqId;
209
214
  this.#attachedNetworkRequestOptions = options;
@@ -254,6 +259,9 @@ export class McpResponse {
254
259
  get consoleMessagesTypes() {
255
260
  return this.#consoleDataOptions?.types;
256
261
  }
262
+ get error() {
263
+ return this.#error;
264
+ }
257
265
  appendResponseLine(value) {
258
266
  this.#textResponseLines.push(value);
259
267
  }
@@ -273,6 +281,14 @@ export class McpResponse {
273
281
  staticData,
274
282
  };
275
283
  }
284
+ setHeapSnapshotNodes(nodes, options) {
285
+ this.#heapSnapshotOptions = {
286
+ ...this.#heapSnapshotOptions,
287
+ include: true,
288
+ nodes,
289
+ pagination: options,
290
+ };
291
+ }
276
292
  attachImage(value) {
277
293
  this.#images.push(value);
278
294
  }
@@ -365,14 +381,14 @@ export class McpResponse {
365
381
  if (this.#listExtensions) {
366
382
  extensions = await context.listExtensions();
367
383
  }
368
- let inPageTools;
369
- if (this.#listInPageTools) {
384
+ let thirdPartyDeveloperTools;
385
+ if (this.#listThirdPartyDeveloperTools) {
370
386
  const page = this.#page ?? context.getSelectedMcpPage();
371
- inPageTools = await getToolGroup(page);
372
- page.inPageTools = inPageTools;
387
+ thirdPartyDeveloperTools = await getToolGroup(page);
388
+ page.thirdPartyDeveloperTools = thirdPartyDeveloperTools;
373
389
  }
374
390
  let webmcpTools;
375
- if (this.#listWebMcpTools && this.#args.experimentalWebmcp) {
391
+ if (this.#listWebMcpTools && this.#args.categoryExperimentalWebmcp) {
376
392
  const page = this.#page ?? context.getSelectedMcpPage();
377
393
  webmcpTools = page.getWebMcpTools();
378
394
  }
@@ -453,8 +469,9 @@ export class McpResponse {
453
469
  traceSummary: this.#attachedTraceSummary,
454
470
  extensions,
455
471
  lighthouseResult: this.#attachedLighthouseResult,
456
- inPageTools,
472
+ thirdPartyDeveloperTools,
457
473
  webmcpTools,
474
+ errorMessage: this.#error?.message,
458
475
  });
459
476
  }
460
477
  format(toolName, context, data) {
@@ -645,6 +662,17 @@ Call ${handleDialog.name} to handle it before continuing.`);
645
662
  response.push(formatter.toString());
646
663
  structuredContent.heapSnapshotData = formatter.toJSON();
647
664
  }
665
+ const nodes = this.#heapSnapshotOptions.nodes;
666
+ if (nodes) {
667
+ const sortedItems = nodes.items
668
+ .filter(isNodeLike)
669
+ .sort((a, b) => b.retainedSize - a.retainedSize);
670
+ const paginationData = this.#dataWithPagination(sortedItems, this.#heapSnapshotOptions.pagination);
671
+ response.push(HeapSnapshotFormatter.formatNodes(paginationData.items));
672
+ structuredContent.pagination = paginationData.pagination;
673
+ response.push(...paginationData.info);
674
+ structuredContent.heapSnapshotNodes = paginationData.items;
675
+ }
648
676
  }
649
677
  if (data.detailedNetworkRequest) {
650
678
  response.push(data.detailedNetworkRequest.toStringDetailed());
@@ -672,14 +700,16 @@ Call ${handleDialog.name} to handle it before continuing.`);
672
700
  response.push(extensionsMessage);
673
701
  }
674
702
  }
675
- if (this.#listInPageTools) {
676
- structuredContent.inPageTools = data.inPageTools ?? undefined;
677
- response.push('## In-page tools');
678
- if (!data.inPageTools || !data.inPageTools.tools) {
679
- response.push('No in-page tools available.');
703
+ if (this.#listThirdPartyDeveloperTools) {
704
+ structuredContent.thirdPartyDeveloperTools =
705
+ data.thirdPartyDeveloperTools ?? undefined;
706
+ response.push('## Third-party developer tools');
707
+ if (!data.thirdPartyDeveloperTools ||
708
+ !data.thirdPartyDeveloperTools.tools) {
709
+ response.push('No third-party developer tools available.');
680
710
  }
681
711
  else {
682
- const toolGroup = data.inPageTools;
712
+ const toolGroup = data.thirdPartyDeveloperTools;
683
713
  response.push(`${toolGroup.name}: ${toolGroup.description}`);
684
714
  response.push('Available tools:');
685
715
  const toolDefinitionsMessage = toolGroup.tools
@@ -733,16 +763,21 @@ Call ${handleDialog.name} to handle it before continuing.`);
733
763
  const messages = data.consoleMessages ?? [];
734
764
  response.push('## Console messages');
735
765
  if (messages.length) {
736
- const paginationData = this.#dataWithPagination(messages, this.#consoleDataOptions.pagination);
766
+ const grouped = ConsoleFormatter.groupConsecutive(messages);
767
+ const paginationData = this.#dataWithPagination(grouped, this.#consoleDataOptions.pagination);
737
768
  structuredContent.pagination = paginationData.pagination;
738
769
  response.push(...paginationData.info);
739
- response.push(...paginationData.items.map(message => message.toString()));
740
- structuredContent.consoleMessages = paginationData.items.map(message => message.toJSON());
770
+ response.push(...paginationData.items.map(item => item.toString()));
771
+ structuredContent.consoleMessages = paginationData.items.map(item => item.toJSON());
741
772
  }
742
773
  else {
743
774
  response.push('<no console messages found>');
744
775
  }
745
776
  }
777
+ if (data.errorMessage) {
778
+ response.push(`Error: ${data.errorMessage}`);
779
+ structuredContent.errorMessage = data.errorMessage;
780
+ }
746
781
  const text = {
747
782
  type: 'text',
748
783
  text: response.join('\n'),
@@ -804,3 +839,4 @@ function createStructuredPage(page, context) {
804
839
  }
805
840
  return entry;
806
841
  }
842
+ //# sourceMappingURL=McpResponse.js.map
@@ -35,3 +35,4 @@ export class Mutex {
35
35
  resolve();
36
36
  }
37
37
  }
38
+ //# sourceMappingURL=Mutex.js.map
@@ -294,3 +294,4 @@ export class NetworkCollector extends PageCollector {
294
294
  navigations.splice(this.maxNavigationSaved);
295
295
  }
296
296
  }
297
+ //# sourceMappingURL=PageCollector.js.map
@@ -16,3 +16,4 @@ export class SlimMcpResponse extends McpResponse {
16
16
  };
17
17
  }
18
18
  }
19
+ //# sourceMappingURL=SlimMcpResponse.js.map
@@ -99,8 +99,8 @@ export class TextSnapshot {
99
99
  return snapshot;
100
100
  }
101
101
  // ExtraHandles represent DOM nodes which might not be part of the accessibility tree, e.g. DOM nodes
102
- // returned by in-page tools. We insert them into the tree by finding the closest ancestor in the
103
- // tree and inserting the node as a child. The ancestor's child nodes are re-parented if necessary.
102
+ // returned by third-party developer tools. We insert them into the tree by finding the closest ancestor
103
+ // in the tree and inserting the node as a child. The ancestor's child nodes are re-parented if necessary.
104
104
  static async insertExtraNodes(page, idToNode, seenUniqueIds, snapshotId, idCounter, rootNodeWithId, seenBackendNodeIds, extraHandles) {
105
105
  const { uniqueBackendNodeIdToMcpId } = page;
106
106
  const createExtraNode = async (handle) => {
@@ -160,6 +160,9 @@ export class TextSnapshot {
160
160
  };
161
161
  const findDescendantNodes = async (backendNodeId) => {
162
162
  const descendantIds = new Set();
163
+ if (!backendNodeId) {
164
+ return descendantIds;
165
+ }
163
166
  try {
164
167
  // @ts-expect-error internal API
165
168
  const client = page.pptrPage._client();
@@ -213,6 +216,7 @@ export class TextSnapshot {
213
216
  if (extraHandles.length) {
214
217
  page.extraHandles = extraHandles;
215
218
  }
219
+ const reorgInfo = [];
216
220
  for (const handle of page.extraHandles) {
217
221
  const extraNode = await createExtraNode(handle);
218
222
  if (!extraNode) {
@@ -220,11 +224,13 @@ export class TextSnapshot {
220
224
  }
221
225
  idToNode.set(extraNode.id, extraNode);
222
226
  const attachTarget = (await findAncestorNode(handle)) || rootNodeWithId;
223
- if (extraNode.backendNodeId !== undefined) {
224
- const descendantIds = await findDescendantNodes(extraNode.backendNodeId);
225
- const index = moveChildNodes(attachTarget, extraNode, descendantIds);
226
- attachTarget.children.splice(index, 0, extraNode);
227
- }
227
+ const descendantIds = await findDescendantNodes(extraNode.backendNodeId);
228
+ reorgInfo.push({ extraNode, attachTarget, descendantIds });
229
+ }
230
+ for (const { extraNode, attachTarget, descendantIds } of reorgInfo) {
231
+ const index = moveChildNodes(attachTarget, extraNode, descendantIds);
232
+ attachTarget.children.splice(index, 0, extraNode);
228
233
  }
229
234
  }
230
235
  }
236
+ //# sourceMappingURL=TextSnapshot.js.map
@@ -104,8 +104,10 @@ export class WaitForHelper {
104
104
  });
105
105
  }
106
106
  async waitForEventsAfterAction(action, options) {
107
+ let dialogOpened = false;
107
108
  if (options?.handleDialog) {
108
109
  const dialogHandler = (dialog) => {
110
+ dialogOpened = true;
109
111
  if (options.handleDialog === 'dismiss') {
110
112
  void dialog.dismiss();
111
113
  }
@@ -142,6 +144,9 @@ export class WaitForHelper {
142
144
  }
143
145
  try {
144
146
  await navigationFinished;
147
+ if (dialogOpened) {
148
+ return;
149
+ }
145
150
  // Wait for stable dom after navigation so we execute in
146
151
  // the correct context
147
152
  await this.waitForStableDom();
@@ -168,3 +173,4 @@ export function getNetworkMultiplierFromString(condition) {
168
173
  }
169
174
  return 1;
170
175
  }
176
+ //# sourceMappingURL=WaitForHelper.js.map
@@ -23,3 +23,4 @@ if (cachePath) {
23
23
  // Ignore errors.
24
24
  }
25
25
  }
26
+ //# sourceMappingURL=check-latest-version.js.map