Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/426-cursor-agent-plugins-expansion.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"agent-bundle": patch
---

Expand Agent Plugins placeholders for Cursor at install time. The `install.mjs` emitted with a `portable` bundle now rewrites `mcp.json` in the `~/.cursor/plugins/local/<name>` copy — `${PLUGIN_ROOT}` to the absolute plugin root, `${PLUGIN_DATA}` to `~/.cursor/agent-bundle/plugin-data/<name>` (created), an omitted `cwd` to the plugin root, plugin-relative `./` commands to absolute paths, and `PLUGIN_ROOT`/`PLUGIN_DATA` into every stdio server's `env` — because Cursor 3.18.25 performs none of that resolution and every spec-shaped stdio server failed to spawn there. The bundle itself is untouched, the pre-expansion document is recorded in the install receipt (`cursorExpansion`), reruns stay idempotent and older unexpanded copies are replaced on the next run. `agent-bundle doctor --host cursor` validates the Agent Plugins contract (`AB7320`) against the recorded document and adds `AB7326` (`expanded` / `unexpanded` / `drifted`) for the launch proof; `cursor`-target bundles are never rewritten. (#482)
34 changes: 34 additions & 0 deletions docs/audits/2026-09-03-agent-plugins-cursor-ide-proof.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,3 +152,37 @@ correct; only the §7.2.1/§9 path contract is missing.
time-gated on a Cursor release; the harness above (`/tmp/w426/iso-setup.sh`
shape: five probes + the emitted pack, reload, read the three log surfaces)
is the procedure to repeat.

## 5. Framework-side support landed after this run (install-time expansion)

Recorded 2026-09-03 (later the same day; the observations above are unchanged).
The maintainer's direction is that a host-side gap the framework can absorb
without contacting the vendor is absorbed. Every failing item in §3 is a path
Cursor hands to `spawn` verbatim, and the control run proved that verbatim
absolute paths connect. The emitted portable `install.mjs` therefore performs
the §7.2.1/§9 resolution itself, for the Cursor copy only:

- Detection: root `plugin.json` with an `https://agent-plugins.org/schemas/`
`$schema` and no `.cursor-plugin/plugin.json`. A `cursor`-target bundle is
never rewritten.
- Rewrite of `mcp.json` in `~/.cursor/plugins/local/<name>`, every `stdio`
server: `${PLUGIN_ROOT}` → the absolute plugin root; `${PLUGIN_DATA}` →
`~/.cursor/agent-bundle/plugin-data/<name>` (created before install, spec
§9.1); omitted `cwd` → the plugin root; `./` in `command` or `cwd` →
resolved against the plugin root; `PLUGIN_ROOT` / `PLUGIN_DATA` added to
`env`. Remote servers and skills-only packs are copied byte-identically.
- The bundle stays as §4 says (spec shape, portable). The artifact inventory
hashes the expanded form so reruns are `Already installed`, same-version
rebuilds are `Replaced`, and an older unexpanded receipt-managed copy is
replaced on the next run. The receipt gains `cursorExpansion`
`{ pluginRoot, pluginData, documents: { "mcp.json": <shipped text> } }`.
- `agent-bundle doctor --host cursor` validates the Agent Plugins contract
(`AB7320`) against the shipped document from the receipt and proves the
expansion (`AB7326`: `expanded`, `unexpanded` for a copy still in spec
shape, `drifted` for a moved/duplicated/edited copy).
- Provenance is `derived` everywhere it is reported
(`portable-1.0.0.json` `plugin.cursorLocalInstall.placeholderExpansion`);
the `mcp.pathTokens` rows keep describing the bundle. The §4 re-run
procedure is unchanged: when a Cursor build expands the placeholders itself,
the installer's rewrite becomes redundant, not wrong, and the capability row
can then cite native expansion.
25 changes: 24 additions & 1 deletion docs/diagnostics.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ even when no error diagnostic was reported.
| `AB7010`–`AB7013` | npm prepack inventory, artifact freshness, package bin targets, and release-version agreement. |
| `AB7200`–`AB7202`, `AB7210`–`AB7211` | Development rebuilds and live host surfaces: rebuild admission and phase failures, development host install sync, and the dev-epoch contract gate (see below). |
| `AB7xxx` | Project preparation and development rebuilds. |
| `AB7300`–`AB7325` | Read-only install Doctor: host probes, installed inventory, bundle comparison and registration proof, runtime endpoint health and identity, durable-state inventory, static bytes-at-rest validation, foreign-install detection (`AB7321`; see below), Cursor plugin hook registration / marketplace staging (`AB7322`–`AB7324`; see below), and host load refusal (`AB7325`; see below). |
| `AB7300`–`AB7326` | Read-only install Doctor: host probes, installed inventory, bundle comparison and registration proof, runtime endpoint health and identity, durable-state inventory, static bytes-at-rest validation, foreign-install detection (`AB7321`; see below), Cursor plugin hook registration / marketplace staging (`AB7322`–`AB7324`; see below), host load refusal (`AB7326`; see below), and the Cursor Agent Plugins launch proof (`AB7326`; see below). |
| `AB8200`–`AB8209` | Workbench development runtime routes (`/api/runtime/**`): `AB8200` development runtime provider configuration, load, or lifecycle failure, `AB8201` runtime/session/run not available, `AB8202` invalid route path, `AB8203` invalid request shape, `AB8204` stale runtime generation or MCP session revision (409), `AB8205` runtime request could not be completed, `AB8206` Workbench runtime client failure, `AB8207` Agent Document decoding needs the optional `@agent-bundle/runtime` peer (503), `AB8208` stored Flight could not be decoded as an Agent Document (409), `AB8209` decoded Agent Document over the 16 MiB budget (413) or an invalid document response. |
| `AB8210`–`AB8214` | Workbench semantic lifecycle replay routes (`/api/lifecycles`, `/api/lifecycles/replays`): `AB8210` invalid path, `AB8211` malformed replay request or native envelope (400, carries the shared validator message), `AB8212` replay unavailable or could not be completed, `AB8213` stale manifest binding (409; the page repairs it with refresh → explicit re-run), `AB8214` replay over the 16 MiB budget (413). |
| `AB8215`–`AB8218` | Workbench read-only host discovery route. |
Expand Down Expand Up @@ -878,3 +878,26 @@ The JSON report exposes the same facts: `hosts[].inventory.findings[].errors`,
`hosts[].bundle.errors`, and `hosts[].bundle.comparison.errors`. The text
report prints the comparison as `installed copy: load failed (installed
<version>, refused by the host: <errors>)`.

