Releases: extensiondev/mcp
Release list
v10.4.3
Four truths an agent reads got sharper. A share recorded twice is one
share, a feature map that points at source without the feature is a lie,
and a stale corpus snapshot makes both worse.
extension_sharesdedupeslocalOnlyby artifactId and counts distinct
shares in its hint; the append-only record file is untouched and
localRecordreports entries beside shares so both readings stay
available.add_featurestops pointing options and devtools at a template that
carries neither surface, returning the full plan with an honest note
instead of a wrong reference; the vue, svelte and preact sidebar rows
point at their real in-framework files. A guard test now validates
every mapping against the bundled snapshot.- The offline template snapshot moves from the July 16 corpus to the
current nightly: 52 templates, the renamed sidebar-monorepo-turborepo,
sane surface data. @extension.dev/urlsmoves from 0.3.0 to the published 0.6.0, ending
the last published-versus-checkout divergence the provenance guard
tracked.
v10.4.2
The engine moved twice in a day and the agent lane has to move with it.
extension-develop 4.0.30 carries the MAIN-world public path fix: a
content script running in the page's own world now resolves extension
assets through the isolated-world bridge base instead of falling back to
the host page, where it 404'd.
extension-create,extension-developandextension-installmove
from 4.0.29 to 4.0.30. The nightlyengine-pinjob added in 10.4.1 is
what would have caught this drift; the bump is the first it prompted.
v10.4.1
The engine this server spawns had drifted nine releases behind the one a
human gets from npx extension@latest. Agents were building on 4.0.20
while the published CLI was 4.0.29, so every engine fix in that range was
missing from the agent lane, including both theme-colour conversions
shipped this week: a theme.colors project a human built green still
failed when an agent built it. Nothing in this repository changed when
the engine shipped, which is why it went unnoticed until a walk of the
MCP journey read extension_doctor closely.
extension-create,extension-developandextension-installmove
from 4.0.20 to 4.0.29, so the agent lane spawns the engine npm serves.- A nightly
engine-pinjob now reads npm rather than the working tree
and fails when a pin trails the published engine, when a pin is a range
or a dist tag, or when it is missing. It sits beside the existing
registry-listing watch, because the event that invalidates both happens
in another repository.
v10.4.0
Every place a tool named a template, it pointed the caller at GitHub and
never at the catalog that serves the same template with screenshots,
metadata and a deploy button. The MCP was routing around its own front
door. Template emissions now carry the catalog detail URL alongside the
existing fields, and the offline fallback corpus moved to the commit the
catalog actually serves.
extension_templateslist results gaincatalogUrl,
https://templates.extension.dev/<slug>?utm_source=mcp&utm_medium=tool,
besiderepositoryUrlanddownloads, which are unchanged. The source
view andextension_add_feature's reference template carry the same
field.extension_createresults gaintemplateCatalogUrlfor the template
the project was scaffolded from, withutm_source=mcp-createso
create-path visits stay distinguishable from browse-path visits.- The pinned fallback template corpus moved to
52c0d871c433, the commit
the livelatestchannel pointer resolves to, so the offline corpus and
the catalog agree.
v10.3.1
The 10.3.0 tarball shipped two comments a stranger could read: an HTML
comment in the popup markup naming a private package, and an eight line
internal roadmap comment plus two inline script comments in the live
preview sandbox page. Both were removed at source right after that
release. This release exists to take them off the registry, because a
published tarball cannot be edited in place.
- The popup markup no longer names any private package.
extensions/live-preview/chromium/sandbox/page-0.htmlships exactly
the markup it runs, with no comments at all.
v10.3.0
Four fixes for places where a tool answered confidently about something it
had not actually checked: a login refusal that never said where the slugs
come from, a doctor report labelled with a browser that had no session, a
preview probe that certified whatever was listening on the port, and a
release gate that passed on a stale bundle.
extension_auth loginrefused a malformedprojectwithout saying
where the two slugs come from. Both the refusal and the input schema now
name the console address bar as the source: an existing project's page is
console.extension.dev/<workspace>/<project>, and a project that does
not exist yet is created at extension.dev/new first.extension_doctordiagnoses the session that exists, not a hardcoded
default. The shared session resolver only counts contracts marked
"ready", which is exactly wrong for the tool you reach for when a session
failed: doctor fell back to chrome, labelled the report with it, read the
absent contract, and returned a healthy verdict over a session that had
errored. When there is no ready session, the browser with a contract on
disk wins, newest first and whatever its status.extension_preview_webcertifies the probe against the build the call
just minted instead of against the port. Anything can be listening on the
local preview port, and the old probe readhostReachableand
previewLoadableas true off whatever JSON came back, then echoed that
server'sidentifier, name and version into the envelope unmarked, which
made any local server a text channel into the agent's context. The name
and version must now match thedistmanifest read off disk, and the
identifier is echoed only when it matches the derivation the real
middleware uses. A mismatch returns the status
host-serving-different-artifactwithpreviewLoadable: false, and
anything the host claimed that could not be confirmed locally travels
clipped underprobe.hostReported, named as the host's claim.- The release gate asserts the version the built bundle exports rather than
grepping the bundle for the version string. The grep greenwashed: the
bundle carries the pinned engine version and fifty-two"version":"1.0.0"
strings from the template corpus, so a release cut at any of those
numbers passed the gate over a staledist.dist/module.jsnow exports
the manifest version it inlined at build time, and the gate reads it back
and compares.
v10.2.0
A production walk of the full share-and-publish loop, then a fix for every
roughness it surfaced, on top of a week of engine tracking and preview
hardening. Everything landed since 10.1.0 ships here; the 10.1.1 version
bump was never published and is folded in.
- Revoking a share accepts the ids the platform actually mints:
gen_plus
64 hex characters, with the older 32-character form still valid. A
malformed reference is refused by name, with what a real id looks like,
instead of being silently cut down to a 32-character prefix that revokes
nothing while reporting the wrong cause. - Every revoke handle the tools return points at
www.extension.dev
directly, so one plain DELETE works. The apexextension.devanswers
DELETE with a redirect, and a caller that does not follow redirects got
"Redirecting..." back while the share stayed live. extension_doctorhands over the CLI's own report when the engine exits
nonzero with output the server cannot parse: the response carries
cliReport, the doctor's actual check list and remediations, instead of
discarding it and guessing that the local CLI is stale.extension_inspectwith nourlranks the extension's own surfaces
first and the toolchain's pages last, so it no longer inspects the
Extension.js welcome surface and reports on the wrong document. When only
toolchain or override pages are open, a warning names exactly which page
was inspected and how to target yours.extension_publishagainst a host where the token's project does not
exist now says what to do: check the token's scope withextension_auth,
create the project first at extension.dev/new, or log in against a
project that exists there.extension_auth statusasks the platform who the stored credential
really is instead of trusting the file on this machine, and reports the
three answers apart: the server confirmed it, the server refused it and
the workspace shown is only a local claim, or the server could not be
reached and nothing here is server confirmed. A credential minted against
a development deployment reads as refused where production refuses it,
which is what it always was and never said.- The MCP
isErrorflag now agrees with the envelope: everyok: false
result is markedisError: true, so an agent branching on the transport
flag no longer reads a platform refusal, a publish 404 or an auth 401, as
success. extension_preview_webwithshare: trueno longer fails a working
share because the local dev lane is dead: the uploaded link is what was
asked for, so the result isok: truewith statussharedand a warning
naming the unreachable local leg, which is expected outside the
extension.dev monorepo.- The Extension.js engine pin moved to 4.0.20, the server asks the engine
its version and JSON support from its published capabilities instead of
paying for a second build to find out, and a canary pin no longer parses
to NaN. - Safari packaging lets an agent name the app and bundle id it is
packaging, and the docs say what a derived bundle id actually costs a
developer instead of claiming Apple rejects it. - Preview and share housekeeping:
hostUrlis pinned to a local server,
stale carriers are cleared on start and swept on every exit, re-sharing
is documented for what it really does, and the CORS verdict is read off
the last hop a browser would follow. - The offline template fallback points at the published corpus, three
control refusals are told apart instead of sharing one message, and a
stray binary file no longer ships in the package.
v10.1.0
A six-lens audit of the whole surface (auth, platform, run, see, act and
build, plumbing) followed by a fix pass over everything it confirmed. 33
fixes, the ones you would notice first:
- A failed spawn of the dev CLI (npx missing from PATH) no longer crashes
the whole MCP server; it fails that one call with guidance. extension_waitnow ignores ready contracts stamped before the current
session, so a leftover file from a crashed run can no longer report the
previous session's compile error as yours.extension_createnever deletes a directory it did not create: the
transient-failure cleanup used to wipe pre-existing directories, including
their.git, when scaffolding into one.extension_submitdry runs are platform-primary: a platform-reported
preflight failure can no longer be overwritten by locally computed store
health, and token-only CI callers no longer fail the dry run for lacking a
local credentials file. The tool also gained aprojectPathinput so the
STORE.md advisory check reads the project, not the server's cwd.manifest.jsonwithworld: "MAIN"no longer fails Firefox validation:
Firefox has supported the MAIN world since 128. The rule is now a
strict_min_version advisory.- Device login reports hard server errors as errors, on both the tool and
the CLI paths, instead of authorization-pending or a bogus timeout; the
login flow now enforces the same cleartext-http refusal as every other
token-bearing path, validates the returned project scope before storing
credentials, and the CLI prints the one-click approval link. extension_publishwith a pinned buildSha no longer fills the response
with a different build's metadata when the pin is not in the local index.extension_sharesno longer labels your own expired shares as "not owned
by this token" when listing withstatus: "live", and revocation is only
reported permanent when the platform confirmed it.extension_logsaccepts thenewtab,history, andbookmarks
contexts,level: "off"silences console output instead of returning all
of it, and a stream error mid-follow returns what was collected instead
of discarding it.extension_stopand session bookkeeping survive an MCP restart: session
markers are cleaned when a session exits on its own, single-project stop
consults the same on-disk markers asall: true, orphan reaping escapes
regex metachars in project paths and only kills plausible session
processes, and a replaced session can no longer unregister its successor.extension_opentrusts the live browser over the computed id hash when
they disagree (symlinked dist paths), so it no longer navigates to a
nonexistent extension id and no longer reports a successfully opened
surface as a failure.- Offline first runs work: the bundled template catalog snapshot is now the
fallback when the network and cache are both unavailable, a corrupted
cache file heals instead of erroring, and a shapeless 200 response is no
longer cached for an hour. extension-mcp --help,--version, and unknown commands now answer
instead of silently starting a stdio server.- The release pipeline bumps the version before building, so the published
bundle reports the version it ships as, with an assertion gating publish
on it and on the type declarations existing. - Docs and drop-ins caught up with the code: template count corrected to
50+, the AI template slugs areai-claudeandai-chatgpt, Firefox
support noted forextension_list_extensionsand Safari for
extension_submit, and theextension_dom_snapshotdescription names
which subpaths need the debug port.
v10.0.0
Every tool now returns the same frame. Before this, 28 tools hand-built 142
different JSON shapes: ok appeared on 71 of them, error on 67, hint on 60,
message on 49, status on 46, and five more keys carried the same meaning
under different names. An agent could not tell success from failure without
knowing which tool it had called.
The frame is schema 1, the same one the Extension.js CLI emits under
--output json:
{
"schema": 1,
"ok": false,
"command": "extension_dev",
"status": "compile-failed",
"value": null,
"error": { "code": "E_FIRST_COMPILE", "message": "…" },
"hint": "…",
"warnings": []
}command names the tool. status is a kebab-case word from that tool's own
vocabulary. error.code is stable and worth branching on; error.message is
free copy and is not. The payload moved under value, and every advisory note
that used to have its own key is now an entry in warnings.
Breaking. Every payload key moved one level down. build.success is now
ok, doctor.healthy is now ok, manifest_validate.valid is now
value.valid, and wait gained the ok it never had. The ready contract's own
command is carried as value.sessionCommand, because the envelope claims that
key. authorization_pending became authorization-pending, with the old
spelling echoed as value.legacyStatus for one minor.
extension_dev and extension_start also stopped reading the dev server's
prose to decide whether the first compile failed. They poll its ready.json
contract instead, which splits a locked profile out of a dead browser and
returns the compile errors as a list. The output scrape survives only as a
fallback for a project whose own CLI predates the contract, is confined to one
@deprecated module, and says so in warnings whenever it is used. The choice
is a capability probe, never a version check: a project-local extension binary
wins over this package's pin, so the version is not knowable in advance. The
contract's own error stamps are read whatever the engine's age, so a locked
profile is named as one the moment the engine records it.
v9.0.0
Every client pays for this server's tool list at the start of every session,
whether or not the user ever touches an extension. That list was 36 tools and
52,403 bytes on the wire (roughly 13,100 tokens). It is now 28 tools and
43,214 bytes (roughly 10,800 tokens), a 17.5% cut, with no capability removed.
Eleven tools folded into the four that already owned their resource,
extension_preview folded into extension_start, and the prose was tightened
everywhere it repeated the schema or a parameter name.
9.0.0 lands close behind 8.0.0 on purpose. 8.0.0 renamed four tools for
disambiguation; this release cuts what the surface costs. Both are breaking,
adoption is still low, and doing them as one migration is cheaper for early
users than spacing them out.
Migration
| Old tool | New call |
|---|---|
extension_detect_browsers({ browsers }) |
extension_browsers({ action: "detect", browsers }) |
extension_list_browsers() |
extension_browsers({ action: "list" }) |
extension_install_browser({ browser }) |
extension_browsers({ action: "install", browser }) |
extension_uninstall_browser({ browser, all }) |
extension_browsers({ action: "uninstall", browser, all }) |
extension_login({ project, deviceCode, api }) |
extension_auth({ action: "login", project, deviceCode, api }) |
extension_whoami() |
extension_auth({ action: "status" }) |
extension_logout() |
extension_auth({ action: "logout" }) |
extension_list_templates({ surface, framework, tags, featured, query }) |
extension_templates({ action: "list", surface, framework, tags, featured, query }) |
extension_get_template_source({ slug, files }) |
extension_templates({ action: "source", slug, files }) |
extension_release_list({ workspace, project, api }) |
extension_release_status({ include: ["releases"], workspace, project, api }) |
extension_store_status({ workspace, project, api }) |
extension_release_status({ include: ["stores"], workspace, project, api }) |
extension_preview({ projectPath, browser, port, noBrowser, ...launch }) |
extension_start({ projectPath, build: false, browser, port, noBrowser, ...launch }) |
Every argument keeps its name and its meaning. action defaults to the most
common case (detect, status, list), so extension_browsers({}) scans,
extension_auth({}) reports the login, and extension_templates({}) lists.
extension_release_status returns both sections by default and nests each
under releases and stores; the old flat bodies are unchanged inside them.
The CLI is untouched: extension-mcp login|logout|whoami|release still work
exactly as before.
extension_submit, extension_publish, extension_analyze,
extension_inspect and extension_dom_snapshot were deliberately NOT merged.
8.0.0 separated them because agents confused them; folding them behind an
action parameter would hide that ambiguity rather than remove it.
Upgrading from 7.0.0
Most installs are still on 7.0.0 and two majors have landed on top of it. Do
both in one pass: apply the 8.0.0 renames, then the 9.0.0 merges above. 7.0.0
advertised 36 tools; 9.0.0 advertises 28, and every capability survived.
| 7.0.0 call | 9.0.0 call | Landed in |
|---|---|---|
extension_deploy(...) |
extension_submit(...) |
8.0.0 |
extension_inspect({ projectPath }) |
extension_analyze({ projectPath }) |
8.0.0 |
extension_source_inspect(...) |
extension_inspect(...) |
8.0.0 |
extension_dom_inspect(...) |
extension_dom_snapshot(...) |
8.0.0 |
extension_detect_browsers({ browsers }) |
extension_browsers({ action: "detect", browsers }) |
9.0.0 |
extension_list_browsers() |
extension_browsers({ action: "list" }) |
9.0.0 |
extension_install_browser({ browser }) |
extension_browsers({ action: "install", browser }) |
9.0.0 |
extension_uninstall_browser({ browser, all }) |
extension_browsers({ action: "uninstall", browser, all }) |
9.0.0 |
extension_login({ project, deviceCode, api }) |
extension_auth({ action: "login", project, deviceCode, api }) |
9.0.0 |
extension_whoami() |
extension_auth({ action: "status" }) |
9.0.0 |
extension_logout() |
extension_auth({ action: "logout" }) |
9.0.0 |
extension_list_templates({ surface, framework, tags, featured, query }) |
extension_templates({ action: "list", surface, framework, tags, featured, query }) |
9.0.0 |
extension_get_template_source({ slug, files }) |
extension_templates({ action: "source", slug, files }) |
9.0.0 |
extension_release_list({ workspace, project, api }) |
extension_release_status({ include: ["releases"], workspace, project, api }) |
9.0.0 |
extension_store_status({ workspace, project, api }) |
extension_release_status({ include: ["stores"], workspace, project, api }) |
9.0.0 |
extension_preview({ projectPath, browser, port, noBrowser, ...launch }) |
extension_start({ projectPath, build: false, browser, port, noBrowser, ...launch }) |
9.0.0 |
Read the extension_inspect row before any of the others. That name exists in
both versions and does not mean the same thing in each. In 7.0.0 it read a
BUILT extension's files off disk. In 9.0.0 it reads a RUNNING extension over
the browser's debugger protocol and needs a live extension_dev or
extension_start session. A 7.0.0 call left alone does not fail with an
unknown-tool error, it silently reaches the wrong tool and reports no dev
session instead of the file sizes you asked for. The disk reader is
extension_analyze now. Every call that passed a bare projectPath and
expected sizes, permissions and store-readiness back has to move.
extension_deploy carried its error names with it into extension_submit:
DeployAuthError, DeployInputError, DeployConfigError,
DeployNetworkError and DeployError are now SubmitAuthError,
SubmitInputError, SubmitConfigError, SubmitNetworkError and
SubmitError. Anything branching on those strings has to move with them.
Every argument keeps its name and its meaning across both majors, with two
exceptions:
extension_release_statusnests what the two 7.0.0 tools returned flat,
underreleasesandstores. The bodies inside are byte-for-byte the old
ones. Omittingincludereturns both sections.extension_startgainedbuild, defaulting totrue.build: falseis
whatextension_previewwas.
Nothing else moved. extension_publish, extension_preview_web,
extension_shares, extension_release_promote, extension_dev,
extension_build, extension_create, extension_add_feature,
extension_wait, extension_stop, extension_logs, extension_eval,
extension_storage, extension_reload, extension_open,
extension_list_extensions, extension_manifest_validate,
extension_theme_verify and extension_doctor are unchanged in name and in
arguments, and the CLI (extension-mcp login|logout|whoami|release) never
moved at all.
Merged
- Four browser tools are one.
extension_browsersdetects, lists,
installs, and uninstalls.detectandlistwere the confusable pair: both
answered "what browsers do I have", and telling them apart took a sentence of
prose in each description. An action enum settles it in the schema. - Three auth tools are one.
extension_authsigns in, reports the stored
login, and clears it. They were a lifecycle triad that each re-explained the
same token model. - Two template tools are one.
extension_templatessearches the catalog
and reads a template's source. The slug you read comes from the list you just
searched, so the pair is one resource. - The two read-only release tools are one.
extension_release_status
returns release channels and recent builds, browser-store submissions and
review state, or both. They took identical arguments and read the same
registry.extension_release_promotestays separate on purpose: it is the
only verb that writes, and putting a write behind the sameaction
parameter as a read is how an agent promotes a build it meant to list. extension_previewfolded intoextension_start. Both answered "run the
production build in a browser"; the only difference was whether a build ran
first. That is nowbuild, defaulting totrue, which matches
extension_preview_web, wherebuild: falsealready means the same thing.
Sharpened
extension_devandextension_startnow say which one to pick.
They are not merged:devis the only tool that can unlock the control
channel (allowControl,allowEval) thatextension_storage,
extension_reload,extension_open,extension_dom_snapshotand
extension_evalneed, andstartruns a production build with none of it.
Amodeparameter would have made those flags look valid on a session that
cannot honor them. Instead each description now opens with the thing that
decides between them and names the other tool.- Descriptions no longer repeat the schema. The biggest cuts, in bytes of
description:extension_shares1,661 to 1,250,extension_preview_web
1,151 to 639,extension_submit1,478 to 1,194,extension_eval1,128 to
831,extension_wait985 to 784,extension_list_extensions944 to 696,
extension_dom_snapshot965 to 854. What was cut was prose that restated a
parameter name, repeated a property's own description, or explained the
response shape the response already carries. What was kept is anything that
stops a tool being misused: theactiveTabgesture warning on
extension_open, the MV3 service-worker CSP note onextension_eval, the
profile-lock explanation onextension_dev, and the irreversibility of
extension_submitand of revoking a share. - Repeated property schemas are shared.
projectPath, the session
browser, the calltimeout, the platformapibase and the launch browser
enum are defined once insrc/lib/common-schema.tsinstead of being
re-typed...