aboutsummaryrefslogtreecommitdiff
path: root/mcp
diff options
context:
space:
mode:
authorIgor Soarez <igor@soarez.org>2026-10-15 23:11:41 +0100
committerIgor Soarez <igor@soarez.org>2026-10-15 23:11:41 +0100
commit2a3854c2e246d38636779c2c8dea7cd368f0fe4b (patch)
treedf18fb142a2589015fc83d1fd43ff6e3ad189798 /mcp
parentcc86d6114291305fcea1ed8b0904c8be0e633767 (diff)
mcp: pi-chrome-mcp joins the fork as sz-chrome-remote-extension, loading the fork's own extension
Diffstat (limited to 'mcp')
-rw-r--r--mcp/.gitignore2
-rw-r--r--mcp/README.md46
-rw-r--r--mcp/package-lock.json19
-rw-r--r--mcp/package.json1
-rwxr-xr-xmcp/run.sh25
-rwxr-xr-xmcp/server.mjs17
6 files changed, 29 insertions, 81 deletions
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 = (() => {