Skip to content

Commit b67067a

Browse files
committed
chore: generate a json file for flag usage metrics
1 parent e102a43 commit b67067a

5 files changed

Lines changed: 358 additions & 2 deletions

File tree

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
"typecheck": "tsc --noEmit",
1717
"format": "eslint --cache --fix . && prettier --write --cache .",
1818
"check-format": "eslint --cache . && prettier --check --cache .;",
19-
"gen": "npm run build && npm run docs:generate && npm run cli:generate && npm run update-tool-call-metrics && npm run format",
19+
"gen": "npm run build && npm run docs:generate && npm run cli:generate && npm run update-tool-call-metrics && npm run update-flag-usage-metrics && npm run format",
2020
"docs:generate": "node --experimental-strip-types scripts/generate-docs.ts",
2121
"start": "npm run build && node build/src/index.js",
2222
"start-debug": "DEBUG=mcp:* DEBUG_COLORS=false npm run build && node build/src/index.js",
@@ -28,6 +28,7 @@
2828
"verify-server-json-version": "node --experimental-strip-types scripts/verify-server-json-version.ts",
2929
"update-lighthouse": "node --experimental-strip-types scripts/update-lighthouse.ts",
3030
"update-tool-call-metrics": "node --experimental-strip-types scripts/update_tool_call_metrics.ts",
31+
"update-flag-usage-metrics": "node --experimental-strip-types scripts/update_flag_usage_metrics.ts",
3132
"verify-npm-package": "node scripts/verify-npm-package.mjs",
3233
"eval": "npm run build && node --experimental-strip-types scripts/eval_gemini.ts",
3334
"count-tokens": "node --experimental-strip-types scripts/count_tokens.ts"
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/**
2+
* @license
3+
* Copyright 2026 Google LLC
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
import * as fs from 'node:fs';
8+
import * as path from 'node:path';
9+
10+
import {cliOptions} from '../build/src/bin/chrome-devtools-mcp-cli-options.js';
11+
import {
12+
getPossibleFlagMetrics,
13+
type FlagMetric,
14+
} from '../build/src/telemetry/flagUtils.js';
15+
16+
function writeFlagUsageMetrics() {
17+
const outputPath = path.resolve('src/telemetry/flag_usage_metrics.json');
18+
19+
const dir = path.dirname(outputPath);
20+
if (!fs.existsSync(dir)) {
21+
throw new Error(`Error: Directory ${dir} does not exist.`);
22+
}
23+
24+
const metrics = getPossibleFlagMetrics(cliOptions);
25+
26+
// Sort metrics by name for deterministic output
27+
metrics.sort((a: FlagMetric, b: FlagMetric) => a.name.localeCompare(b.name));
28+
29+
fs.writeFileSync(outputPath, JSON.stringify(metrics, null, 2) + '\n');
30+
31+
console.log(
32+
`Successfully wrote ${metrics.length} flag usage metrics to ${outputPath}`,
33+
);
34+
}
35+
36+
writeFlagUsageMetrics();

src/telemetry/flagUtils.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ type CliOptions = typeof cliOptions;
2121
* - The provided value differs from the default value.
2222
* - Boolean flags are logged with their literal value.
2323
* - String flags with defined `choices` (Enums) are logged as their uppercase value.
24+
*
25+
* IMPORTANT: keep getPossibleFlagMetrics() in sync with this function.
2426
*/
2527
export function computeFlagUsage(
2628
args: Record<string, unknown>,
@@ -58,3 +60,45 @@ export function computeFlagUsage(
5860

5961
return usage;
6062
}
63+
64+
export interface FlagMetric {
65+
name: string;
66+
flagType: 'boolean' | 'string';
67+
}
68+
69+
/**
70+
* Computes the list of possible flag metrics based on the CLI options.
71+
*
72+
* IMPORTANT: keep this function in sync with computeFlagUsage().
73+
*/
74+
export function getPossibleFlagMetrics(options: CliOptions): FlagMetric[] {
75+
const metrics: FlagMetric[] = [];
76+
77+
for (const [flagName, config] of Object.entries(options)) {
78+
const snakeCaseName = toSnakeCase(flagName);
79+
80+
// _present is always a possible metric
81+
metrics.push({
82+
name: `${snakeCaseName}_present`,
83+
flagType: 'boolean',
84+
});
85+
86+
if (config.type === 'boolean') {
87+
metrics.push({
88+
name: snakeCaseName,
89+
flagType: 'boolean',
90+
});
91+
} else if (
92+
config.type === 'string' &&
93+
'choices' in config &&
94+
config.choices
95+
) {
96+
metrics.push({
97+
name: snakeCaseName,
98+
flagType: 'string',
99+
});
100+
}
101+
}
102+
103+
return metrics;
104+
}
Lines changed: 242 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,242 @@
1+
[
2+
{
3+
"name": "accept_insecure_certs",
4+
"flagType": "boolean"
5+
},
6+
{
7+
"name": "accept_insecure_certs_present",
8+
"flagType": "boolean"
9+
},
10+
{
11+
"name": "auto_connect",
12+
"flagType": "boolean"
13+
},
14+
{
15+
"name": "auto_connect_present",
16+
"flagType": "boolean"
17+
},
18+
{
19+
"name": "browser_url_present",
20+
"flagType": "boolean"
21+
},
22+
{
23+
"name": "category_emulation",
24+
"flagType": "boolean"
25+
},
26+
{
27+
"name": "category_emulation_present",
28+
"flagType": "boolean"
29+
},
30+
{
31+
"name": "category_extensions",
32+
"flagType": "boolean"
33+
},
34+
{
35+
"name": "category_extensions_present",
36+
"flagType": "boolean"
37+
},
38+
{
39+
"name": "category_in_page_tools",
40+
"flagType": "boolean"
41+
},
42+
{
43+
"name": "category_in_page_tools_present",
44+
"flagType": "boolean"
45+
},
46+
{
47+
"name": "category_network",
48+
"flagType": "boolean"
49+
},
50+
{
51+
"name": "category_network_present",
52+
"flagType": "boolean"
53+
},
54+
{
55+
"name": "category_performance",
56+
"flagType": "boolean"
57+
},
58+
{
59+
"name": "category_performance_present",
60+
"flagType": "boolean"
61+
},
62+
{
63+
"name": "channel",
64+
"flagType": "string"
65+
},
66+
{
67+
"name": "channel_present",
68+
"flagType": "boolean"
69+
},
70+
{
71+
"name": "chrome_arg_present",
72+
"flagType": "boolean"
73+
},
74+
{
75+
"name": "clearcut_endpoint_present",
76+
"flagType": "boolean"
77+
},
78+
{
79+
"name": "clearcut_force_flush_interval_ms_present",
80+
"flagType": "boolean"
81+
},
82+
{
83+
"name": "clearcut_include_pid_header",
84+
"flagType": "boolean"
85+
},
86+
{
87+
"name": "clearcut_include_pid_header_present",
88+
"flagType": "boolean"
89+
},
90+
{
91+
"name": "executable_path_present",
92+
"flagType": "boolean"
93+
},
94+
{
95+
"name": "experimental_devtools",
96+
"flagType": "boolean"
97+
},
98+
{
99+
"name": "experimental_devtools_present",
100+
"flagType": "boolean"
101+
},
102+
{
103+
"name": "experimental_include_all_pages",
104+
"flagType": "boolean"
105+
},
106+
{
107+
"name": "experimental_include_all_pages_present",
108+
"flagType": "boolean"
109+
},
110+
{
111+
"name": "experimental_interop_tools",
112+
"flagType": "boolean"
113+
},
114+
{
115+
"name": "experimental_interop_tools_present",
116+
"flagType": "boolean"
117+
},
118+
{
119+
"name": "experimental_page_id_routing",
120+
"flagType": "boolean"
121+
},
122+
{
123+
"name": "experimental_page_id_routing_present",
124+
"flagType": "boolean"
125+
},
126+
{
127+
"name": "experimental_screencast",
128+
"flagType": "boolean"
129+
},
130+
{
131+
"name": "experimental_screencast_present",
132+
"flagType": "boolean"
133+
},
134+
{
135+
"name": "experimental_structured_content",
136+
"flagType": "boolean"
137+
},
138+
{
139+
"name": "experimental_structured_content_present",
140+
"flagType": "boolean"
141+
},
142+
{
143+
"name": "experimental_vision",
144+
"flagType": "boolean"
145+
},
146+
{
147+
"name": "experimental_vision_present",
148+
"flagType": "boolean"
149+
},
150+
{
151+
"name": "experimental_webmcp",
152+
"flagType": "boolean"
153+
},
154+
{
155+
"name": "experimental_webmcp_present",
156+
"flagType": "boolean"
157+
},
158+
{
159+
"name": "headless",
160+
"flagType": "boolean"
161+
},
162+
{
163+
"name": "headless_present",
164+
"flagType": "boolean"
165+
},
166+
{
167+
"name": "ignore_default_chrome_arg_present",
168+
"flagType": "boolean"
169+
},
170+
{
171+
"name": "isolated",
172+
"flagType": "boolean"
173+
},
174+
{
175+
"name": "isolated_present",
176+
"flagType": "boolean"
177+
},
178+
{
179+
"name": "log_file_present",
180+
"flagType": "boolean"
181+
},
182+
{
183+
"name": "performance_crux",
184+
"flagType": "boolean"
185+
},
186+
{
187+
"name": "performance_crux_present",
188+
"flagType": "boolean"
189+
},
190+
{
191+
"name": "proxy_server_present",
192+
"flagType": "boolean"
193+
},
194+
{
195+
"name": "redact_network_headers",
196+
"flagType": "boolean"
197+
},
198+
{
199+
"name": "redact_network_headers_present",
200+
"flagType": "boolean"
201+
},
202+
{
203+
"name": "slim",
204+
"flagType": "boolean"
205+
},
206+
{
207+
"name": "slim_present",
208+
"flagType": "boolean"
209+
},
210+
{
211+
"name": "usage_statistics",
212+
"flagType": "boolean"
213+
},
214+
{
215+
"name": "usage_statistics_present",
216+
"flagType": "boolean"
217+
},
218+
{
219+
"name": "user_data_dir_present",
220+
"flagType": "boolean"
221+
},
222+
{
223+
"name": "via_cli",
224+
"flagType": "boolean"
225+
},
226+
{
227+
"name": "via_cli_present",
228+
"flagType": "boolean"
229+
},
230+
{
231+
"name": "viewport_present",
232+
"flagType": "boolean"
233+
},
234+
{
235+
"name": "ws_endpoint_present",
236+
"flagType": "boolean"
237+
},
238+
{
239+
"name": "ws_headers_present",
240+
"flagType": "boolean"
241+
}
242+
]

tests/telemetry/flagUtils.test.ts

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@ import assert from 'node:assert/strict';
88
import {describe, it} from 'node:test';
99

1010
import type {cliOptions} from '../../src/bin/chrome-devtools-mcp-cli-options.js';
11-
import {computeFlagUsage} from '../../src/telemetry/flagUtils.js';
11+
import {
12+
computeFlagUsage,
13+
getPossibleFlagMetrics,
14+
} from '../../src/telemetry/flagUtils.js';
1215

1316
describe('computeFlagUsage', () => {
1417
const mockOptions = {
@@ -105,3 +108,33 @@ describe('computeFlagUsage', () => {
105108
});
106109
});
107110
});
111+
112+
describe('getPossibleFlagMetrics', () => {
113+
const mockOptions = {
114+
boolFlag: {
115+
type: 'boolean' as const,
116+
description: 'A boolean flag',
117+
},
118+
stringFlag: {
119+
type: 'string' as const,
120+
description: 'A string flag',
121+
},
122+
enumFlag: {
123+
type: 'string' as const,
124+
description: 'An enum flag',
125+
choices: ['a', 'b'],
126+
},
127+
} as unknown as typeof cliOptions;
128+
129+
it('returns all possible metrics for given options', () => {
130+
const metrics = getPossibleFlagMetrics(mockOptions);
131+
132+
assert.deepEqual(metrics, [
133+
{name: 'bool_flag_present', flagType: 'boolean'},
134+
{name: 'bool_flag', flagType: 'boolean'},
135+
{name: 'string_flag_present', flagType: 'boolean'},
136+
{name: 'enum_flag_present', flagType: 'boolean'},
137+
{name: 'enum_flag', flagType: 'string'},
138+
]);
139+
});
140+
});

0 commit comments

Comments
 (0)