## Read-only Doctor Cursor Agent Plugins launch proof (`AB7326`)

Cursor 3.18.25 loads Agent Plugins 1.0.0 packages from
`~/.cursor/plugins/local/<name>` but spawns their stdio servers without
expanding `${PLUGIN_ROOT}` / `${PLUGIN_DATA}` in `args`, `env` values, or
`cwd`, without providing the reserved `PLUGIN_ROOT` / `PLUGIN_DATA` variables
(spec §9.1), with an omitted `cwd` defaulting to the home directory, and with
plugin-relative `./` commands resolved against the workspace folder (spec
§7.2.1); see `docs/audits/2026-09-03-agent-plugins-cursor-ide-proof.md`. The
emitted portable `install.mjs` therefore rewrites `mcp.json` in the Cursor copy
only — absolute plugin root, `~/.cursor/agent-bundle/plugin-data/<name>`
(created) for the data directory, plugin-root `cwd`, resolved `./` command, and
`PLUGIN_ROOT` / `PLUGIN_DATA` in every stdio server's environment — and records
the substituted values plus the pre-expansion document in the install receipt
(`cursorExpansion`). Doctor validates the Agent Plugins contract (`AB7320`)
against that recorded document and proves the expansion against the installed
bytes. Provenance is always `derived`: nothing here claims Cursor expands the
placeholders itself.

