Skip to content

Commit 8fcba08

Browse files
Merge branch 'main' of github.com:ChromeDevTools/chrome-devtools-mcp into webmcp
2 parents 8f4bb01 + f491b54 commit 8fcba08

29 files changed

Lines changed: 346 additions & 224 deletions

.github/workflows/publish-to-npm-on-tag.yml

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,6 @@ jobs:
3636
node-version-file: '.nvmrc'
3737
registry-url: 'https://registry.npmjs.org'
3838

39-
# Ensure npm 11.5.1 or later is installed
40-
- name: Update npm
41-
run: npm install -g npm@latest
42-
4339
- name: Install dependencies
4440
run: npm ci
4541

.nvmrc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
v22
1+
v24

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1-
# Chrome DevTools MCP
1+
# Chrome DevTools for Agents
22

33
[![npm chrome-devtools-mcp package](https://img.shields.io/npm/v/chrome-devtools-mcp.svg)](https://npmjs.org/package/chrome-devtools-mcp)
44

5-
`chrome-devtools-mcp` lets your coding agent (such as Gemini, Claude, Cursor or Copilot)
5+
Chrome DevTools for Agents (`chrome-devtools-mcp`) lets your coding agent (such as Gemini, Claude, Cursor or Copilot)
66
control and inspect a live Chrome browser. It acts as a Model-Context-Protocol
77
(MCP) server, giving your AI coding assistant access to the full power of
88
Chrome DevTools for reliable automation, in-depth debugging, and performance analysis.
9+
A [CLI](docs/cli.md) is also provided for use without MCP.
910

1011
## [Tool reference](./docs/tool-reference.md) | [Changelog](./CHANGELOG.md) | [Contributing](./CONTRIBUTING.md) | [Troubleshooting](./docs/troubleshooting.md) | [Design Principles](./docs/design-principles.md)
1112

SECURITY.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
11
## Security policy
22

33
The Chrome DevTools MCP project takes security very seriously. Please use [Chromium’s process to report security issues](https://www.chromium.org/Home/chromium-security/reporting-security-bugs/).
4+
5+
### Scope
6+
7+
In general, it is the expectation that the AI agent or client using this MCP server validates any input before sending it. The server provides powerful capabilities for browser automation and inspection, and it is the responsibility of the calling agent to ensure these are used safely and as intended.
8+
9+
Several tools in this project have the ability to perform actions such as writing files to disk (e.g., via browser downloads or screenshots) or dynamically loading Chrome extensions. These are intentional, documented features and are not vulnerabilities.

package-lock.json

Lines changed: 83 additions & 83 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@
6060
"@types/yargs": "^17.0.33",
6161
"@typescript-eslint/eslint-plugin": "^8.43.0",
6262
"@typescript-eslint/parser": "^8.43.0",
63-
"chrome-devtools-frontend": "1.0.1611825",
63+
"chrome-devtools-frontend": "1.0.1613625",
6464
"core-js": "3.49.0",
6565
"debug": "4.4.3",
6666
"eslint": "^9.35.0",
@@ -69,7 +69,7 @@
6969
"globals": "^17.0.0",
7070
"lighthouse": "13.1.0",
7171
"prettier": "^3.6.2",
72-
"puppeteer": "24.40.0",
72+
"puppeteer": "24.41.0",
7373
"rollup": "4.60.1",
7474
"rollup-plugin-cleanup": "^3.2.1",
7575
"rollup-plugin-license": "^3.6.0",

src/McpContext.ts

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,11 @@ import {Locator} from './third_party/index.js';
3333
import {PredefinedNetworkConditions} from './third_party/index.js';
3434
import {listPages} from './tools/pages.js';
3535
import {CLOSE_PAGE_ERROR} from './tools/ToolDefinition.js';
36-
import type {Context, DevToolsData} from './tools/ToolDefinition.js';
36+
import type {
37+
Context,
38+
DevToolsData,
39+
SupportedExtensions,
40+
} from './tools/ToolDefinition.js';
3741
import type {TraceResult} from './trace-processing/parse.js';
3842
import type {
3943
EmulationSettings,
@@ -46,7 +50,7 @@ import {
4650
ExtensionRegistry,
4751
type InstalledExtension,
4852
} from './utils/ExtensionRegistry.js';
49-
import {saveTemporaryFile} from './utils/files.js';
53+
import {ensureExtension, saveTemporaryFile} from './utils/files.js';
5054
import {getNetworkMultiplierFromString} from './WaitForHelper.js';
5155

5256
interface McpContextOptions {
@@ -117,7 +121,7 @@ export class McpContext implements Context {
117121
uncaughtError: event => {
118122
collect(event);
119123
},
120-
issue: event => {
124+
devtoolsAggregatedIssue: event => {
121125
collect(event);
122126
},
123127
} as ListenerMap;
@@ -801,10 +805,14 @@ export class McpContext implements Context {
801805
}
802806
async saveFile(
803807
data: Uint8Array<ArrayBufferLike>,
804-
filename: string,
808+
clientProvidedFilePath: string,
809+
extension: SupportedExtensions,
805810
): Promise<{filename: string}> {
806811
try {
807-
const filePath = path.resolve(filename);
812+
const filePath = ensureExtension(
813+
path.resolve(clientProvidedFilePath),
814+
extension,
815+
);
808816
await fs.mkdir(path.dirname(filePath), {recursive: true});
809817
await fs.writeFile(filePath, data);
810818
return {filename: filePath};

src/McpResponse.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -414,11 +414,12 @@ export class McpResponse implements Response {
414414
if (textSnapshot) {
415415
const formatter = new SnapshotFormatter(textSnapshot);
416416
if (this.#snapshotParams.filePath) {
417-
await context.saveFile(
417+
const result = await context.saveFile(
418418
new TextEncoder().encode(formatter.toString()),
419419
this.#snapshotParams.filePath,
420+
'.txt',
420421
);
421-
snapshot = this.#snapshotParams.filePath;
422+
snapshot = result.filename;
422423
} else {
423424
snapshot = formatter;
424425
}
@@ -440,7 +441,8 @@ export class McpResponse implements Response {
440441
fetchData: true,
441442
requestFilePath: this.#attachedNetworkRequestOptions?.requestFilePath,
442443
responseFilePath: this.#attachedNetworkRequestOptions?.responseFilePath,
443-
saveFile: (data, filename) => context.saveFile(data, filename),
444+
saveFile: (data, filename, extension) =>
445+
context.saveFile(data, filename, extension),
444446
redactNetworkHeaders: this.#redactNetworkHeaders,
445447
});
446448
detailedNetworkRequest = formatter;
@@ -590,7 +592,8 @@ export class McpResponse implements Response {
590592
context.getNetworkRequestStableId(request) ===
591593
this.#networkRequestsOptions?.networkRequestIdInDevToolsUI,
592594
fetchData: false,
593-
saveFile: (data, filename) => context.saveFile(data, filename),
595+
saveFile: (data, filename, extension) =>
596+
context.saveFile(data, filename, extension),
594597
redactNetworkHeaders: this.#redactNetworkHeaders,
595598
}),
596599
),

src/PageCollector.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import type {
1111
CDPSession,
1212
ConsoleMessage,
1313
Protocol,
14+
Issue,
1415
} from './third_party/index.js';
1516
import {DevTools} from './third_party/index.js';
1617
import {
@@ -33,7 +34,7 @@ export class UncaughtError {
3334
}
3435

3536
interface PageEvents extends PuppeteerPageEvents {
36-
issue: DevTools.AggregatedIssue;
37+
devtoolsAggregatedIssue: DevTools.AggregatedIssue;
3738
uncaughtError: UncaughtError;
3839
}
3940

@@ -285,7 +286,7 @@ class PageEventSubscriber {
285286
async subscribe() {
286287
this.#resetIssueAggregator();
287288
this.#page.on('framenavigated', this.#onFrameNavigated);
288-
this.#session.on('Audits.issueAdded', this.#onIssueAdded);
289+
this.#page.on('issue', this.#onIssueAdded);
289290
this.#session.on('Runtime.exceptionThrown', this.#onExceptionThrown);
290291
try {
291292
await this.#session.send('Audits.enable');
@@ -298,7 +299,7 @@ class PageEventSubscriber {
298299
this.#seenKeys.clear();
299300
this.#seenIssues.clear();
300301
this.#page.off('framenavigated', this.#onFrameNavigated);
301-
this.#session.off('Audits.issueAdded', this.#onIssueAdded);
302+
this.#page.off('issue', this.#onIssueAdded);
302303
this.#session.off('Runtime.exceptionThrown', this.#onExceptionThrown);
303304
if (this.#issueAggregator) {
304305
this.#issueAggregator.removeEventListener(
@@ -318,7 +319,7 @@ class PageEventSubscriber {
318319
return;
319320
}
320321
this.#seenIssues.add(event.data);
321-
this.#page.emit('issue', event.data);
322+
this.#page.emit('devtoolsAggregatedIssue', event.data);
322323
};
323324

324325
#onExceptionThrown = (event: Protocol.Runtime.ExceptionThrownEvent) => {
@@ -339,9 +340,8 @@ class PageEventSubscriber {
339340
this.#resetIssueAggregator();
340341
};
341342

342-
#onIssueAdded = (data: Protocol.Audits.IssueAddedEvent) => {
343+
#onIssueAdded = (inspectorIssue: Issue) => {
343344
try {
344-
const inspectorIssue = data.issue;
345345
const issue = DevTools.createIssuesFromProtocolIssue(
346346
null,
347347
// @ts-expect-error Protocol types diverge.

src/formatters/NetworkFormatter.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ export interface NetworkFormatterOptions {
2424
saveFile?: (
2525
data: Uint8Array<ArrayBufferLike>,
2626
filename: string,
27+
extension: '.network-request' | '.network-response',
2728
) => Promise<{filename: string}>;
2829
redactNetworkHeaders: boolean;
2930
}
@@ -88,11 +89,12 @@ export class NetworkFormatter {
8889
throw new Error('saveFile is not provided');
8990
}
9091
if (data) {
91-
await this.#options.saveFile(
92+
const result = await this.#options.saveFile(
9293
Buffer.from(data),
9394
this.#options.requestFilePath,
95+
'.network-request',
9496
);
95-
this.#requestBodyFilePath = this.#options.requestFilePath;
97+
this.#requestBodyFilePath = result.filename;
9698
} else {
9799
this.#requestBody = requestBodyNotAvailableMessage;
98100
}
@@ -119,8 +121,12 @@ export class NetworkFormatter {
119121
if (!this.#options.saveFile) {
120122
throw new Error('saveFile is not provided');
121123
}
122-
await this.#options.saveFile(buffer, this.#options.responseFilePath);
123-
this.#responseBodyFilePath = this.#options.responseFilePath;
124+
const result = await this.#options.saveFile(
125+
buffer,
126+
this.#options.responseFilePath,
127+
'.network-response',
128+
);
129+
this.#responseBodyFilePath = result.filename;
124130
} catch {
125131
// Flatten error handling for buffer() failure and save failure
126132
}

0 commit comments

Comments
 (0)