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
1 change: 1 addition & 0 deletions .npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package-lock=true
30 changes: 18 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,12 @@ Google handles this data in accordance with the [Google Privacy Policy](https://

Google's collection of usage statistics for Chrome DevTools MCP is independent from the Chrome browser's usage statistics. Opting out of Chrome metrics does not automatically opt you out of this tool, and vice-versa.

Collection is disabled if CHROME_DEVTOOLS_MCP_NO_USAGE_STATISTICS or CI env variables are set.
Collection is disabled if `CHROME_DEVTOOLS_MCP_NO_USAGE_STATISTICS` or `CI` env variables are set.

## Update checks

By default, the server periodically checks the npm registry for updates and logs a notification when a newer version is available.
You can disable these update checks by setting the `CHROME_DEVTOOLS_MCP_NO_UPDATE_CHECKS` environment variable.

## Requirements

Expand All @@ -74,7 +79,7 @@ Add the following config to your MCP client:
}
```

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

If you are interested in doing only basic browser tasks, use the `--slim` mode:
Expand Down Expand Up @@ -143,7 +148,7 @@ claude mcp add chrome-devtools --scope user npx chrome-devtools-mcp@latest

**Install as a Plugin (MCP + Skills)**

> [!NOTE]
> [!NOTE]
> If you already had Chrome DevTools MCP installed previously for Claude Code, make sure to remove it first from your installation and configuration files.

To install Chrome DevTools MCP with skills, add the marketplace registry in Claude Code:
Expand Down Expand Up @@ -200,7 +205,7 @@ startup_timeout_ms = 20_000

<details>
<summary>Command Code</summary>

Use the Command Code CLI to add the Chrome DevTools MCP server (<a href="https://commandcode.ai/docs/mcp">MCP guide</a>):

```bash
Expand Down Expand Up @@ -402,10 +407,11 @@ qodercli mcp add -s user chrome-devtools -- npx chrome-devtools-mcp@latest

<details>
<summary>Visual Studio</summary>

**Click the button to install:**