| Code | Severity | Meaning | Recovery |
| --- | --- | --- | --- |
| `AB7326` | info / warning / error | Info (`launch.state = expanded`): the receipt's expansion still describes the installed copy — same plugin root, existing data directory, no placeholder left, absolute `cwd` and plugin-root `command`/`args` paths that exist, `PLUGIN_ROOT` / `PLUGIN_DATA` equal to the recorded values. Warning (`unexpanded`): an Agent Plugins install without a recorded expansion whose stdio servers still rely on the spec forms Cursor does not resolve (the message lists the forms per server); Cursor reports `spawn … ENOENT` / `MODULE_NOT_FOUND` for them. Error (`drifted`, entry `corrupt`): the installed `mcp.json` is not byte-identical to the expansion Doctor recomputes from the recorded document (edited, replaced, or removed after install), the recorded expansion names another plugin root (the copy was moved or duplicated), the data directory or an expanded path no longer exists, or the environment no longer carries the recorded values. Only a byte-identical copy has its recorded document validated by `AB7320`; a drifted copy is validated as the bytes on disk. Packages without stdio servers, and copies already carrying absolute paths with the §9.1 variables, produce no finding. | Reinstall with the bundle's emitted `install.mjs` at the copy's current location; the Cursor-target (`.cursor-plugin/plugin.json`) bundle is never rewritten and is not subject to this check. |
20 changes: 17 additions & 3 deletions docs/framework-mode.md
Original file line number Diff line number Diff line change
Expand Up @@ -493,8 +493,19 @@ plus the `AB6038` provenance note), and by `doctor` for installed Cursor
local plugins that declare the standard's `$schema` (`AB7320`; see
`docs/diagnostics.md`). A dogfood proof
against the real Cursor IDE plugin loader (discovery, skill listing, MCP
launch, and three observed Cursor 3.18.25 placeholder-expansion conformance
gaps) is recorded in `docs/audits/2026-09-02-agent-plugins-cursor-ide-proof.md`.
launch, and the observed Cursor 3.18.25 placeholder-expansion conformance
gaps) is recorded in `docs/audits/2026-09-02-agent-plugins-cursor-ide-proof.md`
and `docs/audits/2026-09-03-agent-plugins-cursor-ide-proof.md`. Because Cursor
expands none of `${PLUGIN_ROOT}` / `${PLUGIN_DATA}`, provides no §9.1
variables, defaults an omitted `cwd` to the home directory and resolves `./`
commands against the workspace, the emitted portable `install.mjs` performs
that expansion itself in the `~/.cursor/plugins/local/<name>` copy of
`mcp.json` (absolute plugin root, `~/.cursor/agent-bundle/plugin-data/<name>`
as `PLUGIN_DATA`, plugin-root `cwd`, resolved `./` command, `PLUGIN_ROOT` /
`PLUGIN_DATA` in each stdio server's `env`), keeps the shipped document in the
install receipt (`cursorExpansion`), and `doctor` proves the expansion with
`AB7326` while validating the Agent Plugins contract against the shipped
document. The bundle stays spec-conformant; the provenance is `derived`.

The framework CLI performs those same operations:

Expand Down Expand Up @@ -550,4 +561,7 @@ plugin's manifest hook registration as `registered`, `stale` (a
`~/.cursor/hooks.json` also points into a plugin (duplicate delivery) or is
unparsable; `AB7324` reports a staged marketplace as imported or still
awaiting the Customize step, and `doctor --from` resolves a marketplace-mode
bundle to that staged copy instead of reporting it missing.
bundle to that staged copy instead of reporting it missing. For Agent Plugins
installs, `AB7326` reports the installer's placeholder expansion as
`expanded`, `unexpanded` (spec forms Cursor cannot launch), or `drifted`
(moved, duplicated, or edited after the expansion was recorded).
11 changes: 11 additions & 0 deletions packages/agent-bundle/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,17 @@ Cursor manage the plugin as a marketplace install; `agent-bundle doctor --host
cursor` reports hook registration (`AB7322`), duplicate user-level delivery
(`AB7323`), and marketplace import state (`AB7324`).

The `portable` target's `install.mjs` copies the Agent Plugins package to the
same `~/.cursor/plugins/local/<name>` location and, because Cursor 3.18.25
expands none of the standard's placeholders (`${PLUGIN_ROOT}` /
`${PLUGIN_DATA}` in `args`, `env`, `cwd`; no `PLUGIN_ROOT` / `PLUGIN_DATA`
variables; omitted `cwd` → home directory; `./` commands → workspace folder),
rewrites `mcp.json` in that copy only with absolute paths
(`PLUGIN_DATA` = `~/.cursor/agent-bundle/plugin-data/<name>`, created), keeps
the shipped document in the install receipt (`cursorExpansion`), and
`agent-bundle doctor --host cursor` proves the expansion (`AB7326`). The bundle
itself stays spec-conformant for other Agent Plugins clients.

