Skip to content

Commit 6ab6d85

Browse files
authored
feat: support --auto-connect to a Chrome instance (#651)
This PR adds a new connection mode called `--auto-connect`. To activate it, add the `--auto-connect` flag to the chrome-devtools-mcp server configuration. With this, chrome-devtools-mcp will automatically connect to the running Chrome instance by looking up the port and the URL in the user data dir for the specified `--channel` (by default, `stable`). Requires Chrome M145.
1 parent 4cd294f commit 6ab6d85

File tree

5 files changed

+68
-4
lines changed

5 files changed

+68
-4
lines changed

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,11 @@ The Chrome DevTools MCP server supports the following configuration option:
350350

351351
<!-- BEGIN AUTO GENERATED OPTIONS -->
352352

353+
- **`--autoConnect`**
354+
If specified, automatically connects to a browser (Chrome 145+) running in the user data directory identified by the channel param.
355+
- **Type:** boolean
356+
- **Default:** `false`
357+
353358
- **`--browserUrl`, `-u`**
354359
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.
355360
- **Type:** string

src/browser.ts

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,9 @@ export async function ensureBrowserConnected(options: {
4848
wsEndpoint?: string;
4949
wsHeaders?: Record<string, string>;
5050
devtools: boolean;
51+
channel?: Channel;
5152
}) {
53+
const {channel} = options;
5254
if (browser?.connected) {
5355
return browser;
5456
}
@@ -66,12 +68,29 @@ export async function ensureBrowserConnected(options: {
6668
}
6769
} else if (options.browserURL) {
6870
connectOptions.browserURL = options.browserURL;
71+
} else if (channel) {
72+
const puppeteerChannel =
73+
channel !== 'stable'
74+
? (`chrome-${channel}` as ChromeReleaseChannel)
75+
: 'chrome';
76+
connectOptions.channel = puppeteerChannel;
6977
} else {
70-
throw new Error('Either browserURL or wsEndpoint must be provided');
78+
throw new Error(
79+
'Either browserURL, wsEndpoint or channel must be provided',
80+
);
7181
}
7282

7383
logger('Connecting Puppeteer to ', JSON.stringify(connectOptions));
74-
browser = await puppeteer.connect(connectOptions);
84+
try {
85+
browser = await puppeteer.connect(connectOptions);
86+
} catch (err) {
87+
throw new Error(
88+
'Could not connect to Chrome. Check if Chrome is running and remote debugging is enabled.',
89+
{
90+
cause: err,
91+
},
92+
);
93+
}
7594
logger('Connected Puppeteer');
7695
return browser;
7796
}

src/cli.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,19 @@ import type {YargsOptions} from './third_party/index.js';
88
import {yargs, hideBin} from './third_party/index.js';
99

1010
export const cliOptions = {
11+
autoConnect: {
12+
type: 'boolean',
13+
description:
14+
'If specified, automatically connects to a browser (Chrome 145+) running in the user data directory identified by the channel param.',
15+
conflicts: ['isolated', 'executablePath', 'userDataDir'],
16+
default: false,
17+
coerce: (value: boolean | undefined) => {
18+
if (!value) {
19+
return;
20+
}
21+
return value;
22+
},
23+
},
1124
browserUrl: {
1225
type: 'string',
1326
description:
@@ -221,6 +234,14 @@ export function parseArguments(version: string, argv = process.argv) {
221234
'$0 --user-data-dir=/tmp/user-data-dir',
222235
'Use a custom user data directory',
223236
],
237+
[
238+
'$0 --auto-connect',
239+
'Connect to a stable Chrome instance (Chrome 145+) running instead of launching a new instance',
240+
],
241+
[
242+
'$0 --auto-connect --channel=canary',
243+
'Connect to a canary Chrome instance (Chrome 145+) running instead of launching a new instance',
244+
],
224245
]);
225246

226247
return yargsInstance

src/main.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,13 @@ async function getContext(): Promise<McpContext> {
5454
}
5555
const devtools = args.experimentalDevtools ?? false;
5656
const browser =
57-
args.browserUrl || args.wsEndpoint
57+
args.browserUrl || args.wsEndpoint || args.autoConnect
5858
? await ensureBrowserConnected({
5959
browserURL: args.browserUrl,
6060
wsEndpoint: args.wsEndpoint,
6161
wsHeaders: args.wsHeaders,
62+
// Important: only pass channel, if autoConnect is true.
63+
channel: args.autoConnect ? (args.channel as Channel) : undefined,
6264
devtools,
6365
})
6466
: await ensureBrowserLaunched({
@@ -140,7 +142,10 @@ function registerTool(tool: ToolDefinition): void {
140142
};
141143
} catch (err) {
142144
logger(`${tool.name} error:`, err, err?.stack);
143-
const errorText = err && 'message' in err ? err.message : String(err);
145+
let errorText = err && 'message' in err ? err.message : String(err);
146+
if ('cause' in err && err.cause) {
147+
errorText += `\nCause: ${err.cause.message}`;
148+
}
144149
return {
145150
content: [
146151
{

tests/cli.test.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ describe('cli args parsing', () => {
1717
categoryPerformance: true,
1818
'category-network': true,
1919
categoryNetwork: true,
20+
'auto-connect': undefined,
21+
autoConnect: undefined,
2022
};
2123

2224
it('parses with default args', async () => {
@@ -208,4 +210,16 @@ describe('cli args parsing', () => {
208210
categoryEmulation: false,
209211
});
210212
});
213+
it('parses auto-connect', async () => {
214+
const args = parseArguments('1.0.0', ['node', 'main.js', '--auto-connect']);
215+
assert.deepStrictEqual(args, {
216+
...defaultArgs,
217+
_: [],
218+
headless: false,
219+
$0: 'npx chrome-devtools-mcp@latest',
220+
channel: 'stable',
221+
'auto-connect': true,
222+
autoConnect: true,
223+
});
224+
});
211225
});

0 commit comments

Comments
 (0)