From 927dd1ee09f4e4842de16b3a1167f4f67f24de2d Mon Sep 17 00:00:00 2001 From: Sebastian Benz Date: Thu, 11 Sep 2025 12:49:05 +0200 Subject: [PATCH] Polish readme * add configuration section * remove existing --help output * move user-data-dir to new concepts section * fix small spelling issues in the readme --- .github/workflows/presubmit.yml | 2 +- README.md | 83 +++++++++++++++++++-------------- package-lock.json | 4 +- scripts/generate-docs.ts | 60 ++++++++++++++++++------ src/index.ts | 72 +++++++++++++++------------- 5 files changed, 136 insertions(+), 85 deletions(-) diff --git a/.github/workflows/presubmit.yml b/.github/workflows/presubmit.yml index af19347f..0fc1e192 100644 --- a/.github/workflows/presubmit.yml +++ b/.github/workflows/presubmit.yml @@ -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 diff --git a/README.md b/README.md index 7811e4a2..7d1cc6a7 100644 --- a/README.md +++ b/README.md @@ -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 @@ -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 @@ -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: + + + +- **`--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` + + + +Pass them via the `args` property in the JSON configuration. For example: ```json { @@ -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: - - - -```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` - +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. diff --git a/package-lock.json b/package-lock.json index 9c3b30de..74116448 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "chrome-devtools-mcp", - "version": "0.1.0", + "version": "0.0.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "chrome-devtools-mcp", - "version": "0.1.0", + "version": "0.0.1", "license": "Apache-2.0", "dependencies": { "@modelcontextprotocol/sdk": "^1.17.5", diff --git a/scripts/generate-docs.ts b/scripts/generate-docs.ts index 091b5dfc..10443599 100644 --- a/scripts/generate-docs.ts +++ b/scripts/generate-docs.ts @@ -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'; @@ -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 = ''; - const endMarker = ''; + const beginMarker = ''; + const endMarker = ''; 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 { @@ -280,15 +315,12 @@ async function generateToolDocumentation(): Promise { 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); diff --git a/src/index.ts b/src/index.ts index ed4fcc76..8b192028 100644 --- a/src/index.ts +++ b/src/index.ts @@ -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 @@ -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) {