Skip to content

Commit 04d4473

Browse files
authored
Merge branch 'main' into fix/940-improve-mcp-invocation
2 parents 31e1a0f + e5973fd commit 04d4473

23 files changed

Lines changed: 4900 additions & 5007 deletions

.github/ISSUE_TEMPLATE/config.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
blank_issues_enabled: false
1+
blank_issues_enabled: true

.github/workflows/run-tests.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,12 @@ jobs:
4141
shell: bash
4242
run: npm ci
4343

44+
- name: Install Chrome Canary
45+
shell: bash
46+
run: |
47+
CANARY_PATH=$(npx @puppeteer/browsers install chrome@canary --format "{{path}}")
48+
echo "CANARY_EXECUTABLE_PATH=$CANARY_PATH" >> $GITHUB_ENV
49+
4450
- name: Build
4551
run: npm run bundle
4652
env:

CONTRIBUTING.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,3 +119,10 @@ export const scenario: TestScenario = {
119119
},
120120
};
121121
```
122+
123+
## Restrictions on JSON schema
124+
125+
- no .nullable(), no .object() types.
126+
- represent complex object as a short formatted string.
127+
128+
TODO: implement eslint for schema https://github.com/ChromeDevTools/chrome-devtools-mcp/issues/1076

docs/tool-reference.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<!-- AUTO GENERATED DO NOT EDIT - run 'npm run docs' to update-->
22

3-
# Chrome DevTools MCP Tool Reference (~7324 cl100k_base tokens)
3+
# Chrome DevTools MCP Tool Reference (~6919 cl100k_base tokens)
44

55
- **[Input automation](#input-automation)** (9 tools)
66
- [`click`](#click)
@@ -222,11 +222,11 @@
222222
**Parameters:**
223223

224224
- **colorScheme** (enum: "dark", "light", "auto") _(optional)_: [`Emulate`](#emulate) the dark or the light mode. Set to "auto" to reset to the default.
225-
- **cpuThrottlingRate** (number) _(optional)_: Represents the CPU slowdown factor. Set the rate to 1 to disable throttling. If omitted, throttling remains unchanged.
226-
- **geolocation** (unknown) _(optional)_: Geolocation to [`emulate`](#emulate). Set to null to clear the geolocation override.
227-
- **networkConditions** (enum: "No emulation", "Offline", "Slow 3G", "Fast 3G", "Slow 4G", "Fast 4G") _(optional)_: Throttle network. Set to "No emulation" to disable. If omitted, conditions remain unchanged.
228-
- **userAgent** (unknown) _(optional)_: User agent to [`emulate`](#emulate). Set to null to clear the user agent override.
229-
- **viewport** (unknown) _(optional)_: Viewport to [`emulate`](#emulate). Set to null to reset to the default viewport.
225+
- **cpuThrottlingRate** (number) _(optional)_: Represents the CPU slowdown factor. Omit or set the rate to 1 to disable throttling
226+
- **geolocation** (string) _(optional)_: Geolocation (`&lt;latitude&gt;x&lt;longitude&gt;`) to [`emulate`](#emulate). Latitude between -90 and 90. Longitude between -180 and 180. Omit clear the geolocation override.
227+
- **networkConditions** (enum: "Offline", "Slow 3G", "Fast 3G", "Slow 4G", "Fast 4G") _(optional)_: Throttle network. Omit to disable throttling.
228+
- **userAgent** (string) _(optional)_: User agent to [`emulate`](#emulate). Set to empty string to clear the user agent override.
229+
- **viewport** (string) _(optional)_: [`Emulate`](#emulate) device viewports '&lt;width&gt;x&lt;height&gt;x&lt;devicePixelRatio&gt;[,mobile][,touch][,landscape]'. 'touch' and 'mobile' to [`emulate`](#emulate) mobile devices. 'landscape' to [`emulate`](#emulate) landscape mode.
230230

231231
---
232232

package-lock.json

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

package.json

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
"bin": "./build/src/index.js",
77
"main": "./build/src/server.js",
88
"scripts": {
9+
"cli:generate": "node --experimental-strip-types scripts/generate-cli.ts",
910
"clean": "node -e \"require('fs').rmSync('build', {recursive: true, force: true})\"",
1011
"bundle": "npm run clean && npm run build && rollup -c rollup.config.mjs && node -e \"require('fs').rmSync('build/node_modules', {recursive: true, force: true})\" && node --experimental-strip-types scripts/append-lighthouse-notices.ts",
1112
"build": "tsc && node --experimental-strip-types --no-warnings=ExperimentalWarning scripts/post-build.ts",
@@ -43,7 +44,7 @@
4344
"devDependencies": {
4445
"@eslint/js": "^9.35.0",
4546
"@google/genai": "^1.37.0",
46-
"@modelcontextprotocol/sdk": "1.26.0",
47+
"@modelcontextprotocol/sdk": "1.27.1",
4748
"@rollup/plugin-commonjs": "^29.0.0",
4849
"@rollup/plugin-json": "^6.1.0",
4950
"@rollup/plugin-node-resolve": "^16.0.3",
@@ -55,14 +56,14 @@
5556
"@types/yargs": "^17.0.33",
5657
"@typescript-eslint/eslint-plugin": "^8.43.0",
5758
"@typescript-eslint/parser": "^8.43.0",
58-
"chrome-devtools-frontend": "1.0.1587572",
59+
"chrome-devtools-frontend": "1.0.1591204",
5960
"core-js": "3.48.0",
6061
"debug": "4.4.3",
6162
"eslint": "^9.35.0",
6263
"eslint-import-resolver-typescript": "^4.4.4",
6364
"eslint-plugin-import": "^2.32.0",
6465
"globals": "^17.0.0",
65-
"lighthouse": "13.0.2",
66+
"lighthouse": "13.0.3",
6667
"prettier": "^3.6.2",
6768
"puppeteer": "24.37.5",
6869
"rollup": "4.59.0",

scripts/generate-cli.ts

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
/**
2+
* @license
3+
* Copyright 2026 Google LLC
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
import fs from 'node:fs';
8+
import path from 'node:path';
9+
10+
import {Client} from '@modelcontextprotocol/sdk/client/index.js';
11+
import {StdioClientTransport} from '@modelcontextprotocol/sdk/client/stdio.js';
12+
13+
import {parseArguments} from '../build/src/cli.js';
14+
import {labels} from '../build/src/tools/categories.js';
15+
import {createTools} from '../build/src/tools/tools.js';
16+
17+
const OUTPUT_PATH = path.join(
18+
import.meta.dirname,
19+
'../src/bin/cliDefinitions.ts',
20+
);
21+
22+
async function fetchTools() {
23+
console.log('Connecting to chrome-devtools-mcp to fetch tools...');
24+
// Use the local build of the server
25+
const serverPath = path.join(import.meta.dirname, '../build/src/index.js');
26+
27+
const transport = new StdioClientTransport({
28+
command: 'node',
29+
args: [serverPath],
30+
env: {...process.env, CHROME_DEVTOOLS_MCP_NO_USAGE_STATISTICS: 'true'},
31+
});
32+
33+
const client = new Client(
34+
{
35+
name: 'chrome-devtools-cli-generator',
36+
version: '0.1.0',
37+
},
38+
{
39+
capabilities: {},
40+
},
41+
);
42+
43+
await client.connect(transport);
44+
try {
45+
const toolsResponse = await client.listTools();
46+
if (!toolsResponse.tools?.length) {
47+
throw new Error(`No tools were fetched`);
48+
}
49+
const tools = toolsResponse.tools || [];
50+
console.log(`Fetched ${tools.length} tools`);
51+
return tools;
52+
} finally {
53+
await client.close();
54+
}
55+
}
56+
57+
interface CliOption {
58+
name: string;
59+
type: string;
60+
description: string;
61+
required: boolean;
62+
default?: unknown;
63+
enum?: unknown[];
64+
}
65+
66+
interface JsonSchema {
67+
type?: string | string[];
68+
description?: string;
69+
properties?: Record<string, JsonSchema>;
70+
required?: string[];
71+
default?: unknown;
72+
enum?: unknown[];
73+
}
74+
75+
function schemaToCLIOptions(schema: JsonSchema): CliOption[] {
76+
if (!schema || !schema.properties) {
77+
return [];
78+
}
79+
const required = schema.required || [];
80+
const properties = schema.properties;
81+
return Object.entries(properties).map(([name, prop]) => {
82+
const isRequired = required.includes(name);
83+
const description = prop.description || '';
84+
if (typeof prop.type !== 'string') {
85+
throw new Error(
86+
`Property ${name} has a complex type not supported by CLI.`,
87+
);
88+
}
89+
return {
90+
name,
91+
type: prop.type,
92+
description,
93+
required: isRequired,
94+
default: prop.default,
95+
enum: prop.enum,
96+
};
97+
});
98+
}
99+
100+
async function generateCli() {
101+
const tools = await fetchTools();
102+
// Sort tools by name
103+
const sortedTools = tools.sort((a, b) => a.name.localeCompare(b.name));
104+
105+
const staticTools = createTools(parseArguments());
106+
const toolNameToCategory = new Map<string, string>();
107+
for (const tool of staticTools) {
108+
toolNameToCategory.set(
109+
tool.name,
110+
labels[tool.annotations.category as keyof typeof labels],
111+
);
112+
}
113+
114+
const commands: Record<
115+
string,
116+
{description: string; category: string; args: Record<string, CliOption>}
117+
> = {};
118+
119+
for (const tool of sortedTools) {
120+
const options = schemaToCLIOptions(tool.inputSchema);
121+
const args: Record<string, CliOption> = {};
122+
for (const opt of options) {
123+
args[opt.name] = opt;
124+
}
125+
const category = toolNameToCategory.get(tool.name);
126+
if (!category) {
127+
throw new Error(`Tool ${tool.name} has no category.`);
128+
}
129+
if (!tool.description) {
130+
throw new Error(`Tool ${tool.name} is missing descripttion`);
131+
}
132+
commands[tool.name] = {
133+
description: tool.description,
134+
category,
135+
args,
136+
};
137+
}
138+
139+
const lines: string[] = [];
140+
lines.push(`/**
141+
* @license
142+
* Copyright ${new Date().getFullYear()} Google LLC
143+
* SPDX-License-Identifier: Apache-2.0
144+
*/
145+
146+
// NOTE: do not edit manually. Auto-generated by 'npm run cli:generate'.
147+
148+
export interface ArgDef {
149+
name: string;
150+
type: string;
151+
description: string;
152+
required: boolean;
153+
default?: string | number | boolean;
154+
enum?: ReadonlyArray<string | number>;
155+
}
156+
export type Commands = Record<
157+
string,
158+
{
159+
description: string;
160+
category: string;
161+
args: Record<string, ArgDef>
162+
}
163+
>;
164+
export const commands: Commands = ${JSON.stringify(commands, null, 2)} as const;
165+
`);
166+
167+
fs.mkdirSync(path.dirname(OUTPUT_PATH), {recursive: true});
168+
fs.writeFileSync(OUTPUT_PATH, lines.join(''));
169+
console.log(`Generated CLI at ${OUTPUT_PATH}`);
170+
}
171+
172+
generateCli().catch(err => {
173+
console.error('Error during generation:', err);
174+
process.exit(1);
175+
});

0 commit comments

Comments
 (0)