chrome-devtools-mcp-profile-pin
Pins chrome-devtools-mcp to a single Google Chrome profile when multiple profiles are open. It lists and selects profiles, remembers the chosen profile, filters pages and workers to the attached profile, and opens new tabs in that profile.
This server does not currently have a deployable release on Glama. Follow the repository's setup instructions to install and configure it for your MCP client, including any required credentials and local dependencies.
chrome-devtools-mcp-profile-pin
Make chrome-devtools-mcp work with one Chrome profile of your choice when several Chrome profiles are open.
The wrapper patches your globally installed chrome-devtools-mcp in a separate copy, so the original install stays untouched. It re-applies itself automatically after every update of chrome-devtools-mcp.
Unofficial, not affiliated with Google. Upstream discussion: ChromeDevTools/chrome-devtools-mcp#694.
The problem
With --autoConnect, all open Chrome profiles share one browser process and one debugging endpoint. Chrome reports the last-focused profile as its default browser context, and that value changes as you switch windows. As a result:
the server binds to whichever profile happened to have focus when it connected;
list_pagesmixes tabs from several profiles;new_pageopens in whichever profile has focus at that moment.
Related MCP server: chrome-mcp
What you get
The server sees exactly one profile. Pages, workers, service workers, console and network of other profiles are never attached.
It asks only when it has to. If only one profile has open windows, nothing changes. If several are open, you are asked which one to use, with one choice per profile, and the answer is remembered. The question comes either as an MCP elicitation form or, where the client can't show forms, from the agent itself (see below).
You can see which profile is attached.
list_pagesshows it in the header, e.g.## Pages (Chrome profile: Work (Profile 1)).A
select_profiletool. Ask the agent to "switch to my work profile". With no arguments, the tool lists your profiles.New tabs always open in the attached profile. If that profile is closed, its window is opened for you.
How the profile is chosen
The first matching rule wins:
select_profilewas called in this session.The
CHROME_DEVTOOLS_MCP_PROFILEenvironment variable is set to a display name or folder name. Set it tooffto disable pinning completely.Only one profile has open windows: no pinning, same as upstream.
You chose a profile earlier and asked to remember it.
The client shows an MCP elicitation form: a single-select list of your profiles.
Otherwise the tool call returns a message that lists each profile with the exact
select_profilecall for it. It tells the agent to ask you with one choice per profile. In Claude Code, for example, the agent asks with one button per profile, and the call is retried after you pick one.
Some clients advertise elicitation but have no UI for it and decline automatically. The Claude desktop app's Code tab runs Claude Code in SDK/print mode and does this. In that case the wrapper falls back to step 6.
Requirements
Node.js 22.12+. The wrapper uses Node's built-in
WebSocket.Google Chrome 144+, with remote debugging enabled at
chrome://inspect/#remote-debugging(the normal--autoConnectsetup).chrome-devtools-mcp installed globally:
npm install -g chrome-devtools-mcp@latest
Tested with chrome-devtools-mcp 1.12.0 and Chrome 157 on Windows. The macOS and Linux code paths exist but are untested.
Installation
Clone the repository somewhere permanent:
git clone https://github.com/shraga100/chrome-devtools-mcp-profile-pin.gitPoint your MCP client at
launch.mjsinstead of chrome-devtools-mcp, and keep your usual flags.Claude Code:
claude mcp add chrome-devtools --scope user -- node /path/to/chrome-devtools-mcp-profile-pin/launch.mjs --autoConnectAny client using JSON config (
~/.claude.json,.mcp.json, Cursor, etc.):{ "mcpServers": { "chrome-devtools": { "command": "node", "args": ["/path/to/chrome-devtools-mcp-profile-pin/launch.mjs", "--autoConnect"] } } }To always use one profile, add
"env": { "CHROME_DEVTOOLS_MCP_PROFILE": "Work" }.Start a new session. On first start the wrapper builds the patched copy into
dist/, which takes a few seconds.
Sessions that are already running keep using whatever they started with. Only new sessions use the wrapper.
Staying patched after chrome-devtools-mcp updates
You don't need to do anything special. Update chrome-devtools-mcp as usual:
npm install -g chrome-devtools-mcp@latestOn the next start, launch.mjs notices the new version, builds a fresh patched copy in dist/<version>-<hash>/ and runs it. Old copies are kept for 14 days, because long-running sessions may still be using them, and are then deleted.
If a new upstream version changed the code the patch relies on, the wrapper does not break your MCP. It logs a warning to stderr and runs the unpatched upstream server, so you only lose profile pinning. Check for an update of this repository:
git pullTo check the status manually:
node build.mjs # build if needed and print status
node build.mjs --force # rebuildConfiguration
Environment variable | Meaning |
| Profile to use: a display name ( |
| Chrome executable, if it isn't found automatically. |
| Path of the chrome-devtools-mcp package to patch, if it isn't the global npm one. |
| Where the remembered choice is stored. Default: |
| Set to |
The select_profile tool takes these arguments:
profile: the profile to switch to.remember: defaulttrue.forget: clears the remembered choice.
Switching reconnects to Chrome, so Chrome may show "Allow remote debugging?" again.
How it works
The wrapper opens one WebSocket to Chrome, so the consent dialog appears once. It resolves the profile on that socket and then hands the same socket to Puppeteer through
puppeteer.connect({transport}).To find a profile's
browserContextId, it runschrome --profile-directory=<dir> "data:text/html,<!--nonce-->". The running Chrome opens that tab inside the profile, opening the profile's window if needed, and the wrapper looks for the nonce inTarget.targetCreated. It runs no script in your pages. If the profile already has other tabs, the helper tab is closed again. A running Chrome ignoresabout:andchrome://URLs from the command line, which is why the helper tab uses adata:URL.Puppeteer's
targetFilterrejects targets whosebrowserContextIdbelongs to another profile. Puppeteer doesn't run that filter for service workers, so the wrapper also filtersbrowser.targets()and thetargetcreated/targetchanged/targetdestroyedevents. Without that, extensions installed in several profiles would show up once per profile under "Extension Service Workers".new_pagefirst triesTarget.createTarget({browserContextId}). Chrome only accepts that while the profile is the last-focused one, so on failure the wrapper falls back to the command-line trick.The patch is applied to the built JavaScript with exact-match anchors (
lib/patch.mjs). If any anchor is missing, the build fails as a whole rather than half-applying.
Limitations
Chrome still asks "Allow remote debugging?" on every new connection. This wrapper doesn't change that.
When a pinned session connects, the profile's window comes to the front, and a helper tab may stay open if the profile had no other tabs.
When
new_pageuses the fallback,background: truecan't be honoured.If every window of the pinned profile is closed while connected, the next
new_pagereconnects. Chrome gives the reopened profile a new id.Only local Chrome connections are supported (
--autoConnect, or--browserUrlpointing at localhost).
Testing
test/e2e.mjs drives the server over stdio against your real Chrome. It uses a temporary state directory, so your remembered choice is not touched.
node test/e2e.mjs offline
node test/e2e.mjs fixed "Work"
node test/e2e.mjs elicit "Profile 1" "Default" # 2+ profiles must be open
node test/e2e.mjs no-elicit # 2+ profiles must be openLicense
Apache-2.0, the same as chrome-devtools-mcp.
This server cannot be deployed
Maintenance
Related MCP Connectors
Browser MCP for logged-in tasks. Uses your Chrome — credentials stay local. Zero-token replay.
One profile — skills, credentials, and memory — synced to every agent tool via one MCP URL.
Hosted real Google Chrome MCP with per-user persistent state. Navigate, click, type, screenshot.
Cross-AI personal memory. Save once in ChatGPT, recall in Claude, Mistral, Grok, or any MCP client.
Related MCP Servers
- AlicenseBqualityDmaintenanceWraps chrome-devtools-mcp to control Brave/Chrome/Edge browsers with real user profiles, supporting multi-browser management, hot switching, and automatic reconnection.34MIT
- AlicenseNot gradedqualityDmaintenanceLocal MCP server for persistent Chrome automation with multi-profile support, enabling tab management, page inspection, element interaction, JavaScript evaluation, and screenshots while preserving login sessions across restarts.112 npm3MIT
- AlicenseNot gradedqualityCmaintenanceEnables MCP clients to drive individually named Chrome profiles, providing tools for tabs, navigation, page interaction, screenshots, JavaScript evaluation, console logs, and network inspection over stdio without a TCP port.MIT
- AlicenseNot gradedqualityBmaintenanceConnects AI agents to an existing Chromium browser profile through a local background service and extension, exposing browser control over MCP stdio. It lets agents navigate tabs, capture accessibility snapshots and screenshots, click, fill forms, type, scroll, drag, and run raw CDP commands, with multiple browser profiles selectable by ID.5 npmDo What The F*ck You Want To Public