chrome-devtools-mcp 1.1.0 → 1.2.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 +12 -3
- package/build/src/DevtoolsUtils.js +57 -0
- package/build/src/HeapSnapshotManager.js +5 -0
- package/build/src/McpContext.js +28 -8
- package/build/src/McpPage.js +9 -9
- package/build/src/McpResponse.js +101 -53
- package/build/src/PageCollector.js +7 -7
- package/build/src/ServiceWorkerCollector.js +171 -0
- package/build/src/TextSnapshot.js +1 -1
- package/build/src/ToolHandler.js +11 -5
- package/build/src/WaitForHelper.js +2 -2
- package/build/src/bin/chrome-devtools-cli-options.js +22 -4
- package/build/src/bin/chrome-devtools-mcp-cli-options.js +19 -4
- package/build/src/bin/chrome-devtools-mcp-main.js +5 -5
- package/build/src/browser.js +8 -4
- package/build/src/daemon/client.js +7 -7
- package/build/src/daemon/daemon.js +12 -12
- package/build/src/daemon/utils.js +2 -2
- package/build/src/formatters/IssueFormatter.js +4 -4
- package/build/src/index.js +13 -1
- package/build/src/telemetry/ClearcutLogger.js +1 -1
- package/build/src/telemetry/WatchdogClient.js +4 -4
- package/build/src/telemetry/persistence.js +2 -2
- 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 +30 -0
- package/build/src/third_party/bundled-packages.json +2 -1
- package/build/src/third_party/devtools-formatter-worker.js +1 -0
- package/build/src/third_party/devtools-heap-snapshot-worker.js +107 -0
- package/build/src/third_party/index.js +5906 -4913
- 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/tools/console.js +7 -0
- package/build/src/tools/emulation.js +1 -0
- package/build/src/tools/extensions.js +5 -2
- package/build/src/tools/input.js +11 -3
- package/build/src/tools/lighthouse.js +11 -6
- package/build/src/tools/memory.js +33 -10
- package/build/src/tools/network.js +2 -2
- package/build/src/tools/pages.js +13 -5
- package/build/src/tools/performance.js +8 -7
- package/build/src/tools/screencast.js +2 -1
- package/build/src/tools/screenshot.js +1 -1
- package/build/src/tools/script.js +2 -2
- 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 +5 -5
- package/build/src/version.js +1 -1
- package/package.json +14 -13
package/README.md
CHANGED
|
@@ -514,8 +514,9 @@ 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** (6 tools)
|
|
518
518
|
- [`take_heapsnapshot`](docs/tool-reference.md#take_heapsnapshot)
|
|
519
|
+
- [`close_heapsnapshot`](docs/tool-reference.md#close_heapsnapshot)
|
|
519
520
|
- [`get_heapsnapshot_class_nodes`](docs/tool-reference.md#get_heapsnapshot_class_nodes)
|
|
520
521
|
- [`get_heapsnapshot_details`](docs/tool-reference.md#get_heapsnapshot_details)
|
|
521
522
|
- [`get_heapsnapshot_retainers`](docs/tool-reference.md#get_heapsnapshot_retainers)
|
|
@@ -608,8 +609,8 @@ The Chrome DevTools MCP server supports the following configuration option:
|
|
|
608
609
|
Whether to enable coordinate-based tools such as click_at(x,y). Usually requires a computer-use model able to produce accurate coordinates by looking at screenshots.
|
|
609
610
|
- **Type:** boolean
|
|
610
611
|
|
|
611
|
-
- **`--
|
|
612
|
-
Whether to enable
|
|
612
|
+
- **`--memoryDebugging`/ `--memory-debugging`, `-experimentalMemory`**
|
|
613
|
+
Whether to enable memory debugging tools.
|
|
613
614
|
- **Type:** boolean
|
|
614
615
|
|
|
615
616
|
- **`--experimentalStructuredContent`/ `--experimental-structured-content`**
|
|
@@ -636,6 +637,14 @@ The Chrome DevTools MCP server supports the following configuration option:
|
|
|
636
637
|
Additional arguments for Chrome. Only applies when Chrome is launched by chrome-devtools-mcp.
|
|
637
638
|
- **Type:** array
|
|
638
639
|
|
|
640
|
+
- **`--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.
|
|
642
|
+
- **Type:** array
|
|
643
|
+
|
|
644
|
+
- **`--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.
|
|
646
|
+
- **Type:** array
|
|
647
|
+
|
|
639
648
|
- **`--ignoreDefaultChromeArg`/ `--ignore-default-chrome-arg`**
|
|
640
649
|
Explicitly disable default arguments for Chrome. Only applies when Chrome is launched by chrome-devtools-mcp.
|
|
641
650
|
- **Type:** array
|
|
@@ -18,6 +18,63 @@ export class FakeIssuesManager extends DevTools.Common.ObjectWrapper
|
|
|
18
18
|
}
|
|
19
19
|
// DevTools CDP errors can get noisy.
|
|
20
20
|
DevTools.ProtocolClient.InspectorBackend.test.suppressRequestErrors = true;
|
|
21
|
+
// Stub out Network emulation commands on the DevTools Agent prototype globally.
|
|
22
|
+
// This prevents the DevTools Frontend from ever resetting/clearing Puppeteer's
|
|
23
|
+
// active network blocking/throttling rules during target setup or session lifetime.
|
|
24
|
+
const networkAgentPrototype = DevTools.ProtocolClient.InspectorBackend.inspectorBackend.agentPrototypes.get('Network');
|
|
25
|
+
if (networkAgentPrototype) {
|
|
26
|
+
Object.defineProperty(networkAgentPrototype, 'invoke_emulateNetworkConditionsByRule', {
|
|
27
|
+
value: () => {
|
|
28
|
+
return Promise.resolve({
|
|
29
|
+
ruleIds: [],
|
|
30
|
+
getError: () => undefined,
|
|
31
|
+
});
|
|
32
|
+
},
|
|
33
|
+
writable: true,
|
|
34
|
+
configurable: true,
|
|
35
|
+
enumerable: true,
|
|
36
|
+
});
|
|
37
|
+
Object.defineProperty(networkAgentPrototype, 'invoke_overrideNetworkState', {
|
|
38
|
+
value: () => {
|
|
39
|
+
return Promise.resolve({
|
|
40
|
+
getError: () => undefined,
|
|
41
|
+
});
|
|
42
|
+
},
|
|
43
|
+
writable: true,
|
|
44
|
+
configurable: true,
|
|
45
|
+
enumerable: true,
|
|
46
|
+
});
|
|
47
|
+
Object.defineProperty(networkAgentPrototype, 'invoke_enable', {
|
|
48
|
+
value: () => {
|
|
49
|
+
return Promise.resolve({
|
|
50
|
+
getError: () => undefined,
|
|
51
|
+
});
|
|
52
|
+
},
|
|
53
|
+
writable: true,
|
|
54
|
+
configurable: true,
|
|
55
|
+
enumerable: true,
|
|
56
|
+
});
|
|
57
|
+
Object.defineProperty(networkAgentPrototype, 'invoke_disable', {
|
|
58
|
+
value: () => {
|
|
59
|
+
return Promise.resolve({
|
|
60
|
+
getError: () => undefined,
|
|
61
|
+
});
|
|
62
|
+
},
|
|
63
|
+
writable: true,
|
|
64
|
+
configurable: true,
|
|
65
|
+
enumerable: true,
|
|
66
|
+
});
|
|
67
|
+
Object.defineProperty(networkAgentPrototype, 'invoke_setBlockedURLs', {
|
|
68
|
+
value: () => {
|
|
69
|
+
return Promise.resolve({
|
|
70
|
+
getError: () => undefined,
|
|
71
|
+
});
|
|
72
|
+
},
|
|
73
|
+
writable: true,
|
|
74
|
+
configurable: true,
|
|
75
|
+
enumerable: true,
|
|
76
|
+
});
|
|
77
|
+
}
|
|
21
78
|
DevTools.I18n.DevToolsLocale.DevToolsLocale.instance({
|
|
22
79
|
create: true,
|
|
23
80
|
data: {
|
|
@@ -122,13 +122,18 @@ export class HeapSnapshotManager {
|
|
|
122
122
|
const snapshot = await snapshotPromise;
|
|
123
123
|
return { snapshot, worker: workerProxy };
|
|
124
124
|
}
|
|
125
|
+
hasSnapshots() {
|
|
126
|
+
return this.#snapshots.size > 0;
|
|
127
|
+
}
|
|
125
128
|
dispose(filePath) {
|
|
126
129
|
const absolutePath = path.resolve(filePath);
|
|
127
130
|
const cached = this.#snapshots.get(absolutePath);
|
|
128
131
|
if (cached) {
|
|
129
132
|
cached.worker.dispose();
|
|
130
133
|
this.#snapshots.delete(absolutePath);
|
|
134
|
+
return true;
|
|
131
135
|
}
|
|
136
|
+
return false;
|
|
132
137
|
}
|
|
133
138
|
}
|
|
134
139
|
//# sourceMappingURL=HeapSnapshotManager.js.map
|
package/build/src/McpContext.js
CHANGED
|
@@ -12,6 +12,7 @@ import { UniverseManager } from './DevtoolsUtils.js';
|
|
|
12
12
|
import { HeapSnapshotManager } from './HeapSnapshotManager.js';
|
|
13
13
|
import { McpPage } from './McpPage.js';
|
|
14
14
|
import { NetworkCollector, ConsoleCollector, } from './PageCollector.js';
|
|
15
|
+
import { ServiceWorkerConsoleCollector } from './ServiceWorkerCollector.js';
|
|
15
16
|
import { Locator, PredefinedNetworkConditions, } from './third_party/index.js';
|
|
16
17
|
import { listPages } from './tools/pages.js';
|
|
17
18
|
import { CLOSE_PAGE_ERROR } from './tools/ToolDefinition.js';
|
|
@@ -33,6 +34,7 @@ export class McpContext {
|
|
|
33
34
|
#networkCollector;
|
|
34
35
|
#consoleCollector;
|
|
35
36
|
#devtoolsUniverseManager;
|
|
37
|
+
#serviceWorkerConsoleCollector;
|
|
36
38
|
#isRunningTrace = false;
|
|
37
39
|
#screenRecorderData = null;
|
|
38
40
|
#nextPageId = 1;
|
|
@@ -63,19 +65,22 @@ export class McpContext {
|
|
|
63
65
|
},
|
|
64
66
|
};
|
|
65
67
|
});
|
|
68
|
+
this.#serviceWorkerConsoleCollector = new ServiceWorkerConsoleCollector(this.browser);
|
|
66
69
|
this.#devtoolsUniverseManager = new UniverseManager(this.browser);
|
|
67
70
|
}
|
|
68
71
|
async #init() {
|
|
69
72
|
const pages = await this.createPagesSnapshot();
|
|
70
|
-
await this.createExtensionServiceWorkersSnapshot();
|
|
73
|
+
const workers = await this.createExtensionServiceWorkersSnapshot();
|
|
71
74
|
await this.#networkCollector.init(pages);
|
|
72
75
|
await this.#consoleCollector.init(pages);
|
|
73
76
|
await this.#devtoolsUniverseManager.init(pages);
|
|
77
|
+
await this.#serviceWorkerConsoleCollector.init(workers);
|
|
74
78
|
}
|
|
75
79
|
dispose() {
|
|
76
80
|
this.#networkCollector.dispose();
|
|
77
81
|
this.#consoleCollector.dispose();
|
|
78
82
|
this.#devtoolsUniverseManager.dispose();
|
|
83
|
+
this.#serviceWorkerConsoleCollector.dispose();
|
|
79
84
|
for (const mcpPage of this.#mcpPages.values()) {
|
|
80
85
|
mcpPage.dispose();
|
|
81
86
|
}
|
|
@@ -148,7 +153,7 @@ export class McpContext {
|
|
|
148
153
|
}
|
|
149
154
|
resolveCdpRequestId(page, cdpRequestId) {
|
|
150
155
|
if (!cdpRequestId) {
|
|
151
|
-
this.logger('no network request');
|
|
156
|
+
this.logger?.('no network request');
|
|
152
157
|
return;
|
|
153
158
|
}
|
|
154
159
|
const request = this.#networkCollector.find(page.pptrPage, request => {
|
|
@@ -156,7 +161,7 @@ export class McpContext {
|
|
|
156
161
|
return request.id === cdpRequestId;
|
|
157
162
|
});
|
|
158
163
|
if (!request) {
|
|
159
|
-
this.logger('no network request for ' + cdpRequestId);
|
|
164
|
+
this.logger?.('no network request for ' + cdpRequestId);
|
|
160
165
|
return;
|
|
161
166
|
}
|
|
162
167
|
return this.#networkCollector.getIdForResource(request);
|
|
@@ -217,7 +222,13 @@ export class McpContext {
|
|
|
217
222
|
const page = targetPage ?? this.getSelectedPptrPage();
|
|
218
223
|
const mcpPage = this.#getMcpPage(page);
|
|
219
224
|
const newSettings = { ...mcpPage.emulationSettings };
|
|
220
|
-
if
|
|
225
|
+
// Skip network emulation if blocklist/allowlist is configured, as it conflicts with blocking rules in Puppeteer.
|
|
226
|
+
if (this.#options.hasNetworkBlockOrAllowlist) {
|
|
227
|
+
if (options.networkConditions !== undefined) {
|
|
228
|
+
throw new Error('Network throttling is not supported when network blocking (allowlist/blocklist) is configured.');
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
else if (!options.networkConditions) {
|
|
221
232
|
await page.emulateNetworkConditions(null);
|
|
222
233
|
delete newSettings.networkConditions;
|
|
223
234
|
}
|
|
@@ -412,6 +423,9 @@ export class McpContext {
|
|
|
412
423
|
});
|
|
413
424
|
return this.#extensionServiceWorkers;
|
|
414
425
|
}
|
|
426
|
+
getServiceWorkerConsoleData(extensionId) {
|
|
427
|
+
return this.#serviceWorkerConsoleCollector.getData(extensionId);
|
|
428
|
+
}
|
|
415
429
|
async createPagesSnapshot() {
|
|
416
430
|
const { pages: allPages, isolatedContextNames } = await this.#getAllPages();
|
|
417
431
|
for (const page of allPages) {
|
|
@@ -421,7 +435,7 @@ export class McpContext {
|
|
|
421
435
|
this.#mcpPages.set(page, mcpPage);
|
|
422
436
|
// We emulate a focused page for all pages to support multi-agent workflows.
|
|
423
437
|
void page.emulateFocusedPage(true).catch(error => {
|
|
424
|
-
this.logger('Error turning on focused page emulation', error);
|
|
438
|
+
this.logger?.('Error turning on focused page emulation', error);
|
|
425
439
|
});
|
|
426
440
|
}
|
|
427
441
|
mcpPage.isolatedContextName = isolatedContextNames.get(page);
|
|
@@ -467,7 +481,7 @@ export class McpContext {
|
|
|
467
481
|
this.#extensionPages.set(target, page);
|
|
468
482
|
}
|
|
469
483
|
catch (e) {
|
|
470
|
-
this.logger('Failed to get page for extension target', e);
|
|
484
|
+
this.logger?.('Failed to get page for extension target', e);
|
|
471
485
|
}
|
|
472
486
|
}
|
|
473
487
|
}
|
|
@@ -502,7 +516,7 @@ export class McpContext {
|
|
|
502
516
|
return { pages: allPages, isolatedContextNames };
|
|
503
517
|
}
|
|
504
518
|
async detectOpenDevToolsWindows() {
|
|
505
|
-
this.logger('Detecting open DevTools windows');
|
|
519
|
+
this.logger?.('Detecting open DevTools windows');
|
|
506
520
|
const { pages } = await this.#getAllPages();
|
|
507
521
|
await Promise.all(pages.map(async (page) => {
|
|
508
522
|
const mcpPage = this.#mcpPages.get(page);
|
|
@@ -557,7 +571,7 @@ export class McpContext {
|
|
|
557
571
|
return { filename: filePath };
|
|
558
572
|
}
|
|
559
573
|
catch (err) {
|
|
560
|
-
this.logger(err);
|
|
574
|
+
this.logger?.(err);
|
|
561
575
|
throw new Error('Could not save a file', { cause: err });
|
|
562
576
|
}
|
|
563
577
|
}
|
|
@@ -639,5 +653,11 @@ export class McpContext {
|
|
|
639
653
|
async getHeapSnapshotRetainers(filePath, nodeId) {
|
|
640
654
|
return await this.#heapSnapshotManager.getRetainers(filePath, nodeId);
|
|
641
655
|
}
|
|
656
|
+
async closeHeapSnapshot(filePath) {
|
|
657
|
+
return this.#heapSnapshotManager.dispose(filePath);
|
|
658
|
+
}
|
|
659
|
+
hasHeapSnapshots() {
|
|
660
|
+
return this.#heapSnapshotManager.hasSnapshots();
|
|
661
|
+
}
|
|
642
662
|
}
|
|
643
663
|
//# sourceMappingURL=McpContext.js.map
|
package/build/src/McpPage.js
CHANGED
|
@@ -30,7 +30,7 @@ export class McpPage {
|
|
|
30
30
|
// Dialog
|
|
31
31
|
#dialog;
|
|
32
32
|
#dialogHandler;
|
|
33
|
-
thirdPartyDeveloperTools;
|
|
33
|
+
thirdPartyDeveloperTools = [];
|
|
34
34
|
constructor(page, id) {
|
|
35
35
|
this.pptrPage = page;
|
|
36
36
|
this.id = id;
|
|
@@ -194,18 +194,18 @@ export class McpPage {
|
|
|
194
194
|
for (const handle of oldHandles) {
|
|
195
195
|
await handle
|
|
196
196
|
.dispose()
|
|
197
|
-
.catch(e => logger('Failed to dispose old handle', e));
|
|
197
|
+
.catch(e => logger?.('Failed to dispose old handle', e));
|
|
198
198
|
}
|
|
199
199
|
}
|
|
200
200
|
const cdpElementIds = await Promise.all(elementHandles.map(async (elementHandle, index) => {
|
|
201
201
|
const backendNodeId = await elementHandle.backendNodeId();
|
|
202
202
|
if (!backendNodeId) {
|
|
203
|
-
logger(`No backendNodeId for stashed DOM element with index ${index}`);
|
|
203
|
+
logger?.(`No backendNodeId for stashed DOM element with index ${index}`);
|
|
204
204
|
return `stashed-${index}`;
|
|
205
205
|
}
|
|
206
206
|
const cdpElementId = this.resolveCdpElementId(backendNodeId);
|
|
207
207
|
if (!cdpElementId) {
|
|
208
|
-
logger(`Could not get cdpElementId for backend node ${backendNodeId}`);
|
|
208
|
+
logger?.(`Could not get cdpElementId for backend node ${backendNodeId}`);
|
|
209
209
|
return `stashed-${index}`;
|
|
210
210
|
}
|
|
211
211
|
return cdpElementId;
|
|
@@ -263,12 +263,12 @@ export class McpPage {
|
|
|
263
263
|
}
|
|
264
264
|
resolveCdpElementId(cdpBackendNodeId) {
|
|
265
265
|
if (!cdpBackendNodeId) {
|
|
266
|
-
logger('no cdpBackendNodeId');
|
|
266
|
+
logger?.('no cdpBackendNodeId');
|
|
267
267
|
return;
|
|
268
268
|
}
|
|
269
269
|
const snapshot = this.textSnapshot;
|
|
270
270
|
if (!snapshot) {
|
|
271
|
-
logger('no text snapshot');
|
|
271
|
+
logger?.('no text snapshot');
|
|
272
272
|
return;
|
|
273
273
|
}
|
|
274
274
|
// TODO: index by backendNodeId instead.
|
|
@@ -286,10 +286,10 @@ export class McpPage {
|
|
|
286
286
|
}
|
|
287
287
|
async getDevToolsData() {
|
|
288
288
|
try {
|
|
289
|
-
logger('Getting DevTools UI data');
|
|
289
|
+
logger?.('Getting DevTools UI data');
|
|
290
290
|
const devtoolsPage = this.devToolsPage;
|
|
291
291
|
if (!devtoolsPage) {
|
|
292
|
-
logger('No DevTools page detected');
|
|
292
|
+
logger?.('No DevTools page detected');
|
|
293
293
|
return {};
|
|
294
294
|
}
|
|
295
295
|
const { cdpRequestId, cdpBackendNodeId } = await devtoolsPage.evaluate(async () => {
|
|
@@ -307,7 +307,7 @@ export class McpPage {
|
|
|
307
307
|
return { cdpBackendNodeId, cdpRequestId };
|
|
308
308
|
}
|
|
309
309
|
catch (err) {
|
|
310
|
-
logger('error getting devtools data', err);
|
|
310
|
+
logger?.('error getting devtools data', err);
|
|
311
311
|
}
|
|
312
312
|
return {};
|
|
313
313
|
}
|
package/build/src/McpResponse.js
CHANGED
|
@@ -11,7 +11,7 @@ import { NetworkFormatter } from './formatters/NetworkFormatter.js';
|
|
|
11
11
|
import { SnapshotFormatter } from './formatters/SnapshotFormatter.js';
|
|
12
12
|
import { UncaughtError } from './PageCollector.js';
|
|
13
13
|
import { TextSnapshot } from './TextSnapshot.js';
|
|
14
|
-
import { DevTools } from './third_party/index.js';
|
|
14
|
+
import { DevTools, toonEncode } from './third_party/index.js';
|
|
15
15
|
import { handleDialog } from './tools/pages.js';
|
|
16
16
|
import { getInsightOutput, getTraceSummary } from './trace-processing/parse.js';
|
|
17
17
|
import { paginate } from './utils/pagination.js';
|
|
@@ -61,7 +61,7 @@ export function replaceHtmlElementsWithUids(schema) {
|
|
|
61
61
|
}
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
|
-
async function
|
|
64
|
+
async function getToolGroups(page) {
|
|
65
65
|
// Check if there is a `devtoolstooldiscovery` event listener
|
|
66
66
|
const windowHandle = await page.pptrPage.evaluateHandle(() => window);
|
|
67
67
|
// @ts-expect-error internal API
|
|
@@ -70,43 +70,78 @@ async function getToolGroup(page) {
|
|
|
70
70
|
objectId: windowHandle.remoteObject().objectId,
|
|
71
71
|
});
|
|
72
72
|
if (listeners.find(l => l.type === 'devtoolstooldiscovery') === undefined) {
|
|
73
|
-
return;
|
|
73
|
+
return [];
|
|
74
74
|
}
|
|
75
|
-
const
|
|
75
|
+
const toolGroups = await page.pptrPage.evaluate(() => {
|
|
76
76
|
return new Promise(resolve => {
|
|
77
77
|
const event = new CustomEvent('devtoolstooldiscovery');
|
|
78
|
+
const groups = [];
|
|
78
79
|
// @ts-expect-error Adding custom property
|
|
79
|
-
event.respondWith =
|
|
80
|
+
event.respondWith = toolGroup => {
|
|
80
81
|
if (!window.__dtmcp) {
|
|
81
82
|
window.__dtmcp = {};
|
|
82
83
|
}
|
|
83
|
-
window.__dtmcp.
|
|
84
|
+
if (!window.__dtmcp.toolGroups) {
|
|
85
|
+
window.__dtmcp.toolGroups = [];
|
|
86
|
+
}
|
|
87
|
+
if (typeof toolGroup.name !== 'string' ||
|
|
88
|
+
typeof toolGroup.description !== 'string' ||
|
|
89
|
+
!Array.isArray(toolGroup.tools)) {
|
|
90
|
+
console.error('Invalid toolGroup:', toolGroup);
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
for (const tool of toolGroup.tools) {
|
|
94
|
+
if (typeof tool.name !== 'string' ||
|
|
95
|
+
typeof tool.description !== 'string' ||
|
|
96
|
+
typeof tool.inputSchema !== 'object' ||
|
|
97
|
+
typeof tool.execute !== 'function') {
|
|
98
|
+
console.error('Invalid tool:', tool);
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
window.__dtmcp.toolGroups.push(toolGroup);
|
|
84
103
|
// When receiving a toolGroup for the first time, expose a simple execution helper
|
|
85
104
|
if (!window.__dtmcp.executeTool) {
|
|
86
105
|
window.__dtmcp.executeTool = async (toolName, args) => {
|
|
87
|
-
if (!window.__dtmcp?.
|
|
106
|
+
if (!window.__dtmcp?.toolGroups ||
|
|
107
|
+
window.__dtmcp.toolGroups.length === 0) {
|
|
88
108
|
throw new Error('No tools found on the page');
|
|
89
109
|
}
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
|
|
110
|
+
for (const group of window.__dtmcp.toolGroups) {
|
|
111
|
+
const tool = group.tools?.find(t => t.name === toolName);
|
|
112
|
+
if (tool) {
|
|
113
|
+
return await tool.execute(args);
|
|
114
|
+
}
|
|
93
115
|
}
|
|
94
|
-
|
|
116
|
+
throw new Error(`Tool ${toolName} not found`);
|
|
95
117
|
};
|
|
96
118
|
}
|
|
97
|
-
|
|
119
|
+
groups.push(toolGroup);
|
|
98
120
|
};
|
|
99
121
|
window.dispatchEvent(event);
|
|
100
|
-
// If
|
|
101
|
-
setTimeout
|
|
102
|
-
|
|
103
|
-
|
|
122
|
+
// If at least one toolGroup was added synchronously, resolve with the array.
|
|
123
|
+
// Otherwise, use setTimeout to allow for any microtask/asynchronous respondWith calls, or resolve with an empty array.
|
|
124
|
+
if (groups.length > 0) {
|
|
125
|
+
resolve(groups);
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
setTimeout(() => {
|
|
129
|
+
if (groups.length > 0) {
|
|
130
|
+
resolve(groups);
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
resolve([]);
|
|
134
|
+
}
|
|
135
|
+
}, 0);
|
|
136
|
+
}
|
|
104
137
|
});
|
|
105
138
|
});
|
|
106
|
-
for (const
|
|
107
|
-
|
|
139
|
+
for (const group of toolGroups) {
|
|
140
|
+
for (const tool of group.tools ?? []) {
|
|
141
|
+
replaceHtmlElementsWithUids(tool.inputSchema);
|
|
142
|
+
}
|
|
108
143
|
}
|
|
109
|
-
return
|
|
144
|
+
return toolGroups;
|
|
110
145
|
}
|
|
111
146
|
export class McpResponse {
|
|
112
147
|
#includePages = false;
|
|
@@ -134,6 +169,9 @@ export class McpResponse {
|
|
|
134
169
|
#redactNetworkHeaders = true;
|
|
135
170
|
#error;
|
|
136
171
|
#attachedWaitForResult;
|
|
172
|
+
get #deviceScope() {
|
|
173
|
+
return this.#page?.viewport?.isMobile ? 'PHONE' : 'DESKTOP';
|
|
174
|
+
}
|
|
137
175
|
constructor(args) {
|
|
138
176
|
this.#args = args;
|
|
139
177
|
}
|
|
@@ -203,6 +241,7 @@ export class McpResponse {
|
|
|
203
241
|
: undefined,
|
|
204
242
|
types: options?.types,
|
|
205
243
|
includePreservedMessages: options?.includePreservedMessages,
|
|
244
|
+
serviceWorkerId: options?.serviceWorkerId,
|
|
206
245
|
};
|
|
207
246
|
}
|
|
208
247
|
setError(error) {
|
|
@@ -306,7 +345,7 @@ export class McpResponse {
|
|
|
306
345
|
get listWebMcpTools() {
|
|
307
346
|
return this.#listWebMcpTools;
|
|
308
347
|
}
|
|
309
|
-
async handle(toolName, context) {
|
|
348
|
+
async handle(toolName, context, useToon = false) {
|
|
310
349
|
if (this.#includePages) {
|
|
311
350
|
await context.createPagesSnapshot();
|
|
312
351
|
}
|
|
@@ -383,12 +422,11 @@ export class McpResponse {
|
|
|
383
422
|
if (this.#listExtensions) {
|
|
384
423
|
extensions = await context.listExtensions();
|
|
385
424
|
}
|
|
386
|
-
|
|
387
|
-
let thirdPartyDeveloperTools;
|
|
425
|
+
let thirdPartyDeveloperTools = [];
|
|
388
426
|
if (this.#args.categoryExperimentalThirdParty &&
|
|
389
427
|
this.#listThirdPartyDeveloperTools &&
|
|
390
428
|
this.#page) {
|
|
391
|
-
thirdPartyDeveloperTools = await
|
|
429
|
+
thirdPartyDeveloperTools = await getToolGroups(this.#page);
|
|
392
430
|
if (thirdPartyDeveloperTools) {
|
|
393
431
|
this.#page.thirdPartyDeveloperTools = thirdPartyDeveloperTools;
|
|
394
432
|
}
|
|
@@ -401,11 +439,18 @@ export class McpResponse {
|
|
|
401
439
|
}
|
|
402
440
|
let consoleMessages;
|
|
403
441
|
if (this.#consoleDataOptions?.include) {
|
|
404
|
-
|
|
405
|
-
|
|
442
|
+
let messages;
|
|
443
|
+
let page;
|
|
444
|
+
if (this.#consoleDataOptions.serviceWorkerId) {
|
|
445
|
+
messages = context.getServiceWorkerConsoleData(this.#consoleDataOptions.serviceWorkerId);
|
|
446
|
+
}
|
|
447
|
+
else {
|
|
448
|
+
page = this.#page;
|
|
449
|
+
if (!page) {
|
|
450
|
+
throw new Error(`Response must have an McpPage`);
|
|
451
|
+
}
|
|
452
|
+
messages = context.getConsoleData(page, this.#consoleDataOptions.includePreservedMessages);
|
|
406
453
|
}
|
|
407
|
-
const page = this.#page;
|
|
408
|
-
let messages = context.getConsoleData(this.#page, this.#consoleDataOptions.includePreservedMessages);
|
|
409
454
|
if (this.#consoleDataOptions.types?.length) {
|
|
410
455
|
const normalizedTypes = new Set(this.#consoleDataOptions.types);
|
|
411
456
|
messages = messages.filter(message => {
|
|
@@ -422,7 +467,9 @@ export class McpResponse {
|
|
|
422
467
|
const consoleMessageStableId = context.getConsoleMessageStableId(item);
|
|
423
468
|
if ('args' in item || item instanceof UncaughtError) {
|
|
424
469
|
const consoleMessage = item;
|
|
425
|
-
const devTools =
|
|
470
|
+
const devTools = page
|
|
471
|
+
? context.getDevToolsUniverse(page)
|
|
472
|
+
: null;
|
|
426
473
|
return await ConsoleFormatter.from(consoleMessage, {
|
|
427
474
|
id: consoleMessageStableId,
|
|
428
475
|
fetchDetailedData: false,
|
|
@@ -479,9 +526,9 @@ export class McpResponse {
|
|
|
479
526
|
thirdPartyDeveloperTools,
|
|
480
527
|
webmcpTools,
|
|
481
528
|
errorMessage: this.#error?.message,
|
|
482
|
-
});
|
|
529
|
+
}, useToon);
|
|
483
530
|
}
|
|
484
|
-
format(toolName, context, data) {
|
|
531
|
+
format(toolName, context, data, useToon) {
|
|
485
532
|
const structuredContent = {};
|
|
486
533
|
const response = [];
|
|
487
534
|
if (this.#textResponseLines.length) {
|
|
@@ -603,7 +650,7 @@ Call ${handleDialog.name} to handle it before continuing.`);
|
|
|
603
650
|
structuredContent.tabId = this.#tabId;
|
|
604
651
|
}
|
|
605
652
|
if (data.traceSummary) {
|
|
606
|
-
const summary = getTraceSummary(data.traceSummary);
|
|
653
|
+
const summary = getTraceSummary(data.traceSummary, this.#deviceScope);
|
|
607
654
|
response.push(summary);
|
|
608
655
|
structuredContent.traceSummary = summary;
|
|
609
656
|
structuredContent.traceInsights = [];
|
|
@@ -617,7 +664,7 @@ Call ${handleDialog.name} to handle it before continuing.`);
|
|
|
617
664
|
}
|
|
618
665
|
}
|
|
619
666
|
if (data.traceInsight) {
|
|
620
|
-
const insightOutput = getInsightOutput(data.traceInsight.trace, data.traceInsight.insightSetId, data.traceInsight.insightName);
|
|
667
|
+
const insightOutput = getInsightOutput(data.traceInsight.trace, data.traceInsight.insightSetId, data.traceInsight.insightName, this.#deviceScope);
|
|
621
668
|
if ('error' in insightOutput) {
|
|
622
669
|
response.push(insightOutput.error);
|
|
623
670
|
}
|
|
@@ -651,9 +698,11 @@ Call ${handleDialog.name} to handle it before continuing.`);
|
|
|
651
698
|
structuredContent.snapshotFilePath = data.snapshot;
|
|
652
699
|
}
|
|
653
700
|
else {
|
|
654
|
-
response.push('## Latest page snapshot');
|
|
655
|
-
response.push(data.snapshot.toString());
|
|
656
701
|
structuredContent.snapshot = data.snapshot.toJSON();
|
|
702
|
+
response.push('## Latest page snapshot');
|
|
703
|
+
response.push(useToon
|
|
704
|
+
? toonEncode(structuredContent.snapshot)
|
|
705
|
+
: data.snapshot.toString());
|
|
657
706
|
}
|
|
658
707
|
}
|
|
659
708
|
if (this.#heapSnapshotOptions?.include) {
|
|
@@ -678,8 +727,10 @@ Call ${handleDialog.name} to handle it before continuing.`);
|
|
|
678
727
|
response.push(...paginationData.info);
|
|
679
728
|
const paginatedRecord = Object.fromEntries(paginationData.items);
|
|
680
729
|
const formatter = new HeapSnapshotFormatter(paginatedRecord);
|
|
681
|
-
response.push(formatter.toString());
|
|
682
730
|
structuredContent.heapSnapshotData = formatter.toJSON();
|
|
731
|
+
response.push(useToon
|
|
732
|
+
? toonEncode(structuredContent.heapSnapshotData)
|
|
733
|
+
: formatter.toString());
|
|
683
734
|
}
|
|
684
735
|
const nodes = this.#heapSnapshotOptions.nodes;
|
|
685
736
|
if (nodes) {
|
|
@@ -728,18 +779,11 @@ Call ${handleDialog.name} to handle it before continuing.`);
|
|
|
728
779
|
response.push(extensionsMessage);
|
|
729
780
|
}
|
|
730
781
|
}
|
|
731
|
-
if (data.thirdPartyDeveloperTools
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
data.thirdPartyDeveloperTools;
|
|
735
|
-
}
|
|
782
|
+
if (data.thirdPartyDeveloperTools.length) {
|
|
783
|
+
structuredContent.thirdPartyDeveloperTools =
|
|
784
|
+
data.thirdPartyDeveloperTools;
|
|
736
785
|
response.push('## Third-party developer tools');
|
|
737
|
-
|
|
738
|
-
!data.thirdPartyDeveloperTools?.tools) {
|
|
739
|
-
response.push('No third-party developer tools available.');
|
|
740
|
-
}
|
|
741
|
-
else {
|
|
742
|
-
const toolGroup = data.thirdPartyDeveloperTools;
|
|
786
|
+
for (const toolGroup of data.thirdPartyDeveloperTools) {
|
|
743
787
|
response.push(`${toolGroup.name}: ${toolGroup.description}`);
|
|
744
788
|
response.push('Available tools:');
|
|
745
789
|
const toolDefinitionsMessage = toolGroup.tools
|
|
@@ -778,11 +822,10 @@ Call ${handleDialog.name} to handle it before continuing.`);
|
|
|
778
822
|
structuredContent.pagination = paginationData.pagination;
|
|
779
823
|
response.push(...paginationData.info);
|
|
780
824
|
if (data.networkRequests) {
|
|
781
|
-
structuredContent.networkRequests =
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
}
|
|
825
|
+
structuredContent.networkRequests = paginationData.items.map(i => i.toJSON());
|
|
826
|
+
response.push(...(useToon
|
|
827
|
+
? [toonEncode(structuredContent.networkRequests)]
|
|
828
|
+
: paginationData.items.map(i => i.toString())));
|
|
786
829
|
}
|
|
787
830
|
}
|
|
788
831
|
else {
|
|
@@ -796,9 +839,14 @@ Call ${handleDialog.name} to handle it before continuing.`);
|
|
|
796
839
|
const grouped = ConsoleFormatter.groupConsecutive(messages);
|
|
797
840
|
const paginationData = this.#dataWithPagination(grouped, this.#consoleDataOptions.pagination);
|
|
798
841
|
structuredContent.pagination = paginationData.pagination;
|
|
799
|
-
response.push(...paginationData.info);
|
|
800
|
-
response.push(...paginationData.items.map(item => item.toString()));
|
|
801
842
|
structuredContent.consoleMessages = paginationData.items.map(item => item.toJSON());
|
|
843
|
+
response.push(...paginationData.info);
|
|
844
|
+
if (useToon) {
|
|
845
|
+
response.push(toonEncode(structuredContent.consoleMessages));
|
|
846
|
+
}
|
|
847
|
+
else {
|
|
848
|
+
response.push(...paginationData.items.map(item => item.toString()));
|
|
849
|
+
}
|
|
802
850
|
}
|
|
803
851
|
else {
|
|
804
852
|
response.push('<no console messages found>');
|
|
@@ -50,7 +50,7 @@ export class PageCollector {
|
|
|
50
50
|
this.addPage(page);
|
|
51
51
|
}
|
|
52
52
|
catch (err) {
|
|
53
|
-
logger('Error getting a page for a target onTargetCreated', err);
|
|
53
|
+
logger?.('Error getting a page for a target onTargetCreated', err);
|
|
54
54
|
}
|
|
55
55
|
};
|
|
56
56
|
#onTargetDestroyed = async (target) => {
|
|
@@ -62,7 +62,7 @@ export class PageCollector {
|
|
|
62
62
|
this.cleanupPageDestroyed(page);
|
|
63
63
|
}
|
|
64
64
|
catch (err) {
|
|
65
|
-
logger('Error getting a page for a target onTargetDestroyed', err);
|
|
65
|
+
logger?.('Error getting a page for a target onTargetDestroyed', err);
|
|
66
66
|
}
|
|
67
67
|
};
|
|
68
68
|
addPage(page) {
|
|
@@ -136,10 +136,10 @@ export class PageCollector {
|
|
|
136
136
|
throw new Error('No requests found for selected page');
|
|
137
137
|
}
|
|
138
138
|
const item = this.find(page, item => item[stableIdSymbol] === stableId);
|
|
139
|
-
if (item) {
|
|
140
|
-
|
|
139
|
+
if (!item) {
|
|
140
|
+
throw new Error('Request not found for selected page');
|
|
141
141
|
}
|
|
142
|
-
|
|
142
|
+
return item;
|
|
143
143
|
}
|
|
144
144
|
find(page, filter) {
|
|
145
145
|
const navigations = this.storage.get(page);
|
|
@@ -241,7 +241,7 @@ class PageEventSubscriber {
|
|
|
241
241
|
// @ts-expect-error Protocol types diverge.
|
|
242
242
|
inspectorIssue)[0];
|
|
243
243
|
if (!issue) {
|
|
244
|
-
logger('No issue mapping for for the issue: ', inspectorIssue.code);
|
|
244
|
+
logger?.('No issue mapping for for the issue: ', inspectorIssue.code);
|
|
245
245
|
return;
|
|
246
246
|
}
|
|
247
247
|
const primaryKey = issue.primaryKey();
|
|
@@ -256,7 +256,7 @@ class PageEventSubscriber {
|
|
|
256
256
|
});
|
|
257
257
|
}
|
|
258
258
|
catch (error) {
|
|
259
|
-
logger('Error creating a new issue', error);
|
|
259
|
+
logger?.('Error creating a new issue', error);
|
|
260
260
|
}
|
|
261
261
|
};
|
|
262
262
|
}
|