chrome-mcp

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-token

Everything 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-uploads permits upload_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 extensionrequired; 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-path

Then chrome://extensions → enable Developer modeLoad 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.

BadgeMeaning
green dotpaired and connected
yellow dotsconnecting
grey circlenot paired yet (no server has run, or no pairing file)
red exclamation marktoken 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-2work). 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.

On this page