chrome-devtools-mcp 0.20.3 → 0.22.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 (55) hide show
  1. package/README.md +97 -20
  2. package/build/src/HeapSnapshotManager.js +94 -0
  3. package/build/src/McpContext.js +26 -49
  4. package/build/src/McpPage.js +16 -0
  5. package/build/src/McpResponse.js +220 -12
  6. package/build/src/PageCollector.js +14 -28
  7. package/build/src/WaitForHelper.js +31 -0
  8. package/build/src/bin/check-latest-version.js +25 -0
  9. package/build/src/bin/chrome-devtools-mcp-cli-options.js +28 -9
  10. package/build/src/bin/chrome-devtools-mcp-main.js +2 -0
  11. package/build/src/bin/chrome-devtools-mcp.js +1 -0
  12. package/build/src/bin/chrome-devtools.js +9 -3
  13. package/build/src/bin/cliDefinitions.js +15 -9
  14. package/build/src/daemon/client.js +1 -1
  15. package/build/src/daemon/daemon.js +2 -6
  16. package/build/src/daemon/utils.js +1 -0
  17. package/build/src/formatters/HeapSnapshotFormatter.js +38 -0
  18. package/build/src/formatters/NetworkFormatter.js +24 -7
  19. package/build/src/index.js +22 -1
  20. package/build/src/telemetry/ClearcutLogger.js +145 -6
  21. package/build/src/telemetry/flagUtils.js +46 -4
  22. package/build/src/telemetry/toolMetricsUtils.js +88 -0
  23. package/build/src/telemetry/types.js +5 -0
  24. package/build/src/telemetry/watchdog/ClearcutSender.js +4 -3
  25. package/build/src/third_party/THIRD_PARTY_NOTICES +1400 -483
  26. package/build/src/third_party/bundled-packages.json +6 -5
  27. package/build/src/third_party/devtools-formatter-worker.js +61 -66
  28. package/build/src/third_party/devtools-heap-snapshot-worker.js +9690 -0
  29. package/build/src/third_party/index.js +61622 -52803
  30. package/build/src/third_party/issue-descriptions/sharedDictionaryUseErrorCrossOriginNoCorsRequest.md +1 -0
  31. package/build/src/third_party/lighthouse-devtools-mcp-bundle.js +10589 -4647
  32. package/build/src/tools/categories.js +5 -0
  33. package/build/src/tools/console.js +42 -39
  34. package/build/src/tools/emulation.js +1 -1
  35. package/build/src/tools/extensions.js +5 -11
  36. package/build/src/tools/inPage.js +105 -0
  37. package/build/src/tools/input.js +18 -16
  38. package/build/src/tools/lighthouse.js +3 -3
  39. package/build/src/tools/memory.js +50 -5
  40. package/build/src/tools/network.js +2 -2
  41. package/build/src/tools/pages.js +14 -6
  42. package/build/src/tools/performance.js +1 -1
  43. package/build/src/tools/screencast.js +2 -1
  44. package/build/src/tools/screenshot.js +3 -3
  45. package/build/src/tools/script.js +22 -16
  46. package/build/src/tools/tools.js +4 -0
  47. package/build/src/tools/webmcp.js +63 -0
  48. package/build/src/utils/check-for-updates.js +73 -0
  49. package/build/src/utils/files.js +4 -0
  50. package/build/src/utils/id.js +15 -0
  51. package/build/src/version.js +1 -1
  52. package/package.json +13 -9
  53. package/build/src/third_party/issue-descriptions/sharedDictionaryUseErrorNoCorpCrossOriginNoCorsRequest.md +0 -3
  54. package/build/src/third_party/issue-descriptions/sharedDictionaryWriteErrorNoCorpCossOriginNoCorsRequest.md +0 -3
  55. package/build/src/utils/ExtensionRegistry.js +0 -35
package/README.md CHANGED
@@ -1,11 +1,12 @@
1
- # Chrome DevTools MCP
1
+ # Chrome DevTools for Agents
2
2
 
