Quickstart
Register the MCP server, load the extension, and pair it. Three steps, one paste.
Up and running in one paste
Hand this to your AI agent (Claude Code, Cursor, Windsurf, anything MCP) and it installs the server, wires it into the client, and walks you through the two steps that must happen inside Chrome:
Set up chrome-mcp on this machine by fetching and following
https://raw.githubusercontent.com/Mehmoodqureshi/chrome-mcp/main/SETUP.md
exactly, step by step. Work autonomously and verify each step.Prefer to read before you run an agent on your machine? SETUP.md
is the exact file the agent follows. The manual steps are below.
1. Register the MCP server with your host.
Claude Code (terminal) — one command, no config file to find
claude mcp add chrome-mcp -s user -- \
npx -y @mehmoodqureshi/chrome-mcp \
--allow-domain example.com --enable-mutations --persist-tokenEverything before -- belongs to Claude Code; everything after it is this
server's command and flags. Keep the -- or --allow-domain gets read as a
Claude Code option.
-s user registers it for every project on your machine. Use -s local (the
default) for just the current project, or -s project to write a .mcp.json
your team can commit.
Check it came up with claude mcp list. After upgrading the server, reconnect it
with /mcp inside a session — no restart needed.
Claude Desktop and other MCP hosts — JSON config
{
"mcpServers": {
"chrome-mcp": {
"command": "npx",
"args": ["-y", "@mehmoodqureshi/chrome-mcp",
"--allow-domain", "example.com", "--enable-mutations",
"--persist-token"]
}
}
}By default everything is deny-all (no domains, no eval, no mutations). Grant
exactly what you need with --allow-domain <glob> (repeatable), --enable-mutations,
--enable-downloads, --enable-uploads, --unsafe-enable-eval, or --unsafe-all-domains.
--enable-uploadspermitsupload_file(setting local file(s) on a page's file<input>). It is off by default because sending local files to a page is an exfiltration risk; it is also gated by the destination-domain allowlist. Pair it with--uploads-dir <path>to restrict uploads to files inside that directory (..traversal is blocked) — strongly recommended for unattended use.
Pair once, never again. Both examples above include --persist-token, which
is what makes the pairing survive a restart — drop it if you'd rather have the
stricter default described next.
Without --persist-token a fresh token is minted every boot (the secure
default), which means re-pairing the extension on each restart. With it, the
token is stored 0600 at ~/.chrome-mcp/token and reused; the extension's
keepalive auto-reconnects with no manual step. CHROME_MCP_TOKEN pins the token
explicitly (and is never written to disk).
2. Load the extension — required; the server can drive nothing without it.
Two ways to get it:
- Install from the Chrome Web Store — one click, no Developer mode, and Chrome keeps it updated. The Web Store build is reviewed before each release, so it can trail the npm package by a version; it pairs with any server and simply skips features it predates.
- Load the bundled folder (below) — always matches the npm package you just installed, and the right choice when you want the newest behaviour.
The extension ships prebuilt inside the npm package, and every time the server boots it copies it to a plain folder right under your home directory:
~/chrome-mcp-extension (macOS / Linux)
%USERPROFILE%\chrome-mcp-extension (Windows)So after step 1 has started the server once (restart your client, or /mcp in
Claude Code), the folder is already there. To create it without a client, or
to print the exact path:
npx -y @mehmoodqureshi/chrome-mcp --extension-pathThen chrome://extensions → enable Developer mode → Load unpacked →
pick chrome-mcp-extension in your home folder. After upgrading the package the
server refreshes the files on its next boot and the extension reloads itself
within 30 seconds; nothing to click. CHROME_MCP_EXTENSION_DIR moves the
folder somewhere else. (Working from a git clone instead? Run
npm install && npm run build:ext first — extension-dist/ is gitignored, and
the server mirrors it to the same home folder.)
3. Pair it — usually nothing to do. Every time the server boots it writes
pairing.json (mode 0600, never shipped in the tarball) into the very
chrome-mcp-extension folder you just loaded. The extension reads that file
from its own folder on startup and pairs itself, so the toolbar badge turns
green with no token to paste. Load the extension before the server has ever
run? It re-checks every 30 seconds and pairs as soon as the file appears.
Where to see the badge: it sits on the extension's icon in Chrome's
toolbar, not on the chrome://extensions page. Chrome hides new extensions
behind the puzzle-piece button at the right of the address bar, so click that,
find MCP Extension for Chrome, and click the pin next to it once; the icon then
stays in the toolbar. Hover it for the status in words.
| Badge | Meaning |
|---|---|
| green dot | paired and connected |
| yellow dots | connecting |
| grey circle | not paired yet (no server has run, or no pairing file) |
| red exclamation mark | token rejected; the server rotated it, re-pairs by itself in a moment |
Manual fallback (a copied folder, a read-only home): run
npx -y @mehmoodqureshi/chrome-mcp --print-pairing, open the extension's
Options page, and paste the port + token from
~/.chrome-mcp/handshake.json. Values saved there take precedence over the
bundled file.
Running more than one session
Every MCP host session (each Claude terminal, tab or window) starts its own chrome-mcp, and they all share your Chrome at once. The first one to start owns the bridge port and the extension connections — the hub. Each later session finds the port held by a live chrome-mcp and joins it as a peer: its tool calls are relayed through the hub to the same browsers, so every session keeps working side by side. Nobody is disconnected.
When the hub's session ends, its peers race for the port; one takes it over
(with the same token, so the extension re-pairs by itself within a few seconds)
and the rest join the new hub. A call that was in flight at that moment fails
once with EXTENSION_DISCONNECTED and is retried automatically when it is safe
to repeat.
Peers authenticate with the pairing token from the 0600 handshake file, so only your own OS user can join. A chrome-mcp too old to share the port is replaced as before: it is verified to be chrome-mcp, then stopped. Anything that isn't a verified chrome-mcp is never touched — a port held by some other program is reported, never killed.
Sessions share one browser, so they also share its tabs: two sessions driving
the same tab at the same moment will step on each other. Give each session its
own tabs (tab_new), or its own Chrome profile (below).
Each session can also drive several browsers at once: load the extension
in each Chrome profile and they all pair to the same server, each under its own
profile name. Tools act on the active profile — pick it with --profile <name>
at startup or the profile_use tool at runtime.
Naming is automatic. Chrome won't tell an extension which profile it runs in, so
each install keeps a random id and the server names it: the first browser is
default, the next profile-2, then profile-3, and so on. Names are stored in
~/.chrome-mcp/profiles.json, so a browser keeps its name across restarts.
chrome_status lists every paired browser (with its active tab as a hint), and
profile_rename gives one a friendly name (profile-2 → work). To pin a name
yourself instead, type it into the extension's Options → Profile; that always wins.
Without --port, each server binds an ephemeral port (no conflict ever), but the
port changes every boot — so you'd re-pair the extension each time. Pin --port
plus --persist-token for a pair-once setup.
Windows
WSL2 is not required — native Windows works. One config change is, though:
on Windows npx is npx.cmd, a batch shim, and MCP hosts spawn the server
without a shell, which cannot execute a .cmd. So "command": "npx" fails to
start. Wrap it in cmd /c:
{
"mcpServers": {
"chrome-mcp": {
"command": "cmd",
"args": ["/c", "npx", "-y", "@mehmoodqureshi/chrome-mcp",
"--allow-domain", "example.com", "--enable-mutations",
"--persist-token"]
}
}
}Or from Claude Code: claude mcp add chrome-mcp -- cmd /c npx -y @mehmoodqureshi/chrome-mcp --allow-domain example.com
Everything else is the same — load %USERPROFILE%\chrome-mcp-extension and pair
as above.