chrome-devtools-mcp 0.17.1 → 0.17.3
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 +4 -1
- package/build/src/McpResponse.js +3 -11
- package/build/src/formatters/NetworkFormatter.js +61 -69
- package/build/src/main.js +1 -1
- package/build/src/third_party/THIRD_PARTY_NOTICES +2 -2
- package/build/src/third_party/bundled-packages.json +1 -1
- package/build/src/third_party/index.js +12 -12
- package/build/src/tools/input.js +6 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -124,6 +124,9 @@ claude mcp add chrome-devtools --scope user npx chrome-devtools-mcp@latest
|
|
|
124
124
|
|
|
125
125
|
**Install as a Plugin (MCP + Skills)**
|
|
126
126
|
|
|
127
|
+
> [!NOTE]
|
|
128
|
+
> If you already had Chrome DevTools MCP installed previously for Claude Code, make sure to remove it first from your installation and configuration files.
|
|
129
|
+
|
|
127
130
|
To install Chrome DevTools MCP with skills, add the marketplace registry in Claude Code:
|
|
128
131
|
|
|
129
132
|
```sh
|
|
@@ -147,7 +150,7 @@ Restart Claude Code to have the MCP server and skills load (check with `/skills`
|
|
|
147
150
|
|
|
148
151
|
<details>
|
|
149
152
|
<summary>Codex</summary>
|
|
150
|
-
Follow the <a href="https://
|
|
153
|
+
Follow the <a href="https://developers.openai.com/codex/mcp/#configure-with-the-cli">configure MCP guide</a>
|
|
151
154
|
using the standard config from above. You can also install the Chrome DevTools MCP server using the Codex CLI:
|
|
152
155
|
|
|
153
156
|
```bash
|
package/build/src/McpResponse.js
CHANGED
|
@@ -398,16 +398,8 @@ Call ${handleDialog.name} to handle it before continuing.`);
|
|
|
398
398
|
response.push(extensionsMessage);
|
|
399
399
|
}
|
|
400
400
|
}
|
|
401
|
-
if (this.#networkRequestsOptions?.include) {
|
|
402
|
-
|
|
403
|
-
// Apply resource type filtering if specified
|
|
404
|
-
if (this.#networkRequestsOptions.resourceTypes?.length) {
|
|
405
|
-
const normalizedTypes = new Set(this.#networkRequestsOptions.resourceTypes);
|
|
406
|
-
requests = requests.filter(request => {
|
|
407
|
-
const type = request.resourceType();
|
|
408
|
-
return normalizedTypes.has(type);
|
|
409
|
-
});
|
|
410
|
-
}
|
|
401
|
+
if (this.#networkRequestsOptions?.include && data.networkRequests) {
|
|
402
|
+
const requests = data.networkRequests;
|
|
411
403
|
response.push('## Network requests');
|
|
412
404
|
if (requests.length) {
|
|
413
405
|
const paginationData = this.#dataWithPagination(requests, this.#networkRequestsOptions.pagination);
|
|
@@ -415,7 +407,7 @@ Call ${handleDialog.name} to handle it before continuing.`);
|
|
|
415
407
|
response.push(...paginationData.info);
|
|
416
408
|
if (data.networkRequests) {
|
|
417
409
|
structuredContent.networkRequests = [];
|
|
418
|
-
for (const formatter of
|
|
410
|
+
for (const formatter of paginationData.items) {
|
|
419
411
|
response.push(formatter.toString());
|
|
420
412
|
structuredContent.networkRequests.push(formatter.toJSON());
|
|
421
413
|
}
|
|
@@ -82,63 +82,10 @@ export class NetworkFormatter {
|
|
|
82
82
|
}
|
|
83
83
|
}
|
|
84
84
|
toString() {
|
|
85
|
-
|
|
86
|
-
return `reqid=${this.#options.requestId} ${this.#request.method()} ${this.#request.url()} ${this.#getStatusFromRequest(this.#request)}${this.#options.selectedInDevToolsUI ? ` [selected in the DevTools Network panel]` : ''}`;
|
|
85
|
+
return convertNetworkRequestConciseToString(this.toJSON());
|
|
87
86
|
}
|
|
88
87
|
toStringDetailed() {
|
|
89
|
-
|
|
90
|
-
response.push(`## Request ${this.#request.url()}`);
|
|
91
|
-
response.push(`Status: ${this.#getStatusFromRequest(this.#request)}`);
|
|
92
|
-
response.push(`### Request Headers`);
|
|
93
|
-
for (const line of this.#getFormattedHeaderValue(this.#request.headers())) {
|
|
94
|
-
response.push(line);
|
|
95
|
-
}
|
|
96
|
-
if (this.#requestBody) {
|
|
97
|
-
response.push(`### Request Body`);
|
|
98
|
-
response.push(this.#requestBody);
|
|
99
|
-
}
|
|
100
|
-
else if (this.#requestBodyFilePath) {
|
|
101
|
-
response.push(`### Request Body`);
|
|
102
|
-
response.push(`Saved to ${this.#requestBodyFilePath}.`);
|
|
103
|
-
}
|
|
104
|
-
const httpResponse = this.#request.response();
|
|
105
|
-
if (httpResponse) {
|
|
106
|
-
response.push(`### Response Headers`);
|
|
107
|
-
for (const line of this.#getFormattedHeaderValue(httpResponse.headers())) {
|
|
108
|
-
response.push(line);
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
if (this.#responseBody) {
|
|
112
|
-
response.push(`### Response Body`);
|
|
113
|
-
response.push(this.#responseBody);
|
|
114
|
-
}
|
|
115
|
-
else if (this.#responseBodyFilePath) {
|
|
116
|
-
response.push(`### Response Body`);
|
|
117
|
-
response.push(`Saved to ${this.#responseBodyFilePath}.`);
|
|
118
|
-
}
|
|
119
|
-
const httpFailure = this.#request.failure();
|
|
120
|
-
if (httpFailure) {
|
|
121
|
-
response.push(`### Request failed with`);
|
|
122
|
-
response.push(httpFailure.errorText);
|
|
123
|
-
}
|
|
124
|
-
const redirectChain = this.#request.redirectChain();
|
|
125
|
-
if (redirectChain.length) {
|
|
126
|
-
response.push(`### Redirect chain`);
|
|
127
|
-
let indent = 0;
|
|
128
|
-
for (const request of redirectChain.reverse()) {
|
|
129
|
-
const id = this.#options.requestIdResolver
|
|
130
|
-
? this.#options.requestIdResolver(request)
|
|
131
|
-
: undefined;
|
|
132
|
-
// We create a temporary synchronous instance just for toString
|
|
133
|
-
const formatter = new NetworkFormatter(request, {
|
|
134
|
-
requestId: id,
|
|
135
|
-
saveFile: this.#options.saveFile,
|
|
136
|
-
});
|
|
137
|
-
response.push(`${' '.repeat(indent)}${formatter.toString()}`);
|
|
138
|
-
indent++;
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
return response.join('\n');
|
|
88
|
+
return converNetworkRequestDetailedToStringDetailed(this.toJSONDetailed());
|
|
142
89
|
}
|
|
143
90
|
toJSON() {
|
|
144
91
|
return {
|
|
@@ -180,27 +127,16 @@ export class NetworkFormatter {
|
|
|
180
127
|
const failure = request.failure();
|
|
181
128
|
let status;
|
|
182
129
|
if (httpResponse) {
|
|
183
|
-
|
|
184
|
-
status =
|
|
185
|
-
responseStatus >= 200 && responseStatus <= 299
|
|
186
|
-
? `[success - ${responseStatus}]`
|
|
187
|
-
: `[failed - ${responseStatus}]`;
|
|
130
|
+
status = httpResponse.status().toString();
|
|
188
131
|
}
|
|
189
132
|
else if (failure) {
|
|
190
|
-
status =
|
|
133
|
+
status = failure.errorText;
|
|
191
134
|
}
|
|
192
135
|
else {
|
|
193
|
-
status = '
|
|
136
|
+
status = 'pending';
|
|
194
137
|
}
|
|
195
138
|
return status;
|
|
196
139
|
}
|
|
197
|
-
#getFormattedHeaderValue(headers) {
|
|
198
|
-
const response = [];
|
|
199
|
-
for (const [name, value] of Object.entries(headers)) {
|
|
200
|
-
response.push(`- ${name}:${value}`);
|
|
201
|
-
}
|
|
202
|
-
return response;
|
|
203
|
-
}
|
|
204
140
|
async #getFormattedResponseBody(httpResponse, sizeLimit = BODY_CONTEXT_SIZE_LIMIT) {
|
|
205
141
|
try {
|
|
206
142
|
const responseBuffer = await httpResponse.buffer();
|
|
@@ -224,3 +160,59 @@ function getSizeLimitedString(text, sizeLimit) {
|
|
|
224
160
|
}
|
|
225
161
|
return text;
|
|
226
162
|
}
|
|
163
|
+
function convertNetworkRequestConciseToString(data) {
|
|
164
|
+
// TODO truncate the URL
|
|
165
|
+
return `reqid=${data.requestId} ${data.method} ${data.url} [${data.status}]${data.selectedInDevToolsUI ? ` [selected in the DevTools Network panel]` : ''}`;
|
|
166
|
+
}
|
|
167
|
+
function formatHeadlers(headers) {
|
|
168
|
+
const response = [];
|
|
169
|
+
for (const [name, value] of Object.entries(headers)) {
|
|
170
|
+
response.push(`- ${name}:${value}`);
|
|
171
|
+
}
|
|
172
|
+
return response;
|
|
173
|
+
}
|
|
174
|
+
function converNetworkRequestDetailedToStringDetailed(data) {
|
|
175
|
+
const response = [];
|
|
176
|
+
response.push(`## Request ${data.url}`);
|
|
177
|
+
response.push(`Status: ${data.status}`);
|
|
178
|
+
response.push(`### Request Headers`);
|
|
179
|
+
for (const line of formatHeadlers(data.requestHeaders)) {
|
|
180
|
+
response.push(line);
|
|
181
|
+
}
|
|
182
|
+
if (data.requestBody) {
|
|
183
|
+
response.push(`### Request Body`);
|
|
184
|
+
response.push(data.requestBody);
|
|
185
|
+
}
|
|
186
|
+
else if (data.requestBodyFilePath) {
|
|
187
|
+
response.push(`### Request Body`);
|
|
188
|
+
response.push(`Saved to ${data.requestBodyFilePath}.`);
|
|
189
|
+
}
|
|
190
|
+
if (data.responseHeaders) {
|
|
191
|
+
response.push(`### Response Headers`);
|
|
192
|
+
for (const line of formatHeadlers(data.responseHeaders)) {
|
|
193
|
+
response.push(line);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
if (data.responseBody) {
|
|
197
|
+
response.push(`### Response Body`);
|
|
198
|
+
response.push(data.responseBody);
|
|
199
|
+
}
|
|
200
|
+
else if (data.responseBodyFilePath) {
|
|
201
|
+
response.push(`### Response Body`);
|
|
202
|
+
response.push(`Saved to ${data.responseBodyFilePath}.`);
|
|
203
|
+
}
|
|
204
|
+
if (data.failure) {
|
|
205
|
+
response.push(`### Request failed with`);
|
|
206
|
+
response.push(data.failure);
|
|
207
|
+
}
|
|
208
|
+
const redirectChain = data.redirectChain;
|
|
209
|
+
if (redirectChain?.length) {
|
|
210
|
+
response.push(`### Redirect chain`);
|
|
211
|
+
let indent = 0;
|
|
212
|
+
for (const request of redirectChain.reverse()) {
|
|
213
|
+
response.push(`${' '.repeat(indent)}${convertNetworkRequestConciseToString(request)})}`);
|
|
214
|
+
indent++;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
return response.join('\n');
|
|
218
|
+
}
|
package/build/src/main.js
CHANGED
|
@@ -20,7 +20,7 @@ import { ToolCategory } from './tools/categories.js';
|
|
|
20
20
|
import { tools } from './tools/tools.js';
|
|
21
21
|
// If moved update release-please config
|
|
22
22
|
// x-release-please-start-version
|
|
23
|
-
const VERSION = '0.17.
|
|
23
|
+
const VERSION = '0.17.3';
|
|
24
24
|
// x-release-please-end
|
|
25
25
|
export const args = parseArguments(VERSION);
|
|
26
26
|
const logFile = args.logFile ? saveLogsToFile(args.logFile) : undefined;
|
|
@@ -636,14 +636,14 @@ SOFTWARE.
|
|
|
636
636
|
|
|
637
637
|
Name: puppeteer-core
|
|
638
638
|
URL: https://github.com/puppeteer/puppeteer/tree/main/packages/puppeteer-core
|
|
639
|
-
Version: 24.37.
|
|
639
|
+
Version: 24.37.4
|
|
640
640
|
License: Apache-2.0
|
|
641
641
|
|
|
642
642
|
-------------------- DEPENDENCY DIVIDER --------------------
|
|
643
643
|
|
|
644
644
|
Name: @puppeteer/browsers
|
|
645
645
|
URL: https://github.com/puppeteer/puppeteer/tree/main/packages/browsers
|
|
646
|
-
Version: 2.
|
|
646
|
+
Version: 2.13.0
|
|
647
647
|
License: Apache-2.0
|
|
648
648
|
|
|
649
649
|
-------------------- DEPENDENCY DIVIDER --------------------
|
|
@@ -40599,7 +40599,7 @@ function mergeUint8Arrays(items) {
|
|
|
40599
40599
|
* Copyright 2025 Google Inc.
|
|
40600
40600
|
* SPDX-License-Identifier: Apache-2.0
|
|
40601
40601
|
*/
|
|
40602
|
-
const packageVersion = '24.37.
|
|
40602
|
+
const packageVersion = '24.37.4';
|
|
40603
40603
|
|
|
40604
40604
|
/**
|
|
40605
40605
|
* @license
|
|
@@ -40865,15 +40865,15 @@ async function getReadableFromProtocolStream(client, handle) {
|
|
|
40865
40865
|
},
|
|
40866
40866
|
});
|
|
40867
40867
|
}
|
|
40868
|
+
const VALID_DIALOG_TYPES = new Set([
|
|
40869
|
+
'alert',
|
|
40870
|
+
'confirm',
|
|
40871
|
+
'prompt',
|
|
40872
|
+
'beforeunload',
|
|
40873
|
+
]);
|
|
40868
40874
|
function validateDialogType(type) {
|
|
40869
40875
|
let dialogType = null;
|
|
40870
|
-
|
|
40871
|
-
'alert',
|
|
40872
|
-
'confirm',
|
|
40873
|
-
'prompt',
|
|
40874
|
-
'beforeunload',
|
|
40875
|
-
]);
|
|
40876
|
-
if (validDialogTypes.has(type)) {
|
|
40876
|
+
if (VALID_DIALOG_TYPES.has(type)) {
|
|
40877
40877
|
dialogType = type;
|
|
40878
40878
|
}
|
|
40879
40879
|
assert(dialogType, `Unknown javascript dialog type: ${type}`);
|
|
@@ -50610,7 +50610,7 @@ let FrameManager$1 = class FrameManager extends EventEmitter {
|
|
|
50610
50610
|
frame.updateClient(target._session());
|
|
50611
50611
|
}
|
|
50612
50612
|
this.setupEventListeners(target._session());
|
|
50613
|
-
void this.initialize(target._session(), frame);
|
|
50613
|
+
void this.initialize(target._session(), frame).catch(debugError);
|
|
50614
50614
|
}
|
|
50615
50615
|
_deviceRequestPromptManager(client) {
|
|
50616
50616
|
let manager = this.#deviceRequestPromptManagerMap.get(client);
|
|
@@ -55541,9 +55541,9 @@ class Puppeteer {
|
|
|
55541
55541
|
* SPDX-License-Identifier: Apache-2.0
|
|
55542
55542
|
*/
|
|
55543
55543
|
const PUPPETEER_REVISIONS = Object.freeze({
|
|
55544
|
-
chrome: '145.0.7632.
|
|
55545
|
-
'chrome-headless-shell': '145.0.7632.
|
|
55546
|
-
firefox: 'stable_147.0.
|
|
55544
|
+
chrome: '145.0.7632.76',
|
|
55545
|
+
'chrome-headless-shell': '145.0.7632.76',
|
|
55546
|
+
firefox: 'stable_147.0.4',
|
|
55547
55547
|
});
|
|
55548
55548
|
|
|
55549
55549
|
/**
|
package/build/src/tools/input.js
CHANGED
|
@@ -156,11 +156,16 @@ async function selectOption(handle, aXNode, value) {
|
|
|
156
156
|
throw new Error(`Could not find option with text "${value}"`);
|
|
157
157
|
}
|
|
158
158
|
}
|
|
159
|
+
function hasOptionChildren(aXNode) {
|
|
160
|
+
return aXNode.children.some(child => child.role === 'option');
|
|
161
|
+
}
|
|
159
162
|
async function fillFormElement(uid, value, context) {
|
|
160
163
|
const handle = await context.getElementByUid(uid);
|
|
161
164
|
try {
|
|
162
165
|
const aXNode = context.getAXNodeByUid(uid);
|
|
163
|
-
|
|
166
|
+
// We assume that combobox needs to be handled as select if it has
|
|
167
|
+
// role='combobox' and option children.
|
|
168
|
+
if (aXNode && aXNode.role === 'combobox' && hasOptionChildren(aXNode)) {
|
|
164
169
|
await selectOption(handle, aXNode, value);
|
|
165
170
|
}
|
|
166
171
|
else {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "chrome-devtools-mcp",
|
|
3
|
-
"version": "0.17.
|
|
3
|
+
"version": "0.17.3",
|
|
4
4
|
"description": "MCP server for Chrome DevTools",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": "./build/src/index.js",
|
|
@@ -62,7 +62,7 @@
|
|
|
62
62
|
"eslint-plugin-import": "^2.32.0",
|
|
63
63
|
"globals": "^17.0.0",
|
|
64
64
|
"prettier": "^3.6.2",
|
|
65
|
-
"puppeteer": "24.37.
|
|
65
|
+
"puppeteer": "24.37.4",
|
|
66
66
|
"rollup": "4.57.1",
|
|
67
67
|
"rollup-plugin-cleanup": "^3.2.1",
|
|
68
68
|
"rollup-plugin-license": "^3.6.0",
|