Skip to content

Releases: extensiondev/mcp

v10.4.3

Choose a tag to compare

@github-actions github-actions released this 05 Aug 20:00

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_shares dedupes localOnly by artifactId and counts distinct
    shares in its hint; the append-only record file is untouched and
    localRecord reports entries beside shares so both readings stay
    available.
  • add_feature stops 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/urls moves from 0.3.0 to the published 0.6.0, ending
    the last published-versus-checkout divergence the provenance guard
    tracked.

v10.4.2

Choose a tag to compare

@github-actions github-actions released this 04 Aug 23:15

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-develop and extension-install move
    from 4.0.29 to 4.0.30. The nightly engine-pin job added in 10.4.1 is
    what would have caught this drift; the bump is the first it prompted.

v10.4.1

Choose a tag to compare

@github-actions github-actions released this 04 Aug 16:37

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-develop and extension-install move
    from 4.0.20 to 4.0.29, so the agent lane spawns the engine npm serves.
  • A nightly engine-pin job 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

Choose a tag to compare

@github-actions github-actions released this 03 Aug 20:42
abd0494

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_templates list results gain catalogUrl,
    https://templates.extension.dev/<slug>?utm_source=mcp&utm_medium=tool,
    beside repositoryUrl and downloads, which are unchanged. The source
    view and extension_add_feature's reference template carry the same
    field.
  • extension_create results gain templateCatalogUrl for the template
    the project was scaffolded from, with utm_source=mcp-create so
    create-path visits stay distinguishable from browse-path visits.
  • The pinned fallback template corpus moved to 52c0d871c433, the commit
    the live latest channel pointer resolves to, so the offline corpus and
    the catalog agree.

v10.3.1

Choose a tag to compare

@github-actions github-actions released this 03 Aug 01:42
0dc16ec

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.html ships exactly
    the markup it runs, with no comments at all.

v10.3.0

Choose a tag to compare

@github-actions github-actions released this 02 Aug 02:51
9192334

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 login refused a malformed project without 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_doctor diagnoses 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_web certifies 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 read hostReachable and
    previewLoadable as true off whatever JSON came back, then echoed that
    server's identifier, 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 the dist manifest 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-artifact with previewLoadable: false, and
    anything the host claimed that could not be confirmed locally travels
    clipped under probe.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 stale dist. dist/module.js now exports
    the manifest version it inlined at build time, and the gate reads it back
    and compares.

v10.2.0

Choose a tag to compare

@github-actions github-actions released this 01 Aug 21:10
79a818e

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 apex extension.dev answers
    DELETE with a redirect, and a caller that does not follow redirects got
    "Redirecting..." back while the share stayed live.
  • extension_doctor hands 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_inspect with no url ranks 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_publish against a host where the token's project does not
    exist now says what to do: check the token's scope with extension_auth,
    create the project first at extension.dev/new, or log in against a
    project that exists there.
  • extension_auth status asks 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 isError flag now agrees with the envelope: every ok: false
    result is marked isError: 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_web with share: true no longer fails a working
    share because the local dev lane is dead: the uploaded link is what was
    asked for, so the result is ok: true with status shared and 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: hostUrl is 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

Choose a tag to compare

@github-actions github-actions released this 27 Jul 18:48

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_wait now 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_create never 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_submit dry 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 a projectPath input so the
    STORE.md advisory check reads the project, not the server's cwd.
  • manifest.json with world: "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_publish with a pinned buildSha no longer fills the response
    with a different build's metadata when the pin is not in the local index.
  • extension_shares no longer labels your own expired shares as "not owned
    by this token" when listing with status: "live", and revocation is only
    reported permanent when the platform confirmed it.
  • extension_logs accepts the newtab, history, and bookmarks
    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_stop and 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 as all: 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_open trusts 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 are ai-claude and ai-chatgpt, Firefox
    support noted for extension_list_extensions and Safari for
    extension_submit, and the extension_dom_snapshot description names
    which subpaths need the debug port.

v10.0.0

Choose a tag to compare

@github-actions github-actions released this 27 Jul 13:21

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

Choose a tag to compare

@github-actions github-actions released this 26 Jul 17:16
fe3f491

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_status nests what the two 7.0.0 tools returned flat,
    under releases and stores. The bodies inside are byte-for-byte the old
    ones. Omitting include returns both sections.
  • extension_start gained build, defaulting to true. build: false is
    what extension_preview was.

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_browsers detects, lists,
    installs, and uninstalls. detect and list were 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_auth signs 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_templates searches 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_promote stays separate on purpose: it is the
    only verb that writes, and putting a write behind the same action
    parameter as a read is how an agent promotes a build it meant to list.
  • extension_preview folded into extension_start. Both answered "run the
    production build in a browser"; the only difference was whether a build ran
    first. That is now build, defaulting to true, which matches
    extension_preview_web, where build: false already means the same thing.

Sharpened

  • extension_dev and extension_start now say which one to pick.
    They are not merged: dev is the only tool that can unlock the control
    channel (allowControl, allowEval) that extension_storage,
    extension_reload, extension_open, extension_dom_snapshot and
    extension_eval need, and start runs a production build with none of it.
    A mode parameter 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_shares 1,661 to 1,250, extension_preview_web
    1,151 to 639, extension_submit 1,478 to 1,194, extension_eval 1,128 to
    831, extension_wait 985 to 784, extension_list_extensions 944 to 696,
    extension_dom_snapshot 965 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: the activeTab gesture warning on
    extension_open, the MV3 service-worker CSP note on extension_eval, the
    profile-lock explanation on extension_dev, and the irreversibility of
    extension_submit and of revoking a share.
  • Repeated property schemas are shared. projectPath, the session
    browser, the call timeout, the platform api base and the launch browser
    enum are defined once in src/lib/common-schema.ts instead of being
    re-typed...
Read more