mcp-browser-extension 0.9.25 → 0.9.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +32 -35
- package/SETUP.md +20 -20
- package/dist/shared/protocol.d.ts +1 -1
- package/dist/shared/tab-border.d.ts +1 -1
- package/dist/shared/tab-border.js +1 -1
- package/dist/src/access.js +2 -1
- package/dist/src/auto-update.d.ts +6 -6
- package/dist/src/auto-update.js +9 -8
- package/dist/src/bridge/auth.d.ts +3 -3
- package/dist/src/bridge/auth.js +5 -4
- package/dist/src/bridge/datadir.d.ts +1 -1
- package/dist/src/bridge/datadir.js +1 -1
- package/dist/src/bridge/evict.d.ts +7 -7
- package/dist/src/bridge/evict.js +13 -13
- package/dist/src/bridge/peer.d.ts +4 -4
- package/dist/src/bridge/peer.js +4 -4
- package/dist/src/bridge/server.d.ts +6 -6
- package/dist/src/bridge/server.js +11 -11
- package/dist/src/cli.d.ts +1 -1
- package/dist/src/cli.js +13 -12
- package/dist/src/compat.d.ts +4 -0
- package/dist/src/compat.js +28 -0
- package/dist/src/config.d.ts +4 -4
- package/dist/src/config.js +27 -26
- package/dist/src/extension-command.js +3 -3
- package/dist/src/extension-install.d.ts +4 -4
- package/dist/src/extension-install.js +8 -6
- package/dist/src/mcp/log.js +2 -2
- package/dist/src/mcp/server.d.ts +1 -1
- package/dist/src/mcp/server.js +1 -1
- package/dist/src/mcp/tools.js +1 -1
- package/dist/src/native-host.d.ts +1 -1
- package/dist/src/native-host.js +1 -1
- package/dist/src/telemetry.d.ts +3 -3
- package/dist/src/telemetry.js +7 -6
- package/docs/BLUEPRINT.md +11 -11
- package/extension-dist/background.js +6 -6
- package/extension-dist/manifest.json +1 -1
- package/extension-dist/options.html +1 -1
- package/package.json +24 -5
package/README.md
CHANGED
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
# MCP Browser Extension
|
|
2
2
|
|
|
3
|
-
The unscoped distribution of `@mehmoodqureshi/chrome-mcp`, with the same implementation and version. Both package names remain supported.
|
|
4
|
-
|
|
5
3
|
[](https://github.com/Mehmoodqureshi/mcp-browser-extension/actions/workflows/ci.yml)
|
|
6
4
|
[](https://www.npmjs.com/package/mcp-browser-extension)
|
|
7
5
|
[](LICENSE)
|
|
8
6
|
|
|
9
7
|
**Website and docs:** [mcp-browser-extension.com](https://mcp-browser-extension.com) ·
|
|
10
8
|
[Quickstart](https://mcp-browser-extension.com/docs/quickstart) ·
|
|
11
|
-
[All
|
|
9
|
+
[All 42 tools](https://mcp-browser-extension.com/docs/tools) ·
|
|
12
10
|
[Install the extension from the Chrome Web Store](https://chromewebstore.google.com/detail/mcp-browser-extension/jelfhdlkhbfmlpbghoeaepijllcnplgh)
|
|
13
11
|
|
|
14
12
|
**Let Claude use the Chrome you are already logged into.** Not a fresh
|
|
@@ -24,8 +22,7 @@ localhost WebSocket server and drives the browser you already have open, through
|
|
|
24
22
|
other MCP host.
|
|
25
23
|
|
|
26
24
|
MCP Browser Extension has two parts: the extension, and a small MCP server you run
|
|
27
|
-
with `npx` (npm package `mcp-browser-extension
|
|
28
|
-
names predate the rename and stay so existing setups keep working). The extension
|
|
25
|
+
with `npx` (npm package and command: `mcp-browser-extension`). The extension
|
|
29
26
|
comes from the
|
|
30
27
|
[Chrome Web Store](https://chromewebstore.google.com/detail/mcp-browser-extension/jelfhdlkhbfmlpbghoeaepijllcnplgh)
|
|
31
28
|
or loaded unpacked.
|
|
@@ -103,7 +100,7 @@ is the exact file the agent follows. The manual steps are below.
|
|
|
103
100
|
<summary><b>Claude Code (terminal)</b> — one command, no config file to find</summary>
|
|
104
101
|
|
|
105
102
|
```bash
|
|
106
|
-
claude mcp add
|
|
103
|
+
claude mcp add mcp-browser-extension -s user -- \
|
|
107
104
|
npx -y mcp-browser-extension@latest \
|
|
108
105
|
--allow-domain example.com --enable-mutations --persist-token
|
|
109
106
|
```
|
|
@@ -127,7 +124,7 @@ with `/mcp` inside a session — no restart needed.
|
|
|
127
124
|
```jsonc
|
|
128
125
|
{
|
|
129
126
|
"mcpServers": {
|
|
130
|
-
"
|
|
127
|
+
"mcp-browser-extension": {
|
|
131
128
|
"command": "npx",
|
|
132
129
|
"args": ["-y", "mcp-browser-extension@latest",
|
|
133
130
|
"--allow-domain", "example.com", "--enable-mutations",
|
|
@@ -155,8 +152,8 @@ stricter default described next.
|
|
|
155
152
|
|
|
156
153
|
Without `--persist-token` a fresh token is minted every boot (the secure
|
|
157
154
|
default), which means re-pairing the extension on each restart. With it, the
|
|
158
|
-
token is stored 0600 at `~/.
|
|
159
|
-
keepalive auto-reconnects with no manual step. `
|
|
155
|
+
token is stored 0600 at `~/.mcp-browser-extension/token` and reused; the extension's
|
|
156
|
+
keepalive auto-reconnects with no manual step. `MCP_BROWSER_EXTENSION_TOKEN` pins the token
|
|
160
157
|
explicitly (and is never written to disk).
|
|
161
158
|
|
|
162
159
|
**2. Install the extension** — **required**; the server can drive nothing without it.
|
|
@@ -177,8 +174,8 @@ or paste.
|
|
|
177
174
|
<summary>Developers: load it unpacked instead</summary>
|
|
178
175
|
|
|
179
176
|
The extension also ships inside the npm package, and every time the server boots
|
|
180
|
-
it copies it to `~/
|
|
181
|
-
Windows; `
|
|
177
|
+
it copies it to `~/mcp-browser-extension` (`%USERPROFILE%\mcp-browser-extension` on
|
|
178
|
+
Windows; `MCP_BROWSER_EXTENSION_EXTENSION_DIR` moves it). `chrome://extensions` → enable
|
|
182
179
|
**Developer mode** → **Load unpacked** → pick that folder. It pairs itself from a
|
|
183
180
|
`pairing.json` the server writes there, and the server refreshes the files on each
|
|
184
181
|
boot (never with an older build) so it reloads itself within 30 seconds.
|
|
@@ -211,7 +208,7 @@ stays in the toolbar. Hover it for the status in words.
|
|
|
211
208
|
Manual fallback (the helper is turned off with `--no-native-host`, or Connect
|
|
212
209
|
reports a problem): run `npx -y mcp-browser-extension@latest --print-pairing`,
|
|
213
210
|
open the extension's **Settings**, and paste the `port` + `token` from
|
|
214
|
-
`~/.
|
|
211
|
+
`~/.mcp-browser-extension/handshake.json`.
|
|
215
212
|
|
|
216
213
|
### Extension missing after restarting Chrome
|
|
217
214
|
|
|
@@ -234,7 +231,7 @@ npx -y mcp-browser-extension@latest extension repair --apply
|
|
|
234
231
|
```
|
|
235
232
|
|
|
236
233
|
The repair backs up the original preferences under
|
|
237
|
-
`~/.
|
|
234
|
+
`~/.mcp-browser-extension/extension-repair-backups/`, removes only the confirmed obsolete
|
|
238
235
|
registration, and preserves the current extension and its saved settings. It
|
|
239
236
|
refuses to write while a Chromium browser is running. Reopen Chrome afterwards.
|
|
240
237
|
Use `--user-data-dir <directory>` for a custom browser data location, or
|
|
@@ -280,7 +277,7 @@ at startup or the `profile_use` tool at runtime.
|
|
|
280
277
|
Naming is automatic. Chrome won't tell an extension which profile it runs in, so
|
|
281
278
|
each install keeps a random id and the server names it: the first browser is
|
|
282
279
|
`default`, the next `profile-2`, then `profile-3`, and so on. Names are stored in
|
|
283
|
-
`~/.
|
|
280
|
+
`~/.mcp-browser-extension/profiles.json`, so a browser keeps its name across restarts.
|
|
284
281
|
`chrome_status` lists every paired browser (with its active tab as a hint), and
|
|
285
282
|
`profile_rename` gives one a friendly name (`profile-2` → `work`). To pin a name
|
|
286
283
|
yourself instead, type it into the extension's Options → Profile; that always wins.
|
|
@@ -299,7 +296,7 @@ start. Wrap it in `cmd /c`:
|
|
|
299
296
|
```jsonc
|
|
300
297
|
{
|
|
301
298
|
"mcpServers": {
|
|
302
|
-
"
|
|
299
|
+
"mcp-browser-extension": {
|
|
303
300
|
"command": "cmd",
|
|
304
301
|
"args": ["/c", "npx", "-y", "mcp-browser-extension@latest",
|
|
305
302
|
"--allow-domain", "example.com", "--enable-mutations",
|
|
@@ -309,14 +306,14 @@ start. Wrap it in `cmd /c`:
|
|
|
309
306
|
}
|
|
310
307
|
```
|
|
311
308
|
|
|
312
|
-
Or from Claude Code: `claude mcp add
|
|
309
|
+
Or from Claude Code: `claude mcp add mcp-browser-extension -- cmd /c npx -y mcp-browser-extension@latest --allow-domain example.com`
|
|
313
310
|
|
|
314
311
|
For normal use, install the extension from the Chrome Web Store as described
|
|
315
312
|
above, then click **Connect**. Do not use **Load unpacked** as part of the normal
|
|
316
313
|
Windows setup. The unpacked developer build has the same extension ID as the
|
|
317
314
|
Store build, so loading it can replace the Store installation; Chrome may then
|
|
318
|
-
disable the non-Store copy. If you are developing
|
|
319
|
-
`%USERPROFILE%\
|
|
315
|
+
disable the non-Store copy. If you are developing mcp-browser-extension, load
|
|
316
|
+
`%USERPROFILE%\mcp-browser-extension` in a separate Chrome profile.
|
|
320
317
|
|
|
321
318
|
The tools cover tabs, navigation, interaction (`click`/`type`/`press`/`hover`/
|
|
322
319
|
`scroll`/`select_option`), reads (`get_text`/`get_html`/`screenshot`/`eval`/`wait_for`),
|
|
@@ -349,7 +346,7 @@ It prints a connector URL like `https://<random>.trycloudflare.com/mcp/<key>`:
|
|
|
349
346
|
- **Claude Code elsewhere / agents:** use `<url>/mcp` with `Authorization: Bearer <key>`;
|
|
350
347
|
the exact `claude mcp add` line is printed too.
|
|
351
348
|
|
|
352
|
-
The key (32 random bytes, kept in `~/.
|
|
349
|
+
The key (32 random bytes, kept in `~/.mcp-browser-extension/remote-key`) guards every
|
|
353
350
|
request; a wrong or missing key gets a bare 404. Anyone holding the URL can drive
|
|
354
351
|
your browser within the allowed sites, so keep it private and keep the allowlist
|
|
355
352
|
tight. `--rotate-remote-key` cuts every client off. The quick-tunnel URL changes on
|
|
@@ -557,7 +554,7 @@ for a run that was never going to print a PDF or upload a file.
|
|
|
557
554
|
`--unsafe-enable-eval`.
|
|
558
555
|
- An unknown name fails at startup and prints the catalog, so a typo can never
|
|
559
556
|
quietly drop `click` from the surface.
|
|
560
|
-
- `
|
|
557
|
+
- `mcp-browser-extension --help` prints the full catalog of 42 names to pick from.
|
|
561
558
|
|
|
562
559
|
## Status
|
|
563
560
|
|
|
@@ -569,7 +566,7 @@ full-page and element capture), and focuses newly opened tabs by default. 111
|
|
|
569
566
|
automated tests + a gated headed extension smoke.
|
|
570
567
|
|
|
571
568
|
v0.2.0 — all six build phases complete and green. End-to-end working:
|
|
572
|
-
`npx
|
|
569
|
+
`npx mcp-browser-extension` ⇄ bridge ⇄ extension ⇄ your real Chrome, with a Playwright CDP
|
|
573
570
|
fallback. v0.2 adds the accessibility `snapshot` + element refs, auto-wait,
|
|
574
571
|
cookies/storage/`select_option`, trusted input (`chrome.debugger`), a toolbar
|
|
575
572
|
status badge, and a stable pairing token (`--persist-token`).
|
|
@@ -616,11 +613,11 @@ status badge, and a stable pairing token (`--persist-token`).
|
|
|
616
613
|
`eval` off, downloads off, mutating tools off. Opt in explicitly:
|
|
617
614
|
|
|
618
615
|
```
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
616
|
+
mcp-browser-extension --allow-domain example.com --enable-mutations
|
|
617
|
+
mcp-browser-extension --policy ./policy.json # see policy.example.json
|
|
618
|
+
mcp-browser-extension --unsafe-all-domains # loud footgun
|
|
619
|
+
mcp-browser-extension --enable-observers # console/network/dialog capture (patches page globals)
|
|
620
|
+
mcp-browser-extension --redact # scrub secret-shaped strings out of page reads
|
|
624
621
|
```
|
|
625
622
|
|
|
626
623
|
**Allowing a site without a restart.** When a call is refused because its site
|
|
@@ -628,7 +625,7 @@ is not allowed, the extension's toolbar icon shows a `?`. Click it: the popup
|
|
|
628
625
|
lists the blocked site with an **Allow** button, shows whether the site you are on
|
|
629
626
|
is allowed (with its own Allow), and lists the allowed sites. The Options page
|
|
630
627
|
(Settings in the popup) also has a box to allow any other site. An allowed site applies at once to every session sharing that Chrome, and
|
|
631
|
-
is saved to `~/.
|
|
628
|
+
is saved to `~/.mcp-browser-extension/allowed-sites.json` so it survives restarts. Sites
|
|
632
629
|
allowed there can be removed there; sites from `--allow-domain` stay until the
|
|
633
630
|
flags change. The catch-all `*` can only come from `--unsafe-all-domains`. The
|
|
634
631
|
agent cannot approve a site itself: MCP Browser Extension never reads or drives the
|
|
@@ -656,12 +653,12 @@ policy verdict (`allowed`/`denied`), how long it took, how many bytes came back,
|
|
|
656
653
|
and how many secrets were scrubbed — so "what did the agent do in my browser" has
|
|
657
654
|
an answer after the fact.
|
|
658
655
|
|
|
659
|
-
The per-boot 256-bit token in `~/.
|
|
656
|
+
The per-boot 256-bit token in `~/.mcp-browser-extension/handshake.json` (mode 0600) is the
|
|
660
657
|
only trust boundary; it is never written to stdout/stderr. On POSIX the mode is
|
|
661
658
|
re-verified after every write and the server **fails closed** if the file ends up
|
|
662
659
|
group/other-accessible. Windows has no such bits — `chmod` there only toggles the
|
|
663
660
|
read-only attribute — so the check is skipped and the token's confidentiality
|
|
664
|
-
rests on the per-user ACL of `%USERPROFILE%\.
|
|
661
|
+
rests on the per-user ACL of `%USERPROFILE%\.mcp-browser-extension`.
|
|
665
662
|
|
|
666
663
|
## Updates
|
|
667
664
|
|
|
@@ -671,9 +668,9 @@ npm when it starts: if a newer version is published, it starts that version in
|
|
|
671
668
|
its place, with the same arguments, so an MCP config written months ago still
|
|
672
669
|
runs the newest release. The check gives up after 1.5 s when you are offline,
|
|
673
670
|
and a copy run from a git checkout never updates itself. The new server also
|
|
674
|
-
refreshes the unpacked extension in `~/
|
|
671
|
+
refreshes the unpacked extension in `~/mcp-browser-extension`, which reloads
|
|
675
672
|
itself; the Chrome Web Store copy updates through Chrome as usual. Turn it off
|
|
676
|
-
with `--no-auto-update` or `
|
|
673
|
+
with `--no-auto-update` or `MCP_BROWSER_EXTENSION_AUTO_UPDATE=0`.
|
|
677
674
|
|
|
678
675
|
## Telemetry
|
|
679
676
|
|
|
@@ -681,7 +678,7 @@ The MCP Browser Extension **server** (not the extension) sends anonymous usage s
|
|
|
681
678
|
project can see how many installs are active, which versions and platforms are
|
|
682
679
|
in use, and which tools fail most. A notice is printed the first time it runs.
|
|
683
680
|
|
|
684
|
-
What is sent: a random install id (kept in `~/.
|
|
681
|
+
What is sent: a random install id (kept in `~/.mcp-browser-extension/telemetry.json`), the
|
|
685
682
|
server version, OS, CPU architecture and Node major version, whether the
|
|
686
683
|
session owns the bridge port or shares it, how many browsers are paired, the
|
|
687
684
|
paired extension's version and whether it is the store or an unpacked copy, and
|
|
@@ -709,7 +706,7 @@ The **browser extension sends nothing** — it only ever talks to `127.0.0.1`.
|
|
|
709
706
|
Turn it off with any of:
|
|
710
707
|
|
|
711
708
|
```bash
|
|
712
|
-
|
|
709
|
+
MCP_BROWSER_EXTENSION_TELEMETRY=0 # or false / off
|
|
713
710
|
DO_NOT_TRACK=1
|
|
714
711
|
--no-telemetry # server flag
|
|
715
712
|
```
|
|
@@ -749,11 +746,11 @@ for local development; do not load this unpacked copy in the same Chrome profile
|
|
|
749
746
|
as the Store extension. On Windows, Chrome may disable a non-Store build.
|
|
750
747
|
|
|
751
748
|
`extension/` builds (esbuild) to `extension-dist/`, loaded via
|
|
752
|
-
`chrome://extensions` → **Load unpacked** → select `~/
|
|
749
|
+
`chrome://extensions` → **Load unpacked** → select `~/mcp-browser-extension`, the
|
|
753
750
|
mirror the server refreshes from `extension-dist/` on every boot (loading
|
|
754
751
|
`extension-dist/` directly also works). It pairs itself from the `pairing.json`
|
|
755
752
|
the server writes into that folder; the **Options** page paste of `port` +
|
|
756
|
-
`token` from `~/.
|
|
753
|
+
`token` from `~/.mcp-browser-extension/handshake.json` (run
|
|
757
754
|
`npx -y mcp-browser-extension@latest --print-pairing` to get the path) is only
|
|
758
755
|
the fallback.
|
|
759
756
|
|
package/SETUP.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
machine and prove it works.** MCP Browser Extension lets an MCP client drive the human's
|
|
5
5
|
real Chrome browser: real logins, real cookies, no separate Chromium. It has two
|
|
6
6
|
halves that must be paired: a stdio MCP server (npm package
|
|
7
|
-
|
|
7
|
+
`mcp-browser-extension`) and a Manifest V3 extension that ships inside the
|
|
8
8
|
same package. Work autonomously; pause only for the two actions that must happen
|
|
9
9
|
inside Chrome's own UI (loading the extension, pasting the pairing token).
|
|
10
10
|
|
|
@@ -40,10 +40,10 @@ extension to a plain folder directly under the home directory, and prints its
|
|
|
40
40
|
path:
|
|
41
41
|
|
|
42
42
|
```
|
|
43
|
-
npx -y
|
|
43
|
+
npx -y mcp-browser-extension@latest --extension-path
|
|
44
44
|
```
|
|
45
45
|
|
|
46
|
-
It prints `~/
|
|
46
|
+
It prints `~/mcp-browser-extension` (on Windows `%USERPROFILE%\mcp-browser-extension`).
|
|
47
47
|
Verify with `ls` (or `dir`) that the folder holds `manifest.json` and
|
|
48
48
|
`background.js`. Record the path; the human needs it in step 4. The install is
|
|
49
49
|
small and downloads no browser: the server drives the Chrome the human already
|
|
@@ -62,14 +62,14 @@ unless they ask.
|
|
|
62
62
|
**Claude Code** (registers for every project on this machine):
|
|
63
63
|
|
|
64
64
|
```
|
|
65
|
-
claude mcp add
|
|
66
|
-
npx -y
|
|
65
|
+
claude mcp add mcp-browser-extension -s user -- \
|
|
66
|
+
npx -y mcp-browser-extension@latest \
|
|
67
67
|
--allow-domain example.com --enable-mutations --persist-token
|
|
68
68
|
```
|
|
69
69
|
|
|
70
70
|
Everything before `--` belongs to Claude Code. Everything after it is the
|
|
71
71
|
server's own command line. Keep the `--` or `--allow-domain` gets read as a
|
|
72
|
-
Claude Code option. Verify with `claude mcp list`:
|
|
72
|
+
Claude Code option. Verify with `claude mcp list`: mcp-browser-extension should be listed.
|
|
73
73
|
It will show as connected once the server boots, even before the extension has
|
|
74
74
|
paired.
|
|
75
75
|
|
|
@@ -80,9 +80,9 @@ Code uses a top-level `servers` key instead of `mcpServers`).
|
|
|
80
80
|
```json
|
|
81
81
|
{
|
|
82
82
|
"mcpServers": {
|
|
83
|
-
"
|
|
83
|
+
"mcp-browser-extension": {
|
|
84
84
|
"command": "npx",
|
|
85
|
-
"args": ["-y", "
|
|
85
|
+
"args": ["-y", "mcp-browser-extension@latest",
|
|
86
86
|
"--allow-domain", "example.com",
|
|
87
87
|
"--enable-mutations",
|
|
88
88
|
"--persist-token"]
|
|
@@ -92,13 +92,13 @@ Code uses a top-level `servers` key instead of `mcpServers`).
|
|
|
92
92
|
```
|
|
93
93
|
|
|
94
94
|
**Windows:** the command must be `cmd` with args `["/c", "npx", "-y",
|
|
95
|
-
"
|
|
96
|
-
`claude mcp add
|
|
95
|
+
"mcp-browser-extension@latest", ...]`, or for Claude Code:
|
|
96
|
+
`claude mcp add mcp-browser-extension -s user -- cmd /c npx -y mcp-browser-extension@latest --allow-domain example.com --enable-mutations --persist-token`.
|
|
97
97
|
|
|
98
98
|
Tell the human to restart the client (or run `/mcp` in Claude Code) so the
|
|
99
|
-
server loads. The first boot writes `~/.
|
|
99
|
+
server loads. The first boot writes `~/.mcp-browser-extension/handshake.json` (mode 0600)
|
|
100
100
|
holding the bridge port and the pairing token, and with `--persist-token` it
|
|
101
|
-
also stores the token at `~/.
|
|
101
|
+
also stores the token at `~/.mcp-browser-extension/token` for reuse.
|
|
102
102
|
|
|
103
103
|
## Step 4 — Load the extension (human action inside Chrome)
|
|
104
104
|
|
|
@@ -106,7 +106,7 @@ You cannot do this step yourself. Give the human these exact instructions:
|
|
|
106
106
|
|
|
107
107
|
1. Open `chrome://extensions` in Chrome.
|
|
108
108
|
2. Turn on **Developer mode** (top right).
|
|
109
|
-
3. Click **Load unpacked** and choose the `
|
|
109
|
+
3. Click **Load unpacked** and choose the `mcp-browser-extension` folder in
|
|
110
110
|
their home directory (the path from step 2).
|
|
111
111
|
4. Confirm an extension named **MCP Browser Extension** now appears in the list.
|
|
112
112
|
|
|
@@ -149,7 +149,7 @@ retry the tool.
|
|
|
149
149
|
## Step 6 — Check the pairing
|
|
150
150
|
|
|
151
151
|
Pairing needs no paste. Every time the server boots it writes `pairing.json`
|
|
152
|
-
(mode 0600) into the same `
|
|
152
|
+
(mode 0600) into the same `mcp-browser-extension` folder the human loaded in step 4.
|
|
153
153
|
The extension reads that file from its own folder and pairs itself. So make
|
|
154
154
|
sure a server has booted at least once since step 2, in either of these ways:
|
|
155
155
|
|
|
@@ -157,13 +157,13 @@ sure a server has booted at least once since step 2, in either of these ways:
|
|
|
157
157
|
- start one yourself in pairing mode and leave it running in the background:
|
|
158
158
|
|
|
159
159
|
```
|
|
160
|
-
npx -y
|
|
160
|
+
npx -y mcp-browser-extension@latest --print-pairing --persist-token
|
|
161
161
|
```
|
|
162
162
|
|
|
163
163
|
Verify the file exists (do not print it):
|
|
164
164
|
|
|
165
165
|
```
|
|
166
|
-
ls "$(npx -y
|
|
166
|
+
ls "$(npx -y mcp-browser-extension@latest --extension-path)/pairing.json"
|
|
167
167
|
```
|
|
168
168
|
|
|
169
169
|
Then tell the human where to look. The status badge is on the extension's
|
|
@@ -190,11 +190,11 @@ extension folder or a read-only global install): read the port without
|
|
|
190
190
|
printing the token,
|
|
191
191
|
|
|
192
192
|
```
|
|
193
|
-
node -e "console.log(require(require('os').homedir()+'/.
|
|
193
|
+
node -e "console.log(require(require('os').homedir()+'/.mcp-browser-extension/handshake.json').port)"
|
|
194
194
|
```
|
|
195
195
|
|
|
196
196
|
then have the human open **Details** on MCP Browser Extension, then **Extension
|
|
197
|
-
options**, enter that **Port**, open `~/.
|
|
197
|
+
options**, enter that **Port**, open `~/.mcp-browser-extension/handshake.json` in a text
|
|
198
198
|
editor and paste its `token` into the **Token** field, leave **Profile** as
|
|
199
199
|
`default`, and click **Save**. The status line should read **connected** within
|
|
200
200
|
a few seconds. Values saved by hand take precedence over the bundled file.
|
|
@@ -225,7 +225,7 @@ not stick. Re-check the port and token before anything else.
|
|
|
225
225
|
Summarize for the human:
|
|
226
226
|
|
|
227
227
|
- Node and package versions installed.
|
|
228
|
-
- The absolute `
|
|
228
|
+
- The absolute `mcp-browser-extension` path they loaded.
|
|
229
229
|
- Which MCP client was configured, at which scope, with which domains and gates.
|
|
230
230
|
- Whether the badge went green, and whether `tabs_list`, an allowed
|
|
231
231
|
navigation, and a refused navigation each verified.
|
|
@@ -233,4 +233,4 @@ Summarize for the human:
|
|
|
233
233
|
|
|
234
234
|
For any failure, name the exact symptom, what you tried, and the matching
|
|
235
235
|
section of the README's troubleshooting notes at
|
|
236
|
-
`https://github.com/Mehmoodqureshi/
|
|
236
|
+
`https://github.com/Mehmoodqureshi/mcp-browser-extension#readme`.
|
|
@@ -268,7 +268,7 @@ export type ServerFrame = CommandFrame | WelcomeFrame | UnauthFrame | PingFrame
|
|
|
268
268
|
/** Frames the EXTENSION sends to the server. */
|
|
269
269
|
export type ExtensionFrame = HelloFrame | ResultFrame | ErrorFrame | EventFrame | PongFrame | SiteGrantFrame;
|
|
270
270
|
export type Frame = ServerFrame | ExtensionFrame;
|
|
271
|
-
/** Shape of `$
|
|
271
|
+
/** Shape of `$MCP_BROWSER_EXTENSION_DATA/handshake.json`. The token is a secret. */
|
|
272
272
|
export interface HandshakeFile {
|
|
273
273
|
v: ProtocolVersion;
|
|
274
274
|
port: number;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* shared/tab-border.ts — the pure half of the "
|
|
2
|
+
* shared/tab-border.ts — the pure half of the "mcp-browser-extension is working here"
|
|
3
3
|
* border: choosing its colour from the page's theme and writing the CSS.
|
|
4
4
|
*
|
|
5
5
|
* The extension paints the border with `chrome.scripting.insertCSS` on
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
|
-
* shared/tab-border.ts — the pure half of the "
|
|
3
|
+
* shared/tab-border.ts — the pure half of the "mcp-browser-extension is working here"
|
|
4
4
|
* border: choosing its colour from the page's theme and writing the CSS.
|
|
5
5
|
*
|
|
6
6
|
* The extension paints the border with `chrome.scripting.insertCSS` on
|
package/dist/src/access.js
CHANGED
|
@@ -6,7 +6,8 @@ exports.requireAccess = requireAccess;
|
|
|
6
6
|
const node_crypto_1 = require("node:crypto");
|
|
7
7
|
const node_fs_1 = require("node:fs");
|
|
8
8
|
const node_path_1 = require("node:path");
|
|
9
|
-
const
|
|
9
|
+
const compat_1 = require("./compat");
|
|
10
|
+
const SITE = ((0, compat_1.runtimeEnv)('ACCESS_API') ?? 'https://mcp-browser-extension.com/api/access').replace(/\/$/, '');
|
|
10
11
|
let accessPollMs = 2_000;
|
|
11
12
|
let accessWaitMs = 45_000;
|
|
12
13
|
const EMAIL_SCHEMA = {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* src/auto-update.ts — keep every install on the latest
|
|
2
|
+
* src/auto-update.ts — keep every install on the latest mcp-browser-extension.
|
|
3
3
|
*
|
|
4
|
-
* Most people run
|
|
4
|
+
* Most people run mcp-browser-extension through `npx -y mcp-browser-extension` in an
|
|
5
5
|
* MCP config. npx reuses whatever copy it cached first, so installs sat on old
|
|
6
6
|
* versions for weeks (telemetry: three times as many sessions on 0.9.13 as on
|
|
7
7
|
* the current 0.9.15) and never got the fixes. Nobody edits their MCP config to
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
*
|
|
10
10
|
* So at startup, before binding the port, the server asks the npm registry for
|
|
11
11
|
* the latest version. If it is newer than this copy, it hands off: it runs
|
|
12
|
-
* `npx -y
|
|
12
|
+
* `npx -y mcp-browser-extension@<latest>` with the same arguments, passes
|
|
13
13
|
* stdin/stdout/stderr straight through (the MCP host talks to the new version
|
|
14
14
|
* directly), and exits when that does. The new copy's server also mirrors its
|
|
15
15
|
* extension build into the unpacked folder, so the extension follows.
|
|
@@ -17,12 +17,12 @@
|
|
|
17
17
|
* It never gets in the way: a dev checkout (not under node_modules) is left
|
|
18
18
|
* alone, the check gives up after a short timeout when offline, a failed spawn
|
|
19
19
|
* falls back to running this copy, and the child is marked so it never hands
|
|
20
|
-
* off again. Off with
|
|
20
|
+
* off again. Off with MCP_BROWSER_EXTENSION_AUTO_UPDATE=0 or --no-auto-update.
|
|
21
21
|
*/
|
|
22
22
|
import { type ChildProcess, type SpawnOptions } from 'node:child_process';
|
|
23
|
-
export declare const PACKAGE_NAME = "
|
|
23
|
+
export declare const PACKAGE_NAME = "mcp-browser-extension";
|
|
24
24
|
/** Set on the handed-off child: it is already the latest, never hand off again. */
|
|
25
|
-
export declare const HANDOFF_ENV = "
|
|
25
|
+
export declare const HANDOFF_ENV = "MCP_BROWSER_EXTENSION_UPDATED_FROM";
|
|
26
26
|
export interface HandOffOptions {
|
|
27
27
|
current: string;
|
|
28
28
|
/** This copy's package directory: only an installed copy (under node_modules) updates. */
|
package/dist/src/auto-update.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
|
-
* src/auto-update.ts — keep every install on the latest
|
|
3
|
+
* src/auto-update.ts — keep every install on the latest mcp-browser-extension.
|
|
4
4
|
*
|
|
5
|
-
* Most people run
|
|
5
|
+
* Most people run mcp-browser-extension through `npx -y mcp-browser-extension` in an
|
|
6
6
|
* MCP config. npx reuses whatever copy it cached first, so installs sat on old
|
|
7
7
|
* versions for weeks (telemetry: three times as many sessions on 0.9.13 as on
|
|
8
8
|
* the current 0.9.15) and never got the fixes. Nobody edits their MCP config to
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
*
|
|
11
11
|
* So at startup, before binding the port, the server asks the npm registry for
|
|
12
12
|
* the latest version. If it is newer than this copy, it hands off: it runs
|
|
13
|
-
* `npx -y
|
|
13
|
+
* `npx -y mcp-browser-extension@<latest>` with the same arguments, passes
|
|
14
14
|
* stdin/stdout/stderr straight through (the MCP host talks to the new version
|
|
15
15
|
* directly), and exits when that does. The new copy's server also mirrors its
|
|
16
16
|
* extension build into the unpacked folder, so the extension follows.
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
* It never gets in the way: a dev checkout (not under node_modules) is left
|
|
19
19
|
* alone, the check gives up after a short timeout when offline, a failed spawn
|
|
20
20
|
* falls back to running this copy, and the child is marked so it never hands
|
|
21
|
-
* off again. Off with
|
|
21
|
+
* off again. Off with MCP_BROWSER_EXTENSION_AUTO_UPDATE=0 or --no-auto-update.
|
|
22
22
|
*/
|
|
23
23
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
24
24
|
exports.HANDOFF_ENV = exports.PACKAGE_NAME = void 0;
|
|
@@ -28,17 +28,18 @@ exports.isInstalledCopy = isInstalledCopy;
|
|
|
28
28
|
exports.maybeHandOff = maybeHandOff;
|
|
29
29
|
const node_child_process_1 = require("node:child_process");
|
|
30
30
|
const node_path_1 = require("node:path");
|
|
31
|
-
|
|
31
|
+
const compat_1 = require("./compat");
|
|
32
|
+
exports.PACKAGE_NAME = 'mcp-browser-extension';
|
|
32
33
|
const REGISTRY_URL = `https://registry.npmjs.org/${exports.PACKAGE_NAME}/latest`;
|
|
33
34
|
/** The registry answers in well under this; offline must not delay startup more. */
|
|
34
35
|
const CHECK_TIMEOUT_MS = 1_500;
|
|
35
36
|
/** Set on the handed-off child: it is already the latest, never hand off again. */
|
|
36
|
-
exports.HANDOFF_ENV = '
|
|
37
|
+
exports.HANDOFF_ENV = 'MCP_BROWSER_EXTENSION_UPDATED_FROM';
|
|
37
38
|
/** Whether auto-update is off, by env, flag, or because this IS the handed-off copy. */
|
|
38
39
|
function autoUpdateDisabled(env, disabledByFlag = false) {
|
|
39
|
-
if (disabledByFlag ||
|
|
40
|
+
if (disabledByFlag || (0, compat_1.runtimeEnv)('UPDATED_FROM', env))
|
|
40
41
|
return true;
|
|
41
|
-
const v = (
|
|
42
|
+
const v = ((0, compat_1.runtimeEnv)('AUTO_UPDATE', env) ?? '').trim().toLowerCase();
|
|
42
43
|
return ['0', 'false', 'off', 'no'].includes(v);
|
|
43
44
|
}
|
|
44
45
|
/** True when `latest` is a plain release strictly newer than `current` (x.y.z). */
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* drafts was deleted on purpose).
|
|
4
4
|
*
|
|
5
5
|
* - Fresh 256-bit token EVERY boot by default, never persisted across restarts.
|
|
6
|
-
* Persistence is OPT-IN (`--persist-token` / `
|
|
6
|
+
* Persistence is OPT-IN (`--persist-token` / `MCP_BROWSER_EXTENSION_TOKEN`) for hosts
|
|
7
7
|
* that want a stable token so the extension never has to re-pair.
|
|
8
8
|
* - Written atomically (tmp + rename) to `handshake.json` at mode 0600; the
|
|
9
9
|
* mode is re-verified after write and we FAIL CLOSED if it can't be set.
|
|
@@ -27,7 +27,7 @@ export declare function readPersistedToken(dir: string): string | null;
|
|
|
27
27
|
export declare function writePersistedToken(dir: string, token: string): string;
|
|
28
28
|
/**
|
|
29
29
|
* Resolve the token for this boot:
|
|
30
|
-
* 1. `
|
|
30
|
+
* 1. `MCP_BROWSER_EXTENSION_TOKEN` env — an explicit pin; used verbatim, never written to disk.
|
|
31
31
|
* 2. `persist` on — reuse the on-disk token (creating + saving one on first run),
|
|
32
32
|
* so the extension stays paired across restarts.
|
|
33
33
|
* 3. otherwise — a fresh per-boot token (the secure default; never persisted).
|
|
@@ -63,7 +63,7 @@ export declare function portIsListening(port: number, timeoutMs?: number): Promi
|
|
|
63
63
|
/**
|
|
64
64
|
* Should a hub on `port` leave `<extDir>/pairing.json` alone? Yes when the file
|
|
65
65
|
* already points the extension at a DIFFERENT port that is still live: another
|
|
66
|
-
*
|
|
66
|
+
* mcp-browser-extension (e.g. one started with its own MCP_BROWSER_EXTENSION_DATA / --ws-port) owns
|
|
67
67
|
* that pairing, and overwriting it would silently steal the user's extension.
|
|
68
68
|
* A file naming our own port, a dead port, or no file at all is fair game.
|
|
69
69
|
*/
|
package/dist/src/bridge/auth.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* drafts was deleted on purpose).
|
|
5
5
|
*
|
|
6
6
|
* - Fresh 256-bit token EVERY boot by default, never persisted across restarts.
|
|
7
|
-
* Persistence is OPT-IN (`--persist-token` / `
|
|
7
|
+
* Persistence is OPT-IN (`--persist-token` / `MCP_BROWSER_EXTENSION_TOKEN`) for hosts
|
|
8
8
|
* that want a stable token so the extension never has to re-pair.
|
|
9
9
|
* - Written atomically (tmp + rename) to `handshake.json` at mode 0600; the
|
|
10
10
|
* mode is re-verified after write and we FAIL CLOSED if it can't be set.
|
|
@@ -35,6 +35,7 @@ const node_crypto_1 = require("node:crypto");
|
|
|
35
35
|
const node_net_1 = require("node:net");
|
|
36
36
|
const protocol_1 = require("../../shared/protocol");
|
|
37
37
|
const datadir_1 = require("./datadir");
|
|
38
|
+
const compat_1 = require("../compat");
|
|
38
39
|
/** A fresh 256-bit token, base64url. Generated once per server boot. */
|
|
39
40
|
function generateToken() {
|
|
40
41
|
return (0, node_crypto_1.randomBytes)(32).toString('base64url');
|
|
@@ -91,13 +92,13 @@ function writePersistedToken(dir, token) {
|
|
|
91
92
|
}
|
|
92
93
|
/**
|
|
93
94
|
* Resolve the token for this boot:
|
|
94
|
-
* 1. `
|
|
95
|
+
* 1. `MCP_BROWSER_EXTENSION_TOKEN` env — an explicit pin; used verbatim, never written to disk.
|
|
95
96
|
* 2. `persist` on — reuse the on-disk token (creating + saving one on first run),
|
|
96
97
|
* so the extension stays paired across restarts.
|
|
97
98
|
* 3. otherwise — a fresh per-boot token (the secure default; never persisted).
|
|
98
99
|
*/
|
|
99
100
|
function resolveToken(dir, opts) {
|
|
100
|
-
const fromEnv =
|
|
101
|
+
const fromEnv = (0, compat_1.runtimeEnv)('TOKEN')?.trim();
|
|
101
102
|
if (fromEnv)
|
|
102
103
|
return fromEnv;
|
|
103
104
|
if (opts.persist) {
|
|
@@ -176,7 +177,7 @@ function portIsListening(port, timeoutMs = 500) {
|
|
|
176
177
|
/**
|
|
177
178
|
* Should a hub on `port` leave `<extDir>/pairing.json` alone? Yes when the file
|
|
178
179
|
* already points the extension at a DIFFERENT port that is still live: another
|
|
179
|
-
*
|
|
180
|
+
* mcp-browser-extension (e.g. one started with its own MCP_BROWSER_EXTENSION_DATA / --ws-port) owns
|
|
180
181
|
* that pairing, and overwriting it would silently steal the user's extension.
|
|
181
182
|
* A file naming our own port, a dead port, or no file at all is fair game.
|
|
182
183
|
*/
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* src/bridge/datadir.ts — the Electron-free data dir (the LinkedIn repo resolved
|
|
3
|
-
* this via `app.getPath`; we use `$
|
|
3
|
+
* this via `app.getPath`; we use `$MCP_BROWSER_EXTENSION_DATA` || `~/.mcp-browser-extension`). Holds
|
|
4
4
|
* the 0600 handshake and, later, the CDP-fallback profile.
|
|
5
5
|
*/
|
|
6
6
|
/** Create (if needed) and return the data dir, 0700 so only the user can read it. */
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
3
|
* src/bridge/datadir.ts — the Electron-free data dir (the LinkedIn repo resolved
|
|
4
|
-
* this via `app.getPath`; we use `$
|
|
4
|
+
* this via `app.getPath`; we use `$MCP_BROWSER_EXTENSION_DATA` || `~/.mcp-browser-extension`). Holds
|
|
5
5
|
* the 0600 handshake and, later, the CDP-fallback profile.
|
|
6
6
|
*/
|
|
7
7
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* src/bridge/evict.ts — take a pinned port back from a stale
|
|
2
|
+
* src/bridge/evict.ts — take a pinned port back from a stale mcp-browser-extension.
|
|
3
3
|
*
|
|
4
|
-
* A second session normally JOINS the
|
|
4
|
+
* A second session normally JOINS the mcp-browser-extension holding the port as a peer
|
|
5
5
|
* (see ./peer) and nobody is stopped. This is the fallback for when joining
|
|
6
|
-
* fails: an older
|
|
6
|
+
* fails: an older mcp-browser-extension that can't share the port, or a hung one. It will
|
|
7
7
|
* never release the port on its own, so rather than fail with EADDRINUSE and
|
|
8
8
|
* make the user kill it by hand, we do that for them.
|
|
9
9
|
*
|
|
@@ -12,13 +12,13 @@
|
|
|
12
12
|
* - the handshake names that port (so it describes *this* listener), and
|
|
13
13
|
* - the pid is not us, and
|
|
14
14
|
* - the pid is alive, and
|
|
15
|
-
* - its command line mentions
|
|
15
|
+
* - its command line mentions mcp-browser-extension.
|
|
16
16
|
* If any check fails we leave the process alone and let the caller surface the
|
|
17
17
|
* plain-English port-busy error instead. Refusing to evict is always safe; a
|
|
18
18
|
* wrong kill is not.
|
|
19
19
|
*/
|
|
20
|
-
/** Does this command line belong to a
|
|
21
|
-
export declare function
|
|
20
|
+
/** Does this command line belong to a mcp-browser-extension server? */
|
|
21
|
+
export declare function looksLikeMcpBrowserExtension(cmdline: string): boolean;
|
|
22
22
|
export interface EvictDeps {
|
|
23
23
|
/** Injectable for tests; defaults to the real probes. */
|
|
24
24
|
isAlive?: (pid: number) => boolean;
|
|
@@ -26,7 +26,7 @@ export interface EvictDeps {
|
|
|
26
26
|
kill?: (pid: number, signal: NodeJS.Signals) => void;
|
|
27
27
|
}
|
|
28
28
|
/**
|
|
29
|
-
* Try to free `port` by terminating the
|
|
29
|
+
* Try to free `port` by terminating the mcp-browser-extension that owns it.
|
|
30
30
|
* Returns true only if an owner was found, killed, and confirmed gone.
|
|
31
31
|
*/
|
|
32
32
|
export declare function evictPortOwner(dataDir: string, port: number, log: (message: string) => void, deps?: EvictDeps): Promise<boolean>;
|