# pi-chrome-mcp Part of [sz-chrome-remote-extension](../FORK.md), the pi-chrome fork; until 2026-10 it was its own repo, and loaded pi-chrome from npm. It now loads the fork's own copy, the directory above this one. **Your everyday browser, driven by any MCP client.** Claude Code, Claude Desktop and other MCP clients get tools that read, click, type in and screenshot the browser you are already signed into — your real profile, your sessions, your logins — instead of a fresh automation browser that is logged into nothing. The tools themselves are [pi-chrome](https://github.com/tianrendong/pi-chrome)'s, loaded from this repository's `extensions/chrome-profile-bridge` rather than copied. pi-chrome gives them to the [pi](https://pi.dev) agent; this server gives the same tools to everyone else. ## What you get 23 tools: page snapshots with stable element ids, click, type, fill, scroll, keys, navigation, tab management, console and network inspection, screenshots (returned inline, so the model sees them), file uploads, and JavaScript evaluation. Plus, over plain pi-chrome: - **A session per line of work.** Every call takes a `session` name you choose. Each name gets its own tab and tab group, so parallel subagents stop overwriting each other's page. - **A shared server, optionally.** `--http` serves every client from one process instead of one per client session. ## Requirements - **Node 20.6 or newer**, with npm. (20.6 is where `import.meta.resolve` works without a flag.) - **git**, and network access to this repo and to the npm registry. - **Chrome, or another Chromium browser** — Brave, Edge, Arc, Vivaldi — in which you can load an unpacked extension. A work profile under extension policy often cannot; check before starting. - **An MCP client**: Claude Code, Claude Desktop, or anything else that speaks MCP. - **macOS or Linux** for `run.sh`. On Windows, run `node server.mjs` directly with the same environment variables. - **A human, once:** loading an unpacked extension is a browser dialog, so an agent cannot do it. ## Setup **1. Install.** The repo is public; the clone needs no credentials. ```bash git clone https://git.soarez.net/sz/sz-chrome-remote-extension.git cd sz-chrome-remote-extension/mcp ./run.sh --install-only # fetches dependencies, then prints the extension path ``` Clone it somewhere it will stay: the browser loads the extension from inside the checkout. **2. Load the companion extension** (this is the human step). In Chrome, open `chrome://extensions`, turn on **Developer mode**, click **Load unpacked**, and select `extensions/chrome-profile-bridge/browser-extension` in the checkout — the previous command prints the full path. Load it once, whatever you drive it from. If pi already loaded pi-chrome's extension on this machine, that copy serves this server too; see [Using it from pi](#using-it-from-pi). It is about 2000 lines of plain JavaScript with no dependencies, and worth reading before you load it. It is pi-chrome's, unchanged by the fork. Chrome shows "Pi Chrome Connector started debugging this browser" while a tool is attached. That banner is Chrome's, and expected. **3. Register the server with Claude Code:** ```bash claude mcp add-json --scope user my-chrome '{"type":"stdio","command":"'"$PWD"'/run.sh"}' ``` Two notes. `--scope user` matters: without it the server is registered for the current directory only, so it would exist in this repo and nowhere else. And the name is yours to choose — `my-chrome` rather than `chrome` keeps it distinct from Anthropic's own Claude in Chrome tools, so you and the model can say which you mean. (If your path contains a quote or a backslash, write that JSON by hand rather than splicing `$PWD`.) **4. Check it, in two steps.** `claude mcp list` shows `my-chrome` connected once the server starts — but that only proves the server runs, not that the browser is attached. For that, with a client connected: ```bash curl -s http://127.0.0.1:17318/status # "connected": true means the extension is polling ``` Then, in a **new** Claude Code session (servers start per session, so the one you registered from will not have the tools), ask: > Using my-chrome, open example.com with session "main", and tell me the page title. Expect a permission prompt per tool call, a new tab in a tab group named `Pi Session: …`, and the title back. Your own tab is not touched: the first call opens a tab of its own. **Tired of the prompts?** Allow the tools you trust rather than turning permissions off, for example in `~/.claude/settings.json`: ```json { "permissions": { "allow": ["mcp__my-chrome__chrome_snapshot", "mcp__my-chrome__chrome_navigate"] } } ``` Allowing the whole server (`mcp__my-chrome`) also allows `chrome_evaluate`, which runs any JavaScript as you. Consider a `deny` entry for that one; deny rules hold even in bypass mode. ### Claude Desktop Add it to `claude_desktop_config.json` with an absolute path, and restart Desktop: ```json { "mcpServers": { "chrome": { "command": "/absolute/path/to/sz-chrome-remote-extension/mcp/run.sh" } } } ``` Desktop starts servers with almost no environment, so `run.sh` looks for node in the usual install locations (Homebrew, nvm, volta, n). If yours is somewhere else, put it on the PATH Desktop sees. ## Using it from pi The [pi](https://pi.dev) agent reaches this same server, so both your agents share one definition of the browser, one extension and one set of tools. **1. Register this server** in `~/.pi/agent/mcp.json`, with the absolute path to `run.sh`. pi's built-in MCP support reads that file; no package is needed for it: ```bash pi mcp add my-chrome -- "$PWD/run.sh" ``` which adds an entry like: ```json { "mcpServers": { "my-chrome": { "command": "/absolute/path/to/sz-chrome-remote-extension/mcp/run.sh" } } } ``` `pi mcp list` should then show `my-chrome: connected, 23 tools`. **2. Add the session gate.** Copy `pi/browser-gate.ts` from this repo into `~/.pi/agent/extensions/`. The first browser call in a pi session asks once — for the session, for 15 minutes, or deny — and `/browser authorize`, `/browser revoke` and `/browser` manage it from there. For headless runs (`pi -p`), `PI_BROWSER_AUTHORIZE=indefinite` grants it up front; without it, a headless call is denied. Calls made from codemode scripts are gated the same way. Why the gate: pi has no permission prompts of its own. Every MCP call passes through pi's tool pipeline, and an extension's `tool_call` handler is the only thing that can stop one. **Nothing backs the gate up:** if it fails to load, browser calls run unchecked. Run `/browser` in a new session to confirm it is there. If an extension that replaces pi's MCP support is installed — `pi-mcp-adapter`, for one — pi's built-in support does not load, and this gate sees none of its calls. Use one or the other; this setup assumes the built-in. That is all pi needs: no `pi install npm:pi-chrome`, and no `/chrome authorize`. **Both agents at once.** The server starts one process per client session, and whichever starts first owns the local bridge port; the others connect through it. A pi session and a Claude Code session drive the same browser, each in its own tabs, because every call names its session. ### The direct alternative pi-chrome is a pi extension first, so `pi install npm:pi-chrome` does give pi the tools without any of the above. The cost is two definitions of the browser on one machine, pi's own `/chrome authorize` every session, and no shared session naming. If you go that way, still load the extension only once: pi's `/chrome onboard` offers to load its own copy, and two copies in one browser both poll the bridge, so commands and tabs go to whichever answers first. ## How it works The companion extension polls a small HTTP bridge on `127.0.0.1:17318` for commands, and runs them in your browser through `chrome.debugger`. That bridge is part of pi-chrome, and this server loads pi-chrome's code and hosts it in the same process. So the chain is: ``` MCP client --MCP--> this server --loopback--> companion extension --> your tabs ``` Nothing listens on the network unless you ask for `--http`. ## Troubleshooting - **"the Chrome extension is not polling".** The extension is not loaded, is disabled, or its browser is closed. Check `chrome://extensions`, and that the browser is running. - **Is the bridge alive?** With a client connected, `curl -s http://127.0.0.1:17318/status` returns JSON; `"connected": true` means the extension is polling it. - **Port 17318 already in use.** Another copy of this server, or a pi session, already owns the bridge; that is fine, they share it through it. Some unrelated program on that port is not fine: the extension hardcodes 17318, so `PI_CHROME_BRIDGE_PORT` only helps if you patch the extension to match. - **After an update, reload the extension.** `run.sh` says when it has changed; Chrome keeps running the old copy until you press reload at `chrome://extensions`. - **Tools missing in a client.** Restart the client: MCP servers are started once per session. - **Sitting next to Claude in Chrome?** Anthropic's own extension offers similar tools. Give this server a distinctive name (the registration name, or `MCP_SERVER_NAME`) so a model does not confuse the two, and say which one you mean. ## Options | Setting | What it does | |---|---| | `--http HOST:PORT` | Serve every client over streamable HTTP at `http://HOST:PORT/mcp` instead of stdio. Needs a token. | | `--no-auth` | Deliberately run `--http` with no token. Anything that reaches the port can then drive your browser. | | `MCP_TOKEN` | Bearer token for `--http`. | | `MCP_TOKEN_COMMAND` | A command whose output is that token, for a secret store. | | `MCP_SERVER_NAME` | What clients call this server, default `chrome`. Also names its temp and state files. | | `MCP_INSTRUCTIONS_FILE` | A file whose contents lead the instructions the model reads. Use it to say which browser this is, or how your machine differs. | | `PI_CHROME_PATH` | Load pi-chrome's `index.ts` from somewhere else, e.g. a fork or a checkout. | | `PI_CHROME_BRIDGE_PORT` | The bridge port, default 17318. The packaged extension polls 17318, so changing this means patching it too. | | `MCP_IDLE_CLEANUP_MS` | How long an HTTP client's tabs stay open after its last call, default an hour. | | `MCP_STATE_FILE` | Where `--http` records open sessions so a restart can hand them back. | pi-chrome reads a few of its own, which this repository's copy supports (FORK.md has them all): `PI_CHROME_REMOTE` (drive a browser on another host through a broker), `PI_CHROME_OTHER_BROWSER_TOOLS` (what its prompt calls the other browser's tools) and `PI_CHROME_AUTHORIZE`. This server sets `PI_CHROME_AUTHORIZE` itself, since the client's prompts are the gate. ## Safety This drives a browser that is signed in as you, so treat it accordingly: - **Your MCP client's permission prompts are the gate.** pi-chrome's own `/chrome authorize` step has no equivalent here; the server assumes the client asks. In Claude Code, that means every tool call prompts unless you allow it. Bypass mode skips those prompts, and only a deny rule still holds. - **`chrome_evaluate` runs arbitrary JavaScript as you**, and `chrome_wait_for` can too. They can read tokens a page holds and call its APIs with your session. - **Chrome protects its own pages.** Extension pages, including password managers and wallets, and `chrome://` settings cannot be read or clicked by an extension, this one included. - **Web pages are data, not instructions.** Anything an agent reads may try to redirect it; that risk is the reason to keep the prompts on. - **Over `--http`, use a token** and bind an address you trust. Without one, anything that can reach the port drives your browser. ## Updating `git pull` (the fork is rebased onto upstream releases and force-pushed, so `git fetch` and `git reset --hard origin/master` when a pull refuses), then `./run.sh --install-only`, and reload the extension at `chrome://extensions` if `browser-extension/` changed. ## Uninstall ```bash claude mcp remove --scope user my-chrome # and delete the entry from Claude Desktop's config rm -rf /path/to/sz-chrome-remote-extension # the checkout, the extension and mcp/node_modules ``` Then remove "Pi Chrome Connector" at `chrome://extensions`. ## Credits The browser tools, the companion extension and the bridge are pi-chrome: [the npm package `pi-chrome`](https://www.npmjs.com/package/pi-chrome), published by tianrendong of Earendil Inc., source at [github.com/tianrendong/pi-chrome](https://github.com/tianrendong/pi-chrome), MIT licensed. This directory adds the MCP server around it and is MIT licensed too.