chromedevtools--chrome-devtools-mcp
a4533326a4
The script will be used to generate CLI for the MCP server at `src/bin/cliDefinitions.ts`. It uses the live server JSON schemas to make it more compatible with the actual MCP server at runtime. The code definitions are used to extend the CLI with tool categories.
176 行
4.4 KiB
TypeScript
176 行
4.4 KiB
TypeScript
/**
|
|
* @license
|
|
* Copyright 2026 Google LLC
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
import fs from 'node:fs';
|
|
import path from 'node:path';
|
|
|
|
import {Client} from '@modelcontextprotocol/sdk/client/index.js';
|
|
import {StdioClientTransport} from '@modelcontextprotocol/sdk/client/stdio.js';
|
|
|
|
import {parseArguments} from '../build/src/cli.js';
|
|
import {labels} from '../build/src/tools/categories.js';
|
|
import {createTools} from '../build/src/tools/tools.js';
|
|
|
|
const OUTPUT_PATH = path.join(
|
|
import.meta.dirname,
|
|
'../src/bin/cliDefinitions.ts',
|
|
);
|
|
|
|
async function fetchTools() {
|
|
console.log('Connecting to chrome-devtools-mcp to fetch tools...');
|
|
// Use the local build of the server
|
|
const serverPath = path.join(import.meta.dirname, '../build/src/index.js');
|
|
|
|
const transport = new StdioClientTransport({
|
|
command: 'node',
|
|
args: [serverPath],
|
|
env: {...process.env, CHROME_DEVTOOLS_MCP_NO_USAGE_STATISTICS: 'true'},
|
|
});
|
|
|
|
const client = new Client(
|
|
{
|
|
name: 'chrome-devtools-cli-generator',
|
|
version: '0.1.0',
|
|
},
|
|
{
|
|
capabilities: {},
|
|
},
|
|
);
|
|
|
|
await client.connect(transport);
|
|
try {
|
|
const toolsResponse = await client.listTools();
|
|
if (!toolsResponse.tools?.length) {
|
|
throw new Error(`No tools were fetched`);
|
|
}
|
|
const tools = toolsResponse.tools || [];
|
|
console.log(`Fetched ${tools.length} tools`);
|
|
return tools;
|
|
} finally {
|
|
await client.close();
|
|
}
|
|
}
|
|
|
|
interface CliOption {
|
|
name: string;
|
|
type: string;
|
|
description: string;
|
|
required: boolean;
|
|
default?: unknown;
|
|
enum?: unknown[];
|
|
}
|
|
|
|
interface JsonSchema {
|
|
type?: string | string[];
|
|
description?: string;
|
|
properties?: Record<string, JsonSchema>;
|
|
required?: string[];
|
|
default?: unknown;
|
|
enum?: unknown[];
|
|
}
|
|
|
|
function schemaToCLIOptions(schema: JsonSchema): CliOption[] {
|
|
if (!schema || !schema.properties) {
|
|
return [];
|
|
}
|
|
const required = schema.required || [];
|
|
const properties = schema.properties;
|
|
return Object.entries(properties).map(([name, prop]) => {
|
|
const isRequired = required.includes(name);
|
|
const description = prop.description || '';
|
|
if (typeof prop.type !== 'string') {
|
|
throw new Error(
|
|
`Property ${name} has a complex type not supported by CLI.`,
|
|
);
|
|
}
|
|
return {
|
|
name,
|
|
type: prop.type,
|
|
description,
|
|
required: isRequired,
|
|
default: prop.default,
|
|
enum: prop.enum,
|
|
};
|
|
});
|
|
}
|
|
|
|
async function generateCli() {
|
|
const tools = await fetchTools();
|
|
// Sort tools by name
|
|
const sortedTools = tools.sort((a, b) => a.name.localeCompare(b.name));
|
|
|
|
const staticTools = createTools(parseArguments());
|
|
const toolNameToCategory = new Map<string, string>();
|
|
for (const tool of staticTools) {
|
|
toolNameToCategory.set(
|
|
tool.name,
|
|
labels[tool.annotations.category as keyof typeof labels],
|
|
);
|
|
}
|
|
|
|
const commands: Record<
|
|
string,
|
|
{description: string; category: string; args: Record<string, CliOption>}
|
|
> = {};
|
|
|
|
for (const tool of sortedTools) {
|
|
const options = schemaToCLIOptions(tool.inputSchema);
|
|
const args: Record<string, CliOption> = {};
|
|
for (const opt of options) {
|
|
args[opt.name] = opt;
|
|
}
|
|
const category = toolNameToCategory.get(tool.name);
|
|
if (!category) {
|
|
throw new Error(`Tool ${tool.name} has no category.`);
|
|
}
|
|
if (!tool.description) {
|
|
throw new Error(`Tool ${tool.name} is missing descripttion`);
|
|
}
|
|
commands[tool.name] = {
|
|
description: tool.description,
|
|
category,
|
|
args,
|
|
};
|
|
}
|
|
|
|
const lines: string[] = [];
|
|
lines.push(`/**
|
|
* @license
|
|
* Copyright ${new Date().getFullYear()} Google LLC
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
// NOTE: do not edit manually. Auto-generated by 'npm run cli:generate'.
|
|
|
|
export interface ArgDef {
|
|
name: string;
|
|
type: string;
|
|
description: string;
|
|
required: boolean;
|
|
default?: string | number | boolean;
|
|
enum?: ReadonlyArray<string | number>;
|
|
}
|
|
export type Commands = Record<
|
|
string,
|
|
{
|
|
description: string;
|
|
category: string;
|
|
args: Record<string, ArgDef>
|
|
}
|
|
>;
|
|
export const commands: Commands = ${JSON.stringify(commands, null, 2)} as const;
|
|
`);
|
|
|
|
fs.mkdirSync(path.dirname(OUTPUT_PATH), {recursive: true});
|
|
fs.writeFileSync(OUTPUT_PATH, lines.join(''));
|
|
console.log(`Generated CLI at ${OUTPUT_PATH}`);
|
|
}
|
|
|
|
generateCli().catch(err => {
|
|
console.error('Error during generation:', err);
|
|
process.exit(1);
|
|
});
|