feat: support config file (#2661)

Utilizes the Yargs default. We may need to change this to resolve this
ourself, but for now we can explicitly allow this.
This commit is contained in:
Nikolay Vitkov
2026-09-08 11:20:46 +00:00
committed by GitHub
parent 1cec9cd1a3
commit 314a5fabfe
4 changed files with 123 additions and 0 deletions
+5
View File
@@ -226,6 +226,11 @@ The Chrome DevTools MCP server supports the following configuration option:
- **Type:** array
- **Default:** `OS temp directory`
- **`--config`**
Path to JSON configuration file.
- **Type:** string
- **Default:** `false`
<!-- END AUTO GENERATED OPTIONS -->
Pass them via the `args` property in the JSON configuration. For example:
+31
View File
@@ -7,6 +7,7 @@
import type {YargsOptions} from '../third_party/index.js';
import {yargs, hideBin} from '../third_party/index.js';
import os from 'node:os';
import {readFileSync} from 'node:fs';
export const DEFAULT_FILESYSTEM_ROOT = [os.tmpdir()];
@@ -259,6 +260,10 @@ export const mcpOptions = {
describe:
'A directory that filesystem tools are allowed to access. May be specified more than once.',
},
config: {
type: 'string',
describe: 'Path to JSON configuration file.',
},
} satisfies Record<string, YargsOptions>;
export type ParsedArguments = ReturnType<typeof parseArguments>;
@@ -433,6 +438,32 @@ export function parser(
]);
return yargsInstance
.config('config', 'Path to JSON configuration file', configPath => {
try {
const parsed = JSON.parse(readFileSync(configPath, 'utf-8'));
if (
typeof parsed !== 'object' ||
parsed === null ||
Array.isArray(parsed)
) {
throw new Error('Config must be a JSON object');
}
return yargs()
.parserConfiguration({
'strip-aliased': true,
'camel-case-expansion': false,
})
.options(options)
.config(parsed)
.strict()
.fail(false)
.exitProcess(false)
.parseSync([]);
} catch (err) {
throw new Error(`Invalid JSON config file: ${(err as Error).message}`);
}
})
.wrap(Math.min(120, yargsInstance.terminalWidth()))
.help()
.version(version);
+4
View File
@@ -445,5 +445,9 @@
{
"name": "source_maps",
"flagType": "boolean"
},
{
"name": "config_present",
"flagType": "boolean"
}
]
+83
View File
@@ -6,6 +6,9 @@
import assert from 'node:assert';
import {describe, it} from 'node:test';
import fs from 'node:fs';
import os from 'node:os';
import path from 'node:path';
import {
DEFAULT_FILESYSTEM_ROOT,
@@ -19,6 +22,21 @@ function parseArguments(argv: string[], env: NodeJS.ProcessEnv = {}) {
.parseSync();
}
function createTempFile(content: string, fileName: string) {
const filePath = path.join(os.tmpdir(), fileName);
fs.writeFileSync(filePath, content);
return {
path: filePath,
[Symbol.dispose]() {
try {
fs.unlinkSync(filePath);
} catch {
// ignore
}
},
};
}
describe('cli args parsing', () => {
const defaultArgs = {
categoryInput: true,
@@ -430,4 +448,69 @@ describe('cli args parsing', () => {
const explicitTrueArgs = parseArguments(['--source-maps=true']);
assert.strictEqual(explicitTrueArgs.sourceMaps, true);
});
it('parses config option', async () => {
using testConfig = createTempFile(
JSON.stringify({
headless: true,
categoryInput: false,
blockedUrlPattern: ['https://example.com/*'],
}),
'cd4a.test.config.json',
);
const args = parseArguments(['--config', testConfig.path]);
assert.strictEqual(args.config, testConfig.path);
assert.strictEqual(args.headless, true);
assert.strictEqual(args.categoryInput, false);
assert.deepStrictEqual(args.blockedUrlPattern, ['https://example.com/*']);
});
it('parses config option mixed with cli arguments', async () => {
using testConfig = createTempFile(
JSON.stringify({
headless: true,
categoryInput: false,
}),
'cd4a.test.config.mixed.json',
);
const args = parseArguments([
'--config',
testConfig.path,
'--headless=false',
'--category-network=false',
]);
assert.strictEqual(args.config, testConfig.path);
assert.strictEqual(args.headless, false);
assert.strictEqual(args.categoryInput, false);
assert.strictEqual(args.categoryNetwork, false);
assert.strictEqual(args.categoryMemory, true);
});
it('parses config should not allow no prefix', async () => {
using testConfig = createTempFile(
JSON.stringify({
headless: true,
'no-category-memory': true,
}),
'cd4a.test.config.mixed.json',
);
assert.throws(
() => parseArguments(['--config', testConfig.path]),
/Invalid JSON config file: Unknown argument: no-category-memory/,
);
});
it('parses config should not allow dashed property', async () => {
using testConfig = createTempFile(
JSON.stringify({
headless: true,
'category-memory': false,
}),
'cd4a.test.config.mixed.json',
);
assert.throws(
() => parseArguments(['--config', testConfig.path]),
/Invalid JSON config file: Unknown argument: category-memory/,
);
});
});