Skip to main content
Glama
sadewadee

chrome-profile-mcp

by sadewadee
README.md
# chrome-profile-mcp

Drive your **real** Chrome profiles (with existing logins, cookies, sessions) from Claude Code via [`@playwright/mcp --extension`](https://github.com/microsoft/playwright-mcp) + the Playwright Chrome Extension relay.

## Why this exists

Upstream `@playwright/mcp --extension` auto-discovers *which* Chrome profile to open — `Default` first, then numeric `Profile N`, preferring `last_used` — with **no knob to pin a specific profile**. If the browser extension is installed in several profiles, the relay tab can land in the wrong one (e.g. Diana's instead of jamu's).

This repo adds one env var, `PLAYWRIGHT_MCP_PROFILE_DIRECTORY`, via a small idempotent patch script, so each MCP server deterministically opens its own profile.

No extension code is modified. No credentials or tokens are stored here.

## How it works

1. `npm ci` installs pinned `@playwright/mcp@0.0.80` (which pulls `playwright-core`).
2. `npm run postinstall` runs `patch-profile-directory.mjs`, which rewrites one line in `node_modules/playwright-core/lib/coreBundle.js` (`createExtensionBrowser`):

```js
// before (upstream)
const profileDirectory = userDataDir ? await findPlaywrightExtensionProfile(userDataDir) : void 0;
// after (patched)
const profileDirectory = process.env.PLAYWRIGHT_MCP_PROFILE_DIRECTORY ?? (userDataDir ? await findPlaywrightExtensionProfile(userDataDir) : void 0); // PATCHED
```

3. Each MCP server runs the vendored CLI with its own token + profile:

```json
{
  "mcpServers": {
    "mcp-jamu": {
      "type": "stdio",
      "command": "node",
      "args": ["<repo>/node_modules/@playwright/mcp/cli.js", "--extension"],
      "env": {
        "PLAYWRIGHT_MCP_EXTENSION_TOKEN": "<token-from-that-profile>",
        "PLAYWRIGHT_MCP_PROFILE_DIRECTORY": "Profile 5"
      }
    }
  }
}
```

The token must equal that profile's extension `localStorage` `auth-token` (open `chrome-extension://mmlmfjhmonkocbjadbfplnigmagldckm/connect.html` in the target profile to view/regenerate it). Matching token → silent auto-connect; mismatch → `Invalid token provided.`; no token → manual tab picker.

## Setup

```bash
git clone <this-repo> ~/.chrome-mcp/extension-servers   # or anywhere
cd ~/.chrome-mcp/extension-servers
npm ci          # installs + patches (postinstall)
node patch-profile-directory.mjs   # re-run any time node_modules is reinstalled
```

Requirements: Google Chrome (stable channel path), [Playwright Extension](https://chromewebstore.google.com/detail/playwright-extension/mmlmfjhmonkocbjadbfplnigmagldckm) installed **in each target profile**, `@playwright/mcp@0.0.80` (pinned; the patch anchors to this bundle layout and fails loudly otherwise).

## Constraints / gotchas

- **Never set `--user-data-dir`** to the live Chrome home — a second copy fights `SingletonLock`. With defaults (`channel: chrome` → `~/Library/Application Support/Google/Chrome` on macOS) the relay attaches to the running Chrome session.
- **Never run two servers with the same/empty profile pin** — each needs its own relay port + connect tab.
- One profile = one token. Never commit tokens — use `.mcp.json` env (local, gitignored by clients) or your process manager's secret store.
- Patch is version-pinned: bumping `@playwright/mcp` requires re-verifying the anchor strings in `patch-profile-directory.mjs`.
- Tools surface as `browser_*` (`browser_navigate`, `browser_snapshot`, …), not `puppeteer_*`.

## Verify

```bash
# should print: Version 0.0.80
./node_modules/.bin/playwright-mcp --version
# should print the patched line
grep -n PLAYWRIGHT_MCP_PROFILE_DIRECTORY node_modules/playwright-core/lib/coreBundle.js
```

Then point an MCP client at the vendored CLI, call `browser_navigate https://example.com`, and confirm the visit lands in the pinned profile's `History` (copy the live `History` file first — Chrome locks it while running).

## Files

| File | Purpose |
|---|---|
| `package.json` | Exact-pinned `@playwright/mcp@0.0.80` + `postinstall` hook |
| `patch-profile-directory.mjs` | Idempotent patch script (the actual patch) |
| `.mcp.json.example` | Template server entries (tokens redacted) |
| `.gitignore` | Excludes `node_modules/` (patched bundle is regenerated) |

## License

MIT — patch script and docs. Upstream packages retain their own licenses (Apache-2.0).