[<img src="https://img.shields.io/badge/Visual_Studio-Install-C16FDE?logo=visualstudio&logoColor=white" alt="Install in Visual Studio">](https://vs-open.link/mcp-install?%7B%22name%22%3A%22chrome-devtools%22%2C%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22chrome-devtools-mcp%40latest%22%5D%7D)

**Click the button to install:**

[<img src="https://img.shields.io/badge/Visual_Studio-Install-C16FDE?logo=visualstudio&logoColor=white" alt="Install in Visual Studio">](https://vs-open.link/mcp-install?%7B%22name%22%3A%22chrome-devtools%22%2C%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22chrome-devtools-mcp%40latest%22%5D%7D)

</details>

<details>
Expand All @@ -431,7 +437,7 @@ Check the performance of https://developers.chrome.com

Your MCP client should open the browser and record a performance trace.

> [!NOTE]
> [!NOTE]
> The MCP server will start the browser automatically once the MCP client uses a tool that requires a running browser instance. Connecting to the Chrome DevTools MCP server on its own will not automatically start the browser.

## Tools
Expand Down Expand Up @@ -572,7 +578,7 @@ The Chrome DevTools MCP server supports the following configuration option:
- **Default:** `true`

- **`--usageStatistics`/ `--usage-statistics`**
Set to false to opt-out of usage statistics collection. Google collects usage data to improve the tool, handled under the Google Privacy Policy (https://policies.google.com/privacy). This is independent from Chrome browser metrics. Disabled if CHROME_DEVTOOLS_MCP_NO_USAGE_STATISTICS or CI env variables are set.
Set to false to opt-out of usage statistics collection. Google collects usage data to improve the tool, handled under the Google Privacy Policy (https://policies.google.com/privacy). This is independent from Chrome browser metrics. Disabled if `CHROME_DEVTOOLS_MCP_NO_USAGE_STATISTICS` or `CI` env variables are set.
- **Type:** boolean
- **Default:** `true`

Expand Down Expand Up @@ -686,7 +692,7 @@ Make sure your browser is running. Open gemini-cli and run the following prompt:
Check the performance of https://developers.chrome.com
```

> [!NOTE]
> [!NOTE]
> The <code>autoConnect</code> option requires the user to start Chrome. If the user has multiple active profiles, the MCP server will connect to the default profile (as determined by Chrome). The MCP server has access to all open windows for the selected profile.

The Chrome DevTools MCP server will try to connect to your running Chrome
Expand Down Expand Up @@ -722,7 +728,7 @@ Add the `--browser-url` option to your MCP client configuration. The value of th

**Step 2: Start the Chrome browser**

> [!WARNING]
> [!WARNING]
> Enabling the remote debugging port opens up a debugging port on the running browser instance. Any application on your machine can connect to this port and control the browser. Make sure that you are not browsing any sensitive websites while the debugging port is open.

Start the Chrome browser with the remote debugging port enabled. Make sure to close any running Chrome instances before starting a new one with the debugging port enabled. The port number you choose must be the same as the one you specified in the `--browser-url` option in your MCP client configuration.
Expand Down
32 changes: 32 additions & 0 deletions src/bin/check-latest-version.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/**
* @license
* Copyright 2026 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/

import fs from 'node:fs/promises';
import path from 'node:path';
import process from 'node:process';

const cachePath = process.argv[2];

if (cachePath) {
try {
const response = await fetch(
'https://registry.npmjs.org/chrome-devtools-mcp/latest',
);
const data = response.ok ? await response.json() : null;

if (
data &&
typeof data === 'object' &&
'version' in data &&
typeof data.version === 'string'
) {
await fs.mkdir(path.dirname(cachePath), {recursive: true});
await fs.writeFile(cachePath, JSON.stringify({version: data.version}));
}
} catch {
// Ignore errors.
}
}
2 changes: 1 addition & 1 deletion src/bin/chrome-devtools-mcp-cli-options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ export const cliOptions = {
type: 'boolean',
default: true,
describe:
'Set to false to opt-out of usage statistics collection. Google collects usage data to improve the tool, handled under the Google Privacy Policy (https://policies.google.com/privacy). This is independent from Chrome browser metrics. Disabled if CHROME_DEVTOOLS_MCP_NO_USAGE_STATISTICS or CI env variables are set.',
'Set to false to opt-out of usage statistics collection. Google collects usage data to improve the tool, handled under the Google Privacy Policy (https://policies.google.com/privacy). This is independent from Chrome browser metrics. Disabled if `CHROME_DEVTOOLS_MCP_NO_USAGE_STATISTICS` or `CI` env variables are set.',
},
clearcutEndpoint: {
type: 'string',
Expand Down
5 changes: 5 additions & 0 deletions src/bin/chrome-devtools-mcp-main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,15 @@ import {createMcpServer, logDisclaimers} from '../index.js';
import {logger, saveLogsToFile} from '../logger.js';
import {computeFlagUsage} from '../telemetry/flagUtils.js';
import {StdioServerTransport} from '../third_party/index.js';
import {checkForUpdates} from '../utils/check-for-updates.js';
import {VERSION} from '../version.js';

import {cliOptions, parseArguments} from './chrome-devtools-mcp-cli-options.js';

await checkForUpdates(
'Run `npm install chrome-devtools-mcp@latest` to update.',
);

export const args = parseArguments(VERSION);

const logFile = args.logFile ? saveLogsToFile(args.logFile) : undefined;
Expand Down
5 changes: 5 additions & 0 deletions src/bin/chrome-devtools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,16 @@ import {
import {isDaemonRunning, serializeArgs} from '../daemon/utils.js';
import {logDisclaimers} from '../index.js';
import {hideBin, yargs, type CallToolResult} from '../third_party/index.js';
import {checkForUpdates} from '../utils/check-for-updates.js';
import {VERSION} from '../version.js';

import {commands} from './chrome-devtools-cli-options.js';
import {cliOptions, parseArguments} from './chrome-devtools-mcp-cli-options.js';

await checkForUpdates(
'Run `npm install -g chrome-devtools-mcp@latest` and `chrome-devtools start` to update and restart the daemon.',
);

async function start(args: string[]) {
const combinedArgs = [...args, ...defaultArgs];
await startDaemon(combinedArgs);
Expand Down
96 changes: 96 additions & 0 deletions src/utils/check-for-updates.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/**
* @license
* Copyright 2026 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/

import child_process from 'node:child_process';
import fs from 'node:fs/promises';
import os from 'node:os';
import path from 'node:path';
import process from 'node:process';

import {VERSION} from '../version.js';

/**
* Notifies the user if an update is available.
* @param message The message to display in the update notification.
*/
let isChecking = false;

/** @internal Reset flag for tests only. */
export function resetUpdateCheckFlagForTesting() {
isChecking = false;
}

export async function checkForUpdates(message: string) {
Comment thread
mathiasbynens marked this conversation as resolved.
if (isChecking || process.env['CHROME_DEVTOOLS_MCP_NO_UPDATE_CHECKS']) {
return;
}
isChecking = true;

const cachePath = path.join(
os.homedir(),
'.cache',
'chrome-devtools-mcp',
'latest.json',
);

let cachedVersion: string | undefined;
let stats: {mtimeMs: number} | undefined;
try {
stats = await fs.stat(cachePath);
const data = await fs.readFile(cachePath, 'utf8');
cachedVersion = JSON.parse(data).version;
} catch {
// Ignore errors reading cache.
}

if (cachedVersion && cachedVersion !== VERSION) {
console.warn(
`\nUpdate available: ${VERSION} -> ${cachedVersion}\n${message}\n`,
);
}

const now = Date.now();
if (stats && now - stats.mtimeMs < 24 * 60 * 60 * 1000) {
return;
}

// Update mtime immediately to prevent multiple subprocesses.
try {
const parentDir = path.dirname(cachePath);
await fs.mkdir(parentDir, {recursive: true});
const nowTime = new Date();
if (stats) {
await fs.utimes(cachePath, nowTime, nowTime);
} else {
await fs.writeFile(cachePath, JSON.stringify({version: VERSION}));
}
} catch {
// Ignore errors.
}

// In a separate process, check the latest available version number
// and update the local snapshot accordingly.
const scriptPath = path.join(
import.meta.dirname,
'..',
'bin',
'check-latest-version.js',
);

try {
const child = child_process.spawn(
process.execPath,
[scriptPath, cachePath],
{
detached: true,
stdio: 'ignore',
},
);
child.unref();
} catch {
// Fail silently in case of any errors.
}
}
Loading
Loading