Skip to content
This repository was archived by the owner on Apr 16, 2026. It is now read-only.

Commit d6577b2

Browse files
nroscinowolfib
authored andcommitted
chore: enable dynamic tool definition based on CLI arguments (ChromeDevTools#1031)
This PR allows tools to be created dynamically based on the provided cli arguments
1 parent cc5f6fb commit d6577b2

7 files changed

Lines changed: 84 additions & 34 deletions

File tree

scripts/generate-docs.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@ import type {Tool} from '@modelcontextprotocol/sdk/types.js';
1212
import {get_encoding} from 'tiktoken';
1313

1414
import {cliOptions} from '../build/src/cli.js';
15+
import type {ParsedArguments} from '../build/src/cli.js';
1516
import {ToolCategory, labels} from '../build/src/tools/categories.js';
16-
import {tools as slimTools} from '../build/src/tools/slim/tools.js';
17-
import {tools} from '../build/src/tools/tools.js';
17+
import {createTools} from '../build/src/tools/tools.js';
1818

1919
const OUTPUT_PATH = './docs/tool-reference.md';
2020
const SLIM_OUTPUT_PATH = './docs/slim-tool-reference.md';
@@ -504,7 +504,7 @@ async function generateToolDocumentation(): Promise<void> {
504504

505505
{
506506
const {toolsWithAnnotations, categories, sortedCategories} =
507-
getToolsAndCategories(tools);
507+
getToolsAndCategories(createTools({slim: false} as ParsedArguments));
508508
await generateReference(
509509
'Chrome DevTools MCP Tool Reference',
510510
OUTPUT_PATH,
@@ -521,7 +521,7 @@ async function generateToolDocumentation(): Promise<void> {
521521

522522
{
523523
const {toolsWithAnnotations, categories, sortedCategories} =
524-
getToolsAndCategories(slimTools);
524+
getToolsAndCategories(createTools({slim: true} as ParsedArguments));
525525
await generateReference(
526526
'Chrome DevTools MCP Slim Tool Reference',
527527
SLIM_OUTPUT_PATH,

src/cli.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,8 @@ export const cliOptions = {
243243
},
244244
} satisfies Record<string, YargsOptions>;
245245

246+
export type ParsedArguments = ReturnType<typeof parseArguments>;
247+
246248
export function parseArguments(version: string, argv = process.argv) {
247249
const yargsInstance = yargs(hideBin(argv))
248250
.scriptName('npx chrome-devtools-mcp@latest')

src/main.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,8 @@ import {
2727
SetLevelRequestSchema,
2828
} from './third_party/index.js';
2929
import {ToolCategory} from './tools/categories.js';
30-
import {tools as slimTools} from './tools/slim/tools.js';
3130
import type {ToolDefinition} from './tools/ToolDefinition.js';
32-
import {tools} from './tools/tools.js';
31+
import {createTools} from './tools/tools.js';
3332
import {VERSION} from './version.js';
3433

3534
export const args = parseArguments(VERSION);
@@ -256,7 +255,8 @@ function registerTool(tool: ToolDefinition): void {
256255
);
257256
}
258257

259-
for (const tool of args.slim ? slimTools : tools) {
258+
const tools = createTools(args);
259+
for (const tool of tools) {
260260
registerTool(tool);
261261
}
262262

src/tools/ToolDefinition.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
* SPDX-License-Identifier: Apache-2.0
55
*/
66

7+
import type {ParsedArguments} from '../cli.js';
78
import type {TextSnapshotNode, GeolocationOptions} from '../McpContext.js';
89
import {zod} from '../third_party/index.js';
910
import type {
@@ -161,6 +162,20 @@ export type Context = Readonly<{
161162

162163
export function defineTool<Schema extends zod.ZodRawShape>(
163164
definition: ToolDefinition<Schema>,
165+
): ToolDefinition<Schema>;
166+
167+
export function defineTool<
168+
Schema extends zod.ZodRawShape,
169+
Args extends ParsedArguments = ParsedArguments,
170+
>(
171+
definition: (args: Args) => ToolDefinition<Schema>,
172+
): (args: Args) => ToolDefinition<Schema>;
173+
174+
export function defineTool<
175+
Schema extends zod.ZodRawShape,
176+
Args extends ParsedArguments = ParsedArguments,
177+
>(
178+
definition: ToolDefinition<Schema> | ((args: Args) => ToolDefinition<Schema>),
164179
) {
165180
return definition;
166181
}

src/tools/slim/tools.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
import type {Dialog} from '../../third_party/index.js';
88
import {zod} from '../../third_party/index.js';
99
import {ToolCategory} from '../categories.js';
10-
import type {ToolDefinition} from '../ToolDefinition.js';
1110
import {defineTool} from '../ToolDefinition.js';
1211

1312
export const screenshot = defineTool({
@@ -86,5 +85,3 @@ export const evaluate = defineTool({
8685
}
8786
},
8887
});
89-
90-
export const tools = [screenshot, evaluate, navigate] as ToolDefinition[];

src/tools/tools.ts

Lines changed: 36 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
* SPDX-License-Identifier: Apache-2.0
55
*/
66

7+
import type {ParsedArguments} from '../cli.js';
8+
79
import * as consoleTools from './console.js';
810
import * as emulationTools from './emulation.js';
911
import * as extensionTools from './extensions.js';
@@ -16,27 +18,42 @@ import * as performanceTools from './performance.js';
1618
import * as screencastTools from './screencast.js';
1719
import * as screenshotTools from './screenshot.js';
1820
import * as scriptTools from './script.js';
21+
import * as slimTools from './slim/tools.js';
1922
import * as snapshotTools from './snapshot.js';
2023
import type {ToolDefinition} from './ToolDefinition.js';
2124

22-
const tools = [
23-
...Object.values(consoleTools),
24-
...Object.values(emulationTools),
25-
...Object.values(extensionTools),
26-
...Object.values(inPageTools),
27-
...Object.values(inputTools),
28-
...Object.values(memoryTools),
29-
...Object.values(networkTools),
30-
...Object.values(pagesTools),
31-
...Object.values(performanceTools),
32-
...Object.values(screencastTools),
33-
...Object.values(screenshotTools),
34-
...Object.values(scriptTools),
35-
...Object.values(snapshotTools),
36-
] as ToolDefinition[];
25+
export const createTools = (args: ParsedArguments) => {
26+
const rawTools = args.slim
27+
? Object.values(slimTools)
28+
: [
29+
...Object.values(consoleTools),
30+
...Object.values(emulationTools),
31+
...Object.values(extensionTools),
32+
...Object.values(inPageTools),
33+
...Object.values(inputTools),
34+
...Object.values(memoryTools),
35+
...Object.values(networkTools),
36+
...Object.values(pagesTools),
37+
...Object.values(performanceTools),
38+
...Object.values(screencastTools),
39+
...Object.values(screenshotTools),
40+
...Object.values(scriptTools),
41+
...Object.values(snapshotTools),
42+
];
43+
44+
const tools: ToolDefinition[] = [];
45+
for (const tool of rawTools) {
46+
if (typeof tool === 'function') {
47+
// @ts-expect-error none of the tools for now implement the function type tool has type "never"
48+
tools.push(tool(args) as ToolDefinition);
49+
} else {
50+
tools.push(tool as ToolDefinition);
51+
}
52+
}
3753

38-
tools.sort((a, b) => {
39-
return a.name.localeCompare(b.name);
40-
});
54+
tools.sort((a, b) => {
55+
return a.name.localeCompare(b.name);
56+
});
4157

42-
export {tools};
58+
return tools;
59+
};

tests/index.test.ts

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -92,16 +92,35 @@ describe('e2e', () => {
9292
const files = fs.readdirSync('build/src/tools');
9393
const definedNames = [];
9494
for (const file of files) {
95-
if (file === 'ToolDefinition.js' || file === 'slim') {
95+
if (
96+
file === 'ToolDefinition.js' ||
97+
file === 'tools.js' ||
98+
file === 'slim'
99+
) {
96100
continue;
97101
}
98102
const fileTools = await import(`../src/tools/${file}`);
99-
for (const maybeTool of Object.values<ToolDefinition>(fileTools)) {
100-
if ('name' in maybeTool) {
101-
if (maybeTool.annotations?.conditions) {
103+
for (const maybeTool of Object.values<unknown>(fileTools)) {
104+
if (typeof maybeTool === 'function') {
105+
const tool = (maybeTool as (val: boolean) => ToolDefinition)(false);
106+
if (tool && typeof tool === 'object' && 'name' in tool) {
107+
if (tool.annotations?.conditions) {
108+
continue;
109+
}
110+
definedNames.push(tool.name);
111+
}
112+
continue;
113+
}
114+
if (
115+
typeof maybeTool === 'object' &&
116+
maybeTool !== null &&
117+
'name' in maybeTool
118+
) {
119+
const tool = maybeTool as ToolDefinition;
120+
if (tool.annotations?.conditions) {
102121
continue;
103122
}
104-
definedNames.push(maybeTool.name);
123+
definedNames.push(tool.name);
105124
}
106125
}
107126
}

0 commit comments

Comments
 (0)