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 .github/workflows/presubmit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ jobs:
diff_file=$(mktemp doc_diff_XXXXXX)
git diff --color > $diff_file
if [[ -s $diff_file ]]; then
echo "Please update the documentation by running 'npm run docs'. The following was the diff"
echo "Please update the documentation by running 'npm run generate-docs'. The following was the diff"
cat $diff_file
rm $diff_file
exit 1
Expand Down
83 changes: 48 additions & 35 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ prototype!

`chrome-devtools-mcp` exposes content of the browser instance to the MCP clients
allowing them to inspect, debug, and modify any data in the browser or DevTools.
Avoid sharing sensitive or personal information that you do want to share with
Avoid sharing sensitive or personal information that you don't want to share with
MCP clients.

## Requirements
Expand All @@ -33,7 +33,7 @@ Add the following config to your MCP client:
```

> [!NOTE]
> `Using `chrome-devtools-mcp@latest` ensures that your MCP client will always use the latest version of the Chrome DevTools MCP server.
> Using `chrome-devtools-mcp@latest` ensures that your MCP client will always use the latest version of the Chrome DevTools MCP server.

### MCP Client specific configuration

Expand Down Expand Up @@ -119,7 +119,37 @@ claude mcp add chrome-devtools-mcp npx chrome-devtools-mcp@latest

## Configuration

For example, to launch the system-installed Chrome Canary pass `--channel=canary` as an argument:
The Chrome DevTools MCP server supports the following configuration option:

<!-- BEGIN AUTO GENERATED OPTIONS -->

- **`--browserUrl`, `-u`**
Connect to a running Chrome instance using port forwarding. For more details see: https://developer.chrome.com/docs/devtools/remote-debugging/local-server.
- **Type:** string

- **`--headless`**
Whether to run in headless (no UI) mode.
- **Type:** boolean
- **Default:** `false`

- **`--executablePath`, `-e`**
Path to custom Chrome executable.
- **Type:** string

- **`--isolated`**
If specified, creates a temporary user-data-dir that is automatically cleaned up after the browser is closed.
- **Type:** boolean
- **Default:** `false`

- **`--channel`**
Specify a different Chrome channel that should be used.
- **Type:** string
- **Choices:** `stable`, `canary`, `beta`, `dev`
- **Default:** `stable`

<!-- END AUTO GENERATED OPTIONS -->

Pass them via the `args` property in the JSON configuration. For example:

```json
{
Expand All @@ -129,44 +159,27 @@ For example, to launch the system-installed Chrome Canary pass `--channel=canary
"args": [
"chrome-devtools-mcp@latest"
"--channel=canary",
"--headless=true",
"--isolated=true",
]
}
}
}
```

You can also run `npx chrome-devtools-mcp@latest --help` to see all available configuration options.

## Concepts

### User data directory

`chrome-devtools-mcp` starts a Chrome's stable channel instance using the user
data directory at `$HOME/.cache/chrome-devtools-mcp/mcp-profile-$CHANNEL` on
Linux/MacOS and `%HOMEPATH%/.cache/chrome-devtools-mcp/mcp-profile-$CHANNEL` in
Windows. The user data directory is not cleared between runs and shared across
all instances of `chrome-devtools-mcp`.

## CLI

Run `npx chrome-devtools-mcp@latest --help` to see all available configuration options:

<!-- BEGIN AUTO GENERATED CLI -->

```sh
Options:
--version Show version number [boolean]
-u, --browserUrl The browser URL to connect to [string]
--headless Whether to run in headless (no UI) mode [boolean] [default: false]
-e, --executablePath Path to custom Chrome executable [string]
--isolated If specified, creates a temporary user-data-dir that is automatically cleaned up after the browser is closed. [boolean] [default: false]
--channel System installed browser channel to use. [string] [choices: "stable", "canary", "beta", "dev"]
--help Show help [boolean]

Examples:
npx chrome-devtools-mcp@latest --browserUrl http://127.0.0.1:9222 Connect to an existing browser instance
npx chrome-devtools-mcp@latest --channel beta Use Chrome Beta installed on this system
npx chrome-devtools-mcp@latest --channel canary Use Chrome Canary installed on this system
npx chrome-devtools-mcp@latest --channel dev Use Chrome Dev installed on this system
npx chrome-devtools-mcp@latest --channel stable Use stable Chrome installed on this system
npx chrome-devtools-mcp@latest --logFile /tmp/log.txt Save logs to a file
npx chrome-devtools-mcp@latest --help Print CLI options
```
`chrome-devtools-mcp` starts a Chrome's stable channel instance using the following user
data directory:

- Linux / MacOS: `$HOME/.cache/chrome-devtools-mcp/mcp-profile-$CHANNEL`
- Window: `%HOMEPATH%/.cache/chrome-devtools-mcp/mcp-profile-$CHANNEL`

<!-- END AUTO GENERATED CLI -->
The user data directory is not cleared between runs and shared across
all instances of `chrome-devtools-mcp`. Set the `isolated` option to `true`
to use a temporary user data dir instead which will be cleared automatically after
the browser is closed.
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

60 changes: 46 additions & 14 deletions scripts/generate-docs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import {Client} from '@modelcontextprotocol/sdk/client/index.js';
import {StdioClientTransport} from '@modelcontextprotocol/sdk/client/stdio.js';
import type {Tool} from '@modelcontextprotocol/sdk/types.js';
import {ToolCategories} from '../build/src/tools/categories.js';
import {cliOptions} from '../build/src/index.js';
import fs from 'fs';
import {spawnSync} from 'child_process';

const MCP_SERVER_PATH = 'build/src/index.js';
const OUTPUT_PATH = './docs/tool-reference.md';
Expand Down Expand Up @@ -102,27 +102,62 @@ function updateReadmeWithToolsTOC(toolsTOC: string): void {
console.log('Updated README.md with tools table of contents');
}

function updateReadmeWithConfigOptions(help: string): void {
function generateConfigOptionsMarkdown(): string {
let markdown = '';

for (const [optionName, optionConfig] of Object.entries(cliOptions)) {
// Skip hidden options
if (optionConfig.hidden) {
continue;
}

const aliasText = optionConfig.alias ? `, \`-${optionConfig.alias}\`` : '';
const description = optionConfig.description || optionConfig.describe || '';

