chrome-devtools-mcp 0.12.1 → 0.13.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 +43 -23
- package/build/src/McpContext.js +14 -5
- package/build/src/McpResponse.js +36 -17
- package/build/src/browser.js +3 -2
- package/build/src/cli.js +30 -0
- package/build/src/formatters/SnapshotFormatter.js +128 -0
- package/build/src/formatters/consoleFormatter.js +40 -5
- package/build/src/main.js +41 -7
- package/build/src/telemetry/clearcut-logger.js +28 -0
- package/build/src/telemetry/clearcut-sender.js +11 -0
- package/build/src/telemetry/flag-utils.js +45 -0
- package/build/src/telemetry/types.js +27 -0
- package/build/src/third_party/THIRD_PARTY_NOTICES +5 -5
- package/build/src/third_party/index.js +1213 -799
- package/build/src/tools/input.js +31 -4
- package/build/src/tools/pages.js +42 -7
- package/build/src/tools/performance.js +32 -7
- package/build/src/utils/string.js +36 -0
- package/package.json +13 -13
- package/build/src/formatters/snapshotFormatter.js +0 -73
- package/build/src/third_party/issue-descriptions/SameSiteInvalidSameParty.md +0 -8
- package/build/src/third_party/issue-descriptions/SameSiteSamePartyCrossPartyContextSet.md +0 -10
package/build/src/main.js
CHANGED
|
@@ -6,21 +6,27 @@
|
|
|
6
6
|
import './polyfill.js';
|
|
7
7
|
import process from 'node:process';
|
|
8
8
|
import { ensureBrowserConnected, ensureBrowserLaunched } from './browser.js';
|
|
9
|
-
import { parseArguments } from './cli.js';
|
|
9
|
+
import { cliOptions, parseArguments } from './cli.js';
|
|
10
10
|
import { loadIssueDescriptions } from './issue-descriptions.js';
|
|
11
11
|
import { logger, saveLogsToFile } from './logger.js';
|
|
12
12
|
import { McpContext } from './McpContext.js';
|
|
13
13
|
import { McpResponse } from './McpResponse.js';
|
|
14
14
|
import { Mutex } from './Mutex.js';
|
|
15
|
+
import { ClearcutLogger } from './telemetry/clearcut-logger.js';
|
|
16
|
+
import { computeFlagUsage } from './telemetry/flag-utils.js';
|
|
15
17
|
import { McpServer, StdioServerTransport, SetLevelRequestSchema, } from './third_party/index.js';
|
|
16
18
|
import { ToolCategory } from './tools/categories.js';
|
|
17
19
|
import { tools } from './tools/tools.js';
|
|
18
20
|
// If moved update release-please config
|
|
19
21
|
// x-release-please-start-version
|
|
20
|
-
const VERSION = '0.
|
|
22
|
+
const VERSION = '0.13.0';
|
|
21
23
|
// x-release-please-end
|
|
22
24
|
export const args = parseArguments(VERSION);
|
|
23
25
|
const logFile = args.logFile ? saveLogsToFile(args.logFile) : undefined;
|
|
26
|
+
let clearcutLogger;
|
|
27
|
+
if (args.usageStatistics) {
|
|
28
|
+
clearcutLogger = new ClearcutLogger();
|
|
29
|
+
}
|
|
24
30
|
process.on('unhandledRejection', (reason, promise) => {
|
|
25
31
|
logger('Unhandled promise rejection', promise, reason);
|
|
26
32
|
});
|
|
@@ -35,9 +41,10 @@ server.server.setRequestHandler(SetLevelRequestSchema, () => {
|
|
|
35
41
|
});
|
|
36
42
|
let context;
|
|
37
43
|
async function getContext() {
|
|
38
|
-
const
|
|
44
|
+
const chromeArgs = (args.chromeArg ?? []).map(String);
|
|
45
|
+
const ignoreDefaultChromeArgs = (args.ignoreDefaultChromeArg ?? []).map(String);
|
|
39
46
|
if (args.proxyServer) {
|
|
40
|
-
|
|
47
|
+
chromeArgs.push(`--proxy-server=${args.proxyServer}`);
|
|
41
48
|
}
|
|
42
49
|
const devtools = args.experimentalDevtools ?? false;
|
|
43
50
|
const browser = args.browserUrl || args.wsEndpoint || args.autoConnect
|
|
@@ -58,7 +65,8 @@ async function getContext() {
|
|
|
58
65
|
userDataDir: args.userDataDir,
|
|
59
66
|
logFile,
|
|
60
67
|
viewport: args.viewport,
|
|
61
|
-
|
|
68
|
+
chromeArgs,
|
|
69
|
+
ignoreDefaultChromeArgs,
|
|
62
70
|
acceptInsecureCerts: args.acceptInsecureCerts,
|
|
63
71
|
devtools,
|
|
64
72
|
});
|
|
@@ -74,6 +82,11 @@ const logDisclaimers = () => {
|
|
|
74
82
|
console.error(`chrome-devtools-mcp exposes content of the browser instance to the MCP clients allowing them to inspect,
|
|
75
83
|
debug, and modify any data in the browser or DevTools.
|
|
76
84
|
Avoid sharing sensitive or personal information that you do not want to share with MCP clients.`);
|
|
85
|
+
if (args.usageStatistics) {
|
|
86
|
+
console.error(`
|
|
87
|
+
Google collects usage statistics to improve Chrome DevTools MCP. To opt-out, run with --no-usage-statistics.
|
|
88
|
+
For more details, visit: https://github.com/ChromeDevTools/chrome-devtools-mcp#usage-statistics`);
|
|
89
|
+
}
|
|
77
90
|
};
|
|
78
91
|
const toolMutex = new Mutex();
|
|
79
92
|
function registerTool(tool) {
|
|
@@ -89,12 +102,22 @@ function registerTool(tool) {
|
|
|
89
102
|
args.categoryNetwork === false) {
|
|
90
103
|
return;
|
|
91
104
|
}
|
|
105
|
+
if (tool.annotations.conditions?.includes('computerVision') &&
|
|
106
|
+
!args.experimentalVision) {
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
if (tool.annotations.conditions?.includes('experimentalInteropTools') &&
|
|
110
|
+
!args.experimentalInteropTools) {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
92
113
|
server.registerTool(tool.name, {
|
|
93
114
|
description: tool.description,
|
|
94
115
|
inputSchema: tool.schema,
|
|
95
116
|
annotations: tool.annotations,
|
|
96
117
|
}, async (params) => {
|
|
97
118
|
const guard = await toolMutex.acquire();
|
|
119
|
+
const startTime = Date.now();
|
|
120
|
+
let success = false;
|
|
98
121
|
try {
|
|
99
122
|
logger(`${tool.name} request: ${JSON.stringify(params, null, ' ')}`);
|
|
100
123
|
const context = await getContext();
|
|
@@ -104,10 +127,15 @@ function registerTool(tool) {
|
|
|
104
127
|
await tool.handler({
|
|
105
128
|
params,
|
|
106
129
|
}, response, context);
|
|
107
|
-
const content = await response.handle(tool.name, context);
|
|
108
|
-
|
|
130
|
+
const { content, structuredContent } = await response.handle(tool.name, context);
|
|
131
|
+
const result = {
|
|
109
132
|
content,
|
|
110
133
|
};
|
|
134
|
+
success = true;
|
|
135
|
+
if (args.experimentalStructuredContent) {
|
|
136
|
+
result.structuredContent = structuredContent;
|
|
137
|
+
}
|
|
138
|
+
return result;
|
|
111
139
|
}
|
|
112
140
|
catch (err) {
|
|
113
141
|
logger(`${tool.name} error:`, err, err?.stack);
|
|
@@ -126,6 +154,11 @@ function registerTool(tool) {
|
|
|
126
154
|
};
|
|
127
155
|
}
|
|
128
156
|
finally {
|
|
157
|
+
void clearcutLogger?.logToolInvocation({
|
|
158
|
+
toolName: tool.name,
|
|
159
|
+
success,
|
|
160
|
+
latencyMs: Date.now() - startTime,
|
|
161
|
+
});
|
|
129
162
|
guard.dispose();
|
|
130
163
|
}
|
|
131
164
|
});
|
|
@@ -138,3 +171,4 @@ const transport = new StdioServerTransport();
|
|
|
138
171
|
await server.connect(transport);
|
|
139
172
|
logger('Chrome DevTools MCP Server connected');
|
|
140
173
|
logDisclaimers();
|
|
174
|
+
void clearcutLogger?.logServerStart(computeFlagUsage(args, cliOptions));
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
import { ClearcutSender } from './clearcut-sender.js';
|
|
7
|
+
export class ClearcutLogger {
|
|
8
|
+
#sender;
|
|
9
|
+
constructor(sender) {
|
|
10
|
+
this.#sender = sender ?? new ClearcutSender();
|
|
11
|
+
}
|
|
12
|
+
async logToolInvocation(args) {
|
|
13
|
+
await this.#sender.send({
|
|
14
|
+
tool_invocation: {
|
|
15
|
+
tool_name: args.toolName,
|
|
16
|
+
success: args.success,
|
|
17
|
+
latency_ms: args.latencyMs,
|
|
18
|
+
},
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
async logServerStart(flagUsage) {
|
|
22
|
+
await this.#sender.send({
|
|
23
|
+
server_start: {
|
|
24
|
+
flag_usage: flagUsage,
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
import { toSnakeCase } from '../utils/string.js';
|
|
7
|
+
/**
|
|
8
|
+
* Computes telemetry flag usage from parsed arguments and CLI options.
|
|
9
|
+
*
|
|
10
|
+
* Iterates over the defined CLI options to construct a payload:
|
|
11
|
+
* - Flag names are converted to snake_case (e.g. `browserUrl` -> `browser_url`).
|
|
12
|
+
* - A flag is logged as `{flag_name}_present` if:
|
|
13
|
+
* - It has no default value, OR
|
|
14
|
+
* - The provided value differs from the default value.
|
|
15
|
+
* - Boolean flags are logged with their literal value.
|
|
16
|
+
* - String flags with defined `choices` (Enums) are logged as their uppercase value.
|
|
17
|
+
*/
|
|
18
|
+
export function computeFlagUsage(args, options) {
|
|
19
|
+
const usage = {};
|
|
20
|
+
for (const [flagName, config] of Object.entries(options)) {
|
|
21
|
+
const value = args[flagName];
|
|
22
|
+
const snakeCaseName = toSnakeCase(flagName);
|
|
23
|
+
// If there isn't a default value provided for the flag,
|
|
24
|
+
// we're going to log whether it's present on the args user
|
|
25
|
+
// provided or not. If there is a default value, we only log presence
|
|
26
|
+
// if the value differs from the default, implying explicit user intent.
|
|
27
|
+
if (!('default' in config) || value !== config.default) {
|
|
28
|
+
usage[`${snakeCaseName}_present`] = value !== undefined && value !== null;
|
|
29
|
+
}
|
|
30
|
+
if (config.type === 'boolean' && typeof value === 'boolean') {
|
|
31
|
+
// For boolean options, we're going to log the value directly.
|
|
32
|
+
usage[snakeCaseName] = value;
|
|
33
|
+
}
|
|
34
|
+
else if (config.type === 'string' &&
|
|
35
|
+
typeof value === 'string' &&
|
|
36
|
+
'choices' in config &&
|
|
37
|
+
config.choices) {
|
|
38
|
+
// For enums, log the value as uppercase
|
|
39
|
+
// We're going to have an enum for such flags with choices represented
|
|
40
|
+
// as an `enum` where the keys of the enum will map to the uppercase `choice`.
|
|
41
|
+
usage[snakeCaseName] = value.toUpperCase();
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return usage;
|
|
45
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Google LLC
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
// Enums
|
|
7
|
+
export var OsType;
|
|
8
|
+
(function (OsType) {
|
|
9
|
+
OsType[OsType["OS_TYPE_UNSPECIFIED"] = 0] = "OS_TYPE_UNSPECIFIED";
|
|
10
|
+
OsType[OsType["OS_TYPE_WINDOWS"] = 1] = "OS_TYPE_WINDOWS";
|
|
11
|
+
OsType[OsType["OS_TYPE_MACOS"] = 2] = "OS_TYPE_MACOS";
|
|
12
|
+
OsType[OsType["OS_TYPE_LINUX"] = 3] = "OS_TYPE_LINUX";
|
|
13
|
+
})(OsType || (OsType = {}));
|
|
14
|
+
export var ChromeChannel;
|
|
15
|
+
(function (ChromeChannel) {
|
|
16
|
+
ChromeChannel[ChromeChannel["CHROME_CHANNEL_UNSPECIFIED"] = 0] = "CHROME_CHANNEL_UNSPECIFIED";
|
|
17
|
+
ChromeChannel[ChromeChannel["CHROME_CHANNEL_CANARY"] = 1] = "CHROME_CHANNEL_CANARY";
|
|
18
|
+
ChromeChannel[ChromeChannel["CHROME_CHANNEL_DEV"] = 2] = "CHROME_CHANNEL_DEV";
|
|
19
|
+
ChromeChannel[ChromeChannel["CHROME_CHANNEL_BETA"] = 3] = "CHROME_CHANNEL_BETA";
|
|
20
|
+
ChromeChannel[ChromeChannel["CHROME_CHANNEL_STABLE"] = 4] = "CHROME_CHANNEL_STABLE";
|
|
21
|
+
})(ChromeChannel || (ChromeChannel = {}));
|
|
22
|
+
export var McpClient;
|
|
23
|
+
(function (McpClient) {
|
|
24
|
+
McpClient[McpClient["MCP_CLIENT_UNSPECIFIED"] = 0] = "MCP_CLIENT_UNSPECIFIED";
|
|
25
|
+
McpClient[McpClient["MCP_CLIENT_CLAUDE_CODE"] = 1] = "MCP_CLIENT_CLAUDE_CODE";
|
|
26
|
+
McpClient[McpClient["MCP_CLIENT_GEMINI_CLI"] = 2] = "MCP_CLIENT_GEMINI_CLI";
|
|
27
|
+
})(McpClient || (McpClient = {}));
|
|
@@ -421,7 +421,7 @@ SOFTWARE.
|
|
|
421
421
|
|
|
422
422
|
Name: @modelcontextprotocol/sdk
|
|
423
423
|
URL: https://modelcontextprotocol.io
|
|
424
|
-
Version: 1.
|
|
424
|
+
Version: 1.25.2
|
|
425
425
|
License: MIT
|
|
426
426
|
|
|
427
427
|
MIT License
|
|
@@ -635,14 +635,14 @@ SOFTWARE.
|
|
|
635
635
|
|
|
636
636
|
Name: puppeteer-core
|
|
637
637
|
URL: https://github.com/puppeteer/puppeteer/tree/main/packages/puppeteer-core
|
|
638
|
-
Version: 24.
|
|
638
|
+
Version: 24.35.0
|
|
639
639
|
License: Apache-2.0
|
|
640
640
|
|
|
641
641
|
-------------------- DEPENDENCY DIVIDER --------------------
|
|
642
642
|
|
|
643
643
|
Name: @puppeteer/browsers
|
|
644
644
|
URL: https://github.com/puppeteer/puppeteer/tree/main/packages/browsers
|
|
645
|
-
Version: 2.11.
|
|
645
|
+
Version: 2.11.1
|
|
646
646
|
License: Apache-2.0
|
|
647
647
|
|
|
648
648
|
-------------------- DEPENDENCY DIVIDER --------------------
|
|
@@ -1054,7 +1054,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
1054
1054
|
|
|
1055
1055
|
Name: basic-ftp
|
|
1056
1056
|
URL: https://github.com/patrickjuchli/basic-ftp.git
|
|
1057
|
-
Version: 5.0
|
|
1057
|
+
Version: 5.1.0
|
|
1058
1058
|
License: MIT
|
|
1059
1059
|
|
|
1060
1060
|
Copyright (c) 2019 Patrick Juchli
|
|
@@ -1388,7 +1388,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
1388
1388
|
|
|
1389
1389
|
Name: ws
|
|
1390
1390
|
URL: https://github.com/websockets/ws
|
|
1391
|
-
Version: 8.
|
|
1391
|
+
Version: 8.19.0
|
|
1392
1392
|
License: MIT
|
|
1393
1393
|
|
|
1394
1394
|
Copyright (c) 2011 Einar Otto Stangvik <einaros@gmail.com>
|