Skip to content

Commit 9fab20c

Browse files
yulunzwolfib
authored andcommitted
chore: generate a json file for flag usage metrics (#1881)
This is the second most highly touched metric areas - so let's automate this portion first. The approach is similar to what we have for tool call args. The append-only logic will be added in the follow-up PR: #1882
1 parent 8518428 commit 9fab20c

7 files changed

Lines changed: 396 additions & 8 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -558,7 +558,7 @@ The Chrome DevTools MCP server supports the following configuration option:
558558
- **`--channel`**
559559
Specify a different Chrome channel that should be used. The default is the stable channel version.
560560
- **Type:** string
561-
- **Choices:** `stable`, `canary`, `beta`, `dev`
561+
- **Choices:** `canary`, `dev`, `beta`, `stable`
562562

563563
- **`--logFile`/ `--log-file`**
564564
Path to a file to write debug logs to. Set the env variable `DEBUG` to `*` to enable verbose logs. Useful for submitting bug reports.

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/bin/chrome-devtools-mcp-cli-options.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ export const cliOptions = {
113113
type: 'string',
114114
description:
115115
'Specify a different Chrome channel that should be used. The default is the stable channel version.',
116-
choices: ['stable', 'canary', 'beta', 'dev'] as const,
116+
choices: ['canary', 'dev', 'beta', 'stable'] as const,
117117
conflicts: ['browserUrl', 'wsEndpoint', 'executablePath'],
118118
},
119119
logFile: {

src/telemetry/flagUtils.ts

Lines changed: 61 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,15 @@ import type {FlagUsage} from './types.js';
1111

1212
type CliOptions = typeof cliOptions;
1313

14+
/**
15+
* For enums, log the value as uppercase.
16+
* We're going to have an enum for such flags with choices represented
17+
* as an `enum` where the keys of the enum will map to the uppercase `choice`.
18+
*/
19+
function formatEnumChoice(snakeCaseName: string, choice: string): string {
20+
return `${snakeCaseName}_${choice}`.toUpperCase();
21+
}
22+
1423
/**
1524
* Computes telemetry flag usage from parsed arguments and CLI options.
1625
*
@@ -21,6 +30,8 @@ type CliOptions = typeof cliOptions;
2130
* - The provided value differs from the default value.
2231
* - Boolean flags are logged with their literal value.
2332
* - String flags with defined `choices` (Enums) are logged as their uppercase value.
33+
*
34+
* IMPORTANT: keep getPossibleFlagMetrics() in sync with this function.
2435
*/
2536
export function computeFlagUsage(
2637
args: Record<string, unknown>,
@@ -49,12 +60,58 @@ export function computeFlagUsage(
4960
'choices' in config &&
5061
config.choices
5162
) {
52-
// For enums, log the value as uppercase
53-
// We're going to have an enum for such flags with choices represented
54-
// as an `enum` where the keys of the enum will map to the uppercase `choice`.
55-
usage[snakeCaseName] = `${snakeCaseName}_${value}`.toUpperCase();
63+
usage[snakeCaseName] = formatEnumChoice(snakeCaseName, value);
5664
}
5765
}
5866

5967
return usage;
6068
}
69+
70+
export interface FlagMetric {
71+
name: string;
72+
flagType: 'boolean' | 'enum';
73+
choices?: string[];
74+
}
75+
76+
/**
77+
* Computes the list of possible flag metrics based on the CLI options.
78+
*
79+
* IMPORTANT: keep this function in sync with computeFlagUsage().
80+
*/
81+
export function getPossibleFlagMetrics(options: CliOptions): FlagMetric[] {
82+
const metrics: FlagMetric[] = [];
83+
84+
for (const [flagName, config] of Object.entries(options)) {
85+
const snakeCaseName = toSnakeCase(flagName);
86+
87+
// _present is always a possible metric
88+
metrics.push({
89+
name: `${snakeCaseName}_present`,
90+
flagType: 'boolean',
91+
});
92+
93+
if (config.type === 'boolean') {
94+
metrics.push({
95+
name: snakeCaseName,
96+
flagType: 'boolean',
97+
});
98+
} else if (
99+
config.type === 'string' &&
100+
'choices' in config &&
101+
config.choices
102+
) {
103+
metrics.push({
104+
name: snakeCaseName,
105+
flagType: 'enum',
106+
choices: [
107+
`${snakeCaseName.toUpperCase()}_UNSPECIFIED`,
108+
...config.choices.map(choice =>
109+
formatEnumChoice(snakeCaseName, choice),
110+
),
111+
],
112+
});
113+
}
114+
}
115+
116+
return metrics;
117+
}

0 commit comments

Comments
 (0)