@extension.dev/mcp 10.8.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.
@@ -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.8.0",
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.8.0",
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,97 @@
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
+
82
+ ## 10.9.0
83
+
84
+ The client ran its browser work through CLI packages pinned three minor
85
+ releases back, so an agent driving the MCP got 4.0.30's behavior while the
86
+ same person running `extension` on their own machine got 4.1.2's. Every fix
87
+ shipped in between reached the terminal and stopped at the MCP.
88
+
89
+ - `extension-create`, `extension-develop` and `extension-install` move from
90
+ 4.0.30 to 4.1.2. The browser, logging and install paths the MCP tools call
91
+ into now behave the way the documented CLI does.
92
+ - No tool surface changes: the same tools take the same inputs and answer the
93
+ same shapes.
94
+
3
95
  ## 10.8.0
4
96
 
5
97
  Some MCP actions change what a public channel serves or hand something to a
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.
@@ -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
  ---