豆豆友情提示:这是一个非官方 GitHub 代理镜像,主要用于网络测试或访问加速。请勿在此进行登录、注册或处理任何敏感信息。进行这些操作请务必访问官方网站 github.com。 Raw 内容也通过此代理提供。
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ The CLI acts as a client to a background `chrome-devtools-mcp` daemon (uses Unix

- **Automatic Start**: The first time you call a tool (e.g., `list_pages`), the CLI automatically starts the MCP server and the browser in the background if they aren't already running.
- **Persistence**: The same background instance is reused for subsequent commands, preserving the browser state (open pages, cookies, etc.).
- **Manual Control**: You can explicitly manage the background process using `start`, `stop`, and `status`. The `start` command forwards all subsequent arguments to the underlying MCP server (e.g., `--headless`, `--userDataDir`).
- **Manual Control**: You can explicitly manage the background process using `start`, `stop`, and `status`. The `start` command forwards all subsequent arguments to the underlying MCP server (e.g., `--headless`, `--userDataDir`) but not all args are supported. Run `chrome-devtools start --help` for supported args. Headless and isolated are enabled by default.

```sh
# Check if the daemon is running
Expand Down
38 changes: 37 additions & 1 deletion src/bin/chrome-devtools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,35 @@ async function start(args: string[]) {

const defaultArgs = ['--viaCli', '--experimentalStructuredContent'];

const startCliOptions = {
Comment thread
Lightning00Blade marked this conversation as resolved.
...cliOptions,
} as Partial<typeof cliOptions>;

// Not supported in CLI on purpose.
delete startCliOptions.autoConnect;
// Missing CLI serialization.
delete startCliOptions.viewport;
// CLI is generated based on the default tool definitions. To enable conditional
// tools, they needs to be enabled during CLI generation.
delete startCliOptions.experimentalPageIdRouting;
delete startCliOptions.experimentalVision;
delete startCliOptions.experimentalInteropTools;
delete startCliOptions.experimentalScreencast;
delete startCliOptions.categoryEmulation;
delete startCliOptions.categoryPerformance;
delete startCliOptions.categoryNetwork;
delete startCliOptions.categoryExtensions;
// Always on in CLI.
delete startCliOptions.experimentalStructuredContent;
// Change the defaults.
if (!('default' in cliOptions.headless)) {
throw new Error('headless cli option unexpectedly does not have a default');
}
if ('default' in cliOptions.isolated) {
throw new Error('headless cli option unexpectedly does not have a default');
}
startCliOptions.headless!.default = true;

const y = yargs(hideBin(process.argv))
.scriptName('chrome-devtools')
.showHelpOnFail(true)
Expand All @@ -50,7 +79,7 @@ y.command(
'Start or restart chrome-devtools-mcp',
y =>
y
.options(cliOptions)
.options(startCliOptions)
.example(
'$0 start --browserUrl http://localhost:9222',
'Start the server connecting to an existing browser',
Expand All @@ -60,6 +89,13 @@ y.command(
if (isDaemonRunning()) {
await stopDaemon();
}
// Defaults but we do not want to affect the yargs conflict resolution.
if (argv.isolated === undefined) {
argv.isolated = true;
}
if (argv.headless === undefined) {
argv.headless = true;
}
const args = serializeArgs(cliOptions, argv);
await start(args);
process.exit(0);
Expand Down
3 changes: 2 additions & 1 deletion src/browser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ interface McpLaunchOptions {
ignoreDefaultChromeArgs?: string[];
devtools: boolean;
enableExtensions?: boolean;
viaCli?: boolean;
}

export function detectDisplay(): void {
Expand Down Expand Up @@ -181,7 +182,7 @@ export async function launch(options: McpLaunchOptions): Promise<Browser> {
userDataDir = path.join(
os.homedir(),
'.cache',
'chrome-devtools-mcp',
options.viaCli ? 'chrome-devtools-mcp-cli' : 'chrome-devtools-mcp',
profileDirName,
);
await fs.promises.mkdir(userDataDir, {
Expand Down
2 changes: 1 addition & 1 deletion src/daemon/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ export async function startDaemon(mcpArgs: string[] = []) {
fs.unlinkSync(pidFilePath);
}

logger('Starting daemon...');
logger('Starting daemon...', ...mcpArgs);
const child = spawn(process.execPath, [DAEMON_SCRIPT_PATH, ...mcpArgs], {
detached: true,
stdio: 'ignore',
Expand Down
3 changes: 1 addition & 2 deletions src/daemon/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import os from 'node:os';
import path from 'node:path';
import process from 'node:process';

import type {ParsedArguments} from '../cli.js';
import {logger} from '../logger.js';
import type {YargsOptions} from '../third_party/index.js';

Expand Down Expand Up @@ -102,7 +101,7 @@ export function isDaemonRunning(pid = getDaemonPid()): pid is number {

export function serializeArgs(
options: Record<string, YargsOptions>,
argv: ParsedArguments,
argv: Record<string, unknown>,
): string[] {
const args: string[] = [];
for (const key of Object.keys(options)) {
Expand Down
1 change: 1 addition & 0 deletions src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ export async function createMcpServer(
acceptInsecureCerts: serverArgs.acceptInsecureCerts,
devtools,
enableExtensions: serverArgs.categoryExtensions,
viaCli: serverArgs.viaCli,
});

if (context?.browser !== browser) {
Expand Down
12 changes: 5 additions & 7 deletions tests/e2e/chrome-devtools.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@ import {describe, it, afterEach, beforeEach} from 'node:test';
const CLI_PATH = path.resolve('build/src/bin/chrome-devtools.js');

describe('chrome-devtools', () => {
const START_ARGS = ['--headless', '--isolated'];

function assertDaemonIsNotRunning() {
const result = spawnSync('node', [CLI_PATH, 'status']);
assert.strictEqual(
Expand Down Expand Up @@ -45,7 +43,7 @@ describe('chrome-devtools', () => {
it('reports daemon status correctly', () => {
assertDaemonIsNotRunning();

const startResult = spawnSync('node', [CLI_PATH, 'start', ...START_ARGS]);
const startResult = spawnSync('node', [CLI_PATH, 'start']);
assert.strictEqual(
startResult.status,
0,
Expand All @@ -58,7 +56,7 @@ describe('chrome-devtools', () => {
it('can start and stop the daemon', () => {
assertDaemonIsNotRunning();

const startResult = spawnSync('node', [CLI_PATH, 'start', ...START_ARGS]);
const startResult = spawnSync('node', [CLI_PATH, 'start']);
assert.strictEqual(
startResult.status,
0,
Expand All @@ -80,7 +78,7 @@ describe('chrome-devtools', () => {
it('can invoke list_pages', async () => {
assertDaemonIsNotRunning();

const startResult = spawnSync('node', [CLI_PATH, 'start', ...START_ARGS]);
const startResult = spawnSync('node', [CLI_PATH, 'start']);
assert.strictEqual(
startResult.status,
0,
Expand All @@ -102,7 +100,7 @@ describe('chrome-devtools', () => {
});

it('can take screenshot', async () => {
const startResult = spawnSync('node', [CLI_PATH, 'start', ...START_ARGS]);
const startResult = spawnSync('node', [CLI_PATH, 'start']);
assert.strictEqual(
startResult.status,
0,
Expand All @@ -122,7 +120,7 @@ describe('chrome-devtools', () => {
});

it('forwards disclaimers to stderr on start', () => {
const result = spawnSync('node', [CLI_PATH, 'start', ...START_ARGS]);
const result = spawnSync('node', [CLI_PATH, 'start']);
assert.strictEqual(
result.status,
0,
Expand Down
Loading