-
Notifications
You must be signed in to change notification settings - Fork 1.3k
feat: add --profile-directory option to specify Chrome profile #753
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 5 commits
320e974
e15f5dc
0abdf77
08f98e4
3d5da8b
276f81a
6744657
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -43,13 +43,21 @@ function makeTargetFilter() { | |
| }; | ||
| } | ||
|
|
||
| //Extracts the profile directory name from a user data dir path. | ||
| function getProfileNameFromUserDataDir(userDataDir: string): string { | ||
| const normalized = userDataDir.replace(/\\/g, '/'); | ||
| const parts = normalized.split('/'); | ||
| return parts[parts.length - 1] || 'Default'; | ||
| } | ||
|
|
||
| export async function ensureBrowserConnected(options: { | ||
| browserURL?: string; | ||
| wsEndpoint?: string; | ||
| wsHeaders?: Record<string, string>; | ||
| devtools: boolean; | ||
| channel?: Channel; | ||
| userDataDir?: string; | ||
| profileDirectory?: string; | ||
| }) { | ||
| const {channel} = options; | ||
| if (browser?.connected) { | ||
|
|
@@ -126,6 +134,39 @@ export async function ensureBrowserConnected(options: { | |
| }, | ||
| ); | ||
| } | ||
|
|
||
| if (options.profileDirectory && options.userDataDir) { | ||
| try { | ||
| const portPath = path.join(options.userDataDir, 'DevToolsActivatePort'); | ||
|
||
| const fileContent = await fs.promises.readFile(portPath, 'utf8'); | ||
| const lines = fileContent | ||
| .split('\n') | ||
| .map(line => line.trim()) | ||
| .filter(line => line); | ||
|
|
||
| if (lines.length >= 2) { | ||
| const browserPath = lines[1]; | ||
| const actualProfile = getProfileNameFromUserDataDir(browserPath); | ||
| const requestedProfile = options.profileDirectory; | ||
|
|
||
| if (actualProfile !== requestedProfile) { | ||
| await browser.disconnect(); | ||
| throw new Error( | ||
| `Profile mismatch: Requested profile "${requestedProfile}" but Chrome is running with profile "${actualProfile}". ` + | ||
| `Please close Chrome and restart with the correct profile, or remove the --profile-directory flag.`, | ||
| ); | ||
| } | ||
|
|
||
| logger(`Successfully validated profile: ${actualProfile}`); | ||
| } | ||
| } catch (error) { | ||
| if ((error as Error).message.includes('Profile mismatch')) { | ||
| throw error; | ||
| } | ||
|
|
||
| logger('Could not validate profile directory: ', error); | ||
| } | ||
| } | ||
| logger('Connected Puppeteer'); | ||
| return browser; | ||
| } | ||
|
|
@@ -145,6 +186,7 @@ interface McpLaunchOptions { | |
| chromeArgs?: string[]; | ||
| ignoreDefaultChromeArgs?: string[]; | ||
| devtools: boolean; | ||
| profileDirectory?: string; | ||
| } | ||
|
|
||
| export async function launch(options: McpLaunchOptions): Promise<Browser> { | ||
|
|
@@ -171,6 +213,9 @@ export async function launch(options: McpLaunchOptions): Promise<Browser> { | |
| ...(options.chromeArgs ?? []), | ||
| '--hide-crash-restore-bubble', | ||
| ]; | ||
| if (options.profileDirectory) { | ||
| args.push(`--profile-directory=${options.profileDirectory}`); | ||
| } | ||
| const ignoreDefaultArgs: LaunchOptions['ignoreDefaultArgs'] = | ||
| options.ignoreDefaultChromeArgs ?? false; | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -198,6 +198,13 @@ export const cliOptions = { | |
| default: true, | ||
| describe: 'Set to false to exclude tools related to network.', | ||
| }, | ||
| profileDirectory: { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. we probably should be using https://pptr.dev/api/puppeteer.browser.browsercontexts to locate the browser context for the profile (currently the default browser context is used) |
||
| type: 'string', | ||
| description: | ||
| 'Specify which Chrome profile to use by specifying its directory name (e.g., "Profile 1", "Default") inside a chrome user data directory. Only works with --autoConnect or when launching Chrome via the Chrome DevTools MCP server.', | ||
| alias: 'profile-dir', | ||
| conflicts: ['browserUrl', 'wsEndpoint'], | ||
| }, | ||
| usageStatistics: { | ||
| type: 'boolean', | ||
| // Marked as `false` until the feature is ready to be enabled by default. | ||
|
|
@@ -273,6 +280,14 @@ export function parseArguments(version: string, argv = process.argv) { | |
| '$0 --auto-connect --channel=canary', | ||
| 'Connect to a canary Chrome instance (Chrome 145+) running instead of launching a new instance', | ||
| ], | ||
| [ | ||
| '$0 --auto-connect --profile-directory="Profile 1"', | ||
| 'Connect to Chrome using a specific profile (requires Chrome 145+)', | ||
| ], | ||
| [ | ||
| '$0 --channel=stable --profile-directory="Work Profile"', | ||
| 'Launch stable Chrome with a specific profile', | ||
| ], | ||
| ]); | ||
|
|
||
| return yargsInstance | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I do not think this check should be in the connection code branch.