chrome-devtools-mcp 1.1.1 → 1.3.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 (109) hide show
  1. package/README.md +33 -4
  2. package/build/src/HeapSnapshotManager.js +30 -16
  3. package/build/src/McpContext.js +63 -9
  4. package/build/src/McpPage.js +9 -9
  5. package/build/src/McpResponse.js +176 -58
  6. package/build/src/PageCollector.js +8 -8
  7. package/build/src/ServiceWorkerCollector.js +171 -0
  8. package/build/src/TextSnapshot.js +1 -1
  9. package/build/src/ToolHandler.js +10 -4
  10. package/build/src/WaitForHelper.js +2 -2
  11. package/build/src/bin/chrome-devtools-cli-options.js +106 -4
  12. package/build/src/bin/chrome-devtools-mcp-cli-options.js +64 -5
  13. package/build/src/bin/chrome-devtools-mcp-main.js +5 -5
  14. package/build/src/browser.js +8 -4
  15. package/build/src/daemon/client.js +7 -7
  16. package/build/src/daemon/daemon.js +12 -12
  17. package/build/src/daemon/utils.js +2 -2
  18. package/build/src/{DevToolsConnectionAdapter.js → devtools/DevToolsConnectionAdapter.js} +1 -1
  19. package/build/src/{DevtoolsUtils.js → devtools/DevtoolsUtils.js} +79 -18
  20. package/build/src/devtools/McpHostBindingAdapter.js +165 -0
  21. package/build/src/formatters/ConsoleFormatter.js +1 -1
  22. package/build/src/formatters/HeapSnapshotFormatter.js +22 -0
  23. package/build/src/formatters/IssueFormatter.js +4 -4
  24. package/build/src/index.js +13 -1
  25. package/build/src/telemetry/ClearcutLogger.js +1 -1
  26. package/build/src/telemetry/WatchdogClient.js +4 -4
  27. package/build/src/telemetry/persistence.js +2 -2
  28. package/build/src/telemetry/watchdog/ClearcutSender.js +10 -10
  29. package/build/src/telemetry/watchdog/main.js +5 -5
  30. package/build/src/third_party/THIRD_PARTY_NOTICES +31 -1
  31. package/build/src/third_party/bundled-packages.json +2 -1
  32. package/build/src/third_party/devtools-formatter-worker.js +1 -1
  33. package/build/src/third_party/devtools-heap-snapshot-worker.js +161 -3
  34. package/build/src/third_party/index.js +7020 -5774
  35. package/build/src/third_party/issue-descriptions/emailVerificationRequestAccountsEmptyList.md +1 -0
  36. package/build/src/third_party/issue-descriptions/emailVerificationRequestAccountsHttpNotFound.md +1 -0
  37. package/build/src/third_party/issue-descriptions/emailVerificationRequestAccountsInvalidContentType.md +1 -0
  38. package/build/src/third_party/issue-descriptions/emailVerificationRequestAccountsInvalidResponse.md +1 -0
  39. package/build/src/third_party/issue-descriptions/emailVerificationRequestAccountsNoResponse.md +1 -0
  40. package/build/src/third_party/issue-descriptions/emailVerificationRequestDnsFetchFailed.md +1 -0
  41. package/build/src/third_party/issue-descriptions/emailVerificationRequestDnsInvalidRecord.md +1 -0
  42. package/build/src/third_party/issue-descriptions/emailVerificationRequestEmailVerificationWellKnownHttpNotFound.md +1 -0
  43. package/build/src/third_party/issue-descriptions/emailVerificationRequestEmailVerificationWellKnownInvalidContentType.md +1 -0
  44. package/build/src/third_party/issue-descriptions/emailVerificationRequestEmailVerificationWellKnownInvalidResponse.md +1 -0
  45. package/build/src/third_party/issue-descriptions/emailVerificationRequestEmailVerificationWellKnownNoResponse.md +1 -0
  46. package/build/src/third_party/issue-descriptions/emailVerificationRequestInvalidEmail.md +1 -0
  47. package/build/src/third_party/issue-descriptions/emailVerificationRequestJwksHttpNotFound.md +1 -0
  48. package/build/src/third_party/issue-descriptions/emailVerificationRequestJwksInvalidResponse.md +1 -0
  49. package/build/src/third_party/issue-descriptions/emailVerificationRequestKeyBindingSigningFailed.md +1 -0
  50. package/build/src/third_party/issue-descriptions/emailVerificationRequestRpOriginIsOpaque.md +1 -0
  51. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenHttpNotFound.md +1 -0
  52. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenInvalidContentType.md +1 -0
  53. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenInvalidResponse.md +1 -0
  54. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenInvalidSdJwt.md +1 -0
  55. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenMalformedSdJwt.md +1 -0
  56. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenNoResponse.md +1 -0
  57. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbInvalidAudience.md +1 -0
  58. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbInvalidIssuedAt.md +1 -0
  59. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbInvalidNonce.md +1 -0
  60. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbInvalidSdHash.md +1 -0
  61. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbInvalidTyp.md +1 -0
  62. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbMissingAud.md +1 -0
  63. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbMissingCnf.md +1 -0
  64. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbMissingIat.md +1 -0
  65. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbMissingNonce.md +1 -0
  66. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbMissingSdHash.md +1 -0
  67. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbSignatureFailed.md +1 -0
  68. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtInvalidEmail.md +1 -0
  69. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtInvalidEmailVerified.md +1 -0
  70. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtInvalidHolderKey.md +1 -0
  71. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtInvalidIssuedAt.md +1 -0
  72. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtInvalidIssuer.md +1 -0
  73. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtJwksMissingKeys.md +1 -0
  74. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtMissingCnf.md +1 -0
  75. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtMissingEmail.md +1 -0
  76. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtMissingIat.md +1 -0
  77. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtMissingIss.md +1 -0
  78. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtSignatureFailed.md +1 -0
  79. package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtUnsupportedHeaderAlg.md +1 -0
  80. package/build/src/third_party/issue-descriptions/emailVerificationRequestUserLoggedOut.md +1 -0
  81. package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownAccountsEndpointCrossOrigin.md +1 -0
  82. package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownHttpNotFound.md +1 -0
  83. package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownInvalidContentType.md +1 -0
  84. package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownInvalidResponse.md +1 -0
  85. package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownIssuanceEndpointCrossOrigin.md +1 -0
  86. package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownListEmpty.md +1 -0
  87. package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownMissingAccountsEndpoint.md +1 -0
  88. package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownMissingIssuanceEndpoint.md +1 -0
  89. package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownNoResponse.md +1 -0
  90. package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownUnsupportedSigningAlgorithm.md +1 -0
  91. package/build/src/tools/console.js +7 -0
  92. package/build/src/tools/emulation.js +1 -0
  93. package/build/src/tools/extensions.js +5 -2
  94. package/build/src/tools/input.js +11 -3
  95. package/build/src/tools/lighthouse.js +11 -6
  96. package/build/src/tools/memory.js +109 -10
  97. package/build/src/tools/network.js +2 -2
  98. package/build/src/tools/pages.js +13 -5
  99. package/build/src/tools/performance.js +8 -7
  100. package/build/src/tools/screencast.js +32 -10
  101. package/build/src/tools/screenshot.js +158 -76
  102. package/build/src/tools/script.js +1 -1
  103. package/build/src/tools/slim/tools.js +3 -0
  104. package/build/src/tools/snapshot.js +3 -2
  105. package/build/src/tools/thirdPartyDeveloper.js +12 -2
  106. package/build/src/tools/webmcp.js +2 -0
  107. package/build/src/trace-processing/parse.js +5 -5
  108. package/build/src/version.js +1 -1
  109. package/package.json +4 -3
