chrome-devtools-mcp 0.6.1 → 0.7.1
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 +65 -2
- package/build/src/McpResponse.js +25 -5
- package/build/src/browser.js +27 -23
- package/build/src/cli.js +5 -0
- package/build/src/formatters/networkFormatter.js +44 -0
- package/build/src/main.js +10 -1
- package/build/src/tools/console.js +1 -1
- package/build/src/tools/emulation.js +13 -2
- package/build/src/tools/network.js +1 -1
- package/build/src/tools/performance.js +0 -1
- package/build/src/trace-processing/parse.js +6 -5
- package/package.json +5 -4
package/README.md
CHANGED
|
@@ -111,7 +111,7 @@ Start the dialog to add a new MCP server by running:
|
|
|
111
111
|
/mcp add
|
|
112
112
|
```
|
|
113
113
|
|
|
114
|
-
Configure the following fields and press `
|
|
114
|
+
Configure the following fields and press `CTRL+S` to save the configuration:
|
|
115
115
|
|
|
116
116
|
- **Server name:** `chrome-devtools`
|
|
117
117
|
- **Server Type:** `[1] Local`
|
|
@@ -328,6 +328,69 @@ all instances of `chrome-devtools-mcp`. Set the `isolated` option to `true`
|
|
|
328
328
|
to use a temporary user data dir instead which will be cleared automatically after
|
|
329
329
|
the browser is closed.
|
|
330
330
|
|
|
331
|
+
### Connecting to a running Chrome instance
|
|
332
|
+
|
|
333
|
+
You can connect to a running Chrome instance by using the `--browser-url` option. This is useful if you want to use your existing Chrome profile or if you are running the MCP server in a sandboxed environment that does not allow starting a new Chrome instance.
|
|
334
|
+
|
|
335
|
+
Here is a step-by-step guide on how to connect to a running Chrome Stable instance:
|
|
336
|
+
|
|
337
|
+
**Step 1: Configure the MCP client**
|
|
338
|
+
|
|
339
|
+
Add the `--browser-url` option to your MCP client configuration. The value of this option should be the URL of the running Chrome instance. `http://localhost:9222` is a common default.
|
|
340
|
+
|
|
341
|
+
```json
|
|
342
|
+
{
|
|
343
|
+
"mcpServers": {
|
|
344
|
+
"chrome-devtools": {
|
|
345
|
+
"command": "npx",
|
|
346
|
+
"args": [
|
|
347
|
+
"chrome-devtools-mcp@latest",
|
|
348
|
+
"--browser-url=http://localhost:9222"
|
|
349
|
+
]
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
**Step 2: Start the Chrome browser**
|
|
356
|
+
|
|
357
|
+
> [!WARNING]
|
|
358
|
+
> Enabling the remote debugging port opens up a debugging port on the running browser instance. Any application on your machine can connect to this port and control the browser. Make sure that you are not browsing any sensitive websites while the debugging port is open.
|
|
359
|
+
|
|
360
|
+
Start the Chrome browser with the remote debugging port enabled. Make sure to close any running Chrome instances before starting a new one with the debugging port enabled. The port number you choose must be the same as the one you specified in the `--browser-url` option in your MCP client configuration.
|
|
361
|
+
|
|
362
|
+
For security reasons, [Chrome requires you to use a non-default user data directory](https://developer.chrome.com/blog/remote-debugging-port) when enabling the remote debugging port. You can specify a custom directory using the `--user-data-dir` flag. This ensures that your regular browsing profile and data are not exposed to the debugging session.
|
|
363
|
+
|
|
364
|
+
**macOS**
|
|
365
|
+
|
|
366
|
+
```bash
|
|
367
|
+
/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --remote-debugging-port=9222 --user-data-dir=/tmp/chrome-profile-stable
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
**Linux**
|
|
371
|
+
|
|
372
|
+
```bash
|
|
373
|
+
/usr/bin/google-chrome --remote-debugging-port=9222 --user-data-dir=/tmp/chrome-profile-stable
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
**Windows**
|
|
377
|
+
|
|
378
|
+
```bash
|
|
379
|
+
"C:\Program Files\Google\Chrome\Application\chrome.exe" --remote-debugging-port=9222 --user-data-dir="%TEMP%\chrome-profile-stable"
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
**Step 3: Test your setup**
|
|
383
|
+
|
|
384
|
+
After configuring the MCP client and starting the Chrome browser, you can test your setup by running a simple prompt in your MCP client:
|
|
385
|
+
|
|
386
|
+
```
|
|
387
|
+
Check the performance of https://developers.chrome.com
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
Your MCP client should connect to the running Chrome instance and receive a performance report.
|
|
391
|
+
|
|
392
|
+
For more details on remote debugging, see the [Chrome DevTools documentation](https://developer.chrome.com/docs/devtools/remote-debugging/).
|
|
393
|
+
|
|
331
394
|
## Known limitations
|
|
332
395
|
|
|
333
396
|
### Operating system sandboxes
|
|
@@ -336,5 +399,5 @@ Some MCP clients allow sandboxing the MCP server using macOS Seatbelt or Linux
|
|
|
336
399
|
containers. If sandboxes are enabled, `chrome-devtools-mcp` is not able to start
|
|
337
400
|
Chrome that requires permissions to create its own sandboxes. As a workaround,
|
|
338
401
|
either disable sandboxing for `chrome-devtools-mcp` in your MCP client or use
|
|
339
|
-
`--
|
|
402
|
+
`--browser-url` to connect to a Chrome instance that you start manually outside
|
|
340
403
|
of the MCP client sandbox.
|
package/build/src/McpResponse.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { formatConsoleEvent } from './formatters/consoleFormatter.js';
|
|
2
|
-
import { getFormattedHeaderValue, getShortDescriptionForRequest, getStatusFromRequest, } from './formatters/networkFormatter.js';
|
|
2
|
+
import { getFormattedHeaderValue, getFormattedResponseBody, getFormattedRequestBody, getShortDescriptionForRequest, getStatusFromRequest, } from './formatters/networkFormatter.js';
|
|
3
3
|
import { formatA11ySnapshot } from './formatters/snapshotFormatter.js';
|
|
4
4
|
import { handleDialog } from './tools/pages.js';
|
|
5
5
|
import { paginate } from './utils/pagination.js';
|
|
6
6
|
export class McpResponse {
|
|
7
7
|
#includePages = false;
|
|
8
8
|
#includeSnapshot = false;
|
|
9
|
-
#
|
|
9
|
+
#attachedNetworkRequestData;
|
|
10
10
|
#includeConsoleData = false;
|
|
11
11
|
#textResponseLines = [];
|
|
12
12
|
#formattedConsoleData;
|
|
@@ -38,7 +38,9 @@ export class McpResponse {
|
|
|
38
38
|
this.#includeConsoleData = value;
|
|
39
39
|
}
|
|
40
40
|
attachNetworkRequest(url) {
|
|
41
|
-
this.#
|
|
41
|
+
this.#attachedNetworkRequestData = {
|
|
42
|
+
networkRequestUrl: url,
|
|
43
|
+
};
|
|
42
44
|
}
|
|
43
45
|
get includePages() {
|
|
44
46
|
return this.#includePages;
|
|
@@ -50,7 +52,7 @@ export class McpResponse {
|
|
|
50
52
|
return this.#includeConsoleData;
|
|
51
53
|
}
|
|
52
54
|
get attachedNetworkRequestUrl() {
|
|
53
|
-
return this.#
|
|
55
|
+
return this.#attachedNetworkRequestData?.networkRequestUrl;
|
|
54
56
|
}
|
|
55
57
|
get networkRequestsPageIdx() {
|
|
56
58
|
return this.#networkRequestsOptions?.pagination?.pageIdx;
|
|
@@ -78,6 +80,16 @@ export class McpResponse {
|
|
|
78
80
|
await context.createTextSnapshot();
|
|
79
81
|
}
|
|
80
82
|
let formattedConsoleMessages;
|
|
83
|
+
if (this.#attachedNetworkRequestData?.networkRequestUrl) {
|
|
84
|
+
const request = context.getNetworkRequestByUrl(this.#attachedNetworkRequestData.networkRequestUrl);
|
|
85
|
+
this.#attachedNetworkRequestData.requestBody =
|
|
86
|
+
await getFormattedRequestBody(request);
|
|
87
|
+
const response = request.response();
|
|
88
|
+
if (response) {
|
|
89
|
+
this.#attachedNetworkRequestData.responseBody =
|
|
90
|
+
await getFormattedResponseBody(response);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
81
93
|
if (this.#includeConsoleData) {
|
|
82
94
|
const consoleMessages = context.getConsoleData();
|
|
83
95
|
if (consoleMessages) {
|
|
@@ -193,7 +205,7 @@ Call ${handleDialog.name} to handle it before continuing.`);
|
|
|
193
205
|
}
|
|
194
206
|
#getIncludeNetworkRequestsData(context) {
|
|
195
207
|
const response = [];
|
|
196
|
-
const url = this.#
|
|
208
|
+
const url = this.#attachedNetworkRequestData?.networkRequestUrl;
|
|
197
209
|
if (!url) {
|
|
198
210
|
return response;
|
|
199
211
|
}
|
|
@@ -204,6 +216,10 @@ Call ${handleDialog.name} to handle it before continuing.`);
|
|
|
204
216
|
for (const line of getFormattedHeaderValue(httpRequest.headers())) {
|
|
205
217
|
response.push(line);
|
|
206
218
|
}
|
|
219
|
+
if (this.#attachedNetworkRequestData?.requestBody) {
|
|
220
|
+
response.push(`### Request Body`);
|
|
221
|
+
response.push(this.#attachedNetworkRequestData.requestBody);
|
|
222
|
+
}
|
|
207
223
|
const httpResponse = httpRequest.response();
|
|
208
224
|
if (httpResponse) {
|
|
209
225
|
response.push(`### Response Headers`);
|
|
@@ -211,6 +227,10 @@ Call ${handleDialog.name} to handle it before continuing.`);
|
|
|
211
227
|
response.push(line);
|
|
212
228
|
}
|
|
213
229
|
}
|
|
230
|
+
if (this.#attachedNetworkRequestData?.responseBody) {
|
|
231
|
+
response.push(`### Response Body`);
|
|
232
|
+
response.push(this.#attachedNetworkRequestData.responseBody);
|
|
233
|
+
}
|
|
214
234
|
const httpFailure = httpRequest.failure();
|
|
215
235
|
if (httpFailure) {
|
|
216
236
|
response.push(`### Request failed with`);
|
package/build/src/browser.js
CHANGED
|
@@ -8,36 +8,36 @@ import os from 'node:os';
|
|
|
8
8
|
import path from 'node:path';
|
|
9
9
|
import puppeteer from 'puppeteer-core';
|
|
10
10
|
let browser;
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
]);
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
return true;
|
|
11
|
+
function makeTargetFilter(devtools) {
|
|
12
|
+
const ignoredPrefixes = new Set([
|
|
13
|
+
'chrome://',
|
|
14
|
+
'chrome-extension://',
|
|
15
|
+
'chrome-untrusted://',
|
|
16
|
+
]);
|
|
17
|
+
if (!devtools) {
|
|
18
|
+
ignoredPrefixes.add('devtools://');
|
|
20
19
|
}
|
|
21
|
-
|
|
22
|
-
if (target.url()
|
|
23
|
-
return
|
|
20
|
+
return function targetFilter(target) {
|
|
21
|
+
if (target.url() === 'chrome://newtab/') {
|
|
22
|
+
return true;
|
|
24
23
|
}
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
for (const prefix of ignoredPrefixes) {
|
|
25
|
+
if (target.url().startsWith(prefix)) {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return true;
|
|
30
|
+
};
|
|
27
31
|
}
|
|
28
|
-
|
|
29
|
-
targetFilter,
|
|
30
|
-
// We do not expect any single CDP command to take more than 10sec.
|
|
31
|
-
protocolTimeout: 10_000,
|
|
32
|
-
};
|
|
33
|
-
export async function ensureBrowserConnected(browserURL) {
|
|
32
|
+
export async function ensureBrowserConnected(options) {
|
|
34
33
|
if (browser?.connected) {
|
|
35
34
|
return browser;
|
|
36
35
|
}
|
|
37
36
|
browser = await puppeteer.connect({
|
|
38
|
-
|
|
39
|
-
browserURL,
|
|
37
|
+
targetFilter: makeTargetFilter(options.devtools),
|
|
38
|
+
browserURL: options.browserURL,
|
|
40
39
|
defaultViewport: null,
|
|
40
|
+
handleDevToolsAsPage: options.devtools,
|
|
41
41
|
});
|
|
42
42
|
return browser;
|
|
43
43
|
}
|
|
@@ -64,6 +64,9 @@ export async function launch(options) {
|
|
|
64
64
|
args.push('--screen-info={3840x2160}');
|
|
65
65
|
}
|
|
66
66
|
let puppeteerChannel;
|
|
67
|
+
if (options.devtools) {
|
|
68
|
+
args.push('--auto-open-devtools-for-tabs');
|
|
69
|
+
}
|
|
67
70
|
if (!executablePath) {
|
|
68
71
|
puppeteerChannel =
|
|
69
72
|
channel && channel !== 'stable'
|
|
@@ -72,8 +75,8 @@ export async function launch(options) {
|
|
|
72
75
|
}
|
|
73
76
|
try {
|
|
74
77
|
const browser = await puppeteer.launch({
|
|
75
|
-
...connectOptions,
|
|
76
78
|
channel: puppeteerChannel,
|
|
79
|
+
targetFilter: makeTargetFilter(options.devtools),
|
|
77
80
|
executablePath,
|
|
78
81
|
defaultViewport: null,
|
|
79
82
|
userDataDir,
|
|
@@ -81,6 +84,7 @@ export async function launch(options) {
|
|
|
81
84
|
headless,
|
|
82
85
|
args,
|
|
83
86
|
acceptInsecureCerts: options.acceptInsecureCerts,
|
|
87
|
+
handleDevToolsAsPage: options.devtools,
|
|
84
88
|
});
|
|
85
89
|
if (options.logFile) {
|
|
86
90
|
// FIXME: we are probably subscribing too late to catch startup logs. We
|
package/build/src/cli.js
CHANGED
|
@@ -81,6 +81,11 @@ export const cliOptions = {
|
|
|
81
81
|
type: 'boolean',
|
|
82
82
|
description: `If enabled, ignores errors relative to self-signed and expired certificates. Use with caution.`,
|
|
83
83
|
},
|
|
84
|
+
experimentalDevtools: {
|
|
85
|
+
type: 'boolean',
|
|
86
|
+
describe: 'Whether to enable automation over DevTools targets',
|
|
87
|
+
hidden: true,
|
|
88
|
+
},
|
|
84
89
|
};
|
|
85
90
|
export function parseArguments(version, argv = process.argv) {
|
|
86
91
|
const yargsInstance = yargs(hideBin(argv))
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
* Copyright 2025 Google LLC
|
|
4
4
|
* SPDX-License-Identifier: Apache-2.0
|
|
5
5
|
*/
|
|
6
|
+
import { isUtf8 } from 'node:buffer';
|
|
7
|
+
const BODY_CONTEXT_SIZE_LIMIT = 10000;
|
|
6
8
|
export function getShortDescriptionForRequest(request) {
|
|
7
9
|
return `${request.url()} ${request.method()} ${getStatusFromRequest(request)}`;
|
|
8
10
|
}
|
|
@@ -32,3 +34,45 @@ export function getFormattedHeaderValue(headers) {
|
|
|
32
34
|
}
|
|
33
35
|
return response;
|
|
34
36
|
}
|
|
37
|
+
export async function getFormattedResponseBody(httpResponse, sizeLimit = BODY_CONTEXT_SIZE_LIMIT) {
|
|
38
|
+
try {
|
|
39
|
+
const responseBuffer = await httpResponse.buffer();
|
|
40
|
+
if (isUtf8(responseBuffer)) {
|
|
41
|
+
const responseAsTest = responseBuffer.toString('utf-8');
|
|
42
|
+
if (responseAsTest.length === 0) {
|
|
43
|
+
return `<empty response>`;
|
|
44
|
+
}
|
|
45
|
+
return `${getSizeLimitedString(responseAsTest, sizeLimit)}`;
|
|
46
|
+
}
|
|
47
|
+
return `<binary data>`;
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
// buffer() call might fail with CDP exception, in this case we don't print anything in the context
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
export async function getFormattedRequestBody(httpRequest, sizeLimit = BODY_CONTEXT_SIZE_LIMIT) {
|
|
55
|
+
if (httpRequest.hasPostData()) {
|
|
56
|
+
const data = httpRequest.postData();
|
|
57
|
+
if (data) {
|
|
58
|
+
return `${getSizeLimitedString(data, sizeLimit)}`;
|
|
59
|
+
}
|
|
60
|
+
try {
|
|
61
|
+
const fetchData = await httpRequest.fetchPostData();
|
|
62
|
+
if (fetchData) {
|
|
63
|
+
return `${getSizeLimitedString(fetchData, sizeLimit)}`;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
// fetchPostData() call might fail with CDP exception, in this case we don't print anything in the context
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
function getSizeLimitedString(text, sizeLimit) {
|
|
74
|
+
if (text.length > sizeLimit) {
|
|
75
|
+
return `${text.substring(0, sizeLimit) + '... <truncated>'}`;
|
|
76
|
+
}
|
|
77
|
+
return `${text}`;
|
|
78
|
+
}
|
package/build/src/main.js
CHANGED
|
@@ -58,8 +58,12 @@ async function getContext() {
|
|
|
58
58
|
if (args.proxyServer) {
|
|
59
59
|
extraArgs.push(`--proxy-server=${args.proxyServer}`);
|
|
60
60
|
}
|
|
61
|
+
const devtools = args.experimentalDevtools ?? false;
|
|
61
62
|
const browser = args.browserUrl
|
|
62
|
-
? await ensureBrowserConnected(
|
|
63
|
+
? await ensureBrowserConnected({
|
|
64
|
+
browserURL: args.browserUrl,
|
|
65
|
+
devtools,
|
|
66
|
+
})
|
|
63
67
|
: await ensureBrowserLaunched({
|
|
64
68
|
headless: args.headless,
|
|
65
69
|
executablePath: args.executablePath,
|
|
@@ -70,6 +74,7 @@ async function getContext() {
|
|
|
70
74
|
viewport: args.viewport,
|
|
71
75
|
args: extraArgs,
|
|
72
76
|
acceptInsecureCerts: args.acceptInsecureCerts,
|
|
77
|
+
devtools,
|
|
73
78
|
});
|
|
74
79
|
if (context?.browser !== browser) {
|
|
75
80
|
context = await McpContext.from(browser, logger);
|
|
@@ -115,6 +120,10 @@ function registerTool(tool) {
|
|
|
115
120
|
};
|
|
116
121
|
}
|
|
117
122
|
}
|
|
123
|
+
catch (err) {
|
|
124
|
+
logger(`${tool.name} error: ${err.message}`);
|
|
125
|
+
throw err;
|
|
126
|
+
}
|
|
118
127
|
finally {
|
|
119
128
|
guard.dispose();
|
|
120
129
|
}
|
|
@@ -7,7 +7,7 @@ import { ToolCategories } from './categories.js';
|
|
|
7
7
|
import { defineTool } from './ToolDefinition.js';
|
|
8
8
|
export const consoleTool = defineTool({
|
|
9
9
|
name: 'list_console_messages',
|
|
10
|
-
description: 'List all console messages for the currently selected page',
|
|
10
|
+
description: 'List all console messages for the currently selected page since the last navigation.',
|
|
11
11
|
annotations: {
|
|
12
12
|
category: ToolCategories.DEBUGGING,
|
|
13
13
|
readOnlyHint: true,
|
|
@@ -9,11 +9,12 @@ import { ToolCategories } from './categories.js';
|
|
|
9
9
|
import { defineTool } from './ToolDefinition.js';
|
|
10
10
|
const throttlingOptions = [
|
|
11
11
|
'No emulation',
|
|
12
|
+
'Offline',
|
|
12
13
|
...Object.keys(PredefinedNetworkConditions),
|
|
13
14
|
];
|
|
14
15
|
export const emulateNetwork = defineTool({
|
|
15
16
|
name: 'emulate_network',
|
|
16
|
-
description: `Emulates network conditions such as throttling on the selected page.`,
|
|
17
|
+
description: `Emulates network conditions such as throttling or offline mode on the selected page.`,
|
|
17
18
|
annotations: {
|
|
18
19
|
category: ToolCategories.EMULATION,
|
|
19
20
|
readOnlyHint: false,
|
|
@@ -21,7 +22,7 @@ export const emulateNetwork = defineTool({
|
|
|
21
22
|
schema: {
|
|
22
23
|
throttlingOption: z
|
|
23
24
|
.enum(throttlingOptions)
|
|
24
|
-
.describe(`The network throttling option to emulate. Available throttling options are: ${throttlingOptions.join(', ')}. Set to "No emulation" to disable.`),
|
|
25
|
+
.describe(`The network throttling option to emulate. Available throttling options are: ${throttlingOptions.join(', ')}. Set to "No emulation" to disable. Set to "Offline" to simulate offline network conditions.`),
|
|
25
26
|
},
|
|
26
27
|
handler: async (request, _response, context) => {
|
|
27
28
|
const page = context.getSelectedPage();
|
|
@@ -31,6 +32,16 @@ export const emulateNetwork = defineTool({
|
|
|
31
32
|
context.setNetworkConditions(null);
|
|
32
33
|
return;
|
|
33
34
|
}
|
|
35
|
+
if (conditions === 'Offline') {
|
|
36
|
+
await page.emulateNetworkConditions({
|
|
37
|
+
offline: true,
|
|
38
|
+
download: 0,
|
|
39
|
+
upload: 0,
|
|
40
|
+
latency: 0,
|
|
41
|
+
});
|
|
42
|
+
context.setNetworkConditions('Offline');
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
34
45
|
if (conditions in PredefinedNetworkConditions) {
|
|
35
46
|
const networkCondition = PredefinedNetworkConditions[conditions];
|
|
36
47
|
await page.emulateNetworkConditions(networkCondition);
|
|
@@ -29,7 +29,7 @@ const FILTERABLE_RESOURCE_TYPES = [
|
|
|
29
29
|
];
|
|
30
30
|
export const listNetworkRequests = defineTool({
|
|
31
31
|
name: 'list_network_requests',
|
|
32
|
-
description: `List all requests for the currently selected page
|
|
32
|
+
description: `List all requests for the currently selected page since the last navigation.`,
|
|
33
33
|
annotations: {
|
|
34
34
|
category: ToolCategories.NETWORK,
|
|
35
35
|
readOnlyHint: true,
|
|
@@ -124,7 +124,6 @@ async function stopTracingAndAppendOutput(page, response, context) {
|
|
|
124
124
|
response.appendResponseLine('The performance trace has been stopped.');
|
|
125
125
|
if (traceResultIsSuccess(result)) {
|
|
126
126
|
context.storeTraceRecording(result);
|
|
127
|
-
response.appendResponseLine('Here is a high level summary of the trace and the Insights that were found:');
|
|
128
127
|
const traceSummaryText = getTraceSummary(result);
|
|
129
128
|
response.appendResponseLine(traceSummaryText);
|
|
130
129
|
}
|
|
@@ -53,15 +53,16 @@ const extraFormatDescriptions = `Information on performance traces may contain m
|
|
|
53
53
|
|
|
54
54
|
${PerformanceTraceFormatter.callFrameDataFormatDescription}
|
|
55
55
|
|
|
56
|
-
${PerformanceTraceFormatter.networkDataFormatDescription}
|
|
57
|
-
`;
|
|
56
|
+
${PerformanceTraceFormatter.networkDataFormatDescription}`;
|
|
58
57
|
export function getTraceSummary(result) {
|
|
59
58
|
const focus = AgentFocus.fromParsedTrace(result.parsedTrace);
|
|
60
59
|
const formatter = new PerformanceTraceFormatter(focus);
|
|
61
|
-
const
|
|
62
|
-
return
|
|
60
|
+
const summaryText = formatter.formatTraceSummary();
|
|
61
|
+
return `## Summary of Performance trace findings:
|
|
62
|
+
${summaryText}
|
|
63
63
|
|
|
64
|
-
|
|
64
|
+
## Details on call tree & network request formats:
|
|
65
|
+
${extraFormatDescriptions}`;
|
|
65
66
|
}
|
|
66
67
|
export function getInsightOutput(result, insightName) {
|
|
67
68
|
if (!result.insights) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "chrome-devtools-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.1",
|
|
4
4
|
"description": "MCP server for Chrome DevTools",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": "./build/src/index.js",
|
|
@@ -40,8 +40,9 @@
|
|
|
40
40
|
"@modelcontextprotocol/sdk": "1.19.1",
|
|
41
41
|
"core-js": "3.45.1",
|
|
42
42
|
"debug": "4.4.3",
|
|
43
|
-
"puppeteer-core": "24.
|
|
44
|
-
"yargs": "18.0.0"
|
|
43
|
+
"puppeteer-core": "^24.24.0",
|
|
44
|
+
"yargs": "18.0.0",
|
|
45
|
+
"zod": "^3.25.76"
|
|
45
46
|
},
|
|
46
47
|
"devDependencies": {
|
|
47
48
|
"@eslint/js": "^9.35.0",
|
|
@@ -59,7 +60,7 @@
|
|
|
59
60
|
"eslint-plugin-import": "^2.32.0",
|
|
60
61
|
"globals": "^16.4.0",
|
|
61
62
|
"prettier": "^3.6.2",
|
|
62
|
-
"puppeteer": "24.
|
|
63
|
+
"puppeteer": "24.24.0",
|
|
63
64
|
"sinon": "^21.0.0",
|
|
64
65
|
"typescript": "^5.9.2",
|
|
65
66
|
"typescript-eslint": "^8.43.0"
|