3
3
  [![npm chrome-devtools-mcp package](https://img.shields.io/npm/v/chrome-devtools-mcp.svg)](https://npmjs.org/package/chrome-devtools-mcp)
4
4
 
5
- `chrome-devtools-mcp` lets your coding agent (such as Gemini, Claude, Cursor or Copilot)
5
+ Chrome DevTools for Agents (`chrome-devtools-mcp`) lets your coding agent (such as Gemini, Claude, Cursor or Copilot)
6
6
  control and inspect a live Chrome browser. It acts as a Model-Context-Protocol
7
7
  (MCP) server, giving your AI coding assistant access to the full power of
8
8
  Chrome DevTools for reliable automation, in-depth debugging, and performance analysis.
9
+ A [CLI](docs/cli.md) is also provided for use without MCP.
9
10
 
10
11
  ## [Tool reference](./docs/tool-reference.md) | [Changelog](./CHANGELOG.md) | [Contributing](./CONTRIBUTING.md) | [Troubleshooting](./docs/troubleshooting.md) | [Design Principles](./docs/design-principles.md)
11
12
 
@@ -27,6 +28,10 @@ allowing them to inspect, debug, and modify any data in the browser or DevTools.
27
28
  Avoid sharing sensitive or personal information that you don't want to share with
28
29
  MCP clients.
29
30
 
31
+ `chrome-devtools-mcp` officially supports Google Chrome and [Chrome for Testing](https://developer.chrome.com/blog/chrome-for-testing/) only.
32
+ Other Chromium-based browsers may work, but this is not guaranteed, and you may encounter unexpected behavior. Use at your own discretion.
33
+ We are committed to providing fixes and support for the latest version of [Extended Stable Chrome](https://chromiumdash.appspot.com/schedule).
34
+
30
35
  Performance tools may send trace URLs to the Google CrUX API to fetch real-user
31
36
  experience data. This helps provide a holistic performance picture by
32
37
  presenting field data alongside lab data. This data is collected by the [Chrome
@@ -47,13 +52,18 @@ Google handles this data in accordance with the [Google Privacy Policy](https://
47
52
 
48
53
  Google's collection of usage statistics for Chrome DevTools MCP is independent from the Chrome browser's usage statistics. Opting out of Chrome metrics does not automatically opt you out of this tool, and vice-versa.
49
54
 
50
- Collection is disabled if CHROME_DEVTOOLS_MCP_NO_USAGE_STATISTICS or CI env variables are set.
55
+ Collection is disabled if `CHROME_DEVTOOLS_MCP_NO_USAGE_STATISTICS` or `CI` env variables are set.
56
+
57
+ ## Update checks
58
+
59
+ By default, the server periodically checks the npm registry for updates and logs a notification when a newer version is available.
60
+ You can disable these update checks by setting the `CHROME_DEVTOOLS_MCP_NO_UPDATE_CHECKS` environment variable.
51
61
 
52
62
  ## Requirements
53
63
 
54
64
  - [Node.js](https://nodejs.org/) v20.19 or a newer [latest maintenance LTS](https://github.com/nodejs/Release#release-schedule) version.
55
65
  - [Chrome](https://www.google.com/chrome/) current stable version or newer.
56
- - [npm](https://www.npmjs.com/).
66
+ - [npm](https://www.npmjs.com/)
57
67
 
58
68
  ## Getting started
59
69
 
@@ -70,7 +80,7 @@ Add the following config to your MCP client:
70
80
  }
71
81
  ```
72
82
 
73
- > [!NOTE]
83
+ > [!NOTE]
74
84
  > Using `chrome-devtools-mcp@latest` ensures that your MCP client will always use the latest version of the Chrome DevTools MCP server.
75
85
 
76
86
  If you are interested in doing only basic browser tasks, use the `--slim` mode:
@@ -139,7 +149,7 @@ claude mcp add chrome-devtools --scope user npx chrome-devtools-mcp@latest
139
149
 
140
150
  **Install as a Plugin (MCP + Skills)**
141
151
 
142
- > [!NOTE]
152
+ > [!NOTE]
143
153
  > If you already had Chrome DevTools MCP installed previously for Claude Code, make sure to remove it first from your installation and configuration files.
144
154
 
145
155
  To install Chrome DevTools MCP with skills, add the marketplace registry in Claude Code:
@@ -194,6 +204,17 @@ startup_timeout_ms = 20_000
194
204
 
195
205
  </details>
196
206
 
207
+ <details>
208
+ <summary>Command Code</summary>
209
+
210
+ Use the Command Code CLI to add the Chrome DevTools MCP server (<a href="https://commandcode.ai/docs/mcp">MCP guide</a>):
211
+
212
+ ```bash
213
+ cmd mcp add chrome-devtools --scope user npx chrome-devtools-mcp@latest
214
+ ```
215
+
216
+ </details>
217
+
197
218
  <details>
198
219
  <summary>Copilot CLI</summary>
199
220
 
@@ -220,6 +241,22 @@ Configure the following fields and press `CTRL+S` to save the configuration:
220
241
  <details>
221
242
  <summary>Copilot / VS Code</summary>
222
243
 
244
+ **Install as a Plugin (Recommended)**
245
+
246
+ The easiest way to get up and running is to install `chrome-devtools-mcp` as an agent plugin.
247
+ This bundles the **MCP server** and all **skills** together, so your agent gets both the tools
248
+ and the expert guidance it needs to use them effectively.
249
+
250
+ 1. Open the **Command Palette** (`Cmd+Shift+P` on macOS or `Ctrl+Shift+P` on Windows/Linux).
251
+ 2. Search for and run the **Chat: Install Plugin From Source** command.
252
+ 3. Paste in our repository URL: `https://github.com/ChromeDevTools/chrome-devtools-mcp`
253
+
254
+ That's it! Your agent is now supercharged with Chrome DevTools capabilities.
255
+
256
+ ---
257
+
258
+ **Install as an MCP Server (MCP only)**
259
+
223
260
  **Click the button to install:**
224
261
 
225
262
  [<img src="https://img.shields.io/badge/VS_Code-VS_Code?style=flat-square&label=Install%20Server&color=0098FF" alt="Install in VS Code">](https://vscode.dev/redirect/mcp/install?name=io.github.ChromeDevTools%2Fchrome-devtools-mcp&config=%7B%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22-y%22%2C%22chrome-devtools-mcp%22%5D%2C%22env%22%3A%7B%7D%7D)
@@ -228,8 +265,7 @@ Configure the following fields and press `CTRL+S` to save the configuration:
228
265
 
229
266
  **Or install manually:**
230
267
 
231
- Follow the MCP install <a href="https://code.visualstudio.com/docs/copilot/chat/mcp-servers#_add-an-mcp-server">guide</a>,
232
- with the standard config from above. You can also install the Chrome DevTools MCP server using the VS Code CLI:
268
+ Follow the VS Code [MCP configuration guide](https://code.visualstudio.com/docs/copilot/chat/mcp-servers#_add-an-mcp-server) using the standard config from above, or use the CLI:
233
269
 
234
270
  For macOS and Linux:
235
271
 
@@ -338,6 +374,21 @@ Once connected, the Chrome DevTools MCP tools will be available in StudioAssist.
338
374
 
339
375
  </details>
340
376
 
377
+ <details>
378
+ <summary>Mistral Vibe</summary>
379
+
380
+ Add in ~/.vibe/config.toml:
381
+
382
+ ```toml
383
+ [[mcp_servers]]
384
+ name = "chrome-devtools"
385
+ transport = "stdio"
386
+ command = "npx"
387
+ args = ["chrome-devtools-mcp@latest"]
388
+ ```
389
+
390
+ </details>
391
+
341
392
  <details>
342
393
  <summary>OpenCode</summary>
343
394
 
@@ -387,10 +438,11 @@ qodercli mcp add -s user chrome-devtools -- npx chrome-devtools-mcp@latest
387
438
 
388
439
  <details>
389
440
  <summary>Visual Studio</summary>
390
-
391
- **Click the button to install:**
392
-
393
- [<img src="https://img.shields.io/badge/Visual_Studio-Install-C16FDE?logo=visualstudio&logoColor=white" alt="Install in Visual Studio">](https://vs-open.link/mcp-install?%7B%22name%22%3A%22chrome-devtools%22%2C%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22chrome-devtools-mcp%40latest%22%5D%7D)
441
+
442
+ **Click the button to install:**
443
+
444
+ [<img src="https://img.shields.io/badge/Visual_Studio-Install-C16FDE?logo=visualstudio&logoColor=white" alt="Install in Visual Studio">](https://vs-open.link/mcp-install?%7B%22name%22%3A%22chrome-devtools%22%2C%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22chrome-devtools-mcp%40latest%22%5D%7D)
445
+
394
446
  </details>
395
447
 
396
448
  <details>
@@ -416,7 +468,7 @@ Check the performance of https://developers.chrome.com
416
468
 
417
469
  Your MCP client should open the browser and record a performance trace.
418
470
 
419
- > [!NOTE]
471
+ > [!NOTE]
420
472
  > The MCP server will start the browser automatically once the MCP client uses a tool that requires a running browser instance. Connecting to the Chrome DevTools MCP server on its own will not automatically start the browser.
421
473
 
422
474
  ## Tools
@@ -445,11 +497,10 @@ If you run into any issues, checkout our [troubleshooting guide](./docs/troubles
445
497
  - **Emulation** (2 tools)
446
498
  - [`emulate`](docs/tool-reference.md#emulate)
447
499
  - [`resize_page`](docs/tool-reference.md#resize_page)
448
- - **Performance** (4 tools)
500
+ - **Performance** (3 tools)
449
501
  - [`performance_analyze_insight`](docs/tool-reference.md#performance_analyze_insight)
450
502
  - [`performance_start_trace`](docs/tool-reference.md#performance_start_trace)
451
503
  - [`performance_stop_trace`](docs/tool-reference.md#performance_stop_trace)
452
- - [`take_memory_snapshot`](docs/tool-reference.md#take_memory_snapshot)
453
504
  - **Network** (2 tools)
454
505
  - [`get_network_request`](docs/tool-reference.md#get_network_request)
455
506
  - [`list_network_requests`](docs/tool-reference.md#list_network_requests)
@@ -460,6 +511,14 @@ If you run into any issues, checkout our [troubleshooting guide](./docs/troubles
460
511
  - [`list_console_messages`](docs/tool-reference.md#list_console_messages)
461
512
  - [`take_screenshot`](docs/tool-reference.md#take_screenshot)
462
513
  - [`take_snapshot`](docs/tool-reference.md#take_snapshot)
514
+ - **Extensions** (5 tools)
515
+ - [`install_extension`](docs/tool-reference.md#install_extension)
516
+ - [`list_extensions`](docs/tool-reference.md#list_extensions)
517
+ - [`reload_extension`](docs/tool-reference.md#reload_extension)
518
+ - [`trigger_extension_action`](docs/tool-reference.md#trigger_extension_action)
519
+ - [`uninstall_extension`](docs/tool-reference.md#uninstall_extension)
520
+ - **Memory** (1 tools)
521
+ - [`take_memory_snapshot`](docs/tool-reference.md#take_memory_snapshot)
463
522
 
464
523
  <!-- END AUTO GENERATED TOOLS -->
465
524
 
@@ -470,7 +529,7 @@ The Chrome DevTools MCP server supports the following configuration option:
470
529
  <!-- BEGIN AUTO GENERATED OPTIONS -->
471
530
 
472
531
  - **`--autoConnect`/ `--auto-connect`**
473
- If specified, automatically connects to a browser (Chrome 144+) running locally from the user data directory identified by the channel param (default channel is stable). Requires the remoted debugging server to be started in the Chrome instance via chrome://inspect/#remote-debugging.
532
+ If specified, automatically connects to a browser (Chrome 144+) running locally from the user data directory identified by the channel param (default channel is stable). Requires the remote debugging server to be started in the Chrome instance via chrome://inspect/#remote-debugging.
474
533
  - **Type:** boolean
475
534
  - **Default:** `false`
476
535
 
@@ -506,7 +565,7 @@ The Chrome DevTools MCP server supports the following configuration option:
506
565
  - **`--channel`**
507
566
  Specify a different Chrome channel that should be used. The default is the stable channel version.
508
567
  - **Type:** string
509
- - **Choices:** `stable`, `canary`, `beta`, `dev`
568
+ - **Choices:** `canary`, `dev`, `beta`, `stable`
510
569
 
511
570
  - **`--logFile`/ `--log-file`**
512
571
  Path to a file to write debug logs to. Set the env variable `DEBUG` to `*` to enable verbose logs. Useful for submitting bug reports.
@@ -524,10 +583,18 @@ The Chrome DevTools MCP server supports the following configuration option:
524
583
  If enabled, ignores errors relative to self-signed and expired certificates. Use with caution.
525
584
  - **Type:** boolean
526
585
 
586
+ - **`--experimentalVision`/ `--experimental-vision`**
587
+ 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.
588
+ - **Type:** boolean
589
+
527
590
  - **`--experimentalScreencast`/ `--experimental-screencast`**
528
591
  Exposes experimental screencast tools (requires ffmpeg). Install ffmpeg https://www.ffmpeg.org/download.html and ensure it is available in the MCP server PATH.
529
592
  - **Type:** boolean
530
593
 
594
+ - **`--experimentalWebmcp`/ `--experimental-webmcp`**
595
+ Set to true to enable debugging WebMCP tools. Requires Chrome 149+ with the following flags: `--enable-features=WebMCPTesting,DevToolsWebMCPSupport`
596
+ - **Type:** boolean
597
+
531
598
  - **`--chromeArg`/ `--chrome-arg`**
532
599
  Additional arguments for Chrome. Only applies when Chrome is launched by chrome-devtools-mcp.
533
600
  - **Type:** array
@@ -551,13 +618,18 @@ The Chrome DevTools MCP server supports the following configuration option:
551
618
  - **Type:** boolean
552
619
  - **Default:** `true`
553
620
 
621
+ - **`--categoryExtensions`/ `--category-extensions`**
622
+ Set to true to include tools related to extensions. Note: This feature is currently only supported with a pipe connection. autoConnect, browserUrl, and wsEndpoint are not supported with this feature until 149 will be released.
623
+ - **Type:** boolean
624
+ - **Default:** `false`
625
+
554
626
  - **`--performanceCrux`/ `--performance-crux`**
555
627
  Set to false to disable sending URLs from performance traces to CrUX API to get field performance data.
556
628
  - **Type:** boolean
557
629
  - **Default:** `true`
558
630
 
559
631
  - **`--usageStatistics`/ `--usage-statistics`**
560
- Set to false to opt-out of usage statistics collection. Google collects usage data to improve the tool, handled under the Google Privacy Policy (https://policies.google.com/privacy). This is independent from Chrome browser metrics. Disabled if CHROME_DEVTOOLS_MCP_NO_USAGE_STATISTICS or CI env variables are set.
632
+ Set to false to opt-out of usage statistics collection. Google collects usage data to improve the tool, handled under the Google Privacy Policy (https://policies.google.com/privacy). This is independent from Chrome browser metrics. Disabled if `CHROME_DEVTOOLS_MCP_NO_USAGE_STATISTICS` or `CI` env variables are set.
561
633
  - **Type:** boolean
562
634
  - **Default:** `true`
563
635
 
@@ -565,6 +637,11 @@ The Chrome DevTools MCP server supports the following configuration option:
565
637
  Exposes a "slim" set of 3 tools covering navigation, script execution and screenshots only. Useful for basic browser tasks.
566
638
  - **Type:** boolean
567
639
 
640
+ - **`--redactNetworkHeaders`/ `--redact-network-headers`**
641
+ If true, redacts some of the network headers considered senstive before returning to the client.
642
+ - **Type:** boolean
643
+ - **Default:** `false`
644
+
568
645
  <!-- END AUTO GENERATED OPTIONS -->
569
646
 
570
647
  Pass them via the `args` property in the JSON configuration. For example:
@@ -671,7 +748,7 @@ Make sure your browser is running. Open gemini-cli and run the following prompt:
671
748
  Check the performance of https://developers.chrome.com
672
749
  ```
673
750
 
674
- > [!NOTE]
751
+ > [!NOTE]
675
752
  > The <code>autoConnect</code> option requires the user to start Chrome. If the user has multiple active profiles, the MCP server will connect to the default profile (as determined by Chrome). The MCP server has access to all open windows for the selected profile.
676
753
 
677
754
  The Chrome DevTools MCP server will try to connect to your running Chrome
@@ -707,7 +784,7 @@ Add the `--browser-url` option to your MCP client configuration. The value of th
707
784
 
708
785
  **Step 2: Start the Chrome browser**
709
786
 
710
- > [!WARNING]
787
+ > [!WARNING]
711
788
  > Enabling the remote debugging port opens up a debugging port on the running browser instance. Any application on your machine can connect to this port and control the browser. Make sure that you are not browsing any sensitive websites while the debugging port is open.
712
789
 
713
790
  Start the Chrome browser with the remote debugging port enabled. Make sure to close any running Chrome instances before starting a new one with the debugging port enabled. The port number you choose must be the same as the one you specified in the `--browser-url` option in your MCP client configuration.
@@ -0,0 +1,94 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ import fsSync from 'node:fs';
7
+ import path from 'node:path';
8
+ import { DevTools } from './third_party/index.js';
9
+ import { createIdGenerator, stableIdSymbol, } from './utils/id.js';
10
+ export class HeapSnapshotManager {
11
+ #snapshots = new Map();
12
+ async getSnapshot(filePath) {
13
+ const absolutePath = path.resolve(filePath);
14
+ const cached = this.#snapshots.get(absolutePath);
15
+ if (cached) {
16
+ return cached.snapshot;
17
+ }
18
+ const { snapshot, worker } = await this.#loadSnapshot(absolutePath);
19
+ this.#snapshots.set(absolutePath, {
20
+ snapshot,
21
+ worker,
22
+ uidToClassKey: new Map(),
23
+ classKeyToUid: new Map(),
24
+ idGenerator: createIdGenerator(),
25
+ });
26
+ return snapshot;
27
+ }
28
+ async getAggregates(filePath) {
29
+ const snapshot = await this.getSnapshot(filePath);
30
+ const filter = new DevTools.HeapSnapshotModel.HeapSnapshotModel.NodeFilter();
31
+ const aggregates = await snapshot.aggregatesWithFilter(filter);
32
+ for (const key of Object.keys(aggregates)) {
33
+ const uid = await this.getOrCreateUidForClassKey(filePath, key);
34
+ const aggregate = aggregates[key];
35
+ if (aggregate) {
36
+ aggregate[stableIdSymbol] = uid;
37
+ }
38
+ }
39
+ return aggregates;
40
+ }
41
+ async getStats(filePath) {
42
+ const snapshot = await this.getSnapshot(filePath);
43
+ return await snapshot.getStatistics();
44
+ }
45
+ async getStaticData(filePath) {
46
+ const snapshot = await this.getSnapshot(filePath);
47
+ return snapshot.staticData;
48
+ }
49
+ async getOrCreateUidForClassKey(filePath, classKey) {
50
+ const cached = this.#getCachedSnapshot(filePath);
51
+ let uid = cached.classKeyToUid.get(classKey);
52
+ if (!uid) {
53
+ uid = cached.idGenerator();
54
+ cached.classKeyToUid.set(classKey, uid);
55
+ cached.uidToClassKey.set(uid, classKey);
56
+ }
57
+ return uid;
58
+ }
59
+ #getCachedSnapshot(filePath) {
60
+ const absolutePath = path.resolve(filePath);
61
+ const cached = this.#snapshots.get(absolutePath);
62
+ if (!cached) {
63
+ throw new Error(`Snapshot not loaded for ${filePath}`);
64
+ }
65
+ return cached;
66
+ }
67
+ async #loadSnapshot(absolutePath) {
68
+ const workerProxy = new DevTools.HeapSnapshotModel.HeapSnapshotProxy.HeapSnapshotWorkerProxy(() => {
69
+ /* noop */
70
+ }, import.meta.resolve('./third_party/devtools-heap-snapshot-worker.js'));
71
+ const { promise: snapshotPromise, resolve: resolveSnapshot } = Promise.withResolvers();
72
+ const loaderProxy = workerProxy.createLoader(1, snapshotProxy => {
73
+ resolveSnapshot(snapshotProxy);
74
+ });
75
+ const fileStream = fsSync.createReadStream(absolutePath, {
76
+ encoding: 'utf-8',
77
+ highWaterMark: 1024 * 1024,
78
+ });
79
+ for await (const chunk of fileStream) {
80
+ await loaderProxy.write(chunk);
81
+ }
82
+ await loaderProxy.close();
83
+ const snapshot = await snapshotPromise;
84
+ return { snapshot, worker: workerProxy };
85
+ }
86
+ dispose(filePath) {
87
+ const absolutePath = path.resolve(filePath);
88
+ const cached = this.#snapshots.get(absolutePath);
89
+ if (cached) {
90
+ cached.worker.dispose();
91
+ this.#snapshots.delete(absolutePath);
92
+ }
93
+ }
94
+ }
@@ -6,31 +6,17 @@
6
6
  import fs from 'node:fs/promises';
7
7
  import path from 'node:path';
8
8
  import { UniverseManager } from './DevtoolsUtils.js';
9
+ import { HeapSnapshotManager } from './HeapSnapshotManager.js';
9
10
  import { McpPage } from './McpPage.js';
10
11
  import { NetworkCollector, ConsoleCollector, } from './PageCollector.js';
11
12
  import { Locator } from './third_party/index.js';
12
13
  import { PredefinedNetworkConditions } from './third_party/index.js';
13
14
  import { listPages } from './tools/pages.js';
14
15
  import { CLOSE_PAGE_ERROR } from './tools/ToolDefinition.js';
15
- import { ExtensionRegistry, } from './utils/ExtensionRegistry.js';
16
- import { saveTemporaryFile } from './utils/files.js';
17
- import { WaitForHelper } from './WaitForHelper.js';
16
+ import { ensureExtension, saveTemporaryFile } from './utils/files.js';
17
+ import { getNetworkMultiplierFromString } from './WaitForHelper.js';
18
18
  const DEFAULT_TIMEOUT = 5_000;
19
19
  const NAVIGATION_TIMEOUT = 10_000;
20
- function getNetworkMultiplierFromString(condition) {
21
- const puppeteerCondition = condition;
22
- switch (puppeteerCondition) {
23
- case 'Fast 4G':
24
- return 1;
25
- case 'Slow 4G':
26
- return 2.5;
27
- case 'Fast 3G':
28
- return 5;
29
- case 'Slow 3G':
30
- return 10;
31
- }
32
- return 1;
33
- }
34
20
  export class McpContext {
35
21
  browser;
36
22
  logger;
@@ -45,7 +31,6 @@ export class McpContext {
45
31
  #networkCollector;
46
32
  #consoleCollector;
47
33
  #devtoolsUniverseManager;
48
- #extensionRegistry = new ExtensionRegistry();
49
34
  #isRunningTrace = false;
50
35
  #screenRecorderData = null;
51
36
  #nextPageId = 1;
@@ -56,6 +41,7 @@ export class McpContext {
56
41
  #traceResults = [];
57
42
  #locatorClass;
58
43
  #options;
44
+ #heapSnapshotManager = new HeapSnapshotManager();
59
45
  constructor(browser, logger, options, locatorClass) {
60
46
  this.browser = browser;
61
47
  this.logger = logger;
@@ -70,7 +56,7 @@ export class McpContext {
70
56
  uncaughtError: event => {
71
57
  collect(event);
72
58
  },
73
- issue: event => {
59
+ devtoolsAggregatedIssue: event => {
74
60
  collect(event);
75
61
  },
76
62
  };
@@ -604,9 +590,9 @@ export class McpContext {
604
590
  async saveTemporaryFile(data, filename) {
605
591
  return await saveTemporaryFile(data, filename);
606
592
  }
607
- async saveFile(data, filename) {
593
+ async saveFile(data, clientProvidedFilePath, extension) {
608
594
  try {
609
- const filePath = path.resolve(filename);
595
+ const filePath = ensureExtension(path.resolve(clientProvidedFilePath), extension);
610
596
  await fs.mkdir(path.dirname(filePath), { recursive: true });
611
597
  await fs.writeFile(filePath, data);
612
598
  return { filename: filePath };
@@ -624,16 +610,6 @@ export class McpContext {
624
610
  recordedTraces() {
625
611
  return this.#traceResults;
626
612
  }
627
- getWaitForHelper(page, cpuMultiplier, networkMultiplier) {
628
- return new WaitForHelper(page, cpuMultiplier, networkMultiplier);
629
- }
630
- waitForEventsAfterAction(action, options) {
631
- const page = this.#getSelectedMcpPage();
632
- const cpuMultiplier = page.cpuThrottlingRate;
633
- const networkMultiplier = getNetworkMultiplierFromString(page.networkConditions);
634
- const waitForHelper = this.getWaitForHelper(page.pptrPage, cpuMultiplier, networkMultiplier);
635
- return waitForHelper.waitForEventsAfterAction(action, options);
636
- }
637
613
  getNetworkRequestStableId(request) {
638
614
  return this.#networkCollector.getIdForResource(request);
639
615
  }
@@ -668,33 +644,34 @@ export class McpContext {
668
644
  }
669
645
  async installExtension(extensionPath) {
670
646
  const id = await this.browser.installExtension(extensionPath);
671
- await this.#extensionRegistry.registerExtension(id, extensionPath);
672
647
  return id;
673
648
  }
674
649
  async uninstallExtension(id) {
675
650
  await this.browser.uninstallExtension(id);
676
- this.#extensionRegistry.remove(id);
677
651
  }
678
652
  async triggerExtensionAction(id) {
679
- const page = this.getSelectedPptrPage();
680
- // @ts-expect-error internal puppeteer api is needed since we don't have a way to get
681
- // a tab id at the moment
682
- const theTarget = page._tabId;
683
- const session = await this.browser.target().createCDPSession();
684
- try {
685
- await session.send('Extensions.triggerAction', {
686
- id,
687
- targetId: theTarget,
688
- });
689
- }
690
- finally {
691
- await session.detach();
653
+ const extensions = await this.browser.extensions();
654
+ const extension = extensions.get(id);
655
+ if (!extension) {
656
+ throw new Error(`Extension with ID ${id} not found.`);
692
657
  }
658
+ const page = this.getSelectedPptrPage();
659
+ await extension.triggerAction(page);
693
660
  }
694
661
  listExtensions() {
695
- return this.#extensionRegistry.list();
662
+ return this.browser.extensions();
663
+ }
664
+ async getExtension(id) {
665
+ const pptrExtensions = await this.browser.extensions();
666
+ return pptrExtensions.get(id);
667
+ }
668
+ async getHeapSnapshotAggregates(filePath) {
669
+ return await this.#heapSnapshotManager.getAggregates(filePath);
670
+ }
671
+ async getHeapSnapshotStats(filePath) {
672
+ return await this.#heapSnapshotManager.getStats(filePath);
696
673
  }
697
- getExtension(id) {
698
- return this.#extensionRegistry.getById(id);
674
+ async getHeapSnapshotStaticData(filePath) {
675
+ return await this.#heapSnapshotManager.getStaticData(filePath);
699
676
  }
700
677
  }
@@ -4,6 +4,7 @@
4
4
  * SPDX-License-Identifier: Apache-2.0
5
5
  */
6
6
  import { takeSnapshot } from './tools/snapshot.js';
7
+ import { getNetworkMultiplierFromString, WaitForHelper, } from './WaitForHelper.js';
7
8
  /**
8
9
  * Per-page state wrapper. Consolidates dialog, snapshot, emulation,
9
10
  * and metadata that were previously scattered across Maps in McpContext.
@@ -26,6 +27,7 @@ export class McpPage {
26
27
  // Dialog
27
28
  #dialog;
28
29
  #dialogHandler;
30
+ inPageTools;
29
31
  constructor(page, id) {
30
32
  this.pptrPage = page;
31
33
  this.id = id;
@@ -43,6 +45,12 @@ export class McpPage {
43
45
  clearDialog() {
44
46
  this.#dialog = undefined;
45
47
  }
48
+ getInPageTools() {
49
+ return this.inPageTools;
50
+ }
51
+ getWebMcpTools() {
52
+ return this.pptrPage.webmcp.tools();
53
+ }
46
54
  get networkConditions() {
47
55
  return this.emulationSettings.networkConditions ?? null;
48
56
  }
@@ -61,6 +69,14 @@ export class McpPage {
61
69
  get colorScheme() {
62
70
  return this.emulationSettings.colorScheme ?? null;
63
71
  }
72
+ // Public for testability: tests spy on this method to verify throttle multipliers.
73
+ createWaitForHelper(cpuMultiplier, networkMultiplier) {
74
+ return new WaitForHelper(this.pptrPage, cpuMultiplier, networkMultiplier);
75
+ }
76
+ waitForEventsAfterAction(action, options) {
77
+ const helper = this.createWaitForHelper(this.cpuThrottlingRate, getNetworkMultiplierFromString(this.networkConditions));
78
+ return helper.waitForEventsAfterAction(action, options);
79
+ }
64
80
  dispose() {
65
81
  this.pptrPage.off('dialog', this.#dialogHandler);
66
82
  }