From 2a3854c2e246d38636779c2c8dea7cd368f0fe4b Mon Sep 17 00:00:00 2001 From: Igor Soarez Date: Thu, 15 Oct 2026 23:11:41 +0100 Subject: mcp: pi-chrome-mcp joins the fork as sz-chrome-remote-extension, loading the fork's own extension --- FORK.md | 42 +++++++++++++++++++++++++++++++----------- mcp/.gitignore | 2 -- mcp/README.md | 46 ++++++++++++++++++++++++---------------------- mcp/package-lock.json | 19 ------------------- mcp/package.json | 1 - mcp/run.sh | 25 +------------------------ mcp/server.mjs | 17 ++++------------- 7 files changed, 60 insertions(+), 92 deletions(-) diff --git a/FORK.md b/FORK.md index cf10ecc..ad74b5c 100644 --- a/FORK.md +++ b/FORK.md @@ -1,14 +1,20 @@ # Fork notes -A fork of [tianrendong/pi-chrome](https://github.com/tianrendong/pi-chrome) that -lets pi on **other machines** drive one signed-in browser. It adds a remote mode -to the pi extension and a standalone broker for the browser's host. Without -`PI_CHROME_REMOTE` set, it behaves exactly like upstream. - -Not published to npm and not proposed upstream. +**sz-chrome-remote-extension**: driving a signed-in browser through its +companion extension, from other machines. A fork of +[tianrendong/pi-chrome](https://github.com/tianrendong/pi-chrome) that lets pi +on **other machines** drive one signed-in browser. It adds a remote mode to the +pi extension, a standalone broker for the browser's host, and `mcp/`, an MCP +server for the same tools. Without `PI_CHROME_REMOTE` set, the pi extension +behaves exactly like upstream. Until 2026-10 this repository was named +pi-chrome. + +Not published to npm and not proposed upstream. The package keeps upstream's +name, `pi-chrome`, in `package.json`: renaming it would conflict on every +rebase. ``` -origin https://git.soarez.net/sz/pi-chrome.git +origin https://git.soarez.net/sz/sz-chrome-remote-extension.git upstream https://github.com/tianrendong/pi-chrome.git ``` @@ -89,9 +95,22 @@ switching its tabs. ## MCP clients -[pi-chrome-mcp](https://git.soarez.net/sz/pi-chrome-mcp.git) serves these -tools to Claude Code, Claude Desktop and other MCP clients. Point its -`PI_CHROME_PATH` at this fork's `index.ts` to use remote mode. +`mcp/` serves these tools to Claude Code, Claude Desktop and other MCP clients; +its README has the setup. It loads the pi extension from this repository, +`../extensions/chrome-profile-bridge/index.ts`, so the server, the extension and +the broker always come from one commit. + +It was its own repository, pi-chrome-mcp, from 2026-09-25 (when it moved out of +this fork to stand alone, loading pi-chrome from npm) until 2026-10, when it +came back to keep everything that drives the browser through its extension in +one place. Its commits were replayed here with `git am --directory=mcp`, linear, +so they rebase like any fork commit. It touches no upstream file, so it never +conflicts. It keeps its own `package.json` and lockfile; `mcp/run.sh` installs +them into `mcp/node_modules`. + +Run it from a checkout of its own, not from pi's package clone +(`~/.pi/agent/git/...`): pi runs `git clean -fdx` there whenever it updates the +package, which deletes `mcp/node_modules`. ## Setup @@ -121,7 +140,8 @@ git push --force-with-lease origin master Then update the broker's checkout, restart it, and reload the extension in the browser. `/chrome doctor` reports a version mismatch until you do. -Expect conflicts in the places marked `FORK:` in `index.ts`: the bridge class's +Commits under `broker/` and `mcp/` never conflict. Expect conflicts in the +places marked `FORK:` in `index.ts`: the bridge class's bind, forward and status code (including `connectionStatus`, whose `/status` fetch carries the token), `session_start`, the remote prompt-section handler just ahead of upstream's primer, doctor, and `chrome_upload_file`. If upstream changes the extension↔bridge HTTP contract diff --git a/mcp/.gitignore b/mcp/.gitignore index d8c089f..ca3b858 100644 --- a/mcp/.gitignore +++ b/mcp/.gitignore @@ -1,5 +1,3 @@ node_modules/ .npm-cache/ .install-lock/ -extension/ -.extension-source diff --git a/mcp/README.md b/mcp/README.md index 51a53af..4bda454 100644 --- a/mcp/README.md +++ b/mcp/README.md @@ -1,18 +1,22 @@ # 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 as a -dependency rather than copied. pi-chrome gives them to the [pi](https://pi.dev) agent; this server +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 -21 tools: page snapshots with stable element ids, click, type, fill, scroll, keys, navigation, tab +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. @@ -39,22 +43,23 @@ Plus, over plain pi-chrome: **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 +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 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. +`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 comes from pi-chrome; `run.sh` copies it out of `node_modules` so that updating dependencies -cannot delete the folder Chrome is loading from. +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. @@ -105,7 +110,7 @@ Add it to `claude_desktop_config.json` with an absolute path, and restart Deskto ```json { "mcpServers": { - "chrome": { "command": "/absolute/path/to/pi-chrome-mcp/run.sh" } + "chrome": { "command": "/absolute/path/to/sz-chrome-remote-extension/mcp/run.sh" } } } ``` @@ -131,13 +136,13 @@ which adds an entry like: { "mcpServers": { "my-chrome": { - "command": "/absolute/path/to/pi-chrome-mcp/run.sh" + "command": "/absolute/path/to/sz-chrome-remote-extension/mcp/run.sh" } } } ``` -`pi mcp list` should then show `my-chrome: connected, 21 tools`. +`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 @@ -212,8 +217,7 @@ Nothing listens on the network unless you ask for `--http`. | `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 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. @@ -237,15 +241,15 @@ This drives a browser that is signed in as you, so treat it accordingly: ## 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. +`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/pi-chrome-mcp # the repo, its node_modules and ./extension +rm -rf /path/to/sz-chrome-remote-extension # the checkout, the extension and mcp/node_modules ``` Then remove "Pi Chrome Connector" at `chrome://extensions`. @@ -255,6 +259,4 @@ Then remove "Pi Chrome Connector" at `chrome://extensions`. 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. +MIT licensed. This directory adds the MCP server around it and is MIT licensed too. diff --git a/mcp/package-lock.json b/mcp/package-lock.json index ffe8520..256f320 100644 --- a/mcp/package-lock.json +++ b/mcp/package-lock.json @@ -11,7 +11,6 @@ "dependencies": { "@modelcontextprotocol/sdk": "1.30.0", "jiti": "2.7.0", - "pi-chrome": "0.15.51", "typebox": "1.3.7" } }, @@ -860,24 +859,6 @@ "url": "https://opencollective.com/express" } }, - "node_modules/pi-chrome": { - "version": "0.15.51", - "resolved": "https://registry.npmjs.org/pi-chrome/-/pi-chrome-0.15.51.tgz", - "integrity": "sha512-CUMBJmtSbjjkcukKdhPOyDXZHal+acnl7BbtU/82qj8dgSjFLrpv/3ckLx0+ueBQ72C5C/0mEdimFBmBfXuIhw==", - "license": "MIT", - "peerDependencies": { - "@earendil-works/pi-coding-agent": "*", - "typebox": "*" - }, - "peerDependenciesMeta": { - "@earendil-works/pi-coding-agent": { - "optional": true - }, - "typebox": { - "optional": true - } - } - }, "node_modules/pkce-challenge": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.1.tgz", diff --git a/mcp/package.json b/mcp/package.json index 8fddfa3..ad0ae4d 100644 --- a/mcp/package.json +++ b/mcp/package.json @@ -7,7 +7,6 @@ "dependencies": { "@modelcontextprotocol/sdk": "1.30.0", "jiti": "2.7.0", - "pi-chrome": "0.15.51", "typebox": "1.3.7" } } diff --git a/mcp/run.sh b/mcp/run.sh index 27c8ad9..6fca661 100755 --- a/mcp/run.sh +++ b/mcp/run.sh @@ -53,31 +53,8 @@ if ! cmp -s "${here}/package-lock.json" "${stamp}" 2>/dev/null; then trap - EXIT fi -# Chrome loads the companion extension from ./extension rather than from node_modules, where it -# ships: npm ci deletes that whole tree when dependencies change, and an unpacked extension whose -# folder disappears is disabled until someone loads it again by hand. This copy is refreshed only -# when pi-chrome's version of it differs, so a reinstall that changes nothing leaves Chrome alone. -# Compared by content, not by the manifest's version: two releases could ship the same version of -# the extension with different files, and a copy that silently stayed behind would be debugged as a -# server bug. -packaged="${here}/node_modules/pi-chrome/extensions/chrome-profile-bridge/browser-extension" -if [ -d "${packaged}" ]; then - digest="$(cd "${packaged}" && find . -type f -print | sort | xargs cat | cksum)" - # Kept beside the folder rather than in it: Chrome loads that folder, and nothing there should - # be anything but the extension. - stamp_ext="${here}/.extension-source" - if [ "${digest}" != "$(cat "${stamp_ext}" 2>/dev/null || true)" ]; then - echo "pi-chrome-mcp: updating ./extension; reload it at chrome://extensions" >&2 - rm -rf "${here}/extension.new" - cp -R "${packaged}" "${here}/extension.new" - rm -rf "${here}/extension" - mv "${here}/extension.new" "${here}/extension" - echo "${digest}" > "${stamp_ext}" - fi -fi - if [ "${1:-}" = "--install-only" ]; then - echo "pi-chrome-mcp: ready. Load this folder in chrome://extensions: ${here}/extension" >&2 + echo "pi-chrome-mcp: ready. The companion extension to load in chrome://extensions: $(cd "${here}/../extensions/chrome-profile-bridge/browser-extension" && pwd)" >&2 exit 0 fi exec node "${here}/server.mjs" "$@" diff --git a/mcp/server.mjs b/mcp/server.mjs index c6e70f1..42da739 100755 --- a/mcp/server.mjs +++ b/mcp/server.mjs @@ -6,8 +6,8 @@ // MCP client drives the browser through pi-chrome's companion extension, with one implementation // of the tools, the page snapshots and the bridge client rather than a second copy to drift. // -// pi-chrome itself is a dependency, loaded from node_modules; PI_CHROME_PATH points at another -// copy (a fork, a checkout) instead. +// pi-chrome itself is this repository's own extensions/chrome-profile-bridge, the directory above +// this one; PI_CHROME_PATH points at another copy instead. // // Two transports: // @@ -46,17 +46,8 @@ const log = (message) => console.error(`[${NAME}] ${message}`); // Base64 grows a file by a third; this keeps the encoded image under Claude Desktop's 1MB limit. const MAX_INLINE_IMAGE_BYTES = 700 * 1024; -// pi-chrome's tool layer: the dependency by default, or any other copy of it. -const EXTENSION = (() => { - const override = process.env.PI_CHROME_PATH?.trim(); - if (override) return override; - try { - return fileURLToPath(import.meta.resolve("pi-chrome/extensions/chrome-profile-bridge/index.ts")); - } catch { - log("cannot find pi-chrome. Run ./run.sh (or npm ci) here, or set PI_CHROME_PATH to a copy of its index.ts"); - process.exit(2); - } -})(); +// pi-chrome's tool layer: the one in this repository by default, or any other copy of it. +const EXTENSION = process.env.PI_CHROME_PATH?.trim() || resolve(here, "../extensions/chrome-profile-bridge/index.ts"); // --http HOST:PORT, or unset for stdio. const HTTP = (() => { -- cgit v1.2.3