brave-mcp 0.5.0 → 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.
- package/README.md +88 -10
- package/build/src/HeapSnapshotManager.js +56 -18
- package/build/src/McpContext.js +124 -32
- package/build/src/McpPage.js +9 -9
- package/build/src/McpResponse.js +216 -68
- package/build/src/PageCollector.js +8 -8
- package/build/src/ServiceWorkerCollector.js +171 -0
- package/build/src/TextSnapshot.js +1 -1
- package/build/src/ToolHandler.js +223 -0
- package/build/src/WaitForHelper.js +20 -6
- package/build/src/bin/brave-devtools-cli-options.js +180 -36
- package/build/src/bin/brave-devtools-mcp-cli-options.js +67 -12
- package/build/src/bin/brave-devtools-mcp-main.js +45 -6
- package/build/src/bin/brave-devtools.js +0 -2
- package/build/src/bin/check-latest-version.js +25 -1
- package/build/src/browser.js +45 -4
- package/build/src/daemon/client.js +19 -13
- package/build/src/daemon/daemon.js +74 -17
- package/build/src/daemon/utils.js +2 -2
- package/build/src/{DevToolsConnectionAdapter.js → devtools/DevToolsConnectionAdapter.js} +1 -1
- package/build/src/{DevtoolsUtils.js → devtools/DevtoolsUtils.js} +93 -19
- package/build/src/devtools/McpHostBindingAdapter.js +165 -0
- package/build/src/formatters/ConsoleFormatter.js +1 -1
- package/build/src/formatters/HeapSnapshotFormatter.js +52 -9
- package/build/src/formatters/IssueFormatter.js +4 -4
- package/build/src/index.js +24 -165
- package/build/src/telemetry/ClearcutLogger.js +35 -119
- package/build/src/telemetry/WatchdogClient.js +4 -4
- package/build/src/telemetry/errors.js +18 -0
- package/build/src/telemetry/flagUtils.js +4 -3
- package/build/src/telemetry/{toolMetricsUtils.js → metricsRegistry.js} +3 -3
- package/build/src/telemetry/persistence.js +21 -3
- package/build/src/telemetry/transformation.js +134 -0
- package/build/src/telemetry/types.js +0 -8
- package/build/src/telemetry/watchdog/ClearcutSender.js +10 -10
- package/build/src/telemetry/watchdog/main.js +5 -5
- package/build/src/third_party/THIRD_PARTY_NOTICES +3637 -0
- package/build/src/third_party/bundled-packages.json +12 -0
- package/build/src/third_party/devtools-formatter-worker.js +15299 -7
- package/build/src/third_party/devtools-heap-snapshot-worker.js +9868 -7
- package/build/src/third_party/index.js +159584 -27
- package/build/src/third_party/issue-descriptions/emailVerificationRequestAccountsEmptyList.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestAccountsHttpNotFound.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestAccountsInvalidContentType.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestAccountsInvalidResponse.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestAccountsNoResponse.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestDnsFetchFailed.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestDnsInvalidRecord.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestEmailVerificationWellKnownHttpNotFound.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestEmailVerificationWellKnownInvalidContentType.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestEmailVerificationWellKnownInvalidResponse.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestEmailVerificationWellKnownNoResponse.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestInvalidEmail.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestJwksHttpNotFound.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestJwksInvalidResponse.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestKeyBindingSigningFailed.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestRpOriginIsOpaque.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenHttpNotFound.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenInvalidContentType.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenInvalidResponse.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenInvalidSdJwt.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenMalformedSdJwt.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenNoResponse.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbInvalidAudience.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbInvalidIssuedAt.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbInvalidNonce.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbInvalidSdHash.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbInvalidTyp.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbMissingAud.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbMissingCnf.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbMissingIat.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbMissingNonce.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbMissingSdHash.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationKbSignatureFailed.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtInvalidEmail.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtInvalidEmailVerified.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtInvalidHolderKey.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtInvalidIssuedAt.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtInvalidIssuer.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtJwksMissingKeys.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtMissingCnf.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtMissingEmail.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtMissingIat.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtMissingIss.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtSignatureFailed.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestTokenVerificationSdJwtUnsupportedHeaderAlg.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestUserLoggedOut.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownAccountsEndpointCrossOrigin.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownHttpNotFound.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownInvalidContentType.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownInvalidResponse.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownIssuanceEndpointCrossOrigin.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownListEmpty.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownMissingAccountsEndpoint.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownMissingIssuanceEndpoint.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownNoResponse.md +1 -0
- package/build/src/third_party/issue-descriptions/emailVerificationRequestWellKnownUnsupportedSigningAlgorithm.md +1 -0
- package/build/src/third_party/issue-descriptions/genericBackUINavigationWouldSkipAd.md +4 -0
- package/build/src/third_party/lighthouse-devtools-mcp-bundle.js +4236 -4219
- package/build/src/tools/ToolDefinition.js +2 -2
- package/build/src/tools/console.js +7 -0
- package/build/src/tools/emulation.js +29 -2
- package/build/src/tools/extensions.js +5 -0
- package/build/src/tools/input.js +57 -19
- package/build/src/tools/lighthouse.js +18 -13
- package/build/src/tools/memory.js +142 -21
- package/build/src/tools/network.js +2 -2
- package/build/src/tools/pages.js +13 -5
- package/build/src/tools/performance.js +15 -11
- package/build/src/tools/screencast.js +32 -10
- package/build/src/tools/screenshot.js +158 -76
- package/build/src/tools/script.js +33 -11
- package/build/src/tools/slim/tools.js +3 -0
- package/build/src/tools/snapshot.js +3 -2
- package/build/src/tools/thirdPartyDeveloper.js +12 -2
- package/build/src/tools/webmcp.js +2 -0
- package/build/src/trace-processing/parse.js +7 -7
- package/build/src/utils/files.js +43 -0
- package/build/src/version.js +4 -1
- package/package.json +21 -17
- package/build/src/telemetry/metricUtils.js +0 -15
package/README.md
CHANGED
|
@@ -38,7 +38,7 @@ You can explicitly enable them with `--usage-statistics` if desired.
|
|
|
38
38
|
|
|
39
39
|
## Requirements
|
|
40
40
|
|
|
41
|
-
- [Node.js](https://nodejs.org/)
|
|
41
|
+
- [Node.js](https://nodejs.org/) [LTS](https://github.com/nodejs/Release#release-schedule) version.
|
|
42
42
|
- [Brave Browser](https://brave.com/) current release version or newer.
|
|
43
43
|
- [npm](https://www.npmjs.com/)
|
|
44
44
|
|
|
@@ -279,7 +279,7 @@ To install Chrome DevTools MCP with skills, add the marketplace registry in Clau
|
|
|
279
279
|
Then, install the plugin:
|
|
280
280
|
|
|
281
281
|
```sh
|
|
282
|
-
/plugin install chrome-devtools-mcp
|
|
282
|
+
/plugin install chrome-devtools-mcp@chrome-devtools-plugins
|
|
283
283
|
```
|
|
284
284
|
|
|
285
285
|
Restart Claude Code to have the MCP server and skills load (check with `/skills`).
|
|
@@ -367,7 +367,7 @@ and the expert guidance it needs to use them effectively.
|
|
|
367
367
|
|
|
368
368
|
1. Open the **Command Palette** (`Cmd+Shift+P` on macOS or `Ctrl+Shift+P` on Windows/Linux).
|
|
369
369
|
2. Search for and run the **Chat: Install Plugin From Source** command.
|
|
370
|
-
3. Paste in our repository
|
|
370
|
+
3. Paste in our repository name: `ChromeDevTools/chrome-devtools-mcp`.
|
|
371
371
|
|
|
372
372
|
That's it! Your agent is now supercharged with Chrome DevTools capabilities.
|
|
373
373
|
|
|
@@ -632,11 +632,16 @@ If you run into any issues, checkout our [troubleshooting guide](./docs/troubles
|
|
|
632
632
|
- [`take_snapshot`](docs/tool-reference.md#take_snapshot)
|
|
633
633
|
- [`screencast_start`](docs/tool-reference.md#screencast_start)
|
|
634
634
|
- [`screencast_stop`](docs/tool-reference.md#screencast_stop)
|
|
635
|
-
- **Memory** (
|
|
636
|
-
- [`
|
|
637
|
-
- [`
|
|
638
|
-
- [`
|
|
639
|
-
- [`
|
|
635
|
+
- **Memory** (9 tools)
|
|
636
|
+
- [`take_heapsnapshot`](docs/tool-reference.md#take_heapsnapshot)
|
|
637
|
+
- [`close_heapsnapshot`](docs/tool-reference.md#close_heapsnapshot)
|
|
638
|
+
- [`get_heapsnapshot_class_nodes`](docs/tool-reference.md#get_heapsnapshot_class_nodes)
|
|
639
|
+
- [`get_heapsnapshot_details`](docs/tool-reference.md#get_heapsnapshot_details)
|
|
640
|
+
- [`get_heapsnapshot_dominators`](docs/tool-reference.md#get_heapsnapshot_dominators)
|
|
641
|
+
- [`get_heapsnapshot_edges`](docs/tool-reference.md#get_heapsnapshot_edges)
|
|
642
|
+
- [`get_heapsnapshot_retainers`](docs/tool-reference.md#get_heapsnapshot_retainers)
|
|
643
|
+
- [`get_heapsnapshot_retaining_paths`](docs/tool-reference.md#get_heapsnapshot_retaining_paths)
|
|
644
|
+
- [`get_heapsnapshot_summary`](docs/tool-reference.md#get_heapsnapshot_summary)
|
|
640
645
|
- **Extensions** (5 tools)
|
|
641
646
|
- [`install_extension`](docs/tool-reference.md#install_extension)
|
|
642
647
|
- [`list_extensions`](docs/tool-reference.md#list_extensions)
|
|
@@ -713,10 +718,30 @@ The Brave DevTools MCP server supports the following configuration options:
|
|
|
713
718
|
If enabled, ignores errors relative to self-signed and expired certificates. Use with caution.
|
|
714
719
|
- **Type:** boolean
|
|
715
720
|
|
|
721
|
+
- **`--experimentalPageIdRouting`/ `--experimental-page-id-routing`**
|
|
722
|
+
Whether to expose pageId on page-scoped tools and route requests by page ID (useful for concurrent agent sessions).
|
|
723
|
+
- **Type:** boolean
|
|
724
|
+
|
|
725
|
+
- **`--experimentalDevtools`/ `--experimental-devtools`**
|
|
726
|
+
Whether to enable automation over DevTools targets
|
|
727
|
+
- **Type:** boolean
|
|
728
|
+
|
|
716
729
|
- **`--experimentalVision`/ `--experimental-vision`**
|
|
717
730
|
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.
|
|
718
731
|
- **Type:** boolean
|
|
719
732
|
|
|
733
|
+
- **`--memoryDebugging`/ `--memory-debugging`, `-experimentalMemory`**
|
|
734
|
+
Whether to enable memory debugging tools.
|
|
735
|
+
- **Type:** boolean
|
|
736
|
+
|
|
737
|
+
- **`--experimentalStructuredContent`/ `--experimental-structured-content`**
|
|
738
|
+
Whether to output structured formatted content.
|
|
739
|
+
- **Type:** boolean
|
|
740
|
+
|
|
741
|
+
- **`--experimentalIncludeAllPages`/ `--experimental-include-all-pages`**
|
|
742
|
+
Whether to include all kinds of pages such as webviews or background pages as pages.
|
|
743
|
+
- **Type:** boolean
|
|
744
|
+
|
|
720
745
|
- **`--experimentalScreencast`/ `--experimental-screencast`**
|
|
721
746
|
Exposes experimental screencast tools (requires ffmpeg). Install ffmpeg https://www.ffmpeg.org/download.html and ensure it is available in the MCP server PATH.
|
|
722
747
|
- **Type:** boolean
|
|
@@ -726,13 +751,21 @@ The Brave DevTools MCP server supports the following configuration options:
|
|
|
726
751
|
- **Type:** string
|
|
727
752
|
|
|
728
753
|
- **`--categoryExperimentalWebmcp`/ `--category-experimental-webmcp`**
|
|
729
|
-
Set to true to enable debugging WebMCP tools. Requires Brave with the following flags: `--enable-features=
|
|
754
|
+
Set to true to enable debugging WebMCP tools. Requires Brave with the following flags: `--enable-features=WebMCP,DevToolsWebMCPSupport`
|
|
730
755
|
- **Type:** boolean
|
|
731
756
|
|
|
732
757
|
- **`--braveArg`/ `--brave-arg`**
|
|
733
758
|
Additional arguments for Brave. Only applies when Brave is launched by brave-devtools-mcp.
|
|
734
759
|
- **Type:** array
|
|
735
760
|
|
|
761
|
+
- **`--blockedUrlPattern`/ `--blocked-url-pattern`**
|
|
762
|
+
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.
|
|
763
|
+
- **Type:** array
|
|
764
|
+
|
|
765
|
+
- **`--allowedUrlPattern`/ `--allowed-url-pattern`**
|
|
766
|
+
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.
|
|
767
|
+
- **Type:** array
|
|
768
|
+
|
|
736
769
|
- **`--ignoreDefaultBraveArg`/ `--ignore-default-brave-arg`**
|
|
737
770
|
Explicitly disable default arguments for Brave. Only applies when Brave is launched by brave-devtools-mcp.
|
|
738
771
|
- **Type:** array
|
|
@@ -772,12 +805,29 @@ The Brave DevTools MCP server supports the following configuration options:
|
|
|
772
805
|
- **Type:** boolean
|
|
773
806
|
- **Default:** `false`
|
|
774
807
|
|
|
808
|
+
- **`--screenshotFormat`/ `--screenshot-format`**
|
|
809
|
+
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").
|
|
810
|
+
- **Type:** string
|
|
811
|
+
- **Choices:** `jpeg`, `png`, `webp`
|
|
812
|
+
|
|
813
|
+
- **`--screenshotQuality`/ `--screenshot-quality`**
|
|
814
|
+
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.
|
|
815
|
+
- **Type:** number
|
|
816
|
+
|
|
817
|
+
- **`--screenshotMaxWidth`/ `--screenshot-max-width`**
|
|
818
|
+
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.
|
|
819
|
+
- **Type:** number
|
|
820
|
+
|
|
821
|
+
- **`--screenshotMaxHeight`/ `--screenshot-max-height`**
|
|
822
|
+
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.
|
|
823
|
+
- **Type:** number
|
|
824
|
+
|
|
775
825
|
- **`--slim`**
|
|
776
826
|
Exposes a "slim" set of 3 tools covering navigation, script execution and screenshots only. Useful for basic browser tasks.
|
|
777
827
|
- **Type:** boolean
|
|
778
828
|
|
|
779
829
|
- **`--redactNetworkHeaders`/ `--redact-network-headers`**
|
|
780
|
-
If true, redacts some of the network headers considered
|
|
830
|
+
If true, redacts some of the network headers considered sensitive before returning to the client.
|
|
781
831
|
- **Type:** boolean
|
|
782
832
|
- **Default:** `false`
|
|
783
833
|
|
|
@@ -826,6 +876,34 @@ You can also run `npx chrome-devtools-mcp@latest --help` to see all available co
|
|
|
826
876
|
|
|
827
877
|
## Concepts
|
|
828
878
|
|
|
879
|
+
### Concurrent sessions
|
|
880
|
+
|
|
881
|
+
Most MCP clients start one Chrome DevTools MCP server per conversation. If your
|
|
882
|
+
client shares a single server instance across concurrent agents or subagents,
|
|
883
|
+
start the server with `--experimentalPageIdRouting`. This exposes `pageId` on
|
|
884
|
+
page-scoped tools so each agent can route tool calls to the tab it is working
|
|
885
|
+
with.
|
|
886
|
+
|
|
887
|
+
```json
|
|
888
|
+
{
|
|
889
|
+
"mcpServers": {
|
|
890
|
+
"chrome-devtools": {
|
|
891
|
+
"command": "npx",
|
|
892
|
+
"args": [
|
|
893
|
+
"-y",
|
|
894
|
+
"chrome-devtools-mcp@latest",
|
|
895
|
+
"--experimentalPageIdRouting"
|
|
896
|
+
]
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
```
|
|
901
|
+
|
|
902
|
+
If you run multiple independent MCP client sessions and want each session to
|
|
903
|
+
launch its own temporary Chrome profile, also pass `--isolated`. This avoids
|
|
904
|
+
sharing the default Chrome DevTools MCP user data directory between those
|
|
905
|
+
server instances.
|
|
906
|
+
|
|
829
907
|
### User data directory
|
|
830
908
|
|
|
831
909
|
`brave-devtools-mcp` starts a Brave release channel instance using the following user
|
|
@@ -19,8 +19,8 @@ export class HeapSnapshotManager {
|
|
|
19
19
|
this.#snapshots.set(absolutePath, {
|
|
20
20
|
snapshot,
|
|
21
21
|
worker,
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
idToClassKey: new Map(),
|
|
23
|
+
classKeyToId: new Map(),
|
|
24
24
|
idGenerator: createIdGenerator(),
|
|
25
25
|
});
|
|
26
26
|
return snapshot;
|
|
@@ -30,10 +30,10 @@ export class HeapSnapshotManager {
|
|
|
30
30
|
const filter = new DevTools.HeapSnapshotModel.HeapSnapshotModel.NodeFilter();
|
|
31
31
|
const aggregates = await snapshot.aggregatesWithFilter(filter);
|
|
32
32
|
for (const key of Object.keys(aggregates)) {
|
|
33
|
-
const
|
|
33
|
+
const id = await this.getOrCreateIdForClassKey(filePath, key);
|
|
34
34
|
const aggregate = aggregates[key];
|
|
35
35
|
if (aggregate) {
|
|
36
|
-
aggregate[stableIdSymbol] =
|
|
36
|
+
aggregate[stableIdSymbol] = id;
|
|
37
37
|
}
|
|
38
38
|
}
|
|
39
39
|
return aggregates;
|
|
@@ -46,26 +46,59 @@ export class HeapSnapshotManager {
|
|
|
46
46
|
const snapshot = await this.getSnapshot(filePath);
|
|
47
47
|
return snapshot.staticData;
|
|
48
48
|
}
|
|
49
|
-
async
|
|
49
|
+
async getOrCreateIdForClassKey(filePath, classKey) {
|
|
50
50
|
const cached = this.#getCachedSnapshot(filePath);
|
|
51
|
-
let
|
|
52
|
-
if (!
|
|
53
|
-
|
|
54
|
-
cached.
|
|
55
|
-
cached.
|
|
51
|
+
let id = cached.classKeyToId.get(classKey);
|
|
52
|
+
if (!id) {
|
|
53
|
+
id = cached.idGenerator();
|
|
54
|
+
cached.classKeyToId.set(classKey, id);
|
|
55
|
+
cached.idToClassKey.set(id, classKey);
|
|
56
56
|
}
|
|
57
|
-
return
|
|
57
|
+
return id;
|
|
58
58
|
}
|
|
59
|
-
async
|
|
59
|
+
async getNodesById(filePath, id) {
|
|
60
60
|
const snapshot = await this.getSnapshot(filePath);
|
|
61
61
|
const filter = new DevTools.HeapSnapshotModel.HeapSnapshotModel.NodeFilter();
|
|
62
|
-
const className = await this.
|
|
62
|
+
const className = await this.resolveClassKeyFromId(filePath, id);
|
|
63
63
|
if (!className) {
|
|
64
|
-
throw new Error(`Class with
|
|
64
|
+
throw new Error(`Class with ID ${id} not found in heap snapshot`);
|
|
65
65
|
}
|
|
66
66
|
const provider = snapshot.createNodesProviderForClass(className, filter);
|
|
67
|
-
|
|
68
|
-
|
|
67
|
+
return await provider.serializeItemsRange(0, Infinity);
|
|
68
|
+
}
|
|
69
|
+
async getRetainers(filePath, nodeId) {
|
|
70
|
+
const snapshot = await this.getSnapshot(filePath);
|
|
71
|
+
const nodeIndex = await snapshot.nodeIndexForId(nodeId);
|
|
72
|
+
if (nodeIndex === undefined) {
|
|
73
|
+
throw new Error(`Node with ID ${nodeId} not found`);
|
|
74
|
+
}
|
|
75
|
+
const provider = snapshot.createRetainingEdgesProvider(nodeIndex);
|
|
76
|
+
return await provider.serializeItemsRange(0, Infinity);
|
|
77
|
+
}
|
|
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);
|
|
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);
|
|
101
|
+
return await provider.serializeItemsRange(0, Infinity);
|
|
69
102
|
}
|
|
70
103
|
#getCachedSnapshot(filePath) {
|
|
71
104
|
const absolutePath = path.resolve(filePath);
|
|
@@ -75,9 +108,9 @@ export class HeapSnapshotManager {
|
|
|
75
108
|
}
|
|
76
109
|
return cached;
|
|
77
110
|
}
|
|
78
|
-
async
|
|
111
|
+
async resolveClassKeyFromId(filePath, id) {
|
|
79
112
|
const cached = this.#getCachedSnapshot(filePath);
|
|
80
|
-
return cached.
|
|
113
|
+
return cached.idToClassKey.get(id);
|
|
81
114
|
}
|
|
82
115
|
async #loadSnapshot(absolutePath) {
|
|
83
116
|
const workerProxy = new DevTools.HeapSnapshotModel.HeapSnapshotProxy.HeapSnapshotWorkerProxy(() => {
|
|
@@ -98,13 +131,18 @@ export class HeapSnapshotManager {
|
|
|
98
131
|
const snapshot = await snapshotPromise;
|
|
99
132
|
return { snapshot, worker: workerProxy };
|
|
100
133
|
}
|
|
134
|
+
hasSnapshots() {
|
|
135
|
+
return this.#snapshots.size > 0;
|
|
136
|
+
}
|
|
101
137
|
dispose(filePath) {
|
|
102
138
|
const absolutePath = path.resolve(filePath);
|
|
103
139
|
const cached = this.#snapshots.get(absolutePath);
|
|
104
140
|
if (cached) {
|
|
105
141
|
cached.worker.dispose();
|
|
106
142
|
this.#snapshots.delete(absolutePath);
|
|
143
|
+
return true;
|
|
107
144
|
}
|
|
145
|
+
return false;
|
|
108
146
|
}
|
|
109
147
|
}
|
|
110
148
|
//# sourceMappingURL=HeapSnapshotManager.js.map
|
package/build/src/McpContext.js
CHANGED
|
@@ -4,17 +4,19 @@
|
|
|
4
4
|
* SPDX-License-Identifier: Apache-2.0
|
|
5
5
|
*/
|
|
6
6
|
import fs from 'node:fs/promises';
|
|
7
|
+
import fsPromises from 'node:fs/promises';
|
|
7
8
|
import os from 'node:os';
|
|
8
9
|
import path from 'node:path';
|
|
9
10
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
10
|
-
import { UniverseManager } from './DevtoolsUtils.js';
|
|
11
|
+
import { overrideDevToolsGlobals, UniverseManager, } from './devtools/DevtoolsUtils.js';
|
|
11
12
|
import { HeapSnapshotManager } from './HeapSnapshotManager.js';
|
|
12
13
|
import { McpPage } from './McpPage.js';
|
|
13
14
|
import { NetworkCollector, ConsoleCollector, } from './PageCollector.js';
|
|
15
|
+
import { ServiceWorkerConsoleCollector } from './ServiceWorkerCollector.js';
|
|
14
16
|
import { Locator, PredefinedNetworkConditions, } from './third_party/index.js';
|
|
15
17
|
import { listPages } from './tools/pages.js';
|
|
16
18
|
import { CLOSE_PAGE_ERROR } from './tools/ToolDefinition.js';
|
|
17
|
-
import { ensureExtension, getTempFilePath } from './utils/files.js';
|
|
19
|
+
import { ensureExtension, getTempFilePath, resolveCanonicalPath, } from './utils/files.js';
|
|
18
20
|
import { getNetworkMultiplierFromString } from './WaitForHelper.js';
|
|
19
21
|
const DEFAULT_TIMEOUT = 5_000;
|
|
20
22
|
const NAVIGATION_TIMEOUT = 10_000;
|
|
@@ -32,6 +34,7 @@ export class McpContext {
|
|
|
32
34
|
#networkCollector;
|
|
33
35
|
#consoleCollector;
|
|
34
36
|
#devtoolsUniverseManager;
|
|
37
|
+
#serviceWorkerConsoleCollector;
|
|
35
38
|
#isRunningTrace = false;
|
|
36
39
|
#screenRecorderData = null;
|
|
37
40
|
#nextPageId = 1;
|
|
@@ -44,6 +47,11 @@ export class McpContext {
|
|
|
44
47
|
#heapSnapshotManager = new HeapSnapshotManager();
|
|
45
48
|
#roots = undefined;
|
|
46
49
|
constructor(browser, logger, options, locatorClass) {
|
|
50
|
+
overrideDevToolsGlobals({
|
|
51
|
+
loadResource: (url) => {
|
|
52
|
+
return this.loadResource(url);
|
|
53
|
+
},
|
|
54
|
+
});
|
|
47
55
|
this.browser = browser;
|
|
48
56
|
this.logger = logger;
|
|
49
57
|
this.#locatorClass = locatorClass;
|
|
@@ -62,19 +70,22 @@ export class McpContext {
|
|
|
62
70
|
},
|
|
63
71
|
};
|
|
64
72
|
});
|
|
73
|
+
this.#serviceWorkerConsoleCollector = new ServiceWorkerConsoleCollector(this.browser);
|
|
65
74
|
this.#devtoolsUniverseManager = new UniverseManager(this.browser);
|
|
66
75
|
}
|
|
67
76
|
async #init() {
|
|
68
77
|
const pages = await this.createPagesSnapshot();
|
|
69
|
-
await this.createExtensionServiceWorkersSnapshot();
|
|
78
|
+
const workers = await this.createExtensionServiceWorkersSnapshot();
|
|
70
79
|
await this.#networkCollector.init(pages);
|
|
71
80
|
await this.#consoleCollector.init(pages);
|
|
72
81
|
await this.#devtoolsUniverseManager.init(pages);
|
|
82
|
+
await this.#serviceWorkerConsoleCollector.init(workers);
|
|
73
83
|
}
|
|
74
84
|
dispose() {
|
|
75
85
|
this.#networkCollector.dispose();
|
|
76
86
|
this.#consoleCollector.dispose();
|
|
77
87
|
this.#devtoolsUniverseManager.dispose();
|
|
88
|
+
this.#serviceWorkerConsoleCollector.dispose();
|
|
78
89
|
for (const mcpPage of this.#mcpPages.values()) {
|
|
79
90
|
mcpPage.dispose();
|
|
80
91
|
}
|
|
@@ -106,7 +117,7 @@ export class McpContext {
|
|
|
106
117
|
setRoots(roots) {
|
|
107
118
|
this.#roots = roots;
|
|
108
119
|
}
|
|
109
|
-
validatePath(filePath) {
|
|
120
|
+
async validatePath(filePath) {
|
|
110
121
|
if (filePath === undefined) {
|
|
111
122
|
return;
|
|
112
123
|
}
|
|
@@ -114,19 +125,40 @@ export class McpContext {
|
|
|
114
125
|
if (roots === undefined) {
|
|
115
126
|
return;
|
|
116
127
|
}
|
|
117
|
-
|
|
128
|
+
let canonicalPath;
|
|
129
|
+
try {
|
|
130
|
+
canonicalPath = await resolveCanonicalPath(filePath);
|
|
131
|
+
}
|
|
132
|
+
catch (err) {
|
|
133
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
134
|
+
console.error(`[MCP Context] Error resolving real path for ${filePath}: ${errMsg}`);
|
|
135
|
+
throw new Error(`Access denied: Cannot resolve base path for ${filePath}.`);
|
|
136
|
+
}
|
|
137
|
+
let allowed = false;
|
|
118
138
|
for (const root of roots) {
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
139
|
+
try {
|
|
140
|
+
const rootPathUri = root.uri;
|
|
141
|
+
const rootPath = path.resolve(fileURLToPath(rootPathUri));
|
|
142
|
+
const canonicalRoot = await fsPromises.realpath(rootPath);
|
|
143
|
+
if (canonicalPath === canonicalRoot ||
|
|
144
|
+
canonicalPath.startsWith(canonicalRoot + path.sep)) {
|
|
145
|
+
allowed = true;
|
|
146
|
+
break;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
catch (rootErr) {
|
|
150
|
+
const errMsg = rootErr instanceof Error ? rootErr.message : String(rootErr);
|
|
151
|
+
console.warn(`[MCP Context] Could not resolve configured root ${root.uri}: ${errMsg}`);
|
|
152
|
+
// Skip this root if it cannot be resolved.
|
|
123
153
|
}
|
|
124
154
|
}
|
|
125
|
-
|
|
155
|
+
if (!allowed) {
|
|
156
|
+
throw new Error(`Access denied: path ${filePath} (canonical: ${canonicalPath}) is not within any of the configured workspace roots.`);
|
|
157
|
+
}
|
|
126
158
|
}
|
|
127
159
|
resolveCdpRequestId(page, cdpRequestId) {
|
|
128
160
|
if (!cdpRequestId) {
|
|
129
|
-
this.logger('no network request');
|
|
161
|
+
this.logger?.('no network request');
|
|
130
162
|
return;
|
|
131
163
|
}
|
|
132
164
|
const request = this.#networkCollector.find(page.pptrPage, request => {
|
|
@@ -134,7 +166,7 @@ export class McpContext {
|
|
|
134
166
|
return request.id === cdpRequestId;
|
|
135
167
|
});
|
|
136
168
|
if (!request) {
|
|
137
|
-
this.logger('no network request for ' + cdpRequestId);
|
|
169
|
+
this.logger?.('no network request for ' + cdpRequestId);
|
|
138
170
|
return;
|
|
139
171
|
}
|
|
140
172
|
return this.#networkCollector.getIdForResource(request);
|
|
@@ -195,7 +227,13 @@ export class McpContext {
|
|
|
195
227
|
const page = targetPage ?? this.getSelectedPptrPage();
|
|
196
228
|
const mcpPage = this.#getMcpPage(page);
|
|
197
229
|
const newSettings = { ...mcpPage.emulationSettings };
|
|
198
|
-
if
|
|
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) {
|
|
199
237
|
await page.emulateNetworkConditions(null);
|
|
200
238
|
delete newSettings.networkConditions;
|
|
201
239
|
}
|
|
@@ -213,12 +251,23 @@ export class McpContext {
|
|
|
213
251
|
await page.emulateNetworkConditions(networkCondition);
|
|
214
252
|
newSettings.networkConditions = options.networkConditions;
|
|
215
253
|
}
|
|
254
|
+
const secondarySession = this.getDevToolsUniverse(mcpPage)?.session;
|
|
216
255
|
if (!options.cpuThrottlingRate) {
|
|
217
256
|
await page.emulateCPUThrottling(1);
|
|
257
|
+
if (secondarySession) {
|
|
258
|
+
await secondarySession.send('Emulation.setCPUThrottlingRate', {
|
|
259
|
+
rate: 1,
|
|
260
|
+
});
|
|
261
|
+
}
|
|
218
262
|
delete newSettings.cpuThrottlingRate;
|
|
219
263
|
}
|
|
220
264
|
else {
|
|
221
265
|
await page.emulateCPUThrottling(options.cpuThrottlingRate);
|
|
266
|
+
if (secondarySession) {
|
|
267
|
+
await secondarySession.send('Emulation.setCPUThrottlingRate', {
|
|
268
|
+
rate: options.cpuThrottlingRate,
|
|
269
|
+
});
|
|
270
|
+
}
|
|
222
271
|
newSettings.cpuThrottlingRate = options.cpuThrottlingRate;
|
|
223
272
|
}
|
|
224
273
|
if (!options.geolocation) {
|
|
@@ -250,7 +299,6 @@ export class McpContext {
|
|
|
250
299
|
newSettings.colorScheme = options.colorScheme;
|
|
251
300
|
}
|
|
252
301
|
if (!options.viewport) {
|
|
253
|
-
await page.setViewport(null);
|
|
254
302
|
delete newSettings.viewport;
|
|
255
303
|
}
|
|
256
304
|
else {
|
|
@@ -260,14 +308,22 @@ export class McpContext {
|
|
|
260
308
|
hasTouch: false,
|
|
261
309
|
isLandscape: false,
|
|
262
310
|
};
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
311
|
+
newSettings.viewport = { ...defaults, ...options.viewport };
|
|
312
|
+
}
|
|
313
|
+
if (options.extraHttpHeaders !== undefined) {
|
|
314
|
+
await page.setExtraHTTPHeaders(options.extraHttpHeaders);
|
|
315
|
+
newSettings.extraHttpHeaders = options.extraHttpHeaders;
|
|
316
|
+
if (Object.keys(options.extraHttpHeaders).length === 0) {
|
|
317
|
+
delete newSettings.extraHttpHeaders;
|
|
318
|
+
}
|
|
266
319
|
}
|
|
267
320
|
mcpPage.emulationSettings = Object.keys(newSettings).length
|
|
268
321
|
? newSettings
|
|
269
322
|
: {};
|
|
270
323
|
this.#updateSelectedPageTimeouts();
|
|
324
|
+
// This should happen after updating the page timeouts.
|
|
325
|
+
// Setting the viewport can trigger a reload which we don't want to timeout.
|
|
326
|
+
await page.setViewport(newSettings.viewport ?? null);
|
|
271
327
|
}
|
|
272
328
|
setIsRunningPerformanceTrace(x) {
|
|
273
329
|
this.#isRunningTrace = x;
|
|
@@ -333,9 +389,9 @@ export class McpContext {
|
|
|
333
389
|
page.pptrPage.setDefaultTimeout(DEFAULT_TIMEOUT * cpuMultiplier);
|
|
334
390
|
// 10sec should be enough for the load event to be emitted during
|
|
335
391
|
// navigations.
|
|
336
|
-
// Increased in case we throttle the network requests
|
|
392
|
+
// Increased in case we throttle the network requests or the CPU
|
|
337
393
|
const networkMultiplier = getNetworkMultiplierFromString(page.networkConditions);
|
|
338
|
-
page.pptrPage.setDefaultNavigationTimeout(NAVIGATION_TIMEOUT * networkMultiplier);
|
|
394
|
+
page.pptrPage.setDefaultNavigationTimeout(NAVIGATION_TIMEOUT * networkMultiplier * cpuMultiplier);
|
|
339
395
|
}
|
|
340
396
|
// Linear scan over per-page snapshots. The page count is small (typically
|
|
341
397
|
// 2-10) so a reverse index isn't worthwhile given the uid-reuse lifecycle
|
|
@@ -372,6 +428,9 @@ export class McpContext {
|
|
|
372
428
|
});
|
|
373
429
|
return this.#extensionServiceWorkers;
|
|
374
430
|
}
|
|
431
|
+
getServiceWorkerConsoleData(extensionId) {
|
|
432
|
+
return this.#serviceWorkerConsoleCollector.getData(extensionId);
|
|
433
|
+
}
|
|
375
434
|
async createPagesSnapshot() {
|
|
376
435
|
const { pages: allPages, isolatedContextNames } = await this.#getAllPages();
|
|
377
436
|
for (const page of allPages) {
|
|
@@ -381,7 +440,7 @@ export class McpContext {
|
|
|
381
440
|
this.#mcpPages.set(page, mcpPage);
|
|
382
441
|
// We emulate a focused page for all pages to support multi-agent workflows.
|
|
383
442
|
void page.emulateFocusedPage(true).catch(error => {
|
|
384
|
-
this.logger('Error turning on focused page emulation', error);
|
|
443
|
+
this.logger?.('Error turning on focused page emulation', error);
|
|
385
444
|
});
|
|
386
445
|
}
|
|
387
446
|
mcpPage.isolatedContextName = isolatedContextNames.get(page);
|
|
@@ -427,7 +486,7 @@ export class McpContext {
|
|
|
427
486
|
this.#extensionPages.set(target, page);
|
|
428
487
|
}
|
|
429
488
|
catch (e) {
|
|
430
|
-
this.logger('Failed to get page for extension target', e);
|
|
489
|
+
this.logger?.('Failed to get page for extension target', e);
|
|
431
490
|
}
|
|
432
491
|
}
|
|
433
492
|
}
|
|
@@ -462,7 +521,7 @@ export class McpContext {
|
|
|
462
521
|
return { pages: allPages, isolatedContextNames };
|
|
463
522
|
}
|
|
464
523
|
async detectOpenDevToolsWindows() {
|
|
465
|
-
this.logger('Detecting open DevTools windows');
|
|
524
|
+
this.logger?.('Detecting open DevTools windows');
|
|
466
525
|
const { pages } = await this.#getAllPages();
|
|
467
526
|
await Promise.all(pages.map(async (page) => {
|
|
468
527
|
const mcpPage = this.#mcpPages.get(page);
|
|
@@ -499,7 +558,7 @@ export class McpContext {
|
|
|
499
558
|
}
|
|
500
559
|
async saveTemporaryFile(data, filename) {
|
|
501
560
|
const filepath = await getTempFilePath(filename);
|
|
502
|
-
this.validatePath(filepath);
|
|
561
|
+
await this.validatePath(filepath);
|
|
503
562
|
try {
|
|
504
563
|
await fs.writeFile(filepath, data);
|
|
505
564
|
}
|
|
@@ -509,7 +568,7 @@ export class McpContext {
|
|
|
509
568
|
return { filepath };
|
|
510
569
|
}
|
|
511
570
|
async saveFile(data, clientProvidedFilePath, extension) {
|
|
512
|
-
this.validatePath(clientProvidedFilePath);
|
|
571
|
+
await this.validatePath(clientProvidedFilePath);
|
|
513
572
|
try {
|
|
514
573
|
const filePath = ensureExtension(path.resolve(clientProvidedFilePath), extension);
|
|
515
574
|
await fs.mkdir(path.dirname(filePath), { recursive: true });
|
|
@@ -517,7 +576,7 @@ export class McpContext {
|
|
|
517
576
|
return { filename: filePath };
|
|
518
577
|
}
|
|
519
578
|
catch (err) {
|
|
520
|
-
this.logger(err);
|
|
579
|
+
this.logger?.(err);
|
|
521
580
|
throw new Error('Could not save a file', { cause: err });
|
|
522
581
|
}
|
|
523
582
|
}
|
|
@@ -562,7 +621,6 @@ export class McpContext {
|
|
|
562
621
|
await this.#networkCollector.init(pages);
|
|
563
622
|
}
|
|
564
623
|
async installExtension(extensionPath) {
|
|
565
|
-
this.validatePath(extensionPath);
|
|
566
624
|
const id = await this.browser.installExtension(extensionPath);
|
|
567
625
|
return id;
|
|
568
626
|
}
|
|
@@ -586,20 +644,54 @@ export class McpContext {
|
|
|
586
644
|
return pptrExtensions.get(id);
|
|
587
645
|
}
|
|
588
646
|
async getHeapSnapshotAggregates(filePath) {
|
|
589
|
-
this.validatePath(filePath);
|
|
590
647
|
return await this.#heapSnapshotManager.getAggregates(filePath);
|
|
591
648
|
}
|
|
592
649
|
async getHeapSnapshotStats(filePath) {
|
|
593
|
-
this.validatePath(filePath);
|
|
594
650
|
return await this.#heapSnapshotManager.getStats(filePath);
|
|
595
651
|
}
|
|
596
652
|
async getHeapSnapshotStaticData(filePath) {
|
|
597
|
-
this.validatePath(filePath);
|
|
598
653
|
return await this.#heapSnapshotManager.getStaticData(filePath);
|
|
599
654
|
}
|
|
600
|
-
async
|
|
601
|
-
this.
|
|
602
|
-
|
|
655
|
+
async getHeapSnapshotNodesById(filePath, id) {
|
|
656
|
+
return await this.#heapSnapshotManager.getNodesById(filePath, id);
|
|
657
|
+
}
|
|
658
|
+
async getHeapSnapshotRetainers(filePath, nodeId) {
|
|
659
|
+
return await this.#heapSnapshotManager.getRetainers(filePath, nodeId);
|
|
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);
|
|
603
695
|
}
|
|
604
696
|
}
|
|
605
697
|
//# sourceMappingURL=McpContext.js.map
|