@extension.dev/mcp 10.9.0 → 10.10.0
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/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +79 -0
- package/README.md +34 -0
- package/claude/ARCHITECTURE.md +1 -4
- package/claude/CLAUDE.md +5 -0
- package/claude/commands/extension-debug.md +2 -0
- package/claude/rules/mcp-tools.md +16 -0
- package/dist/module.js +1002 -57
- package/dist/src/index.d.ts +3 -0
- package/dist/src/lib/allowance.d.ts +16 -0
- package/dist/src/lib/analytics-scrub.d.ts +3 -0
- package/dist/src/lib/cdp-extension-page.d.ts +21 -0
- package/dist/src/lib/envelope.d.ts +1 -1
- package/dist/src/lib/preview-upload.d.ts +1 -0
- package/dist/src/lib/safari-automation.d.ts +16 -0
- package/dist/src/lib/template-artifact-source.d.ts +1 -1
- package/dist/src/lib/types.d.ts +2 -0
- package/dist/src/lib/webdriver.d.ts +28 -0
- package/dist/src/tools/eval.d.ts +3 -0
- package/dist/src/tools/open.d.ts +2 -0
- package/package.json +4 -4
- package/server.json +2 -2
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"name": "extension-mcp",
|
|
11
11
|
"source": "./",
|
|
12
12
|
"description": "MCP tools for browser extension development: scaffold from 50+ templates, run the dev server with HMR, inspect the live DOM and logs, and publish store-ready builds for Chrome, Edge, and Firefox.",
|
|
13
|
-
"version": "10.
|
|
13
|
+
"version": "10.10.0",
|
|
14
14
|
"category": "development",
|
|
15
15
|
"author": {
|
|
16
16
|
"name": "Cezar Augusto"
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "extension-mcp",
|
|
3
3
|
"description": "MCP tools for browser extension development: scaffold from 50+ templates, run the dev server with HMR, inspect the live DOM and logs, and publish store-ready builds for Chrome, Edge, and Firefox. Ships /extension, /extension-add, /extension-debug, and /extension-publish commands.",
|
|
4
|
-
"version": "10.
|
|
4
|
+
"version": "10.10.0",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Cezar Augusto",
|
|
7
7
|
"email": "hello@extension.dev",
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,84 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 10.10.0
|
|
4
|
+
|
|
5
|
+
Safari was the one engine this server treated as a dead end, while the
|
|
6
|
+
Extension.js it shells out to had grown a working Safari dev loop. The client
|
|
7
|
+
now rides that loop, and its pinned CLI packages move to the release that
|
|
8
|
+
has it.
|
|
9
|
+
|
|
10
|
+
- `extension-create`, `extension-develop` and `extension-install` move from
|
|
11
|
+
4.1.2 to 4.1.29, the stable release that carries the `navigate` verb
|
|
12
|
+
(first shipped in a 4.1.28 canary). On Safari that engine reloads the extension
|
|
13
|
+
on every save through the extension's own bridge, streams background and
|
|
14
|
+
content lines into the session's log file, and points a tab at a url
|
|
15
|
+
without eval; a project with no local Extension.js now gets all of that
|
|
16
|
+
from the pin.
|
|
17
|
+
- The bridge tools work on a Safari dev session started with
|
|
18
|
+
`allowControl` or `allowEval`: `extension_storage`, `extension_reload`,
|
|
19
|
+
`extension_open` for surfaces, `extension_dom_snapshot` by tab id,
|
|
20
|
+
`extension_logs`, and the assertions `content-script-injected`,
|
|
21
|
+
`background-worker-booted`, `storage-key-present` and
|
|
22
|
+
`console-errors-empty`, measured live on Safari 27. `extension_eval` in
|
|
23
|
+
`content` or `page` needs a tab already open at the url, and Safari's MV3
|
|
24
|
+
background CSP blocks eval in `background` and the bridge's `url`
|
|
25
|
+
navigation, which the engine reports by name. `surface-rendered` and
|
|
26
|
+
`extension_inspect` still need a target list Safari does not expose, and
|
|
27
|
+
say so.
|
|
28
|
+
- `extension_browsers` reports Safari's version and an `automation` block
|
|
29
|
+
(the safaridriver beside it and whether it speaks `--mcp` and `--bidi`),
|
|
30
|
+
and `extension_doctor` with no `projectPath` gains a `safari-agent` leg,
|
|
31
|
+
so an agent learns when Apple's Safari MCP server can pair with this one.
|
|
32
|
+
The packaged CLAUDE.md and `/extension-debug` describe that pairing.
|
|
33
|
+
- `extension_open` with `url` asks the engine's `navigate` verb first, a
|
|
34
|
+
static tabs call inside the extension that works where an MV3 background
|
|
35
|
+
refuses eval, which is every Safari session. Only an engine that does not
|
|
36
|
+
know the verb yet falls back to the background eval, and that fallback
|
|
37
|
+
names the upgrade when Safari's CSP refuses it.
|
|
38
|
+
- If a dev session records a safaridriver session in `ready.json`
|
|
39
|
+
(`webdriverPort`, `webdriverSessionId`), `extension_eval` with context
|
|
40
|
+
`page` and `extension_open` with `url` use it for the page's main world,
|
|
41
|
+
and `extension_doctor` adds a `safari-window` leg; with no such record
|
|
42
|
+
the leg is a skip and both tools take the bridge.
|
|
43
|
+
|
|
44
|
+
Four defects agents met while driving the server on real extension work
|
|
45
|
+
(BUGS_TO_FIX_MCP.md entries 1 to 4) close in the same release.
|
|
46
|
+
|
|
47
|
+
- The initialize result now carries `instructions`: four lines that name
|
|
48
|
+
the moments (run, wait, inspect, drive, build an extension) and the tools
|
|
49
|
+
that own them, for clients that hide tool descriptions behind a search
|
|
50
|
+
step and would otherwise show the model only a server name and a tool
|
|
51
|
+
count. `createServer()` is exported so a client can initialize against
|
|
52
|
+
the same server object the stdio path connects.
|
|
53
|
+
- `extension_open` with surface `newtab`, `history` or `bookmarks` resolves
|
|
54
|
+
the `chrome_url_overrides` page itself and opens it by url. The engine's
|
|
55
|
+
`open` verb never knew those surfaces and answered `E_ARGS` "unknown
|
|
56
|
+
surface" to exactly what the tool description promised; an override page
|
|
57
|
+
is only ever a tab, so this is the surface, not a fallback, and the hint
|
|
58
|
+
says so.
|
|
59
|
+
- `extension_open` with surface `sidebar` on Chromium, when Chrome refuses
|
|
60
|
+
`sidePanel.open()` for lack of a user gesture, opens the extension's own
|
|
61
|
+
sidebar page in a tab, dispatches a synthetic click on it over CDP, calls
|
|
62
|
+
`chrome.sidePanel.open` from inside that click and closes the tab. The
|
|
63
|
+
panel that opens is the real one; the result reports `gesture:
|
|
64
|
+
"synthetic-click"` and a warning that the toolbar wiring was not
|
|
65
|
+
exercised. If the click does not open it either, the sidebar document is
|
|
66
|
+
rendered as a tab with the reason in a warning, and if even that fails
|
|
67
|
+
the refusal gains a hint naming the url to read instead of ending the
|
|
68
|
+
road. Headless sessions keep their tab fallback and never click.
|
|
69
|
+
- `extension_eval` on a Chromium MV3 session evaluates extension pages over
|
|
70
|
+
CDP: contexts `popup`, `options`, `sidebar`, `newtab`, `history` and
|
|
71
|
+
`bookmarks`, and context `page` with a `chrome-extension://` url. The
|
|
72
|
+
inspector path is not governed by the extension page CSP that blocks the
|
|
73
|
+
in-bundle relay's string eval, and script injection could never reach an
|
|
74
|
+
extension page at all, which is where the misleading "Extension manifest
|
|
75
|
+
must request permission to access this host" came from. A page that is
|
|
76
|
+
not open answers `E_NO_TARGET` with the `extension_open` call that opens
|
|
77
|
+
it; a thrown expression answers `E_EVAL` with the exception text; a bare
|
|
78
|
+
top-level `await` parses, as in the DevTools console; MV2 Chromium and
|
|
79
|
+
Gecko sessions keep the relay. Both this path and the sidebar gesture were
|
|
80
|
+
measured live on Chrome for Testing 151 before release.
|
|
81
|
+
|
|
3
82
|
## 10.9.0
|
|
4
83
|
|
|
5
84
|
The client ran its browser work through CLI packages pinned three minor
|
package/README.md
CHANGED
|
@@ -173,6 +173,40 @@ check registry, so a document from one lane can never be mistaken for the
|
|
|
173
173
|
other's. Each check here names the preview check it is the live-browser
|
|
174
174
|
counterpart of, and a contract test holds the two grammars together.
|
|
175
175
|
|
|
176
|
+
## Safari
|
|
177
|
+
|
|
178
|
+
A Safari dev session runs on the same bridge as every other engine. On
|
|
179
|
+
Extension.js 4.1.28 or newer, `extension_dev --browser=safari` (macOS with
|
|
180
|
+
Xcode) builds the app, opens it, and, once you enable the extension in
|
|
181
|
+
Safari > Settings > Extensions, reloads it on every save through the
|
|
182
|
+
extension's own socket to the dev server and streams its background and
|
|
183
|
+
content lines into the session's log file. With `allowControl` or
|
|
184
|
+
`allowEval`, the control channel is on too: `extension_storage`,
|
|
185
|
+
`extension_reload`, `extension_dom_snapshot` (by tab id), `extension_open`
|
|
186
|
+
for surfaces, `extension_logs`, and the assertions `content-script-injected`
|
|
187
|
+
(on a content line at the url), `background-worker-booted`,
|
|
188
|
+
`storage-key-present` and `console-errors-empty` all work against it.
|
|
189
|
+
`extension_eval` in `content` or `page` needs a tab already open at the url,
|
|
190
|
+
and `extension_open` with `url` cannot open one on Safari: the bridge
|
|
191
|
+
navigates through a background eval, and Safari's MV3 background CSP blocks
|
|
192
|
+
eval, which also blocks `extension_eval` in `background`. Open the page in
|
|
193
|
+
Safari by hand, then read it. Safari has no CDP or RDP, so
|
|
194
|
+
`extension_inspect` has no Safari path and `surface-rendered` answers
|
|
195
|
+
inconclusive, pointing at `extension_dom_snapshot` with a `context`.
|
|
196
|
+
|
|
197
|
+
Safari 27 and Safari Technology Preview 247 also ship Apple's own MCP server
|
|
198
|
+
inside `safaridriver` (enable Safari > Settings > Developer > "Allow remote
|
|
199
|
+
automation and external agents", then
|
|
200
|
+
`claude mcp add safari-mcp -- "/usr/bin/safaridriver" --mcp`). It drives an
|
|
201
|
+
isolated automation window with page-level tools and has no extension-aware
|
|
202
|
+
tool. It runs beside a Safari dev session, because this server never opens
|
|
203
|
+
an automation session of its own: if a dev session ever records one in
|
|
204
|
+
`ready.json` (`webdriverPort`, `webdriverSessionId`), `extension_eval` with
|
|
205
|
+
context `page` and `extension_open` with `url` use it for the page's main
|
|
206
|
+
world, and `extension_doctor` shows it as a `safari-window` leg; today no
|
|
207
|
+
Extension.js release records one, and that leg reads `skip`.
|
|
208
|
+
`extension_browsers` reports whether the machine's safaridriver has `--mcp`.
|
|
209
|
+
|
|
176
210
|
## Sharing a build in progress
|
|
177
211
|
|
|
178
212
|
An unpacked extension is unusually hard to hand to someone: the only way to look at a colleague's work-in-progress has been to take their zip and run untrusted code with real browser permissions on your own machine. `extension_preview_web` with `share: true` uploads the `dist/` it just built and returns a link that renders those exact bytes in the emulator. Whoever opens it installs nothing and signs in to nothing, which is what lets a designer, a PM, or a reviewer into the loop at all. Those bytes run in an isolated sandbox origin or they do not run at all: preview refuses a shared build rather than serving it in its own renderer. Sharing needs auth (`extension_auth` or `EXTENSION_DEV_TOKEN`), the link lives 30 days, and `DELETE`ing the returned `revokeUrl` with the same token kills it early. Re-sharing an unchanged build returns that same link rather than a second one, and only a revoked link is replaced by a different one, because revocation is permanent: the address is burned and never resolves again. That makes `revokeUrl` the handle to the link you just made, so every share is also appended to `.extension.dev/shared-previews.json` in the project (gitignored) so it survives losing the tool output. The upload holds up to 2,000 files and about 64MB of text, or roughly 48MB when the build is mostly images, fonts or wasm, which travel base64-encoded. Without `share`, the tool returns a local-only deep link and uploads nothing.
|
package/claude/ARCHITECTURE.md
CHANGED
|
@@ -16,10 +16,7 @@ examples repo (GitHub)
|
|
|
16
16
|
│ Generated by: scripts/generate-templates-meta.mjs
|
|
17
17
|
│ Published as: GitHub release asset (nightly tag)
|
|
18
18
|
│ Contains: slug, surfaces, framework, permissions, files, downloads, integrity
|
|
19
|
-
│
|
|
20
|
-
├── public/<slug>/ Staged sources + pre-built distributions
|
|
21
|
-
│ ├── src/ Committed source for website consumption
|
|
22
|
-
│ └── dist/<browser>/ Pre-built .zip and .xpi files
|
|
19
|
+
│ File paths are repo-relative: examples/<slug>/...
|
|
23
20
|
│
|
|
24
21
|
└── artifacts/ Build pipeline output
|
|
25
22
|
└── <slug>.<browser>.zip Distributable archives
|
package/claude/CLAUDE.md
CHANGED
|
@@ -228,6 +228,7 @@ npm run dev -- --logs info --log-url "example.com"
|
|
|
228
228
|
### Other debugging tools
|
|
229
229
|
|
|
230
230
|
- Use `--browser=firefox` to test cross-browser compatibility
|
|
231
|
+
- **Safari (macOS).** On Extension.js 4.1.28 or newer, `--browser=safari` builds the app, opens it, and after you enable the extension in Safari > Settings > Extensions it reloads on every save through the extension's bridge and streams background and content lines into the session log. Start it with `allowEval: true` and the bridge tools work: `extension_storage`, `extension_reload`, `extension_open` for surfaces, `extension_dom_snapshot` by tab id, `extension_logs`, and the assertions except `surface-rendered`. `extension_eval` in `content` or `page` needs a tab already open at the url, which you open in Safari by hand: `extension_open` with `url` and `extension_eval` in `background` are blocked by Safari's MV3 background CSP, and the engine says so. Safari has no CDP or RDP, so `extension_inspect` has no Safari path. Apple's Safari MCP server (`claude mcp add safari-mcp -- "/usr/bin/safaridriver" --mcp`, Safari 27+, after enabling Safari > Settings > Developer > "Allow remote automation and external agents") reads a page in its own isolated window with no extension-aware tool, and runs beside a dev session, since this server opens no automation session of its own.
|
|
231
232
|
- Check `dist/<browser>/` for build output
|
|
232
233
|
- Use `--wait` flag to check if dev session is ready (outputs ready.json contract)
|
|
233
234
|
- Use `npm run start` to test production builds (builds first, then launches)
|
|
@@ -240,6 +241,10 @@ npm run dev -- --logs info --log-url "example.com"
|
|
|
240
241
|
|
|
241
242
|
These replay your captured listeners, so they work on Chrome and Firefox, but carry **no user gesture**, so `activeTab` is not granted (the result reports `gesture: false`, plus a `warning` when the manifest declares `activeTab`). If you need a genuine-gesture click (activeTab granted), use [chrome-devtools-mcp](https://github.com/ChromeDevTools/chrome-devtools-mcp)'s `trigger_extension_action` (Chromium only) alongside this server.
|
|
242
243
|
|
|
244
|
+
Two surfaces the engine's `open` verb cannot serve are handled by the MCP tool itself. `extension_open` with `surface: "newtab"`, `"history"` or `"bookmarks"` resolves the `chrome_url_overrides` page and opens it in a tab, which is the only place the browser renders it. `extension_open` with `surface: "sidebar"` on Chromium, when Chrome refuses `sidePanel.open()` for lack of a user gesture, opens the extension's own sidebar page in a tab, dispatches a synthetic click on it over CDP, calls `chrome.sidePanel.open` from inside that click and closes the tab: the real panel opens, the result says `gesture: "synthetic-click"`, and a warning notes that the toolbar wiring was not exercised. If that fails too, the sidebar document is rendered as a tab and the warning names the reason.
|
|
245
|
+
|
|
246
|
+
On a Chromium MV3 session, `extension_eval` reaches extension pages over CDP rather than the in-bundle relay: contexts `popup`, `options`, `sidebar`, `newtab`, `history`, `bookmarks`, and `context: "page"` with a `chrome-extension://` url. The MV3 extension CSP blocks the relay's string eval and script injection cannot reach an extension page at all, while the inspector path is governed by neither. The page must already be open (`extension_open` first); a closed one answers `E_NO_TARGET` with the call that opens it.
|
|
247
|
+
|
|
243
248
|
## Contributing templates to the examples repo
|
|
244
249
|
|
|
245
250
|
If you create a new extension pattern worth sharing:
|
|
@@ -33,6 +33,8 @@ Debug the currently running extension dev session. The user said: $ARGUMENTS
|
|
|
33
33
|
|
|
34
34
|
To see what else is loaded in the browser (Chromium): `extension_list_extensions`.
|
|
35
35
|
|
|
36
|
+
**Safari sessions** (Extension.js 4.1.28+) have no CDP, but the bridge works once the extension is enabled in Safari: `extension_logs`, `extension_storage`, `extension_reload`, `extension_open` for surfaces, `extension_dom_snapshot` by tab id, and the assertions except `surface-rendered`. `extension_eval` in `content` or `page` needs a tab the user already opened at the url; `extension_open` with `url` and `extension_eval` in `background` are blocked by Safari's MV3 background CSP. `extension_inspect` has no Safari path. Apple's `safari-mcp` can read a page in its own window beside the dev session; neither it nor this server can open the popup or background page, which stay Web Inspector, attended.
|
|
37
|
+
|
|
36
38
|
4. **Diagnose common issues**
|
|
37
39
|
Based on what you find, check for:
|
|
38
40
|
- **"It didn't load"**: Check extension root count. If 0, content scripts may not be injecting. Check manifest `content_scripts` matches patterns and the target URL.
|
|
@@ -761,6 +761,20 @@ is carried as `value.sessionCommand`.
|
|
|
761
761
|
"engine": "gecko",
|
|
762
762
|
"cdpSupport": false,
|
|
763
763
|
"rdpSupport": true
|
|
764
|
+
},
|
|
765
|
+
{
|
|
766
|
+
"browser": "safari",
|
|
767
|
+
"binaryPath": "/Applications/Safari.app/Contents/MacOS/Safari",
|
|
768
|
+
"source": "system",
|
|
769
|
+
"engine": "webkit",
|
|
770
|
+
"version": "27.0",
|
|
771
|
+
"cdpSupport": false,
|
|
772
|
+
"rdpSupport": false,
|
|
773
|
+
"automation": {
|
|
774
|
+
"safaridriver": "/usr/bin/safaridriver",
|
|
775
|
+
"mcp": true,
|
|
776
|
+
"bidi": true
|
|
777
|
+
}
|
|
764
778
|
}
|
|
765
779
|
],
|
|
766
780
|
"managed": {
|
|
@@ -770,6 +784,8 @@ is carried as `value.sessionCommand`.
|
|
|
770
784
|
}
|
|
771
785
|
```
|
|
772
786
|
|
|
787
|
+
`automation` appears on Safari only (macOS): whether the safaridriver beside it speaks `--mcp` (Apple's Safari MCP server, Safari 27+) and `--bidi`. The hint says how to pair that server when it is there.
|
|
788
|
+
|
|
773
789
|
**Why this matters:** Before Claude runs `extension_dev --browser=firefox`, it should know if Firefox is actually installed. This prevents "browser not found" errors and lets Claude suggest `extension_browsers` when needed. Especially important for Docker/devcontainer environments.
|
|
774
790
|
|
|
775
791
|
---
|