package/README.md CHANGED
@@ -514,11 +514,15 @@ If you run into any issues, checkout our [troubleshooting guide](./docs/troubles
514
514
  - [`take_snapshot`](docs/tool-reference.md#take_snapshot)
515
515
  - [`screencast_start`](docs/tool-reference.md#screencast_start)
516
516
  - [`screencast_stop`](docs/tool-reference.md#screencast_stop)
517
- - **Memory** (5 tools)
517
+ - **Memory** (9 tools)
518
518
  - [`take_heapsnapshot`](docs/tool-reference.md#take_heapsnapshot)
519
+ - [`close_heapsnapshot`](docs/tool-reference.md#close_heapsnapshot)
519
520
  - [`get_heapsnapshot_class_nodes`](docs/tool-reference.md#get_heapsnapshot_class_nodes)
520
521
  - [`get_heapsnapshot_details`](docs/tool-reference.md#get_heapsnapshot_details)
522
+ - [`get_heapsnapshot_dominators`](docs/tool-reference.md#get_heapsnapshot_dominators)
523
+ - [`get_heapsnapshot_edges`](docs/tool-reference.md#get_heapsnapshot_edges)
521
524
  - [`get_heapsnapshot_retainers`](docs/tool-reference.md#get_heapsnapshot_retainers)
525
+ - [`get_heapsnapshot_retaining_paths`](docs/tool-reference.md#get_heapsnapshot_retaining_paths)
522
526
  - [`get_heapsnapshot_summary`](docs/tool-reference.md#get_heapsnapshot_summary)
523
527
  - **Extensions** (5 tools)
524
528
  - [`install_extension`](docs/tool-reference.md#install_extension)
@@ -608,8 +612,8 @@ The Chrome DevTools MCP server supports the following configuration option:
608
612
  Whether to enable coordinate-based tools such as click_at(x,y). Usually requires a computer-use model able to produce accurate coordinates by looking at screenshots.
609
613
  - **Type:** boolean
610
614
 
611
- - **`--experimentalMemory`/ `--experimental-memory`**
612
- Whether to enable experimental memory tools.
615
+ - **`--memoryDebugging`/ `--memory-debugging`, `-experimentalMemory`**
616
+ Whether to enable memory debugging tools.
613
617
  - **Type:** boolean
614
618
 
615
619
  - **`--experimentalStructuredContent`/ `--experimental-structured-content`**
@@ -629,13 +633,21 @@ The Chrome DevTools MCP server supports the following configuration option:
629
633
  - **Type:** string
630
634
 
631
635
  - **`--categoryExperimentalWebmcp`/ `--category-experimental-webmcp`**
632
- Set to true to enable debugging WebMCP tools. Requires Chrome 149+ with the following flags: `--enable-features=WebMCPTesting,DevToolsWebMCPSupport`
636
+ Set to true to enable debugging WebMCP tools. Requires Chrome 149+ with the following flags: `--enable-features=WebMCP,DevToolsWebMCPSupport`
633
637
  - **Type:** boolean
634
638
 
635
639
  - **`--chromeArg`/ `--chrome-arg`**
636
640
  Additional arguments for Chrome. Only applies when Chrome is launched by chrome-devtools-mcp.
637
641
  - **Type:** array
638
642
 
643
+ - **`--blockedUrlPattern`/ `--blocked-url-pattern`**
644
+ Restricts network access by blocking specified URL patterns (uses https://urlpattern.spec.whatwg.org/). Silently detaches from targets with blocked URLs upon connection, and blocks runtime requests (including navigations and subresources). Accepts an array of patterns.
645
+ - **Type:** array
646
+
647
+ - **`--allowedUrlPattern`/ `--allowed-url-pattern`**
648
+ Restricts network access by allowing only specified URL patterns (uses https://urlpattern.spec.whatwg.org/). Requires Chrome 149+. Silently detaches from targets with unallowed URLs upon connection, and blocks runtime requests (including navigations and subresources). Accepts an array of patterns.
649
+ - **Type:** array
650
+
639
651
  - **`--ignoreDefaultChromeArg`/ `--ignore-default-chrome-arg`**
640
652
  Explicitly disable default arguments for Chrome. Only applies when Chrome is launched by chrome-devtools-mcp.
641
653
  - **Type:** array
@@ -675,6 +687,23 @@ The Chrome DevTools MCP server supports the following configuration option:
675
687
  - **Type:** boolean
676
688
  - **Default:** `true`
677
689
 
690
+ - **`--screenshotFormat`/ `--screenshot-format`**
691
+ Override the default output format used by take_screenshot when the caller does not specify one. JPEG and WebP are ~3-5x smaller than PNG, which helps reduce context size in AI conversations. Unset preserves the existing default ("png").
692
+ - **Type:** string
693
+ - **Choices:** `jpeg`, `png`, `webp`
694
+
695
+ - **`--screenshotQuality`/ `--screenshot-quality`**
696
+ Override the default compression quality (0-100) used by take_screenshot for JPEG and WebP when the caller does not specify one. Lower values mean smaller files. Ignored for PNG. Unset preserves the Puppeteer default.
697
+ - **Type:** number
698
+
699
+ - **`--screenshotMaxWidth`/ `--screenshot-max-width`**
700
+ Maximum width in pixels for screenshots. If the captured image is wider, it is downscaled (preserving aspect ratio) before being returned. Reduces context size in AI conversations. Unset means no resize.
701
+ - **Type:** number
702
+
703
+ - **`--screenshotMaxHeight`/ `--screenshot-max-height`**
704
+ Maximum height in pixels for screenshots. If the captured image is taller, it is downscaled (preserving aspect ratio) before being returned. Can be combined with --screenshot-max-width; the smaller scale factor wins. Unset means no resize.
705
+ - **Type:** number
706
+
678
707
  - **`--slim`**
679
708
  Exposes a "slim" set of 3 tools covering navigation, script execution and screenshots only. Useful for basic browser tasks.
680
709
  - **Type:** boolean
@@ -5,7 +5,6 @@
5
5
  */
6
6
  import fsSync from 'node:fs';
7
7
  import path from 'node:path';
8
- import { isNodeLike } from './formatters/HeapSnapshotFormatter.js';
9
8
  import { DevTools } from './third_party/index.js';
10
9
  import { createIdGenerator, stableIdSymbol, } from './utils/id.js';
11
10
  export class HeapSnapshotManager {
@@ -67,28 +66,38 @@ export class HeapSnapshotManager {
67
66
  const provider = snapshot.createNodesProviderForClass(className, filter);
68
67
  return await provider.serializeItemsRange(0, Infinity);
69
68
  }
70
- async findNodeIndexById(filePath, nodeId) {
69
+ async getRetainers(filePath, nodeId) {
71
70
  const snapshot = await this.getSnapshot(filePath);
72
- const aggregates = await this.getAggregates(filePath);
73
- const filter = new DevTools.HeapSnapshotModel.HeapSnapshotModel.NodeFilter();
74
- for (const classKey of Object.keys(aggregates)) {
75
- const provider = snapshot.createNodesProviderForClass(classKey, filter);
76
- const range = await provider.serializeItemsRange(0, Infinity);
77
- for (const item of range.items) {
78
- if (isNodeLike(item) && item.id === nodeId) {
79
- return item.nodeIndex;
80
- }
81
- }
71
+ const nodeIndex = await snapshot.nodeIndexForId(nodeId);
72
+ if (nodeIndex === undefined) {
73
+ throw new Error(`Node with ID ${nodeId} not found`);
82
74
  }
83
- return undefined;
75
+ const provider = snapshot.createRetainingEdgesProvider(nodeIndex);
76
+ return await provider.serializeItemsRange(0, Infinity);
84
77
  }
85
- async getRetainers(filePath, nodeId) {
86
- const nodeIndex = await this.findNodeIndexById(filePath, nodeId);
78
+ async getRetainingPaths(filePath, nodeId, maxDepth, maxNodes, maxSiblings) {
79
+ const snapshot = await this.getSnapshot(filePath);
80
+ const nodeIndex = await snapshot.nodeIndexForId(nodeId);
81
+ if (nodeIndex === undefined) {
82
+ throw new Error(`Node with ID ${nodeId} not found`);
83
+ }
84
+ return await snapshot.getRetainingPaths(nodeIndex, maxDepth, maxNodes, maxSiblings);
85
+ }
86
+ async getDominatorsOf(filePath, nodeId) {
87
+ const snapshot = await this.getSnapshot(filePath);
88
+ const nodeIndex = await snapshot.nodeIndexForId(nodeId);
87
89
  if (nodeIndex === undefined) {
88
90
  throw new Error(`Node with ID ${nodeId} not found`);
89
91
  }
92
+ return await snapshot.getDominatorsOf(nodeIndex);
93
+ }
94
+ async getEdges(filePath, nodeId) {
90
95
  const snapshot = await this.getSnapshot(filePath);
91
- const provider = snapshot.createRetainingEdgesProvider(nodeIndex);
96
+ const nodeIndex = await snapshot.nodeIndexForId(nodeId);
97
+ if (nodeIndex === undefined) {
98
+ throw new Error(`Node with ID ${nodeId} not found`);
99
+ }
100
+ const provider = snapshot.createEdgesProvider(nodeIndex);
92
101
  return await provider.serializeItemsRange(0, Infinity);
93
102
  }
94
103
  #getCachedSnapshot(filePath) {
@@ -122,13 +131,18 @@ export class HeapSnapshotManager {
122
131
  const snapshot = await snapshotPromise;
123
132
  return { snapshot, worker: workerProxy };
124
133
  }
134
+ hasSnapshots() {
135
+ return this.#snapshots.size > 0;
136
+ }
125
137
  dispose(filePath) {
126
138
  const absolutePath = path.resolve(filePath);
127
139
  const cached = this.#snapshots.get(absolutePath);
128
140
  if (cached) {
129
141
  cached.worker.dispose();
130
142
  this.#snapshots.delete(absolutePath);
143
+ return true;
131
144
  }
145
+ return false;
132
146
  }
133
147
  }
134
148
  //# sourceMappingURL=HeapSnapshotManager.js.map
@@ -8,10 +8,11 @@ import fsPromises from 'node:fs/promises';
8
8
  import os from 'node:os';
9
9
  import path from 'node:path';
10
10
  import { fileURLToPath, pathToFileURL } from 'node:url';
11
- import { UniverseManager } from './DevtoolsUtils.js';
11
+ import { overrideDevToolsGlobals, UniverseManager, } from './devtools/DevtoolsUtils.js';
12
12
  import { HeapSnapshotManager } from './HeapSnapshotManager.js';
13
13
  import { McpPage } from './McpPage.js';
14
14
  import { NetworkCollector, ConsoleCollector, } from './PageCollector.js';
15
+ import { ServiceWorkerConsoleCollector } from './ServiceWorkerCollector.js';
15
16
  import { Locator, PredefinedNetworkConditions, } from './third_party/index.js';
16
17
  import { listPages } from './tools/pages.js';
17
18
  import { CLOSE_PAGE_ERROR } from './tools/ToolDefinition.js';
@@ -33,6 +34,7 @@ export class McpContext {
33
34
  #networkCollector;
34
35
  #consoleCollector;
35
36
  #devtoolsUniverseManager;
37
+ #serviceWorkerConsoleCollector;
36
38
  #isRunningTrace = false;
37
39
  #screenRecorderData = null;
38
40
  #nextPageId = 1;
@@ -45,6 +47,11 @@ export class McpContext {
45
47
  #heapSnapshotManager = new HeapSnapshotManager();
46
48
  #roots = undefined;
47
49
  constructor(browser, logger, options, locatorClass) {
50
+ overrideDevToolsGlobals({
51
+ loadResource: (url) => {
52
+ return this.loadResource(url);
53
+ },
54
+ });
48
55
  this.browser = browser;
49
56
  this.logger = logger;
50
57
  this.#locatorClass = locatorClass;
@@ -63,19 +70,22 @@ export class McpContext {
63
70
  },
64
71
  };
65
72
  });
73
+ this.#serviceWorkerConsoleCollector = new ServiceWorkerConsoleCollector(this.browser);
66
74
  this.#devtoolsUniverseManager = new UniverseManager(this.browser);
67
75
  }
68
76
  async #init() {
69
77
  const pages = await this.createPagesSnapshot();
70
- await this.createExtensionServiceWorkersSnapshot();
78
+ const workers = await this.createExtensionServiceWorkersSnapshot();
71
79
  await this.#networkCollector.init(pages);
72
80
  await this.#consoleCollector.init(pages);
73
81
  await this.#devtoolsUniverseManager.init(pages);
82
+ await this.#serviceWorkerConsoleCollector.init(workers);
74
83
  }
75
84
  dispose() {
76
85
  this.#networkCollector.dispose();
77
86
  this.#consoleCollector.dispose();
78
87
  this.#devtoolsUniverseManager.dispose();
88
+ this.#serviceWorkerConsoleCollector.dispose();
79
89
  for (const mcpPage of this.#mcpPages.values()) {
80
90
  mcpPage.dispose();
81
91
  }
@@ -148,7 +158,7 @@ export class McpContext {
148
158
  }
149
159
  resolveCdpRequestId(page, cdpRequestId) {
150
160
  if (!cdpRequestId) {
151
- this.logger('no network request');
161
+ this.logger?.('no network request');
152
162
  return;
153
163
  }
154
164
  const request = this.#networkCollector.find(page.pptrPage, request => {
@@ -156,7 +166,7 @@ export class McpContext {
156
166
  return request.id === cdpRequestId;
157
167
  });
158
168
  if (!request) {
159
- this.logger('no network request for ' + cdpRequestId);
169
+ this.logger?.('no network request for ' + cdpRequestId);
160
170
  return;
161
171
  }
162
172
  return this.#networkCollector.getIdForResource(request);
@@ -217,7 +227,13 @@ export class McpContext {
217
227
  const page = targetPage ?? this.getSelectedPptrPage();
218
228
  const mcpPage = this.#getMcpPage(page);
219
229
  const newSettings = { ...mcpPage.emulationSettings };
220
- if (!options.networkConditions) {
230
+ // Skip network emulation if blocklist/allowlist is configured, as it conflicts with blocking rules in Puppeteer.
231
+ if (this.#options.hasNetworkBlockOrAllowlist) {
232
+ if (options.networkConditions !== undefined) {
233
+ throw new Error('Network throttling is not supported when network blocking (allowlist/blocklist) is configured.');
234
+ }
235
+ }
236
+ else if (!options.networkConditions) {
221
237
  await page.emulateNetworkConditions(null);
222
238
  delete newSettings.networkConditions;
223
239
  }
@@ -412,6 +428,9 @@ export class McpContext {
412
428
  });
413
429
  return this.#extensionServiceWorkers;
414
430
  }
431
+ getServiceWorkerConsoleData(extensionId) {
432
+ return this.#serviceWorkerConsoleCollector.getData(extensionId);
433
+ }
415
434
  async createPagesSnapshot() {
416
435
  const { pages: allPages, isolatedContextNames } = await this.#getAllPages();
417
436
  for (const page of allPages) {
@@ -421,7 +440,7 @@ export class McpContext {
421
440
  this.#mcpPages.set(page, mcpPage);
422
441
  // We emulate a focused page for all pages to support multi-agent workflows.
423
442
  void page.emulateFocusedPage(true).catch(error => {
424
- this.logger('Error turning on focused page emulation', error);
443
+ this.logger?.('Error turning on focused page emulation', error);
425
444
  });
426
445
  }
427
446
  mcpPage.isolatedContextName = isolatedContextNames.get(page);
@@ -467,7 +486,7 @@ export class McpContext {
467
486
  this.#extensionPages.set(target, page);
468
487
  }
469
488
  catch (e) {
470
- this.logger('Failed to get page for extension target', e);
489
+ this.logger?.('Failed to get page for extension target', e);
471
490
  }
472
491
  }
473
492
  }
@@ -502,7 +521,7 @@ export class McpContext {
502
521
  return { pages: allPages, isolatedContextNames };
503
522
  }
504
523
  async detectOpenDevToolsWindows() {
505
- this.logger('Detecting open DevTools windows');
524
+ this.logger?.('Detecting open DevTools windows');
506
525
  const { pages } = await this.#getAllPages();
507
526
  await Promise.all(pages.map(async (page) => {
508
527
  const mcpPage = this.#mcpPages.get(page);
@@ -557,7 +576,7 @@ export class McpContext {
557
576
  return { filename: filePath };
558
577
  }
559
578
  catch (err) {
560
- this.logger(err);
579
+ this.logger?.(err);
561
580
  throw new Error('Could not save a file', { cause: err });
562
581
  }
563
582
  }
@@ -639,5 +658,40 @@ export class McpContext {
639
658
  async getHeapSnapshotRetainers(filePath, nodeId) {
640
659
  return await this.#heapSnapshotManager.getRetainers(filePath, nodeId);
641
660
  }
661
+ async closeHeapSnapshot(filePath) {
662
+ return this.#heapSnapshotManager.dispose(filePath);
663
+ }
664
+ hasHeapSnapshots() {
665
+ return this.#heapSnapshotManager.hasSnapshots();
666
+ }
667
+ async getHeapSnapshotRetainingPaths(filePath, nodeId, maxDepth, maxNodes, maxSiblings) {
668
+ return await this.#heapSnapshotManager.getRetainingPaths(filePath, nodeId, maxDepth, maxNodes, maxSiblings);
669
+ }
670
+ async getHeapSnapshotDominators(filePath, nodeId) {
671
+ return await this.#heapSnapshotManager.getDominatorsOf(filePath, nodeId);
672
+ }
673
+ async loadResource(path) {
674
+ const url = new URL(path);
675
+ switch (url.protocol) {
676
+ case 'https:':
677
+ case 'http:': {
678
+ // TODO: Verify allow/block list
679
+ const response = await fetch(url);
680
+ if (!response.ok) {
681
+ throw new Error(`Failed to load resource: ${url}`);
682
+ }
683
+ return response.text();
684
+ }
685
+ case 'file:': {
686
+ await this.validatePath(fileURLToPath(url));
687
+ return await fsPromises.readFile(url, 'utf-8');
688
+ }
689
+ default:
690
+ throw new Error(`Unsupported protocol for: ${url}`);
691
+ }
692
+ }
693
+ async getHeapSnapshotEdges(filePath, nodeId) {
694
+ return await this.#heapSnapshotManager.getEdges(filePath, nodeId);
695
+ }
642
696
  }
643
697
  //# sourceMappingURL=McpContext.js.map
@@ -30,7 +30,7 @@ export class McpPage {
30
30
  // Dialog
31
31
  #dialog;
32
32
  #dialogHandler;
33
- thirdPartyDeveloperTools;
33
+ thirdPartyDeveloperTools = [];
34
34
  constructor(page, id) {
35
35
  this.pptrPage = page;
36
36
  this.id = id;
@@ -194,18 +194,18 @@ export class McpPage {
194
194
  for (const handle of oldHandles) {
195
195
  await handle
196
196
  .dispose()
197
- .catch(e => logger('Failed to dispose old handle', e));
197
+ .catch(e => logger?.('Failed to dispose old handle', e));
198
198
  }
199
199
  }
200
200
  const cdpElementIds = await Promise.all(elementHandles.map(async (elementHandle, index) => {
201
201
  const backendNodeId = await elementHandle.backendNodeId();
202
202
  if (!backendNodeId) {
203
- logger(`No backendNodeId for stashed DOM element with index ${index}`);
203
+ logger?.(`No backendNodeId for stashed DOM element with index ${index}`);
204
204
  return `stashed-${index}`;
205
205
  }
206
206
  const cdpElementId = this.resolveCdpElementId(backendNodeId);
207
207
  if (!cdpElementId) {
208
- logger(`Could not get cdpElementId for backend node ${backendNodeId}`);
208
+ logger?.(`Could not get cdpElementId for backend node ${backendNodeId}`);
209
209
  return `stashed-${index}`;
210
210
  }
211
211
  return cdpElementId;
@@ -263,12 +263,12 @@ export class McpPage {
263
263
  }
264
264
  resolveCdpElementId(cdpBackendNodeId) {
265
265
  if (!cdpBackendNodeId) {
266
- logger('no cdpBackendNodeId');
266
+ logger?.('no cdpBackendNodeId');
267
267
  return;
268
268
  }
269
269
  const snapshot = this.textSnapshot;
270
270
  if (!snapshot) {
271
- logger('no text snapshot');
271
+ logger?.('no text snapshot');
272
272
  return;
273
273
  }
274
274
  // TODO: index by backendNodeId instead.
@@ -286,10 +286,10 @@ export class McpPage {
286
286
  }
287
287
  async getDevToolsData() {
288
288
  try {
289
- logger('Getting DevTools UI data');
289
+ logger?.('Getting DevTools UI data');
290
290
  const devtoolsPage = this.devToolsPage;
291
291
  if (!devtoolsPage) {
292
- logger('No DevTools page detected');
292
+ logger?.('No DevTools page detected');
293
293
  return {};
294
294
  }
295
295
  const { cdpRequestId, cdpBackendNodeId } = await devtoolsPage.evaluate(async () => {
@@ -307,7 +307,7 @@ export class McpPage {
307
307
  return { cdpBackendNodeId, cdpRequestId };
308
308
  }
309
309
  catch (err) {
310
- logger('error getting devtools data', err);
310
+ logger?.('error getting devtools data', err);
311
311
  }
312
312
  return {};
313
313
  }