chrome-devtools-mcp 0.11.0 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -7,7 +7,7 @@ control and inspect a live Chrome browser. It acts as a Model-Context-Protocol
7
7
  (MCP) server, giving your AI coding assistant access to the full power of
8
8
  Chrome DevTools for reliable automation, in-depth debugging, and performance analysis.
9
9
 
10
- ## [Tool reference](./docs/tool-reference.md) | [Changelog](./CHANGELOG.md) | [Contributing](./CONTRIBUTING.md) | [Troubleshooting](./docs/troubleshooting.md)
10
+ ## [Tool reference](./docs/tool-reference.md) | [Changelog](./CHANGELOG.md) | [Contributing](./CONTRIBUTING.md) | [Troubleshooting](./docs/troubleshooting.md) | [Design Principles](./docs/design-principles.md)
11
11
 
12
12
  ## Key features
13
13
 
@@ -350,6 +350,11 @@ The Chrome DevTools MCP server supports the following configuration option:
350
350
 
351
351
  <!-- BEGIN AUTO GENERATED OPTIONS -->
352
352
 
353
+ - **`--autoConnect`**
354
+ If specified, automatically connects to a browser (Chrome 145+) running in the user data directory identified by the channel param. Requires remote debugging being enabled in Chrome here: chrome://inspect/#remote-debugging.
355
+ - **Type:** boolean
356
+ - **Default:** `false`
357
+
353
358
  - **`--browserUrl`, `-u`**
354
359
  Connect to a running, debuggable Chrome instance (e.g. `http://127.0.0.1:9222`). For more details see: https://github.com/ChromeDevTools/chrome-devtools-mcp#connecting-to-a-running-chrome-instance.
355
360
  - **Type:** string
@@ -32,6 +32,7 @@ function makeTargetFilter() {
32
32
  };
33
33
  }
