Skip to content

Commit e01e333

Browse files
feat: add update notification to both binaries (#1783)
Both `chrome-devtools` and `chrome-devtools-mcp` now log a notification when a newer version is detected to be available. This detection is implemented as follows: 1. Read the latest version from a local 24-hour cache (`~/.cache/chrome-devtools-mcp/latest.json`). 2. If the cache is stale or missing, spawn a detached background process to fetch the latest version from the npm registry and update the cache file.
1 parent 627ed68 commit e01e333

File tree

8 files changed

+306
-13
lines changed

8 files changed

+306
-13
lines changed

.npmrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
package-lock=true

README.md

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,12 @@ Google handles this data in accordance with the [Google Privacy Policy](https://
5151

5252
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.
5353

54-
Collection is disabled if CHROME_DEVTOOLS_MCP_NO_USAGE_STATISTICS or CI env variables are set.
54+
Collection is disabled if `CHROME_DEVTOOLS_MCP_NO_USAGE_STATISTICS` or `CI` env variables are set.
55+
56+
## Update checks
57+
58+
By default, the server periodically checks the npm registry for updates and logs a notification when a newer version is available.
59+
You can disable these update checks by setting the `CHROME_DEVTOOLS_MCP_NO_UPDATE_CHECKS` environment variable.
5560

5661
## Requirements
5762

@@ -74,7 +79,7 @@ Add the following config to your MCP client:
7479
}
7580
```
7681

77-
> [!NOTE]
82+
> [!NOTE]
7883
> Using `chrome-devtools-mcp@latest` ensures that your MCP client will always use the latest version of the Chrome DevTools MCP server.
7984
8085
If you are interested in doing only basic browser tasks, use the `--slim` mode:
@@ -143,7 +148,7 @@ claude mcp add chrome-devtools --scope user npx chrome-devtools-mcp@latest
143148

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

146-
> [!NOTE]
151+
> [!NOTE]
147152
> If you already had Chrome DevTools MCP installed previously for Claude Code, make sure to remove it first from your installation and configuration files.
148153
149154
To install Chrome DevTools MCP with skills, add the marketplace registry in Claude Code:
@@ -200,7 +205,7 @@ startup_timeout_ms = 20_000
200205

201206
<details>
202207
<summary>Command Code</summary>
203-
208+
204209
Use the Command Code CLI to add the Chrome DevTools MCP server (<a href="https://commandcode.ai/docs/mcp">MCP guide</a>):
205210

206211
```bash
@@ -417,10 +422,11 @@ qodercli mcp add -s user chrome-devtools -- npx chrome-devtools-mcp@latest
417422

418423
<details>
419424
<summary>Visual Studio</summary>
420-
421-
**Click the button to install:**
422-
423-
[<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)
425+
426+
**Click the button to install:**
427+
428+
[<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)
429+
424430
</details>
425431

426432
<details>
@@ -446,7 +452,7 @@ Check the performance of https://developers.chrome.com
446452

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

449-
> [!NOTE]
455+
> [!NOTE]
450456
> 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.
451457
452458
## Tools
@@ -591,7 +597,7 @@ The Chrome DevTools MCP server supports the following configuration option:
591597
- **Default:** `true`
592598

593599
- **`--usageStatistics`/ `--usage-statistics`**
594-
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.
600+
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.
595601
- **Type:** boolean
596602
- **Default:** `true`
597603

@@ -705,7 +711,7 @@ Make sure your browser is running. Open gemini-cli and run the following prompt:
705711
Check the performance of https://developers.chrome.com
706712
```
707713

708-
> [!NOTE]
714+
> [!NOTE]
709715
> 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.
710716
711717
The Chrome DevTools MCP server will try to connect to your running Chrome
@@ -741,7 +747,7 @@ Add the `--browser-url` option to your MCP client configuration. The value of th
741747

742748
**Step 2: Start the Chrome browser**
743749

744-
> [!WARNING]
750+
> [!WARNING]
745751
> 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.
746752
747753
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.

src/bin/check-latest-version.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/**
2+
* @license
3+
* Copyright 2026 Google LLC
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
import fs from 'node:fs/promises';
8+
import path from 'node:path';
9+
import process from 'node:process';
10+
11+
const cachePath = process.argv[2];
12+
13+
if (cachePath) {
14+
try {
15+
const response = await fetch(
16+
'https://registry.npmjs.org/chrome-devtools-mcp/latest',
17+
);
18+
const data = response.ok ? await response.json() : null;
19+
20+
if (
21+
data &&
22+
typeof data === 'object' &&
23+
'version' in data &&
24+
typeof data.version === 'string'
25+
) {
26+
await fs.mkdir(path.dirname(cachePath), {recursive: true});
27+
await fs.writeFile(cachePath, JSON.stringify({version: data.version}));
28+
}
29+
} catch {
30+
// Ignore errors.
31+
}
32+
}

src/bin/chrome-devtools-mcp-cli-options.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,7 @@ export const cliOptions = {
233233
type: 'boolean',
234234
default: true,
235235
describe:
236-
'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.',
236+
'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.',
237237
},
238238
clearcutEndpoint: {
239239
type: 'string',

src/bin/chrome-devtools-mcp-main.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,15 @@ import {createMcpServer, logDisclaimers} from '../index.js';
1212
import {logger, saveLogsToFile} from '../logger.js';
1313
import {computeFlagUsage} from '../telemetry/flagUtils.js';
1414
import {StdioServerTransport} from '../third_party/index.js';
15+
import {checkForUpdates} from '../utils/check-for-updates.js';
1516
import {VERSION} from '../version.js';
1617

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

20+
await checkForUpdates(
21+
'Run `npm install chrome-devtools-mcp@latest` to update.',
22+
);
23+
1924
export const args = parseArguments(VERSION);
2025

2126
const logFile = args.logFile ? saveLogsToFile(args.logFile) : undefined;

src/bin/chrome-devtools.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,16 @@ import {
2121
import {isDaemonRunning, serializeArgs} from '../daemon/utils.js';
2222
import {logDisclaimers} from '../index.js';
2323
import {hideBin, yargs, type CallToolResult} from '../third_party/index.js';
24+
import {checkForUpdates} from '../utils/check-for-updates.js';
2425
import {VERSION} from '../version.js';
2526

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

30+
await checkForUpdates(
31+
'Run `npm install -g chrome-devtools-mcp@latest` and `chrome-devtools start` to update and restart the daemon.',
32+
);
33+
2934
async function start(args: string[]) {
3035
const combinedArgs = [...args, ...defaultArgs];
3136
await startDaemon(combinedArgs);

src/utils/check-for-updates.ts

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
/**
2+
* @license
3+
* Copyright 2026 Google LLC
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
import child_process from 'node:child_process';
8+
import fs from 'node:fs/promises';
9+
import os from 'node:os';
10+
import path from 'node:path';
11+
import process from 'node:process';
12+
13+
import {VERSION} from '../version.js';
14+
15+
/**
16+
* Notifies the user if an update is available.
17+
* @param message The message to display in the update notification.
18+
*/
19+
let isChecking = false;
20+
21+
/** @internal Reset flag for tests only. */
22+
export function resetUpdateCheckFlagForTesting() {
23+
isChecking = false;
24+
}
25+
26+
export async function checkForUpdates(message: string) {
27+
if (isChecking || process.env['CHROME_DEVTOOLS_MCP_NO_UPDATE_CHECKS']) {
28+
return;
29+
}
30+
isChecking = true;
31+
32+
const cachePath = path.join(
33+
os.homedir(),
34+
'.cache',
35+
'chrome-devtools-mcp',
36+
'latest.json',
37+
);
38+
39+
let cachedVersion: string | undefined;
40+
let stats: {mtimeMs: number} | undefined;
41+
try {
42+
stats = await fs.stat(cachePath);
43+
const data = await fs.readFile(cachePath, 'utf8');
44+
cachedVersion = JSON.parse(data).version;
45+
} catch {
46+
// Ignore errors reading cache.
47+
}
48+
49+
if (cachedVersion && cachedVersion !== VERSION) {
50+
console.warn(
51+
`\nUpdate available: ${VERSION} -> ${cachedVersion}\n${message}\n`,
52+
);
53+
}
54+
55+
const now = Date.now();
56+
if (stats && now - stats.mtimeMs < 24 * 60 * 60 * 1000) {
57+
return;
58+
}
59+
60+
// Update mtime immediately to prevent multiple subprocesses.
61+
try {
62+
const parentDir = path.dirname(cachePath);
63+
await fs.mkdir(parentDir, {recursive: true});
64+
const nowTime = new Date();
65+
if (stats) {
66+
await fs.utimes(cachePath, nowTime, nowTime);
67+
} else {
68+
await fs.writeFile(cachePath, JSON.stringify({version: VERSION}));
69+
}
70+
} catch {
71+
// Ignore errors.
72+
}
73+
74+
// In a separate process, check the latest available version number
75+
// and update the local snapshot accordingly.
76+
const scriptPath = path.join(
77+
import.meta.dirname,
78+
'..',
79+
'bin',
80+
'check-latest-version.js',
81+
);
82+
83+
try {
84+
const child = child_process.spawn(
85+
process.execPath,
86+
[scriptPath, cachePath],
87+
{
88+
detached: true,
89+
stdio: 'ignore',
90+
},
91+
);
92+
child.unref();
93+
} catch {
94+
// Fail silently in case of any errors.
95+
}
96+
}

0 commit comments

Comments
 (0)