A dedicated Chrome for chrome-devtools-mcp that can sign in
Set up a per-project Chrome profile for chrome-devtools-mcp (Claude Code, Cursor...) to drive: it can still sign in to Google/Cloudflare and keep the session, run several projects in parallel, and never touch your personal Chrome.
On this page
- The problem with the default setup
- Comparing the launch methods
- Why Chrome allows the debug port on a dedicated profile
- Why sign-in is blocked, and how to avoid it legitimately
- How it works
- Setting up a project
- The code
- Launcher open-chrome-mcp.ps1
- The project's two double-click files
- The chrome-devtools entry in .mcp.json
- Day-to-day use
- Security
- Troubleshooting
- References
Letting chrome-devtools-mcp (the MCP server that drives a browser for Claude Code, Cursor,
VS Code...) launch Chrome itself causes three problems: Google and Cloudflare block sign-in,
two projects opening a browser at once collide, and the agent can see every tab in your
personal Chrome. This guide sets up a dedicated Chrome profile per project so the MCP
attaches only to that browser, can still sign in and keep the session, and runs several
projects in parallel.
Throughout, <profiles-root> is the folder that holds your profiles (for example
%USERPROFILE%\chrome-mcp-profiles), and <project-slug> is a project's short name
(lowercase, digits, hyphens). Verified on Windows 11 + Chrome stable 153 +
chrome-devtools-mcp 1.9.0; the idea applies to other MCP clients too.
The problem with the default setup
When .mcp.json declares chrome-devtools-mcp with no flags, the MCP launches Chrome with
Puppeteer using one profile shared across every project. Four consequences:
| Problem | Cause |
|---|---|
| Google blocks sign-in ("This browser or app may not be secure"), Cloudflare Turnstile fails to verify | Puppeteer adds --enable-automation, and the remote-debugging port itself sets navigator.webdriver = true - the site sees a controlled browser |
Two projects opening a browser at once: The browser is already running... Use --isolated | The default profile is shared; Chrome allows only one process per data directory |
| The agent sees the wrong tabs / your personal profile | Bare --autoConnect (with no dedicated profile) attaches to the whole main Chrome process, seeing every profile and tab |
--isolated loses the login every time you close it | --isolated creates a temp directory and deletes it when Chrome closes |
Comparing the launch methods
The crux: when the MCP launches Chrome it always adds an automation flag (sign-in blocked); when you launch Chrome yourself and the MCP only CONNECTS, that flag is absent.
| Method | Google/CF sign-in | Login persists | Automation flag | Projects in parallel | Chrome dies with MCP |
|---|---|---|---|---|---|
| Default (no flags), MCP launches | No | No (shared profile) | Yes | No | Yes |
--isolated, MCP launches | No | No (cleared on close) | Yes | Yes | Yes |
Dedicated --userDataDir only, MCP launches | No | Yes | Yes | No | Yes |
--autoConnect + dedicated --userDataDir, YOU launch Chrome | Yes | Yes | No | Yes | No |
The last row is this guide's goal. You open Chrome with a launcher (no automation flag), and the MCP just reads that folder's debug port and attaches.
Why Chrome allows the debug port on a dedicated profile
Since Chrome 136, --remote-debugging-port and --remote-debugging-pipe are ignored
on the default data directory; to use them you must add --user-data-dir pointing to a
different directory. The reason: since App-Bound Encryption shipped, attackers have used
remote debugging to steal cookies, so Chrome closes that path on the default profile.
Thanks to this change, your personal Chrome (the default profile) cannot have a debug port opened via a command-line flag. Only a DEDICATED data directory can - exactly what we need.
Why sign-in is blocked, and how to avoid it legitimately
Google/Cloudflare block when they see navigator.webdriver === true. That property is true
whenever any control signal is present:
| Flag when Chrome launches | navigator.webdriver | Sign-in works? |
|---|---|---|
--enable-automation (Puppeteer adds it when the MCP launches Chrome) | true | No |
--headless | true | No |
--remote-debugging-port (including =0) or --remote-debugging-pipe | true | No |
| None of the above (a normal Chrome) | false | Yes |
So you cannot sign in while a debug port is open. The legitimate workaround (no spoofing tricks) is to split two modes over the same data directory:
- Login mode: open Chrome with NO debug port and no automation flag. You sign in to Google/Cloudflare like a normal browser. Cookies are saved into the profile folder.
- Work mode: fully close that Chrome, reopen it WITH
--remote-debugging-port=0. The MCP attaches. The cookies from the login are still there, so pages keep the session - and now you do not need to sign in again.
Do not use a stealth plugin or patch navigator.webdriver to "get past" Google/Cloudflare -
that is bot-detection evasion. The two-mode split above signs in with a CLEAN, uncontrolled
browser session, which is entirely legitimate.
How it works
Your main Chrome Project A's Chrome Project B's Chrome
(default profile, no port) <profiles-root>\A <profiles-root>\B
^ --remote-debugging-port=0 --remote-debugging-port=0
| | writes | writes
no MCP attaches here A\DevToolsActivePort (random port) B\DevToolsActivePort
^ reads ^ reads
Project A's MCP Project B's MCP
--autoConnect --autoConnect
--userDataDir=<...>\A --userDataDir=<...>\BThe detailed flow:
- The launcher runs
chrome.exe --user-data-dir="<profiles-root>\<slug>" --no-first-run --no-default-browser-check --remote-debugging-port=0. - Chrome picks a free port and writes
<folder>\DevToolsActivePortwith two lines: the port number and a/devtools/browser/<uuid>path. The port listens on127.0.0.1only. - With
--autoConnect, the MCP readsDevToolsActivePortinside the exact--userDataDirfolder, buildsws://127.0.0.1:<port><path>and connects. If the file is missing or the port does not answer, the MCP reportsCould not connect to Chrome in <folder>and does NOT launch a browser of its own. - When the MCP session ends, the MCP only disconnects - Chrome stays open.
Setting up a project
- Create the root folder
<profiles-root>(e.g.%USERPROFILE%\chrome-mcp-profiles) and put the launcheropen-chrome-mcp.ps1(code below) in it. - Choose
<project-slug>matching^[a-z0-9][a-z0-9-]*$, one slug per project. - Create the two
.cmdfiles in the project root (code below), substituting<project-slug>and the launcher path. - Edit the project's
.mcp.json: point thechrome-devtoolsentry's--autoConnect --userDataDirat<profiles-root>\<project-slug>. - Sign in (if the project needs accounts): run login mode, sign in, fully close it.
- Open work mode, then restart the MCP client so it loads the new
.mcp.json.
The code
Launcher open-chrome-mcp.ps1
Put it in <profiles-root>. Save it as UTF-8 with BOM (PowerShell 5.1 reads a BOM-less
file as ANSI, mangling non-ASCII text). The script locates its own folder, so it holds no
absolute paths.
param(
[Parameter(Mandatory = $true)]
[ValidatePattern('^[a-z0-9][a-z0-9-]*$')]
[string]$Project,
[switch]$Login
)
$ErrorActionPreference = 'Stop'
$profileDir = Join-Path $PSScriptRoot $Project # <profiles-root>\<Project>
$portFile = Join-Path $profileDir 'DevToolsActivePort'
$chrome = @(
"$env:ProgramFiles\Google\Chrome\Application\chrome.exe",
"${env:ProgramFiles(x86)}\Google\Chrome\Application\chrome.exe",
"$env:LOCALAPPDATA\Google\Chrome\Application\chrome.exe"
) | Where-Object { Test-Path $_ } | Select-Object -First 1
if (-not $chrome) { Write-Host 'Google Chrome not found.' -ForegroundColor Red; exit 1 }
# Chrome allows only one process per data directory. If a Chrome is already running on this
# folder, a new launch IGNORES the flags you pass -> check first so we do not open the wrong mode.
function Test-ProfileChromeRunning {
$needle = "--user-data-dir=`"$profileDir`"".ToLowerInvariant()
[bool](Get-CimInstance Win32_Process -Filter "Name='chrome.exe'" |
Where-Object { $_.CommandLine -and $_.CommandLine.ToLowerInvariant().Contains($needle) })
}
# Return the port if the debug port in DevToolsActivePort answers, otherwise $null.
function Get-LiveDebugPort {
try {
if (-not (Test-Path $portFile)) { return $null }
$port = (Get-Content $portFile -TotalCount 1).Trim()
if (-not $port) { return $null }
Invoke-RestMethod "http://127.0.0.1:$port/json/version" -TimeoutSec 2 | Out-Null
return $port
} catch { return $null }
}
if (Test-ProfileChromeRunning) {
$port = Get-LiveDebugPort
if ($Login) {
if ($port) { Write-Host "Already open in work mode (port $port). Close it fully, then retry." -ForegroundColor Yellow; exit 1 }
Write-Host 'Already open in login mode.' -ForegroundColor Green; exit 0
}
if ($port) { Write-Host "Ready for the MCP (port $port)." -ForegroundColor Green; exit 0 }
Write-Host 'Open but no debug port (login mode). Close it fully, then retry.' -ForegroundColor Yellow
exit 1
}
New-Item -ItemType Directory -Force $profileDir | Out-Null
$chromeArgs = @("--user-data-dir=`"$profileDir`"", '--no-first-run', '--no-default-browser-check')
if (-not $Login) {
Remove-Item $portFile -Force -ErrorAction SilentlyContinue # drop the previous run's stale port
$chromeArgs += '--remote-debugging-port=0'
}
Start-Process -FilePath $chrome -ArgumentList $chromeArgs
if ($Login) {
Write-Host 'Opened in login mode. Sign in, close it fully, then reopen work mode.' -ForegroundColor Green
exit 0
}
for ($i = 0; $i -lt 40; $i++) {
$port = Get-LiveDebugPort
if ($port) { Write-Host "Opened for the MCP (port $port, local only)." -ForegroundColor Green; exit 0 }
Start-Sleep -Milliseconds 250
}
Write-Host 'Opened but no debug port after 10 seconds.' -ForegroundColor Red
exit 1The project's two double-click files
Put them in the project root, keep the contents ASCII (cmd reads batch files in the OEM code
page). Substitute <project-slug> and the launcher path.
open-chrome-for-mcp.cmd (work mode):
@echo off
rem Opens this project's dedicated Chrome in work mode (random remote-debugging port)
rem so chrome-devtools-mcp (--autoConnect --userDataDir) can attach to it.
powershell -NoProfile -ExecutionPolicy Bypass -File "%USERPROFILE%\chrome-mcp-profiles\open-chrome-mcp.ps1" -Project <project-slug>
if errorlevel 1 (pause) else (timeout /t 4 >nul)open-chrome-for-mcp-login.cmd (login mode):
@echo off
rem Opens this project's dedicated Chrome in login mode (NO debug port) so Google / Cloudflare
rem sign-in behaves like a normal browser. Sign in, close that Chrome fully, then run
rem open-chrome-for-mcp.cmd. Cookies stay in the profile folder.
powershell -NoProfile -ExecutionPolicy Bypass -File "%USERPROFILE%\chrome-mcp-profiles\open-chrome-mcp.ps1" -Project <project-slug> -Login
if errorlevel 1 (pause) else (timeout /t 6 >nul)-ExecutionPolicy Bypass applies only to that PowerShell process; it does not change any
system setting.
The chrome-devtools entry in .mcp.json
JSON does not expand environment variables, so use an absolute path. In JSON, \ is \\.
{
"mcpServers": {
"chrome-devtools": {
"command": "cmd",
"args": [
"/c", "npx", "-y", "[email protected]",
"--autoConnect",
"--userDataDir=<profiles-root>\\<project-slug>"
]
}
}
}cmd /c is required on Windows because npx is a .cmd file. Pin the version (not
@latest) to avoid pulling a just-published release - one layer of npm supply-chain defense.
Do not add --isolated (it conflicts with both --autoConnect and --userDataDir),
--browserUrl, or --executablePath.
Day-to-day use
| When | What to do |
|---|---|
| First time, or when a site asks you to sign in again | Close the dedicated Chrome, run login mode, sign in, close it fully |
| You want the agent to work in the browser | The agent opens it when needed; or double-click the work-mode file |
| Done | Close the dedicated Chrome window - the login is kept |
| Wipe a project's data | Close the dedicated Chrome, delete <profiles-root>\<slug> (loses all logins) |
While the agent works, you keep using your main Chrome normally. Pick a distinct theme color for this Chrome so it is easy to tell apart.
Security
- While the dedicated Chrome is in work mode, any program on the machine can connect to
the debug port (it listens on
127.0.0.1only, not the network) and control that Chrome, including reading cookies. So: only sign in to the accounts a project truly needs, and close the dedicated Chrome when done. - Keep
chrome://inspect/#remote-debuggingOFF on your main Chrome - it is the one remaining way to open a debug port on the default profile. - The
<profiles-root>\<slug>folder holds login cookies. Do not commit it, sync it to the cloud, or share it. - Consider extra flags:
--redactNetworkHeaders(hide sensitive headers when reading network traffic),--allowedUrlPattern/--blockedUrlPattern(limit which pages the MCP may open).
Troubleshooting
| Symptom | Fix |
|---|---|
Could not connect to Chrome... ENOENT ... DevToolsActivePort | The dedicated Chrome is not open in work mode - run the launcher, then call the tool again |
Could not connect... even though DevToolsActivePort exists | A stale port file from the previous run (Chrome closed) or login mode is open - the launcher deletes the old file when it reopens |
| Google "This browser or app may not be secure" | You are signing in with a debug port open / the MCP attached - use login mode (no port) |
| Non-ASCII text is garbled in the launcher window | open-chrome-mcp.ps1 was saved without a BOM - resave it as UTF-8 with BOM |
running scripts is disabled on this system | Running the .ps1 directly under the default ExecutionPolicy - run it via the .cmd file |
| Launcher says "open but no debug port" even after you closed the window | Chrome is still running in the background ("keep running background apps when closed") - quit it from the system tray, then retry |
The MCP client does not show the chrome-devtools tool after editing .mcp.json | The session was not restarted, or the server is not approved - restart the MCP client |
References
- Chrome for Developers - Changes to remote debugging switches (Chrome 136): https://developer.chrome.com/blog/remote-debugging-port
- chrome-devtools-mcp - Advanced usage (
--autoConnect,--browser-url, debug-port security warning): https://github.com/ChromeDevTools/chrome-devtools-mcp/blob/main/docs/advanced-usage.md - Connect your AI agent to your personal browser with auto-connect (Chrome 144+): https://developer.chrome.com/docs/devtools/agents/use-cases/auto-connect
- MDN - Navigator: webdriver property: https://developer.mozilla.org/en-US/docs/Web/API/Navigator/webdriver