34
34
  export async function ensureBrowserConnected(options) {
35
+ const { channel } = options;
35
36
  if (browser?.connected) {
36
37
  return browser;
37
38
  }
@@ -49,11 +50,56 @@ export async function ensureBrowserConnected(options) {
49
50
  else if (options.browserURL) {
50
51
  connectOptions.browserURL = options.browserURL;
51
52
  }
53
+ else if (channel || options.userDataDir) {
54
+ const userDataDir = options.userDataDir;
55
+ if (userDataDir) {
56
+ // TODO: re-expose this logic via Puppeteer.
57
+ const portPath = path.join(userDataDir, 'DevToolsActivePort');
58
+ try {
59
+ const fileContent = await fs.promises.readFile(portPath, 'utf8');
60
+ const [rawPort, rawPath] = fileContent
61
+ .split('\n')
62
+ .map(line => {
63
+ return line.trim();
64
+ })
65
+ .filter(line => {
66
+ return !!line;
67
+ });
68
+ if (!rawPort || !rawPath) {
69
+ throw new Error(`Invalid DevToolsActivePort '${fileContent}' found`);
70
+ }
71
+ const port = parseInt(rawPort, 10);
72
+ if (isNaN(port) || port <= 0 || port > 65535) {
73
+ throw new Error(`Invalid port '${rawPort}' found`);
74
+ }
75
+ const browserWSEndpoint = `ws://127.0.0.1:${port}${rawPath}`;
76
+ connectOptions.browserWSEndpoint = browserWSEndpoint;
77
+ }
78
+ catch (error) {
79
+ throw new Error(`Could not connect to Chrome in ${userDataDir}. Check if Chrome is running and remote debugging is enabled.`, {
80
+ cause: error,
81
+ });
82
+ }
83
+ }
84
+ else {
85
+ if (!channel) {
86
+ throw new Error('Channel must be provided if userDataDir is missing');
87
+ }
88
+ connectOptions.channel = (channel === 'stable' ? 'chrome' : `chrome-${channel}`);
89
+ }
90
+ }
52
91
  else {
53
- throw new Error('Either browserURL or wsEndpoint must be provided');
92
+ throw new Error('Either browserURL, wsEndpoint, channel or userDataDir must be provided');
54
93
  }
55
94
  logger('Connecting Puppeteer to ', JSON.stringify(connectOptions));
56
- browser = await puppeteer.connect(connectOptions);
95
+ try {
96
+ browser = await puppeteer.connect(connectOptions);
97
+ }
98
+ catch (err) {
99
+ throw new Error('Could not connect to Chrome. Check if Chrome is running and remote debugging is enabled by going to chrome://inspect/#remote-debugging.', {
100
+ cause: err,
101
+ });
102
+ }
57
103
  logger('Connected Puppeteer');
58
104
  return browser;
59
105
  }
package/build/src/cli.js CHANGED
@@ -5,6 +5,18 @@
5
5
  */
6
6
  import { yargs, hideBin } from './third_party/index.js';
7
7
  export const cliOptions = {
8
+ autoConnect: {
9
+ type: 'boolean',
10
+ description: 'If specified, automatically connects to a browser (Chrome 145+) running in the user data directory identified by the channel param. Requires remote debugging being enabled in Chrome here: chrome://inspect/#remote-debugging.',
11
+ conflicts: ['isolated', 'executablePath'],
12
+ default: false,
13
+ coerce: (value) => {
14
+ if (!value) {
15
+ return;
16
+ }
17
+ return value;
18
+ },
19
+ },
8
20
  browserUrl: {
9
21
  type: 'string',
10
22
  description: 'Connect to a running, debuggable Chrome instance (e.g. `http://127.0.0.1:9222`). For more details see: https://github.com/ChromeDevTools/chrome-devtools-mcp#connecting-to-a-running-chrome-instance.',
@@ -204,6 +216,14 @@ export function parseArguments(version, argv = process.argv) {
204
216
  '$0 --user-data-dir=/tmp/user-data-dir',
205
217
  'Use a custom user data directory',
206
218
  ],
219
+ [
220
+ '$0 --auto-connect',
221
+ 'Connect to a stable Chrome instance (Chrome 145+) running instead of launching a new instance',
222
+ ],
223
+ [
224
+ '$0 --auto-connect --channel=canary',
225
+ 'Connect to a canary Chrome instance (Chrome 145+) running instead of launching a new instance',
226
+ ],
207
227
  ]);
208
228
  return yargsInstance
209
229
  .wrap(Math.min(120, yargsInstance.terminalWidth()))
package/build/src/main.js CHANGED
@@ -16,7 +16,7 @@ import { ToolCategory } from './tools/categories.js';
16
16
  import { tools } from './tools/tools.js';
17
17
  // If moved update release-please config
18
18
  // x-release-please-start-version
19
- const VERSION = '0.11.0';
19
+ const VERSION = '0.12.0';
20
20
  // x-release-please-end
21
21
  export const args = parseArguments(VERSION);
22
22
  const logFile = args.logFile ? saveLogsToFile(args.logFile) : undefined;
@@ -36,11 +36,14 @@ async function getContext() {
36
36
  extraArgs.push(`--proxy-server=${args.proxyServer}`);
37
37
  }
38
38
  const devtools = args.experimentalDevtools ?? false;
39
- const browser = args.browserUrl || args.wsEndpoint
39
+ const browser = args.browserUrl || args.wsEndpoint || args.autoConnect
40
40
  ? await ensureBrowserConnected({
41
41
  browserURL: args.browserUrl,
42
42
  wsEndpoint: args.wsEndpoint,
43
43
  wsHeaders: args.wsHeaders,
44
+ // Important: only pass channel, if autoConnect is true.
45
+ channel: args.autoConnect ? args.channel : undefined,
46
+ userDataDir: args.userDataDir,
44
47
  devtools,
45
48
  })
46
49
  : await ensureBrowserLaunched({
@@ -104,7 +107,10 @@ function registerTool(tool) {
104
107
  }
105
108
  catch (err) {
106
109
  logger(`${tool.name} error:`, err, err?.stack);
107
- const errorText = err && 'message' in err ? err.message : String(err);
110
+ let errorText = err && 'message' in err ? err.message : String(err);
111
+ if ('cause' in err && err.cause) {
112
+ errorText += `\nCause: ${err.cause.message}`;
113
+ }
108
114
  return {
109
115
  content: [
110
116
  {