Cursor installation is user-scoped. Claude also accepts `--scope project` and
`--scope local`; Codex is user-scoped. A source-free artifact root is accepted
by `--from` when it contains the selected host target directory.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,8 @@
"2026-09-02: Cursor 3.18.25 (stable, 280eca2911f1) discovers the emitted plugin, its skill, and its mcp.json from ~/.cursor/plugins/local and spawns the configured stdio server (docs/audits/2026-09-02-agent-plugins-cursor-ide-proof.md).",
"2026-09-02: Cursor 3.18.25 does not expand ${PLUGIN_ROOT} in cwd or args and does not default an omitted cwd to the plugin root (spec 7.2.1/9.2 MUSTs), so spec-conformant stdio servers fail to launch there; its proprietary ${CURSOR_PLUGIN_ROOT} expands and connects.",
"2026-09-02: With a launchable configuration the Cursor IDE completed the stdio handshake (connect_success with stable heartbeat), so the launch pipeline itself consumes this format.",
"2026-09-03: Re-verified on the same Cursor 3.18.25 build (realCommit 280eca2911f1…af3; no newer stable release): the cwd, args, and default-cwd gaps reproduce, and single-variable probes additionally show ${PLUGIN_ROOT}/${PLUGIN_DATA} unexpanded in env values (spec 9.2), the reserved PLUGIN_ROOT/PLUGIN_DATA subprocess variables absent (spec 9.1), and plugin-relative ./ commands resolved against the workspace folder (spec 7.2.1); recorded with log excerpts and captures in docs/audits/2026-09-03-agent-plugins-cursor-ide-proof.md; not submitted to Cursor by maintainer decision."
"2026-09-03: Re-verified on the same Cursor 3.18.25 build (realCommit 280eca2911f1…af3; no newer stable release): the cwd, args, and default-cwd gaps reproduce, and single-variable probes additionally show ${PLUGIN_ROOT}/${PLUGIN_DATA} unexpanded in env values (spec 9.2), the reserved PLUGIN_ROOT/PLUGIN_DATA subprocess variables absent (spec 9.1), and plugin-relative ./ commands resolved against the workspace folder (spec 7.2.1); recorded with log excerpts and captures in docs/audits/2026-09-03-agent-plugins-cursor-ide-proof.md; not submitted to Cursor by maintainer decision.",
"2026-09-03: framework-side support for the Cursor gap (#426): the emitted install.mjs detects an Agent Plugins pack (root plugin.json with an agent-plugins.org $schema, no .cursor-plugin/plugin.json) and rewrites mcp.json in the ~/.cursor/plugins/local/<name> copy only — ${PLUGIN_ROOT} → the absolute plugin root, ${PLUGIN_DATA} → ~/.cursor/agent-bundle/plugin-data/<name> (created by the installer), an omitted cwd → the plugin root, ./ commands → absolute paths under it, and PLUGIN_ROOT/PLUGIN_DATA added to every stdio server's env — because Cursor's spawn honours absolute command, args, cwd and env verbatim (the control run that connected). The bundle stays spec-conformant; the pre-expansion mcp.json is kept in the install receipt (cursorExpansion) and Doctor validates it against the Agent Plugins contract while AB7326 proves the expanded paths (expanded / unexpanded / drifted). Provenance: derived — Cursor still expands nothing itself, so the pathTokens rows above describe the bundle, not what Cursor resolves."
],
"legacySse": {
"reason": "2026-09-02: Agent Plugins 1.0.0 §7.2.1 defines the deprecated HTTP+SSE variant as OPTIONAL for clients; the compiler emits only the required modern transports and rejects a legacy transport with AB4339 (https://agent-plugins.org/specification).",
Expand Down Expand Up @@ -151,6 +152,18 @@
},
"observedSpecificationVersion": "1.0.0",
"plugin": {
"cursorLocalInstall": {
"placeholderExpansion": {
"evidence": [
"2026-09-02/03: Cursor 3.18.25 (docs/audits/2026-09-03-agent-plugins-cursor-ide-proof.md) fails every spec-shaped stdio launch (cwd \"${PLUGIN_ROOT}\" → spawn node ENOENT; args \"${PLUGIN_ROOT}/…\" → MODULE_NOT_FOUND under $HOME; omitted cwd → $HOME; env values and the §9.1 variables unexpanded/absent; \"./…\" commands resolved against the workspace) while the same server with absolute paths connects (connect_success, stable heartbeat).",
"2026-09-03: the emitted install.mjs performs that expansion for the Cursor copy at install time and records it in the receipt; tests/install-surface.test.ts (bundle untouched, copy expanded, idempotent rerun, same-version re-expansion, older unexpanded copies repaired, skills-only and Cursor Plugin bundles untouched), tests/doctor.test.ts (AB7326 unexpanded → expanded → drifted → moved) and the portable host-install proof (isolated Cursor home, Doctor AB7326 info) pin it."
],
"installer": "install.mjs (emitted with the portable bundle)",
"provenance": "derived",
"reason": "Cursor 3.18.25 expands no Agent Plugins placeholder, provides no PLUGIN_ROOT/PLUGIN_DATA, defaults an omitted cwd to the home directory and resolves ./ commands against the workspace (spec §7.2.1/§9); the emitted installer rewrites mcp.json in the ~/.cursor/plugins/local/<name> copy with absolute paths (PLUGIN_DATA = ~/.cursor/agent-bundle/plugin-data/<name>, created), keeps the shipped document in the receipt, and Doctor proves the expanded paths (AB7326). Never claimed as Cursor behaviour: the bundle itself still relies on the consuming client.",
"state": "supported"
}
},
"extensionDirectories": {
"reason": "2026-09-02: Agent Plugins 1.0.0 §8.2 reserves top-level reverse-domain directories for client-owned files; the compiler emits no client extension directory because no pinned client publishes a file-based namespace contract for this format (https://agent-plugins.org/specification).",
"state": "unavailable"
Expand Down
Loading
Loading