chrome-devtools-mcp 1.2.0 → 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 +59 -4
- package/build/src/HeapSnapshotManager.js +25 -16
- package/build/src/McpContext.js +35 -1
- package/build/src/McpResponse.js +77 -7
- package/build/src/PageCollector.js +1 -1
- package/build/src/bin/check-latest-version.js +1 -0
- package/build/src/bin/chrome-devtools-cli-options.js +84 -0
- package/build/src/bin/chrome-devtools-mcp-cli-options.js +47 -3
- package/build/src/{DevToolsConnectionAdapter.js → devtools/DevToolsConnectionAdapter.js} +1 -1
- package/build/src/{DevtoolsUtils.js → devtools/DevtoolsUtils.js} +75 -71
- package/build/src/devtools/McpHostBindingAdapter.js +165 -0
- package/build/src/formatters/ConsoleFormatter.js +1 -1
- package/build/src/formatters/HeapSnapshotFormatter.js +22 -0
- package/build/src/formatters/NetworkFormatter.js +3 -1
- package/build/src/third_party/THIRD_PARTY_NOTICES +4 -4
- package/build/src/third_party/bundled-packages.json +3 -3
- package/build/src/third_party/devtools-formatter-worker.js +0 -1
- package/build/src/third_party/devtools-heap-snapshot-worker.js +67 -3
- package/build/src/third_party/index.js +1971 -426
- package/build/src/tools/memory.js +76 -0
- package/build/src/tools/screencast.js +30 -9
- package/build/src/tools/screenshot.js +158 -76
- package/build/src/utils/check-for-updates.js +1 -0
- package/build/src/version.js +1 -1
- package/package.json +7 -6
- package/skills/a11y-debugging/SKILL.md +89 -0
- package/skills/a11y-debugging/references/a11y-snippets.md +92 -0
- package/skills/chrome-devtools/SKILL.md +72 -0
- package/skills/chrome-devtools-cli/SKILL.md +153 -0
- package/skills/chrome-devtools-cli/references/installation.md +14 -0
- package/skills/debug-optimize-lcp/SKILL.md +121 -0
- package/skills/debug-optimize-lcp/references/elements-and-size.md +27 -0
- package/skills/debug-optimize-lcp/references/lcp-breakdown.md +23 -0
- package/skills/debug-optimize-lcp/references/lcp-snippets.md +79 -0
- package/skills/debug-optimize-lcp/references/optimization-strategies.md +38 -0
- package/skills/memory-leak-debugging/SKILL.md +50 -0
- package/skills/memory-leak-debugging/references/common-leaks.md +33 -0
- package/skills/memory-leak-debugging/references/compare_snapshots.js +109 -0
- package/skills/memory-leak-debugging/references/memlab.md +29 -0
- package/skills/troubleshooting/SKILL.md +98 -0
package/README.md
CHANGED
|
@@ -514,12 +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** (
|
|
517
|
+
- **Memory** (9 tools)
|
|
518
518
|
- [`take_heapsnapshot`](docs/tool-reference.md#take_heapsnapshot)
|
|
519
519
|
- [`close_heapsnapshot`](docs/tool-reference.md#close_heapsnapshot)
|
|
520
520
|
- [`get_heapsnapshot_class_nodes`](docs/tool-reference.md#get_heapsnapshot_class_nodes)
|
|
521
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)
|
|
522
524
|
- [`get_heapsnapshot_retainers`](docs/tool-reference.md#get_heapsnapshot_retainers)
|
|
525
|
+
- [`get_heapsnapshot_retaining_paths`](docs/tool-reference.md#get_heapsnapshot_retaining_paths)
|
|
523
526
|
- [`get_heapsnapshot_summary`](docs/tool-reference.md#get_heapsnapshot_summary)
|
|
524
527
|
- **Extensions** (5 tools)
|
|
525
528
|
- [`install_extension`](docs/tool-reference.md#install_extension)
|
|
@@ -550,14 +553,17 @@ The Chrome DevTools MCP server supports the following configuration option:
|
|
|
550
553
|
- **`--browserUrl`/ `--browser-url`, `-u`**
|
|
551
554
|
Connect to a running, debuggable Chrome instance (e.g. `http://127.0.0.1:9222`). For more details see: https://github.com/ChromeDevTools/chrome-devtools-mcp#connecting-to-a-running-chrome-instance.
|
|
552
555
|
- **Type:** string
|
|
556
|
+
- **Default:** `false`
|
|
553
557
|
|
|
554
558
|
- **`--wsEndpoint`/ `--ws-endpoint`, `-w`**
|
|
555
559
|
WebSocket endpoint to connect to a running Chrome instance (e.g., ws://127.0.0.1:9222/devtools/browser/<id>). Alternative to --browserUrl.
|
|
556
560
|
- **Type:** string
|
|
561
|
+
- **Default:** `false`
|
|
557
562
|
|
|
558
563
|
- **`--wsHeaders`/ `--ws-headers`**
|
|
559
564
|
Custom headers for WebSocket connection in JSON format (e.g., '{"Authorization":"Bearer token"}'). Only works with --wsEndpoint.
|
|
560
565
|
- **Type:** string
|
|
566
|
+
- **Default:** `false`
|
|
561
567
|
|
|
562
568
|
- **`--headless`**
|
|
563
569
|
Whether to run in headless (no UI) mode.
|
|
@@ -567,87 +573,108 @@ The Chrome DevTools MCP server supports the following configuration option:
|
|
|
567
573
|
- **`--executablePath`/ `--executable-path`, `-e`**
|
|
568
574
|
Path to custom Chrome executable.
|
|
569
575
|
- **Type:** string
|
|
576
|
+
- **Default:** `false`
|
|
570
577
|
|
|
571
578
|
- **`--isolated`**
|
|
572
579
|
If specified, creates a temporary user-data-dir that is automatically cleaned up after the browser is closed. Defaults to false.
|
|
573
580
|
- **Type:** boolean
|
|
581
|
+
- **Default:** `false`
|
|
574
582
|
|
|
575
583
|
- **`--userDataDir`/ `--user-data-dir`**
|
|
576
584
|
Path to the user data directory for Chrome. Default is $HOME/.cache/chrome-devtools-mcp/chrome-profile$CHANNEL_SUFFIX_IF_NON_STABLE
|
|
577
585
|
- **Type:** string
|
|
586
|
+
- **Default:** `false`
|
|
578
587
|
|
|
579
588
|
- **`--channel`**
|
|
580
589
|
Specify a different Chrome channel that should be used. The default is the stable channel version.
|
|
581
590
|
- **Type:** string
|
|
582
591
|
- **Choices:** `canary`, `dev`, `beta`, `stable`
|
|
592
|
+
- **Default:** `false`
|
|
583
593
|
|
|
584
594
|
- **`--logFile`/ `--log-file`**
|
|
585
595
|
Path to a file to write debug logs to. Set the env variable `DEBUG` to `*` to enable verbose logs. Useful for submitting bug reports.
|
|
586
596
|
- **Type:** string
|
|
597
|
+
- **Default:** `false`
|
|
587
598
|
|
|
588
599
|
- **`--viewport`**
|
|
589
600
|
Initial viewport size for the Chrome instances started by the server. For example, `1280x720`. In headless mode, max size is 3840x2160px.
|
|
590
601
|
- **Type:** string
|
|
602
|
+
- **Default:** `false`
|
|
591
603
|
|
|
592
604
|
- **`--proxyServer`/ `--proxy-server`**
|
|
593
605
|
Proxy server configuration for Chrome passed as --proxy-server when launching the browser. See https://www.chromium.org/developers/design-documents/network-settings/ for details.
|
|
594
606
|
- **Type:** string
|
|
607
|
+
- **Default:** `false`
|
|
595
608
|
|
|
596
609
|
- **`--acceptInsecureCerts`/ `--accept-insecure-certs`**
|
|
597
610
|
If enabled, ignores errors relative to self-signed and expired certificates. Use with caution.
|
|
598
611
|
- **Type:** boolean
|
|
612
|
+
- **Default:** `false`
|
|
599
613
|
|
|
600
614
|
- **`--experimentalPageIdRouting`/ `--experimental-page-id-routing`**
|
|
601
615
|
Whether to expose pageId on page-scoped tools and route requests by page ID (useful for concurrent agent sessions).
|
|
602
616
|
- **Type:** boolean
|
|
617
|
+
- **Default:** `false`
|
|
603
618
|
|
|
604
619
|
- **`--experimentalDevtools`/ `--experimental-devtools`**
|
|
605
620
|
Whether to enable automation over DevTools targets
|
|
606
621
|
- **Type:** boolean
|
|
622
|
+
- **Default:** `false`
|
|
607
623
|
|
|
608
624
|
- **`--experimentalVision`/ `--experimental-vision`**
|
|
609
625
|
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.
|
|
610
626
|
- **Type:** boolean
|
|
627
|
+
- **Default:** `false`
|
|
611
628
|
|
|
612
629
|
- **`--memoryDebugging`/ `--memory-debugging`, `-experimentalMemory`**
|
|
613
630
|
Whether to enable memory debugging tools.
|
|
614
631
|
- **Type:** boolean
|
|
632
|
+
- **Default:** `false`
|
|
615
633
|
|
|
616
634
|
- **`--experimentalStructuredContent`/ `--experimental-structured-content`**
|
|
617
635
|
Whether to output structured formatted content.
|
|
618
636
|
- **Type:** boolean
|
|
637
|
+
- **Default:** `false`
|
|
619
638
|
|
|
620
639
|
- **`--experimentalIncludeAllPages`/ `--experimental-include-all-pages`**
|
|
621
640
|
Whether to include all kinds of pages such as webviews or background pages as pages.
|
|
622
641
|
- **Type:** boolean
|
|
642
|
+
- **Default:** `false`
|
|
623
643
|
|
|
624
644
|
- **`--experimentalScreencast`/ `--experimental-screencast`**
|
|
625
645
|
Exposes experimental screencast tools (requires ffmpeg). Install ffmpeg https://www.ffmpeg.org/download.html and ensure it is available in the MCP server PATH.
|
|
626
646
|
- **Type:** boolean
|
|
647
|
+
- **Default:** `false`
|
|
627
648
|
|
|
628
649
|
- **`--experimentalFfmpegPath`/ `--experimental-ffmpeg-path`**
|
|
629
650
|
Path to ffmpeg executable for screencast recording.
|
|
630
651
|
- **Type:** string
|
|
652
|
+
- **Default:** `false`
|
|
631
653
|
|
|
632
654
|
- **`--categoryExperimentalWebmcp`/ `--category-experimental-webmcp`**
|
|
633
|
-
Set to true to enable debugging WebMCP tools. Requires Chrome 149+ with the following flags: `--enable-features=
|
|
655
|
+
Set to true to enable debugging WebMCP tools. Requires Chrome 149+ with the following flags: `--enable-features=WebMCP,DevToolsWebMCPSupport`
|
|
634
656
|
- **Type:** boolean
|
|
657
|
+
- **Default:** `false`
|
|
635
658
|
|
|
636
659
|
- **`--chromeArg`/ `--chrome-arg`**
|
|
637
660
|
Additional arguments for Chrome. Only applies when Chrome is launched by chrome-devtools-mcp.
|
|
638
661
|
- **Type:** array
|
|
662
|
+
- **Default:** `false`
|
|
639
663
|
|
|
640
664
|
- **`--blockedUrlPattern`/ `--blocked-url-pattern`**
|
|
641
|
-
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.
|
|
665
|
+
Restricts browser's 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.
|
|
642
666
|
- **Type:** array
|
|
667
|
+
- **Default:** `false`
|
|
643
668
|
|
|
644
669
|
- **`--allowedUrlPattern`/ `--allowed-url-pattern`**
|
|
645
|
-
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.
|
|
670
|
+
Restricts browser's 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.
|
|
646
671
|
- **Type:** array
|
|
672
|
+
- **Default:** `false`
|
|
647
673
|
|
|
648
674
|
- **`--ignoreDefaultChromeArg`/ `--ignore-default-chrome-arg`**
|
|
649
675
|
Explicitly disable default arguments for Chrome. Only applies when Chrome is launched by chrome-devtools-mcp.
|
|
650
676
|
- **Type:** array
|
|
677
|
+
- **Default:** `false`
|
|
651
678
|
|
|
652
679
|
- **`--categoryEmulation`/ `--category-emulation`**
|
|
653
680
|
Set to false to exclude tools related to emulation.
|
|
@@ -684,9 +711,31 @@ The Chrome DevTools MCP server supports the following configuration option:
|
|
|
684
711
|
- **Type:** boolean
|
|
685
712
|
- **Default:** `true`
|
|
686
713
|
|
|
714
|
+
- **`--screenshotFormat`/ `--screenshot-format`**
|
|
715
|
+
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").
|
|
716
|
+
- **Type:** string
|
|
717
|
+
- **Choices:** `jpeg`, `png`, `webp`
|
|
718
|
+
- **Default:** `false`
|
|
719
|
+
|
|
720
|
+
- **`--screenshotQuality`/ `--screenshot-quality`**
|
|
721
|
+
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.
|
|
722
|
+
- **Type:** number
|
|
723
|
+
- **Default:** `false`
|
|
724
|
+
|
|
725
|
+
- **`--screenshotMaxWidth`/ `--screenshot-max-width`**
|
|
726
|
+
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.
|
|
727
|
+
- **Type:** number
|
|
728
|
+
- **Default:** `false`
|
|
729
|
+
|
|
730
|
+
- **`--screenshotMaxHeight`/ `--screenshot-max-height`**
|
|
731
|
+
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.
|
|
732
|
+
- **Type:** number
|
|
733
|
+
- **Default:** `false`
|
|
734
|
+
|
|
687
735
|
- **`--slim`**
|
|
688
736
|
Exposes a "slim" set of 3 tools covering navigation, script execution and screenshots only. Useful for basic browser tasks.
|
|
689
737
|
- **Type:** boolean
|
|
738
|
+
- **Default:** `false`
|
|
690
739
|
|
|
691
740
|
- **`--redactNetworkHeaders`/ `--redact-network-headers`**
|
|
692
741
|
If true, redacts some of the network headers considered sensitive before returning to the client.
|
|
@@ -909,3 +958,9 @@ Please consult [these instructions](./docs/debugging-android.md).
|
|
|
909
958
|
## Known limitations
|
|
910
959
|
|
|
911
960
|
See [Troubleshooting](./docs/troubleshooting.md).
|
|
961
|
+
|
|
962
|
+
## Integrating as a browser subagent
|
|
963
|
+
|
|
964
|
+
If you are developing agentic tooling and want to provide an integrated browser subagent as part of your product, we recommend building on top of Chrome DevTools for agents.
|
|
965
|
+
|
|
966
|
+
For a reference implementation, see the [Gemini CLI browser agent documentation](https://geminicli.com/docs/core/subagents/#browser-agent).
|
|
@@ -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
|
|
69
|
+
async getRetainers(filePath, nodeId) {
|
|
71
70
|
const snapshot = await this.getSnapshot(filePath);
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
|
|
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
|
-
|
|
75
|
+
const provider = snapshot.createRetainingEdgesProvider(nodeIndex);
|
|
76
|
+
return await provider.serializeItemsRange(0, Infinity);
|
|
84
77
|
}
|
|
85
|
-
async
|
|
86
|
-
const
|
|
78
|
+
async getRetainingPaths(filePath, nodeId, maxDepth, maxNodes, maxSiblings) {
|
|
79
|
+
const snapshot = await this.getSnapshot(filePath);
|
|
80
|
+
const nodeIndex = await snapshot.nodeIndexForId(nodeId);
|
|
87
81
|
if (nodeIndex === undefined) {
|
|
88
82
|
throw new Error(`Node with ID ${nodeId} not found`);
|
|
89
83
|
}
|
|
84
|
+
return await snapshot.getRetainingPaths(nodeIndex, maxDepth, maxNodes, maxSiblings);
|
|
85
|
+
}
|
|
86
|
+
async getDominatorsOf(filePath, nodeId) {
|
|
90
87
|
const snapshot = await this.getSnapshot(filePath);
|
|
91
|
-
const
|
|
88
|
+
const nodeIndex = await snapshot.nodeIndexForId(nodeId);
|
|
89
|
+
if (nodeIndex === undefined) {
|
|
90
|
+
throw new Error(`Node with ID ${nodeId} not found`);
|
|
91
|
+
}
|
|
92
|
+
return await snapshot.getDominatorsOf(nodeIndex);
|
|
93
|
+
}
|
|
94
|
+
async getEdges(filePath, nodeId) {
|
|
95
|
+
const snapshot = await this.getSnapshot(filePath);
|
|
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) {
|
package/build/src/McpContext.js
CHANGED
|
@@ -8,7 +8,7 @@ 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';
|
|
@@ -47,6 +47,11 @@ export class McpContext {
|
|
|
47
47
|
#heapSnapshotManager = new HeapSnapshotManager();
|
|
48
48
|
#roots = undefined;
|
|
49
49
|
constructor(browser, logger, options, locatorClass) {
|
|
50
|
+
overrideDevToolsGlobals({
|
|
51
|
+
loadResource: (url) => {
|
|
52
|
+
return this.loadResource(url);
|
|
53
|
+
},
|
|
54
|
+
});
|
|
50
55
|
this.browser = browser;
|
|
51
56
|
this.logger = logger;
|
|
52
57
|
this.#locatorClass = locatorClass;
|
|
@@ -659,5 +664,34 @@ export class McpContext {
|
|
|
659
664
|
hasHeapSnapshots() {
|
|
660
665
|
return this.#heapSnapshotManager.hasSnapshots();
|
|
661
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
|
+
}
|
|
662
696
|
}
|
|
663
697
|
//# sourceMappingURL=McpContext.js.map
|
package/build/src/McpResponse.js
CHANGED
|
@@ -73,6 +73,9 @@ async function getToolGroups(page) {
|
|
|
73
73
|
return [];
|
|
74
74
|
}
|
|
75
75
|
const toolGroups = await page.pptrPage.evaluate(() => {
|
|
76
|
+
if (window.__dtmcp) {
|
|
77
|
+
window.__dtmcp.toolGroups = [];
|
|
78
|
+
}
|
|
76
79
|
return new Promise(resolve => {
|
|
77
80
|
const event = new CustomEvent('devtoolstooldiscovery');
|
|
78
81
|
const groups = [];
|
|
@@ -85,7 +88,8 @@ async function getToolGroups(page) {
|
|
|
85
88
|
window.__dtmcp.toolGroups = [];
|
|
86
89
|
}
|
|
87
90
|
if (typeof toolGroup.name !== 'string' ||
|
|
88
|
-
|
|
91
|
+
(toolGroup.description &&
|
|
92
|
+
typeof toolGroup.description !== 'string') ||
|
|
89
93
|
!Array.isArray(toolGroup.tools)) {
|
|
90
94
|
console.error('Invalid toolGroup:', toolGroup);
|
|
91
95
|
return;
|
|
@@ -330,6 +334,20 @@ export class McpResponse {
|
|
|
330
334
|
pagination: options,
|
|
331
335
|
};
|
|
332
336
|
}
|
|
337
|
+
setHeapSnapshotRetainingPaths(retainingPaths) {
|
|
338
|
+
this.#heapSnapshotOptions = {
|
|
339
|
+
...this.#heapSnapshotOptions,
|
|
340
|
+
include: true,
|
|
341
|
+
retainingPaths,
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
setHeapSnapshotDominators(dominators) {
|
|
345
|
+
this.#heapSnapshotOptions = {
|
|
346
|
+
...this.#heapSnapshotOptions,
|
|
347
|
+
include: true,
|
|
348
|
+
dominators,
|
|
349
|
+
};
|
|
350
|
+
}
|
|
333
351
|
attachImage(value) {
|
|
334
352
|
this.#images.push(value);
|
|
335
353
|
}
|
|
@@ -528,7 +546,7 @@ export class McpResponse {
|
|
|
528
546
|
errorMessage: this.#error?.message,
|
|
529
547
|
}, useToon);
|
|
530
548
|
}
|
|
531
|
-
format(toolName, context, data, useToon) {
|
|
549
|
+
async format(toolName, context, data, useToon) {
|
|
532
550
|
const structuredContent = {};
|
|
533
551
|
const response = [];
|
|
534
552
|
if (this.#textResponseLines.length) {
|
|
@@ -608,8 +626,12 @@ Call ${handleDialog.name} to handle it before continuing.`);
|
|
|
608
626
|
const contextLabel = isolatedContextName
|
|
609
627
|
? ` isolatedContext=${isolatedContextName}`
|
|
610
628
|
: '';
|
|
611
|
-
|
|
612
|
-
|
|
629
|
+
const title = await fetchPageTitle(page);
|
|
630
|
+
const pageLabel = title
|
|
631
|
+
? `${truncateTitle(title)} (${page.url()})`
|
|
632
|
+
: page.url();
|
|
633
|
+
parts.push(`${context.getPageId(page)}: ${pageLabel}${context.isPageSelected(page) ? ' [selected]' : ''}${contextLabel}`);
|
|
634
|
+
structuredPages.push(createStructuredPage(page, context, title));
|
|
613
635
|
}
|
|
614
636
|
response.push(...parts);
|
|
615
637
|
structuredContent.pages = structuredPages;
|
|
@@ -623,8 +645,12 @@ Call ${handleDialog.name} to handle it before continuing.`);
|
|
|
623
645
|
const contextLabel = isolatedContextName
|
|
624
646
|
? ` isolatedContext=${isolatedContextName}`
|
|
625
647
|
: '';
|
|
626
|
-
|
|
627
|
-
|
|
648
|
+
const title = await fetchPageTitle(page);
|
|
649
|
+
const pageLabel = title
|
|
650
|
+
? `${truncateTitle(title)} (${page.url()})`
|
|
651
|
+
: page.url();
|
|
652
|
+
response.push(`${context.getPageId(page)}: ${pageLabel}${context.isPageSelected(page) ? ' [selected]' : ''}${contextLabel}`);
|
|
653
|
+
structuredExtensionPages.push(createStructuredPage(page, context, title));
|
|
628
654
|
}
|
|
629
655
|
structuredContent.extensionPages = structuredExtensionPages;
|
|
630
656
|
}
|
|
@@ -752,6 +778,36 @@ Call ${handleDialog.name} to handle it before continuing.`);
|
|
|
752
778
|
response.push(...paginationData.info);
|
|
753
779
|
structuredContent.heapSnapshotNodes = paginationData.items;
|
|
754
780
|
}
|
|
781
|
+
const retainingPaths = this.#heapSnapshotOptions.retainingPaths;
|
|
782
|
+
if (retainingPaths) {
|
|
783
|
+
response.push('### Retaining Paths');
|
|
784
|
+
const { paths, limitsReached } = retainingPaths;
|
|
785
|
+
if (paths.length === 0) {
|
|
786
|
+
response.push('No retaining paths found.');
|
|
787
|
+
}
|
|
788
|
+
else {
|
|
789
|
+
response.push(HeapSnapshotFormatter.formatRetainingPaths(paths));
|
|
790
|
+
}
|
|
791
|
+
const reached = Object.entries(limitsReached)
|
|
792
|
+
.filter(([, hit]) => hit)
|
|
793
|
+
.map(([limit]) => limit);
|
|
794
|
+
if (reached.length > 0) {
|
|
795
|
+
response.push(`Note: results are truncated, the following limits were reached: ${reached.join(', ')}.`);
|
|
796
|
+
}
|
|
797
|
+
structuredContent.heapSnapshotRetainingPaths =
|
|
798
|
+
retainingPaths;
|
|
799
|
+
}
|
|
800
|
+
const dominators = this.#heapSnapshotOptions.dominators;
|
|
801
|
+
if (dominators) {
|
|
802
|
+
response.push('### Dominator Chain');
|
|
803
|
+
if (dominators.length === 0) {
|
|
804
|
+
response.push('No dominators found.');
|
|
805
|
+
}
|
|
806
|
+
else {
|
|
807
|
+
response.push(HeapSnapshotFormatter.formatDominators(dominators));
|
|
808
|
+
}
|
|
809
|
+
structuredContent.heapSnapshotDominators = dominators;
|
|
810
|
+
}
|
|
755
811
|
}
|
|
756
812
|
if (data.detailedNetworkRequest) {
|
|
757
813
|
response.push(data.detailedNetworkRequest.toStringDetailed());
|
|
@@ -905,11 +961,25 @@ Call ${handleDialog.name} to handle it before continuing.`);
|
|
|
905
961
|
this.#textResponseLines = [];
|
|
906
962
|
}
|
|
907
963
|
}
|
|
908
|
-
function
|
|
964
|
+
function truncateTitle(title, maxLength = 50) {
|
|
965
|
+
if (title.length <= maxLength) {
|
|
966
|
+
return title;
|
|
967
|
+
}
|
|
968
|
+
return title.slice(0, maxLength - 3) + '...';
|
|
969
|
+
}
|
|
970
|
+
async function fetchPageTitle(page) {
|
|
971
|
+
return Promise.race([
|
|
972
|
+
page.title().catch(() => ''),
|
|
973
|
+
new Promise(resolve => setTimeout(() => resolve(''), 1000)),
|
|
974
|
+
]);
|
|
975
|
+
}
|
|
976
|
+
function createStructuredPage(page, context, rawTitle) {
|
|
909
977
|
const isolatedContextName = context.getIsolatedContextName(page);
|
|
978
|
+
const title = truncateTitle(rawTitle);
|
|
910
979
|
const entry = {
|
|
911
980
|
id: context.getPageId(page),
|
|
912
981
|
url: page.url(),
|
|
982
|
+
title,
|
|
913
983
|
selected: context.isPageSelected(page),
|
|
914
984
|
};
|
|
915
985
|
if (isolatedContextName) {
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Copyright 2025 Google LLC
|
|
4
4
|
* SPDX-License-Identifier: Apache-2.0
|
|
5
5
|
*/
|
|
6
|
-
import { FakeIssuesManager } from './DevtoolsUtils.js';
|
|
6
|
+
import { FakeIssuesManager } from './devtools/DevtoolsUtils.js';
|
|
7
7
|
import { logger } from './logger.js';
|
|
8
8
|
import { DevTools } from './third_party/index.js';
|
|
9
9
|
import { createIdGenerator, stableIdSymbol, } from './utils/id.js';
|
|
@@ -312,6 +312,54 @@ export const commands = {
|
|
|
312
312
|
},
|
|
313
313
|
},
|
|
314
314
|
},
|
|
315
|
+
get_heapsnapshot_dominators: {
|
|
316
|
+
description: 'Loads a memory heapsnapshot and returns the dominator chain for a specific node ID. This helps to identify which objects are keeping the target node alive. (requires flag: --memoryDebugging=true)',
|
|
317
|
+
category: 'Memory',
|
|
318
|
+
args: {
|
|
319
|
+
filePath: {
|
|
320
|
+
name: 'filePath',
|
|
321
|
+
type: 'string',
|
|
322
|
+
description: 'A path to a .heapsnapshot file to read.',
|
|
323
|
+
required: true,
|
|
324
|
+
},
|
|
325
|
+
nodeId: {
|
|
326
|
+
name: 'nodeId',
|
|
327
|
+
type: 'number',
|
|
328
|
+
description: 'The node ID to get the dominator chain for.',
|
|
329
|
+
required: true,
|
|
330
|
+
},
|
|
331
|
+
},
|
|
332
|
+
},
|
|
333
|
+
get_heapsnapshot_edges: {
|
|
334
|
+
description: 'Loads a memory heapsnapshot and returns outgoing edges (references) for a specific node ID. (requires flag: --memoryDebugging=true)',
|
|
335
|
+
category: 'Memory',
|
|
336
|
+
args: {
|
|
337
|
+
filePath: {
|
|
338
|
+
name: 'filePath',
|
|
339
|
+
type: 'string',
|
|
340
|
+
description: 'A path to a .heapsnapshot file to read.',
|
|
341
|
+
required: true,
|
|
342
|
+
},
|
|
343
|
+
nodeId: {
|
|
344
|
+
name: 'nodeId',
|
|
345
|
+
type: 'number',
|
|
346
|
+
description: 'The node ID to get outgoing edges for.',
|
|
347
|
+
required: true,
|
|
348
|
+
},
|
|
349
|
+
pageIdx: {
|
|
350
|
+
name: 'pageIdx',
|
|
351
|
+
type: 'number',
|
|
352
|
+
description: 'The page index for pagination.',
|
|
353
|
+
required: false,
|
|
354
|
+
},
|
|
355
|
+
pageSize: {
|
|
356
|
+
name: 'pageSize',
|
|
357
|
+
type: 'number',
|
|
358
|
+
description: 'The page size for pagination.',
|
|
359
|
+
required: false,
|
|
360
|
+
},
|
|
361
|
+
},
|
|
362
|
+
},
|
|
315
363
|
get_heapsnapshot_retainers: {
|
|
316
364
|
description: 'Loads a memory heapsnapshot and returns retainers for a specific node ID. (requires flag: --memoryDebugging=true)',
|
|
317
365
|
category: 'Memory',
|
|
@@ -342,6 +390,42 @@ export const commands = {
|
|
|
342
390
|
},
|
|
343
391
|
},
|
|
344
392
|
},
|
|
393
|
+
get_heapsnapshot_retaining_paths: {
|
|
394
|
+
description: 'Loads a memory heapsnapshot and returns retaining paths for a specific node ID. This helps to understand why a node is not being garbage collected. (requires flag: --memoryDebugging=true)',
|
|
395
|
+
category: 'Memory',
|
|
396
|
+
args: {
|
|
397
|
+
filePath: {
|
|
398
|
+
name: 'filePath',
|
|
399
|
+
type: 'string',
|
|
400
|
+
description: 'A path to a .heapsnapshot file to read.',
|
|
401
|
+
required: true,
|
|
402
|
+
},
|
|
403
|
+
nodeId: {
|
|
404
|
+
name: 'nodeId',
|
|
405
|
+
type: 'number',
|
|
406
|
+
description: 'The node ID to get retaining paths for.',
|
|
407
|
+
required: true,
|
|
408
|
+
},
|
|
409
|
+
maxDepth: {
|
|
410
|
+
name: 'maxDepth',
|
|
411
|
+
type: 'number',
|
|
412
|
+
description: 'The maximum depth to search for retaining paths.',
|
|
413
|
+
required: false,
|
|
414
|
+
},
|
|
415
|
+
maxNodes: {
|
|
416
|
+
name: 'maxNodes',
|
|
417
|
+
type: 'number',
|
|
418
|
+
description: 'The maximum number of nodes to return.',
|
|
419
|
+
required: false,
|
|
420
|
+
},
|
|
421
|
+
maxSiblings: {
|
|
422
|
+
name: 'maxSiblings',
|
|
423
|
+
type: 'number',
|
|
424
|
+
description: 'The maximum number of siblings to return.',
|
|
425
|
+
required: false,
|
|
426
|
+
},
|
|
427
|
+
},
|
|
428
|
+
},
|
|
345
429
|
get_heapsnapshot_summary: {
|
|
346
430
|
description: 'Loads a memory heapsnapshot and returns snapshot summary stats. (requires flag: --memoryDebugging=true)',
|
|
347
431
|
category: 'Memory',
|
|
@@ -185,7 +185,7 @@ export const cliOptions = {
|
|
|
185
185
|
},
|
|
186
186
|
categoryExperimentalWebmcp: {
|
|
187
187
|
type: 'boolean',
|
|
188
|
-
describe: 'Set to true to enable debugging WebMCP tools. Requires Chrome 149+ with the following flags: `--enable-features=
|
|
188
|
+
describe: 'Set to true to enable debugging WebMCP tools. Requires Chrome 149+ with the following flags: `--enable-features=WebMCP,DevToolsWebMCPSupport`',
|
|
189
189
|
},
|
|
190
190
|
chromeArg: {
|
|
191
191
|
type: 'array',
|
|
@@ -193,12 +193,12 @@ export const cliOptions = {
|
|
|
193
193
|
},
|
|
194
194
|
blockedUrlPattern: {
|
|
195
195
|
type: 'array',
|
|
196
|
-
describe:
|
|
196
|
+
describe: "Restricts browser's 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.",
|
|
197
197
|
conflicts: ['allowedUrlPattern'],
|
|
198
198
|
},
|
|
199
199
|
allowedUrlPattern: {
|
|
200
200
|
type: 'array',
|
|
201
|
-
describe:
|
|
201
|
+
describe: "Restricts browser's 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.",
|
|
202
202
|
conflicts: ['blockedUrlPattern'],
|
|
203
203
|
},
|
|
204
204
|
ignoreDefaultChromeArg: {
|
|
@@ -256,6 +256,50 @@ export const cliOptions = {
|
|
|
256
256
|
hidden: true,
|
|
257
257
|
describe: 'Include watchdog PID in Clearcut request headers (for testing).',
|
|
258
258
|
},
|
|
259
|
+
screenshotFormat: {
|
|
260
|
+
type: 'string',
|
|
261
|
+
description: '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").',
|
|
262
|
+
choices: ['jpeg', 'png', 'webp'],
|
|
263
|
+
},
|
|
264
|
+
screenshotQuality: {
|
|
265
|
+
type: 'number',
|
|
266
|
+
description: '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.',
|
|
267
|
+
coerce: (value) => {
|
|
268
|
+
if (value === undefined) {
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
if (!Number.isInteger(value) || value < 0 || value > 100) {
|
|
272
|
+
throw new Error(`Invalid screenshotQuality ${value}. Expected an integer between 0 and 100.`);
|
|
273
|
+
}
|
|
274
|
+
return value;
|
|
275
|
+
},
|
|
276
|
+
},
|
|
277
|
+
screenshotMaxWidth: {
|
|
278
|
+
type: 'number',
|
|
279
|
+
description: '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.',
|
|
280
|
+
coerce: (value) => {
|
|
281
|
+
if (value === undefined) {
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
284
|
+
if (!Number.isInteger(value) || value <= 0) {
|
|
285
|
+
throw new Error(`Invalid screenshotMaxWidth ${value}. Expected a positive integer.`);
|
|
286
|
+
}
|
|
287
|
+
return value;
|
|
288
|
+
},
|
|
289
|
+
},
|
|
290
|
+
screenshotMaxHeight: {
|
|
291
|
+
type: 'number',
|
|
292
|
+
description: '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.',
|
|
293
|
+
coerce: (value) => {
|
|
294
|
+
if (value === undefined) {
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
if (!Number.isInteger(value) || value <= 0) {
|
|
298
|
+
throw new Error(`Invalid screenshotMaxHeight ${value}. Expected a positive integer.`);
|
|
299
|
+
}
|
|
300
|
+
return value;
|
|
301
|
+
},
|
|
302
|
+
},
|
|
259
303
|
slim: {
|
|
260
304
|
type: 'boolean',
|
|
261
305
|
describe: 'Exposes a "slim" set of 3 tools covering navigation, script execution and screenshots only. Useful for basic browser tasks.',
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Copyright 2025 Google LLC
|
|
4
4
|
* SPDX-License-Identifier: Apache-2.0
|
|
5
5
|
*/
|
|
6
|
-
import { CDPSessionEvent } from '
|
|
6
|
+
import { CDPSessionEvent } from '../third_party/index.js';
|
|
7
7
|
/**
|
|
8
8
|
* This class makes a puppeteer connection look like DevTools CDPConnection.
|
|
9
9
|
*
|