cli.ts•6.99 kB
/**
* @license
* Copyright 2025 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import type {YargsOptions} from './third_party/index.js';
import {yargs, hideBin} from './third_party/index.js';
export const cliOptions = {
browserUrl: {
type: 'string',
description:
'Connect to a running Chrome instance using port forwarding. For more details see: https://developer.chrome.com/docs/devtools/remote-debugging/local-server.',
alias: 'u',
conflicts: 'wsEndpoint',
coerce: (url: string | undefined) => {
if (!url) {
return;
}
try {
new URL(url);
} catch {
throw new Error(`Provided browserUrl ${url} is not valid URL.`);
}
return url;
},
},
wsEndpoint: {
type: 'string',
description:
'WebSocket endpoint to connect to a running Chrome instance (e.g., ws://127.0.0.1:9222/devtools/browser/<id>). Alternative to --browserUrl.',
alias: 'w',
conflicts: 'browserUrl',
coerce: (url: string | undefined) => {
if (!url) {
return;
}
try {
const parsed = new URL(url);
if (parsed.protocol !== 'ws:' && parsed.protocol !== 'wss:') {
throw new Error(
`Provided wsEndpoint ${url} must use ws:// or wss:// protocol.`,
);
}
return url;
} catch (error) {
if ((error as Error).message.includes('ws://')) {
throw error;
}
throw new Error(`Provided wsEndpoint ${url} is not valid URL.`);
}
},
},
wsHeaders: {
type: 'string',
description:
'Custom headers for WebSocket connection in JSON format (e.g., \'{"Authorization":"Bearer token"}\'). Only works with --wsEndpoint.',
implies: 'wsEndpoint',
coerce: (val: string | undefined) => {
if (!val) {
return;
}
try {
const parsed = JSON.parse(val);
if (typeof parsed !== 'object' || Array.isArray(parsed)) {
throw new Error('Headers must be a JSON object');
}
return parsed as Record<string, string>;
} catch (error) {
throw new Error(
`Invalid JSON for wsHeaders: ${(error as Error).message}`,
);
}
},
},
headless: {
type: 'boolean',
description: 'Whether to run in headless (no UI) mode.',
default: false,
},
executablePath: {
type: 'string',
description: 'Path to custom Chrome executable.',
conflicts: ['browserUrl', 'wsEndpoint'],
alias: 'e',
},
isolated: {
type: 'boolean',
description:
'If specified, creates a temporary user-data-dir that is automatically cleaned up after the browser is closed.',
default: false,
},
channel: {
type: 'string',
description:
'Specify a different Chrome channel that should be used. The default is the stable channel version.',
choices: ['stable', 'canary', 'beta', 'dev'] as const,
conflicts: ['browserUrl', 'wsEndpoint', 'executablePath'],
},
logFile: {
type: 'string',
describe:
'Path to a file to write debug logs to. Set the env variable `DEBUG` to `*` to enable verbose logs. Useful for submitting bug reports.',
},
viewport: {
type: 'string',
describe:
'Initial viewport size for the Chrome instances started by the server. For example, `1280x720`. In headless mode, max size is 3840x2160px.',
coerce: (arg: string | undefined) => {
if (arg === undefined) {
return;
}
const [width, height] = arg.split('x').map(Number);
if (!width || !height || Number.isNaN(width) || Number.isNaN(height)) {
throw new Error('Invalid viewport. Expected format is `1280x720`.');
}
return {
width,
height,
};
},
},
proxyServer: {
type: 'string',
description: `Proxy server configuration for Chrome passed as --proxy-server when launching the browser. See https://www.chromium.org/developers/design-documents/network-settings/ for details.`,
},
acceptInsecureCerts: {
type: 'boolean',
description: `If enabled, ignores errors relative to self-signed and expired certificates. Use with caution.`,
},
experimentalDevtools: {
type: 'boolean',
describe: 'Whether to enable automation over DevTools targets',
hidden: true,
},
chromeArg: {
type: 'array',
describe:
'Additional arguments for Chrome. Only applies when Chrome is launched by chrome-devtools-mcp.',
},
categoryEmulation: {
type: 'boolean',
default: true,
describe: 'Set to false to exlcude tools related to emulation.',
},
categoryPerformance: {
type: 'boolean',
default: true,
describe: 'Set to false to exlcude tools related to performance.',
},
categoryNetwork: {
type: 'boolean',
default: true,
describe: 'Set to false to exlcude tools related to network.',
},
} satisfies Record<string, YargsOptions>;
export function parseArguments(version: string, argv = process.argv) {
const yargsInstance = yargs(hideBin(argv))
.scriptName('npx chrome-devtools-mcp@latest')
.options(cliOptions)
.check(args => {
// We can't set default in the options else
// Yargs will complain
if (
!args.channel &&
!args.browserUrl &&
!args.wsEndpoint &&
!args.executablePath
) {
args.channel = 'stable';
}
return true;
})
.example([
[
'$0 --browserUrl http://127.0.0.1:9222',
'Connect to an existing browser instance via HTTP',
],
[
'$0 --wsEndpoint ws://127.0.0.1:9222/devtools/browser/abc123',
'Connect to an existing browser instance via WebSocket',
],
[
`$0 --wsEndpoint ws://127.0.0.1:9222/devtools/browser/abc123 --wsHeaders '{"Authorization":"Bearer token"}'`,
'Connect via WebSocket with custom headers',
],
['$0 --channel beta', 'Use Chrome Beta installed on this system'],
['$0 --channel canary', 'Use Chrome Canary installed on this system'],
['$0 --channel dev', 'Use Chrome Dev installed on this system'],
['$0 --channel stable', 'Use stable Chrome installed on this system'],
['$0 --logFile /tmp/log.txt', 'Save logs to a file'],
['$0 --help', 'Print CLI options'],
[
'$0 --viewport 1280x720',
'Launch Chrome with the initial viewport size of 1280x720px',
],
[
`$0 --chrome-arg='--no-sandbox' --chrome-arg='--disable-setuid-sandbox'`,
'Launch Chrome without sandboxes. Use with caution.',
],
['$0 --no-category-emulation', 'Disable tools in the emulation category'],
[
'$0 --no-category-performance',
'Disable tools in the performance category',
],
['$0 --no-category-network', 'Disable tools in the network category'],
]);
return yargsInstance
.wrap(Math.min(120, yargsInstance.terminalWidth()))
.help()
.version(version)
.parseSync();
}