// Start with option name and description
markdown += `- **\`--${optionName}\`${aliasText}**\n`;
markdown += ` ${description}\n`;

// Add type information
markdown += ` - **Type:** ${optionConfig.type}\n`;

// Add choices if available
if (optionConfig.choices) {
markdown += ` - **Choices:** ${optionConfig.choices.map(c => `\`${c}\``).join(', ')}\n`;
}

// Add default if available
if (optionConfig.default !== undefined) {
markdown += ` - **Default:** \`${optionConfig.default}\`\n`;
}

markdown += '\n';
}

return markdown.trim();
}

function updateReadmeWithOptionsMarkdown(optionsMarkdown: string): void {
const readmeContent = fs.readFileSync(README_PATH, 'utf8');

const beginMarker = '<!-- BEGIN AUTO GENERATED CLI -->';
const endMarker = '<!-- END AUTO GENERATED CLI -->';
const beginMarker = '<!-- BEGIN AUTO GENERATED OPTIONS -->';
const endMarker = '<!-- END AUTO GENERATED OPTIONS -->';

const beginIndex = readmeContent.indexOf(beginMarker);
const endIndex = readmeContent.indexOf(endMarker);

if (beginIndex === -1 || endIndex === -1) {
console.warn('Could not find auto-generated config markers in README.md');
console.warn('Could not find auto-generated options markers in README.md');
return;
}

const before = readmeContent.substring(0, beginIndex + beginMarker.length);
const after = readmeContent.substring(endIndex);

const updatedContent = before + '\n\n```sh\n' + help + '```\n\n' + after;
const updatedContent = before + '\n\n' + optionsMarkdown + '\n\n' + after;

fs.writeFileSync(README_PATH, updatedContent);
console.log('Updated README.md with config options');
console.log('Updated README.md with options markdown');
}

async function generateToolDocumentation(): Promise<void> {
Expand Down Expand Up @@ -280,15 +315,12 @@ async function generateToolDocumentation(): Promise<void> {
const toolsTOC = generateToolsTOC(categories, sortedCategories);
updateReadmeWithToolsTOC(toolsTOC);

const helpResult = spawnSync('npx chrome-devtools-mcp --help', {
shell: true,
cwd: process.cwd(),
env: process.env,
});
updateReadmeWithConfigOptions(helpResult.stdout.toString('utf-8'));

// Generate and update configuration options
const optionsMarkdown = generateConfigOptionsMarkdown();
updateReadmeWithOptionsMarkdown(optionsMarkdown);
// Clean up
await client.close();
process.exit(0);
} catch (error) {
console.error('Error generating documentation:', error);
process.exit(1);
Expand Down
72 changes: 39 additions & 33 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,52 +34,58 @@ import path from 'node:path';
import fs from 'node:fs';
import assert from 'node:assert';

export const yargsInstance = yargs(hideBin(process.argv))
.scriptName('npx chrome-devtools-mcp@latest')
.option('browserUrl', {
type: 'string',
description: 'The browser URL to connect to',
export const cliOptions = {
browserUrl: {
type: 'string' as const,
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',
coerce: url => {
coerce: (url: string) => {
new URL(url);
return url;
},
})
.option('headless', {
type: 'boolean',
description: 'Whether to run in headless (no UI) mode',
},
headless: {
type: 'boolean' as const,
description: 'Whether to run in headless (no UI) mode.',
default: false,
})
.option('executablePath', {
type: 'string',
description: 'Path to custom Chrome executable',
},
executablePath: {
type: 'string' as const,
description: 'Path to custom Chrome executable.',
conflicts: 'browserUrl',
alias: 'e',
})
.option('isolated', {
type: 'boolean',
},
isolated: {
type: 'boolean' as const,
description:
'If specified, creates a temporary user-data-dir that is automatically cleaned up after the browser is closed.',
default: false,
})
.option('customDevtools', {
type: 'string',
description: 'Path to custom DevTools',
},
customDevtools: {
type: 'string' as const,
description: 'Path to custom DevTools.',
hidden: true,
conflicts: 'browserUrl',
alias: 'd',
})
.option('channel', {
type: 'string',
description: 'System installed browser channel to use.',
choices: ['stable', 'canary', 'beta', 'dev'],
},
channel: {
type: 'string' as const,
description: 'Specify a different Chrome channel that should be used.',
choices: ['stable', 'canary', 'beta', 'dev'] as const,
conflicts: ['browserUrl', 'executablePath'],
})
.option('logFile', {
type: 'string',
describe: 'Save the logs to file',
default: 'stable',
},
logFile: {
type: 'string' as const,
describe: 'Save the logs to file.',
hidden: true,
})
},
};

const yargsInstance = yargs(hideBin(process.argv))
.scriptName('npx chrome-devtools-mcp@latest')
.options(cliOptions)
.check(args => {
// We can't set default in the options else
// Yargs will complain
Expand All @@ -99,11 +105,11 @@ export const yargsInstance = yargs(hideBin(process.argv))
['$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'],
])
]);

.help();
export const args = yargsInstance
.wrap(Math.min(120, yargsInstance.terminalWidth()))
.help()
.parseSync();

if (args.logFile) {
Expand Down