aboutsummaryrefslogtreecommitdiff
path: root/mcp/README.md
diff options
context:
space:
mode:
Diffstat (limited to 'mcp/README.md')
-rw-r--r--mcp/README.md254
1 files changed, 254 insertions, 0 deletions
diff --git a/mcp/README.md b/mcp/README.md
new file mode 100644
index 0000000..d9c6c52
--- /dev/null
+++ b/mcp/README.md
@@ -0,0 +1,254 @@
+# pi-chrome-mcp
+
+**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 as a
+dependency 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
+
+21 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/pi-chrome-mcp.git
+cd pi-chrome-mcp
+./run.sh --install-only # fetches dependencies, then prints the extension path
+```
+
+**2. Load the companion extension** (this is the human step). In Chrome, open
+`chrome://extensions`, turn on **Developer mode**, click **Load unpacked**, and select the
+`extension` folder that the previous command created — it prints the full path. The folder is not
+in git: it is copied out of the installed pi-chrome.
+
+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 comes from pi-chrome; `run.sh` copies it out of `node_modules` so that updating dependencies
+cannot delete the folder Chrome is loading from.
+
+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/pi-chrome-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. Give pi an MCP client.**
+
+```bash
+pi install npm:pi-mcp-adapter
+```
+
+**2. Register this server** in `~/.pi/agent/mcp.json`, with the absolute path to `run.sh`:
+
+```json
+{
+ "mcpServers": {
+ "my-chrome": {
+ "command": "/absolute/path/to/pi-chrome-mcp/run.sh",
+ "approveTools": true
+ }
+ }
+}
+```
+
+**3. 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.
+
+Why the gate: pi has no permission prompts of its own, and the adapter's own approval remembers a
+grant only for identical arguments, which for a browser means a prompt on nearly every call. Keep
+`approveTools: true` as well — if the gate ever fails to load, the adapter asks per call and fails
+closed headless, rather than running unchecked.
+
+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 matter only when `PI_CHROME_PATH` points at a fork that
+supports them: `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
+
+pi-chrome is pinned in `package.json`. To move to a newer one, change the version, run
+`./run.sh --install-only`, and reload the extension at `chrome://extensions` if it reports that the
+extension changed.
+
+## Uninstall
+
+```bash
+claude mcp remove --scope user my-chrome # and delete the entry from Claude Desktop's config
+rm -rf /path/to/pi-chrome-mcp # the repo, its node_modules and ./extension
+```
+
+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. It is pinned by version in `package.json` and by hash in `package-lock.json`, so an
+install fetches exactly what was tested. This repository adds the MCP server around it and is MIT
+licensed too.