diff --git a/.changeset/426-portable-followups.md b/.changeset/426-portable-followups.md new file mode 100644 index 000000000..a0382bf99 --- /dev/null +++ b/.changeset/426-portable-followups.md @@ -0,0 +1,5 @@ +--- +'agent-bundle': patch +--- + +Refresh the `portable` (Agent Plugins 1.0.0) capability table and schema provenance with the 2026-09-03 re-verification: the Cursor 3.18.25 `${PLUGIN_ROOT}` gaps in `cwd`, `args`, and the default working directory reproduce on the current build, and the `mcp` evidence now also records that `env` values are not expanded, the reserved `PLUGIN_ROOT`/`PLUGIN_DATA` variables are not provided, and plugin-relative `./` commands resolve against the workspace; the pinned 1.0.0 schemas were rehashed against the live specification site with no published 1.1.0 release, so the pin and `adapterRevision` are unchanged and the `AB6038` provenance info emitted by `validate` for portable artifacts now reads "re-verified 2026-09-03" (#443). diff --git a/docs/assets/agent-plugins-cursor-proof/2026-09-03-customize-hooks-tab.png b/docs/assets/agent-plugins-cursor-proof/2026-09-03-customize-hooks-tab.png new file mode 100644 index 000000000..64e7e6328 Binary files /dev/null and b/docs/assets/agent-plugins-cursor-proof/2026-09-03-customize-hooks-tab.png differ diff --git a/docs/assets/agent-plugins-cursor-proof/2026-09-03-customize-probe-plugins.png b/docs/assets/agent-plugins-cursor-proof/2026-09-03-customize-probe-plugins.png new file mode 100644 index 000000000..4126538dc Binary files /dev/null and b/docs/assets/agent-plugins-cursor-proof/2026-09-03-customize-probe-plugins.png differ diff --git a/docs/assets/agent-plugins-cursor-proof/2026-09-03-customize-search-discovery.png b/docs/assets/agent-plugins-cursor-proof/2026-09-03-customize-search-discovery.png new file mode 100644 index 000000000..193541071 Binary files /dev/null and b/docs/assets/agent-plugins-cursor-proof/2026-09-03-customize-search-discovery.png differ diff --git a/docs/assets/agent-plugins-cursor-proof/2026-09-03-plugin-detail-cursor-placeholder-connected.png b/docs/assets/agent-plugins-cursor-proof/2026-09-03-plugin-detail-cursor-placeholder-connected.png new file mode 100644 index 000000000..f94a34fc8 Binary files /dev/null and b/docs/assets/agent-plugins-cursor-proof/2026-09-03-plugin-detail-cursor-placeholder-connected.png differ diff --git a/docs/assets/agent-plugins-cursor-proof/2026-09-03-plugin-detail-spec-shape-error.png b/docs/assets/agent-plugins-cursor-proof/2026-09-03-plugin-detail-spec-shape-error.png new file mode 100644 index 000000000..1ebe323cd Binary files /dev/null and b/docs/assets/agent-plugins-cursor-proof/2026-09-03-plugin-detail-spec-shape-error.png differ diff --git a/docs/audits/2026-09-02-agent-plugins-cursor-ide-proof.md b/docs/audits/2026-09-02-agent-plugins-cursor-ide-proof.md index 7ab51c26e..9bfef918e 100644 --- a/docs/audits/2026-09-02-agent-plugins-cursor-ide-proof.md +++ b/docs/audits/2026-09-02-agent-plugins-cursor-ide-proof.md @@ -72,3 +72,22 @@ from any spec-conformant portable package fail to launch on Cursor even though the plugin, its skills, and its MCP configuration are all discovered and surfaced correctly. This is tracked as adoption-issue evidence, not as an adapter defect. + +## Re-verification 2026-09-03 (#426) + +Repeated on the same installed build (3.18.25, `realCommit` +`280eca2911f1774689696e5f1efa5a4f97a87af3`; no newer stable build was +available and the rendered Cursor changelog through Sep 2, 2026 carries no +plugin entry) with the emitted pack plus five single-variable probe plugins. +All three gaps above reproduced byte-for-byte (`spawn node ENOENT` for the +`cwd` shape, `Cannot find module '/${PLUGIN_ROOT}/…'` for the `args` +shape, default `cwd` = `HOME`). The widened probe added three observations on +the same build: `${PLUGIN_ROOT}`/`${PLUGIN_DATA}` are not expanded in `env` +values (§9.2), the reserved `PLUGIN_ROOT`/`PLUGIN_DATA` subprocess variables +are not provided (§9.1), and a plugin-relative `./` `command` is resolved +against the workspace folder instead of the plugin root (§7.2.1). Full +table, log excerpts, and 1440×900 captures: +[`2026-09-03-agent-plugins-cursor-ide-proof.md`](./2026-09-03-agent-plugins-cursor-ide-proof.md); +ready-to-submit vendor report: +[`2026-09-03-cursor-agent-plugins-feedback.md`](./2026-09-03-cursor-agent-plugins-feedback.md). +No capability row moved to `supported`. diff --git a/docs/audits/2026-09-03-agent-plugins-cursor-ide-proof.md b/docs/audits/2026-09-03-agent-plugins-cursor-ide-proof.md new file mode 100644 index 000000000..60eb216c7 --- /dev/null +++ b/docs/audits/2026-09-03-agent-plugins-cursor-ide-proof.md @@ -0,0 +1,154 @@ +# Cursor IDE dogfood re-run — portable Agent Plugins target (#426) + +Date: 2026-09-03. Observed client: Cursor **3.18.25** stable, Linux x64 deb +install at `/usr/share/cursor` (`product.json` `commit` +`280eca2911f1774689696e5f1efa5a4f97a87af0`, `realCommit` +`280eca2911f1774689696e5f1efa5a4f97a87af3`) — the same build the +[2026-09-02 proof](./2026-09-02-agent-plugins-cursor-ide-proof.md) observed. +No newer stable build was installed on this machine, and the rendered + (Aug 13 – Sep 2, 2026 entries) contains no +plugin or placeholder-expansion entry, so this run is a re-verification on the +unchanged build rather than the "when a Cursor release notes placeholder +expansion" re-run the tracker asks for; that re-run stays open (see the +consequence section). + +Harness: isolated instance — dedicated `HOME=/tmp/w426/iso/home`, dedicated +`--user-data-dir=$HOME/.config/Cursor`, `Xvfb :98 -screen 0 1440x900x24`, +`--remote-debugging-port=9334`, workspace `/tmp/w426/iso/ws`, window sized to +1440×900 (`innerWidth`/`innerHeight` confirmed before every capture; no capture +was taken while a loading state was visible). The real `~/.cursor` and +`~/.config/Cursor` were read only for the auth-row transplant the +[#407 harness](./2026-09-03-cursor-plugin-hooks-registration.md) documents. +Package under test: the `portable` target of the +`tests/fixtures/host-install-portable` fixture (`plugin.json` name +`host-install-portable-proof`, version `1.0.0`, `$schema` +`https://agent-plugins.org/schemas/1.0.0/plugin.schema.json`), built with the +workspace CLI (`dist/cli.js build`) from `origin/main` `4edbd493b`. + +## 1. Install paths exercised + +| Path | Command (isolated `HOME`) | Result | +| --- | --- | --- | +| Emitted installer, local mode (default) | `node ./install.mjs` | `Installed host-install-portable-proof@1.0.0 at ~/.cursor/plugins/local/host-install-portable-proof (content 6c00940d94ef)`; second run `Already installed …` (same content hash); receipt `.agent-bundle-install.json` written with `format: agent-bundle-install-receipt/1`. | +| Emitted installer, marketplace mode | `node ./install.mjs --mode marketplace` | Fails closed (exit 1) before touching the filesystem: "`--mode marketplace` requires a Cursor Plugin (`.cursor-plugin/plugin.json`); Cursor marketplaces resolve `plugins//.cursor-plugin/plugin.json`. This bundle is an Agent Plugins (root `plugin.json`) pack: use the default local mode." Cursor's marketplace manifest (`.cursor-plugin/marketplace.json`, ) resolves only Cursor Plugin manifests, so there is no marketplace-mode path for this format to complete, interactively or not. | +| `agent-bundle install cursor --from --mode local` / `--mode marketplace` | CLI from `packages/agent-bundle/bin/agent-bundle.js` | Both refuse with `AB7001` ("No cursor bundle manifest was found in …, its `cursor` target directory, or its `plugin` target directory"): `install cursor` is the Cursor Plugin (`.cursor-plugin/plugin.json`) entry point; Agent Plugins packs are delivered by their emitted `install.mjs`, exactly as `INSTALL.md` in the artifact says. | +| `agent-bundle doctor --host cursor` | after the local install | `0 error(s)`; one `AB7320` info per Agent Plugins entry: "Cursor plugin entry … is a root `plugin.json` declaring `https://agent-plugins.org/schemas/1.0.0/plugin.schema.json`, which Cursor loads as an Agent Plugins package; Doctor validated it against the pinned Agent Plugins 1.0.0 contract." Inventory finding: `{ entry, manifest: 'plugin.json', name, state: 'installed', version: '1.0.0' }`. | + +Installed tree (six files, no `hooks/` directory, no `hooks.json`, no +`.cursor-plugin/`): `INSTALL.md`, `install.mjs`, `mcp.json`, +`mcp/mcp-probe-ba9c736f.mjs`, `plugin.json`, `skills/probe/SKILL.md`. The +fixture config declares a `sessionStart` hook; the portable emission drops it +because Agent Plugins 1.0.0 §7 defines only Skills and MCP servers, which the +repeatable proof asserts as `hooks: 'not-emitted'` +(`tests/host-install-proof.test.ts`). + +Emitted `mcp.json` (spec shape, unchanged from the 2026-09-02 proof): + +```json +{ + "$schema": "https://agent-plugins.org/schemas/1.0.0/mcp.schema.json", + "mcpServers": { + "probe": { + "type": "stdio", + "command": "node", + "args": ["mcp/mcp-probe-ba9c736f.mjs"], + "cwd": "${PLUGIN_ROOT}", + "env": { "AGENT_BUNDLE_PLUGIN_ROOT": "${PLUGIN_ROOT}" } + } + } +} +``` + +## 2. What the IDE established (unchanged from 2026-09-02) + +Plugins placed under `~/.cursor/plugins/local` were not picked up by the +first-boot plugin census (`workbench.mcp.files.log`: `Plugin MCP census: +{"trigger":"reset","providerOutcome":"timeout","descriptorsReceived":0,…}`); a +`Developer: Reload Window` produced +`{"trigger":"plugins_changed","providerOutcome":"ok","descriptorsReceived":19, +"pluginServersAssigned":19,…}` and every local plugin's stdio server was +spawned. Reload-after-copy is therefore still the loading contract, as +`INSTALL.md` states. + +1. **Discovery as an Agent Plugin.** Customize lists "Host Install Portable + Proof" with the `Local` badge; searching `host-install` returns the plugin + (`Plugins 1`) and its skill (`Skills 1`). + ![Customize search discovery](../assets/agent-plugins-cursor-proof/2026-09-03-customize-search-discovery.png) +2. **Skill discovery.** `probe` is listed with the exact frontmatter + description "Confirm that the portable host-install proof fixture was + discovered." +3. **MCP configuration discovery and launch attempt.** The plugin detail page + shows `MCPs 1 (probe)` and `Skills 1 (probe)`; with the spec-shaped + `mcp.json` the server row reads `Error - Show Output` and the per-server + log records `spawn node ENOENT` (section 3, gap 1). + ![Plugin detail, spec shape](../assets/agent-plugins-cursor-proof/2026-09-03-plugin-detail-spec-shape-error.png) +4. **Honest absence of unsupported surfaces.** The detail page renders only + MCPs and Skills for this plugin. The Customize **Hooks** tab lists one hook + (`superpowers` `sessionStart`, an account marketplace plugin) and nothing + attributed to any Agent Plugins entry; a `probe` search returns Plugins, + MCPs, and Skills sections only. The pack ships no hooks, Cursor attributes + none, and Doctor's `hooks` registration field (emitted only for + `.cursor-plugin/plugin.json` entries) is absent from the finding — the + `AB7320` info states which contract was applied. + ![Hooks tab](../assets/agent-plugins-cursor-proof/2026-09-03-customize-hooks-tab.png) +5. **Successful stdio handshake with a launchable configuration.** Rewriting + only the installed copy's `mcp.json` to `args: + ["${CURSOR_PLUGIN_ROOT}/mcp/mcp-probe-ba9c736f.mjs"]` (no `cwd`, env value + `${CURSOR_PLUGIN_ROOT}`) and reloading produced `Successfully connected to + stdio server` / `connection:connect_success` with a heartbeat + (`exthost/anysphere.cursor-mcp/MCP plugin-host-install-portable-proof-probe.log`), + and the detail row turned green ("0 tools enabled" — the fixture server + registers no tools). + ![Plugin detail, connected](../assets/agent-plugins-cursor-proof/2026-09-03-plugin-detail-cursor-placeholder-connected.png) + +## 3. Placeholder-expansion re-verification (five probe plugins) + +Five hand-written Agent Plugins 1.0.0 packs (`ap-probe-*`, root +`plugin.json` + `mcp.json` + one skill) were installed next to the emitted +pack, one variable each. Each `mcp.json` is a **valid** 1.0.0 server entry +(bare `command`, placeholders only in `args`/`env`/`cwd`) except the control, +so a conformant client MUST launch all of them (§7.2.1, §9.2, §11.1). The +`report.mjs` payload appends `{ cwd, argv, env }` to a marker file and then +idles on stdin; the inline `-e` probe does the same without depending on +`args` or `cwd` expansion. Evidence: `exthost/anysphere.cursor-mcp/MCP +plugin--probe.log`, `mcp-server-plugin--probe.workbench.log`, and +the marker file `launches.jsonl`. + +| Probe | Server entry | Observed on 3.18.25 | Spec MUST | Status | +| --- | --- | --- | --- | --- | +| `ap-probe-cwd` (and the emitted pack) | `command: node`, `args: ["mcp/report.mjs"]`, `cwd: "${PLUGIN_ROOT}"` | `Connection failed: spawn node ENOENT` — the literal `${PLUGIN_ROOT}` is passed as the working directory, so the spawn fails before `PATH` lookup matters (the same bare `node` spawns fine in the two probes without `cwd`). | §7.2.1 "Clients MUST expand placeholders before resolving `cwd`"; §9.2 expansion applies to the `cwd` string. | **gap 1 persists** | +| `ap-probe-args` | `command: node`, `args: ["${PLUGIN_ROOT}/mcp/report.mjs"]`, no `cwd` | Node starts and exits: `Error: Cannot find module '/tmp/w426/iso/home/${PLUGIN_ROOT}/mcp/report.mjs'` (`MODULE_NOT_FOUND`), then `MCP error -32000: Connection closed`. | §9.2 expansion applies to "every string element of `args`". | **gap 2 persists** | +| `ap-probe-default-cwd` | `command: node`, `args: ["-e", ""]`, no `cwd`, `env: { PROBE_ROOT: "${PLUGIN_ROOT}", PROBE_DATA: "${PLUGIN_DATA}" }` | Marker: `cwd = /tmp/w426/iso/home` (the `HOME` of the instance, not the plugin root). | §7.2.1 "When `cwd` is omitted, clients MUST use the plugin root as the subprocess working directory." | **gap 3 persists** | +| same probe, env values | as above | Marker: `PROBE_ROOT = "${PLUGIN_ROOT}"`, `PROBE_DATA = "${PLUGIN_DATA}"` (literal). | §9.2 expansion applies to "every string value in `env`". | **gap 4 (new observation)** | +| same probe, reserved variables | as above | Marker: `process.env.PLUGIN_ROOT` and `process.env.PLUGIN_DATA` are `undefined`. | §9.1 "Clients that launch plugin subprocesses … MUST provide `PLUGIN_ROOT` and `PLUGIN_DATA` in each subprocess environment." | **gap 5 (new observation)** | +| `ap-probe-command` | `command: "./mcp/launch.sh"`, `args: []` | `Connection failed: spawn /tmp/w426/iso/ws/mcp/launch.sh ENOENT` — the plugin-relative command is resolved against the **workspace folder**, not the plugin root. | §7.2.1 "Clients … MUST resolve plugin-relative paths against the plugin root." | **gap 6 (new observation)** | +| `ap-probe-cursor-root` (control, not spec-conformant) | `command: node`, `args: ["${CURSOR_PLUGIN_ROOT}/mcp/report.mjs"]`, `env: { PROBE_ROOT: "${CURSOR_PLUGIN_ROOT}" }` | Marker: `argv[1] = /tmp/w426/iso/home/.cursor/plugins/local/ap-probe-cursor-root/mcp/report.mjs`, `PROBE_ROOT` expanded to the same root, `cwd = /tmp/w426/iso/home`. | n/a — Cursor's proprietary placeholder. | expansion pipeline exists and is wired to `${CURSOR_PLUGIN_ROOT}` only | + +![Probe plugins in Customize](../assets/agent-plugins-cursor-proof/2026-09-03-customize-probe-plugins.png) + +Read-out: the three gaps recorded on 2026-09-02 are reproduced byte-for-byte on +the same build, and the widened probe set shows the same root cause on every +§9 surface: Cursor's Agent Plugins loader performs no `${PLUGIN_ROOT}` / +`${PLUGIN_DATA}` expansion anywhere (`args`, `env`, `cwd`), does not inject +the §9.1 reserved variables, and resolves both the omitted `cwd` and +plugin-relative `./` commands against the wrong base (`HOME` and the +workspace folder respectively). Discovery, skills, and the launch pipeline are +correct; only the §7.2.1/§9 path contract is missing. + +## 4. Consequence for the portable adapter and the tracker + +- The emission stays as it is (`cwd: "${PLUGIN_ROOT}"`, cwd-relative `args`, + `${PLUGIN_ROOT}` in `env`): it is the specification's own example shape and + the only shape a conformant client is required to launch. Rewriting to + `${CURSOR_PLUGIN_ROOT}` would make the portable pack non-portable and is not + done. +- Capability rows (`packages/agent-bundle/src/adapters/capabilities/portable-1.0.0.json`, + `mcp.evidence`) gain a dated 2026-09-03 line for the re-verification and the + three additional observations; no row moves to `supported` because nothing + was fixed. +- The vendor feedback that #426 tracks is written up ready to submit in + [`2026-09-03-cursor-agent-plugins-feedback.md`](./2026-09-03-cursor-agent-plugins-feedback.md). +- The "re-run when a Cursor release notes placeholder expansion" item stays + 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. diff --git a/docs/audits/2026-09-03-cursor-agent-plugins-feedback.md b/docs/audits/2026-09-03-cursor-agent-plugins-feedback.md new file mode 100644 index 000000000..b0a0976a7 --- /dev/null +++ b/docs/audits/2026-09-03-cursor-agent-plugins-feedback.md @@ -0,0 +1,266 @@ +# Cursor product feedback — Agent Plugins 1.0.0 `${PLUGIN_ROOT}` expansion (ready to submit) + +Prepared 2026-09-03 for #426. Everything below was observed on the current +installed stable build and is reproducible with the plugin in "Minimal +repro" (one directory, four files — three for items 1–5 plus `mcp/launch.sh` +for item 6 — no dependencies beyond `node`). The maintainer submits +it; this repository cannot post on their behalf. + +**Where to submit (pick one, template-compatible):** + +- Bug report (preferred — every item below is a MUST violation with a + deterministic repro): → + "New Topic" (direct link + ). The + forum's bug template asks for product area, description, steps, expected + behaviour, screenshots, and the About-dialog version block; all of it is + filled in below. Reporting guidance: . +- Feature request, if triaged as "Agent Plugins §9 support" rather than a + defect: . +- Email fallback named by the reporting guide: `hi@cursor.com`. + +Attach the two screenshots from +`docs/assets/agent-plugins-cursor-proof/2026-09-03-plugin-detail-spec-shape-error.png` +and `…/2026-09-03-plugin-detail-cursor-placeholder-connected.png`. + +--- + +## Bug report body + +**Where does the bug appear:** Cursor IDE → MCP & tools (plugin loader for +Agent Plugins packages placed in `~/.cursor/plugins/local`). + +**Version block (Menu → About Cursor):** Cursor 3.18.25, Linux x64 (deb, +`/usr/share/cursor`), commit `280eca2911f1774689696e5f1efa5a4f97a87af0` +(`realCommit` `280eca2911f1774689696e5f1efa5a4f97a87af3`). OS: Linux 7.0.0 +(Xvfb display for the isolated instance; the behaviour does not depend on +the display). + +**Summary.** Cursor discovers Agent Plugins 1.0.0 packages (root +`plugin.json` declaring +`https://agent-plugins.org/schemas/1.0.0/plugin.schema.json`), lists their +Skills and MCP servers in Customize, and spawns the configured stdio +servers — but the spawn ignores every path rule in Agent Plugins §7.2.1 and +§9 (). As a result **no +spec-conformant stdio server that refers to a file shipped in the plugin can +start on Cursor** — every such reference goes through `cwd`, `args`, `env`, +or a plugin-relative `command`, and none of those is resolved against the +plugin root. Only a server that names a globally installed executable and +needs nothing from the package (item 3's inline `node -e` probe) starts; it +still runs from the wrong directory without the §9.1 variables. The same +bundled server starts as soon as the spec placeholder is replaced by Cursor's +proprietary `${CURSOR_PLUGIN_ROOT}`. The standard's own stdio example +(`"cwd": "${PLUGIN_ROOT}"`) fails. Six concrete observations follow; 1–3 were +first recorded on 2026-09-02 and re-verified today, 4–6 were found while +widening the probe. + +**Steps to reproduce (all items).** + +1. Create `~/.cursor/plugins/local/ap-probe/` with the files in "Minimal + repro" below: `plugin.json`, `mcp.json`, and `mcp/report.mjs` cover items + 1–5; item 6 additionally needs the executable `mcp/launch.sh` listed + there. Swap the `probe` entry in `mcp.json` for the item's payload. +2. Reload the window (`Developer: Reload Window`) — local plugins are read on + reload, not on first boot. +3. Open Customize → Plugins → "Ap Probe" (badge `Local`) → MCPs → `probe` + → "Show Output", and read + `/logs//window1/exthost/anysphere.cursor-mcp/MCP plugin-ap-probe-probe.log`. +4. For items 3–5 read the marker file the probe writes (`/tmp/ap-probe-launch.jsonl`). + +### 1. `${PLUGIN_ROOT}` is not expanded in `cwd` + +- **Spec:** §7.2.1 — "Clients MUST expand placeholders before resolving + `cwd`." and "The `args`, `env`, and `cwd` fields in a stdio server + configuration MUST support `${PLUGIN_ROOT}` and `${PLUGIN_DATA}` + expansion." §9.2 — "Expansion applies to … the `cwd` string." The + specification's stdio example uses `"cwd": "${PLUGIN_ROOT}"`. +- **Payload:** + `{"type":"stdio","command":"node","args":["mcp/report.mjs"],"cwd":"${PLUGIN_ROOT}"}` +- **Observed:** `Connection failed: spawn node ENOENT` / + `createClient completed … connected=false, statusType=error, error=spawn node ENOENT`. + The literal string `${PLUGIN_ROOT}` is handed to the child process as its + working directory; the spawn fails before the executable lookup, which is + why an existing `node` reports ENOENT (the identical bare `node` spawns in + items 2–3, which omit `cwd`). Customize shows the server as + `Error - Show Output`. +- **Expected:** `cwd` resolved to + `/home//.cursor/plugins/local/ap-probe`; the server starts and the + marker records that directory as `cwd`. + +### 2. `${PLUGIN_ROOT}` is not expanded in `args` + +- **Spec:** §9.2 — "Clients that launch plugin subprocesses MUST expand + `${PLUGIN_ROOT}` and `${PLUGIN_DATA}` in supported configuration fields. + … Expansion applies to every string element of `args`". +- **Payload:** + `{"type":"stdio","command":"node","args":["${PLUGIN_ROOT}/mcp/report.mjs"]}` +- **Observed:** node starts with the literal argument and exits: + `Error: Cannot find module '/home//${PLUGIN_ROOT}/mcp/report.mjs'` + (`code: 'MODULE_NOT_FOUND'`), followed by + `Connection failed: MCP error -32000: Connection closed`. (The resolved + path also shows the default working directory is the home directory — + item 3.) +- **Expected:** `args[0]` = + `/home//.cursor/plugins/local/ap-probe/mcp/report.mjs`; the server + starts. + +### 3. Omitted `cwd` defaults to the home directory, not the plugin root + +- **Spec:** §7.2.1 — "When `cwd` is omitted, clients MUST use the plugin + root as the subprocess working directory." +- **Payload:** + `{"type":"stdio","command":"node","args":["-e","require('fs').appendFileSync('/tmp/ap-probe-launch.jsonl', JSON.stringify({cwd:process.cwd(),env:process.env})+'\\n'); process.stdin.resume();"]}` +- **Observed:** marker `cwd = /home/` (the process `HOME`; in the + isolated instance `/tmp/w426/iso/home`). +- **Expected:** `cwd = /home//.cursor/plugins/local/ap-probe`. + +### 4. `${PLUGIN_ROOT}` / `${PLUGIN_DATA}` are not expanded in `env` values + +- **Spec:** §9.2 — "Expansion applies to … every string value in `env`". +- **Payload:** item 3's entry plus + `"env":{"PROBE_ROOT":"${PLUGIN_ROOT}","PROBE_DATA":"${PLUGIN_DATA}"}` +- **Observed:** marker `PROBE_ROOT = "${PLUGIN_ROOT}"`, + `PROBE_DATA = "${PLUGIN_DATA}"` (literal). With + `"PROBE_ROOT":"${CURSOR_PLUGIN_ROOT}"` the same variable arrives expanded, + so the env-expansion pipeline exists but is keyed to the proprietary name. +- **Expected:** `PROBE_ROOT = /home//.cursor/plugins/local/ap-probe`, + `PROBE_DATA` = the client-managed data directory for this plugin. + +### 5. Reserved `PLUGIN_ROOT` / `PLUGIN_DATA` variables are not provided + +- **Spec:** §9.1 — "Clients that launch plugin subprocesses (i.e., stdio MCP + servers) MUST provide `PLUGIN_ROOT` and `PLUGIN_DATA` in each subprocess + environment. … It MUST create the [`PLUGIN_DATA`] directory before + launching a plugin subprocess". +- **Payload:** item 3's entry (no `env` needed). +- **Observed:** `process.env.PLUGIN_ROOT` and `process.env.PLUGIN_DATA` are + `undefined` in the child. +- **Expected:** both set to absolute paths, `PLUGIN_DATA` created and + writable. + +### 6. Plugin-relative `./` commands resolve against the workspace folder + +- **Spec:** §7.2.1 — "[`command`] MUST be either a bare executable name or a + plugin-relative path beginning with `./`. Clients … MUST resolve + plugin-relative paths against the plugin root." (No placeholder expansion + applies to `command`, so this is the only portable way to launch a bundled + executable.) +- **Payload:** `{"type":"stdio","command":"./mcp/launch.sh","args":[]}` with + the executable `mcp/launch.sh` from "Minimal repro" in the plugin + (`chmod +x`). +- **Observed:** `Connection failed: spawn /home///mcp/launch.sh ENOENT` + (isolated instance: `spawn /tmp/w426/iso/ws/mcp/launch.sh ENOENT`) — the + path is joined to the open workspace folder. +- **Expected:** `/home//.cursor/plugins/local/ap-probe/mcp/launch.sh` + is executed. + +### Control: the proprietary placeholder works + +`{"type":"stdio","command":"node","args":["${CURSOR_PLUGIN_ROOT}/mcp/report.mjs"],"env":{"PROBE_ROOT":"${CURSOR_PLUGIN_ROOT}"}}` +starts, the marker shows `argv[1]` and `PROBE_ROOT` expanded to +`…/plugins/local/ap-probe`, and the connection reaches +`connection:connect_success` with a stable heartbeat. The fix is therefore a +mapping of `${PLUGIN_ROOT}`/`${PLUGIN_DATA}` (and the §9.1 variables, the +default `cwd`, and `./` command resolution) onto the pipeline that already +serves `${CURSOR_PLUGIN_ROOT}` when the package is an Agent Plugins package +(`$schema` under `https://agent-plugins.org/schemas/`). + +### Why it matters + +Any tool that emits spec-conformant Agent Plugins packages (this project's +`portable` target, and any other publisher following +) produces packs whose Skills load in Cursor but +whose bundled MCP servers — anything that ships its own script or binary and +therefore addresses it through `cwd`, `args`, `env`, or a `./` command — +cannot start there, with a misleading `spawn node ENOENT`. Publishers cannot +work around it without shipping a Cursor-specific copy, which defeats the +portable format. + +--- + +## Minimal repro plugin + +Four files, no dependencies beyond `node` on `PATH`; `mcp/launch.sh` is only +needed for item 6. + +`~/.cursor/plugins/local/ap-probe/plugin.json` + +```json +{ + "$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json", + "name": "ap-probe", + "version": "1.0.0", + "description": "Agent Plugins 1.0.0 placeholder-expansion probe." +} +``` + +`~/.cursor/plugins/local/ap-probe/mcp/report.mjs` + +```js +import { appendFileSync } from 'node:fs'; +appendFileSync('/tmp/ap-probe-launch.jsonl', JSON.stringify({ + argv: process.argv.slice(1), + cwd: process.cwd(), + env: { + PLUGIN_DATA: process.env.PLUGIN_DATA ?? null, + PLUGIN_ROOT: process.env.PLUGIN_ROOT ?? null, + PROBE_DATA: process.env.PROBE_DATA ?? null, + PROBE_ROOT: process.env.PROBE_ROOT ?? null, + }, +}) + '\n'); +process.stdin.resume(); // stay alive like a stdio server; the handshake time-out is expected +``` + +`~/.cursor/plugins/local/ap-probe/mcp/launch.sh` (item 6 only; `chmod +x mcp/launch.sh`) + +```sh +#!/usr/bin/env bash +# Records where the client started us, then runs the reporter next to this script. +export PROBE_LAUNCH_PWD="$PWD" +exec node "$(dirname "$0")/report.mjs" +``` + +A conformant client resolves `./mcp/launch.sh` against the plugin root and the +marker line appears; Cursor 3.18.25 reports +`spawn /mcp/launch.sh ENOENT` and no line is written. (To confirm +the file itself is fine, `cd ~/.cursor/plugins/local/ap-probe && ./mcp/launch.sh` +writes the marker from a shell.) + +`~/.cursor/plugins/local/ap-probe/mcp.json` — the specification's shape +(item 1); swap the `probe` entry for the payload of any other item. + +```json +{ + "$schema": "https://agent-plugins.org/schemas/1.0.0/mcp.schema.json", + "mcpServers": { + "probe": { + "type": "stdio", + "command": "node", + "args": ["mcp/report.mjs"], + "cwd": "${PLUGIN_ROOT}", + "env": { "PROBE_ROOT": "${PLUGIN_ROOT}", "PROBE_DATA": "${PLUGIN_DATA}" } + } + } +} +``` + +Expected result of a conformant client: one line in +`/tmp/ap-probe-launch.jsonl` with `cwd` = the plugin root, `PROBE_ROOT` = +the plugin root, `PROBE_DATA` and `PLUGIN_DATA` = an existing writable +directory, `PLUGIN_ROOT` = the plugin root. Observed on 3.18.25: no line +(spawn fails on `cwd`). + +--- + +## Evidence retained in this repository + +- Re-verification run and log excerpts: + [`2026-09-03-agent-plugins-cursor-ide-proof.md`](./2026-09-03-agent-plugins-cursor-ide-proof.md) + (section 3 table) and the original observation + [`2026-09-02-agent-plugins-cursor-ide-proof.md`](./2026-09-02-agent-plugins-cursor-ide-proof.md). +- Capability table: `packages/agent-bundle/src/adapters/capabilities/portable-1.0.0.json` + (`mcp.evidence`). +- Cursor's own documentation of the proprietary placeholder: + (`${CURSOR_PLUGIN_ROOT}` expansion in + plugin-delivered commands) and + ("put either plugin format in `~/.cursor/plugins/local`"). diff --git a/docs/effect-conventions.md b/docs/effect-conventions.md index 0ae16a177..93c08a400 100644 --- a/docs/effect-conventions.md +++ b/docs/effect-conventions.md @@ -360,3 +360,4 @@ soon as the trigger fires and retire the row. | 2026-09-03 | `@rslib/core` **`0.23.2`** — root, `packages/agent-bundle`, `packages/rsc-runtime`, `packages/create-agent-bundle` devDependencies. Stays on `0.23.x` until rslib 1.0 leaves rc. | `npm view @rslib/core dist-tags`: `latest` `0.23.2`, `rc` `1.0.0-rc.2`, `beta` `1.0.0-beta.3`, `canary` `0.20.0-canary-202603101`. | `latest` becomes `1.x`. Bump all four pins in one chore; re-run `pnpm build`, `lint:package`, `check:release`, and the Rslib-driven compile tests. | | 2026-09-03 | `effect-rstest` **pkg.pr.new preview `e5f8d5f`** (`https://pkg.pr.new/ScriptedAlchemy/effect-rstest@e5f8d5f`) — `packages/agent-bundle`, `packages/rsc-runtime` devDependencies. Needs a real release pin once published. | `npm view effect-rstest versions`: **E404 — not published to npm** (no versions, no dist-tags). | First npm publish of `effect-rstest`. Replace both preview URLs with the exact published version, refresh `pnpm-lock.yaml`, re-run `pnpm test:unit` (`it.effect` / `it.live` suites). | | 2026-09-03 | `effect` **`4.0.0-rc.112`** (`packages/agent-bundle`, `packages/rsc-runtime`, `packages/workbench`), `@effect/atom-react` `4.0.0-rc.112` (`packages/workbench`), `@effect/language-service` `0.87.2` and `@effect/tsgo` `0.39.0` (root). Auto re-pin in lockstep + `repos/effect` subtree + Workbench atom phase 4 unblock (stream-backed derived atoms) once the post-rc.112 disposal fix ships. | `npm view effect dist-tags`: `rc` **`4.0.0-rc.112`** (unchanged), `beta` `4.0.0-beta.107`, `latest` `3.22.1`. `@effect/atom-react`: `rc` `4.0.0-rc.112`. `@effect/language-service`: `latest` `0.87.2`. `@effect/tsgo`: `latest` `0.39.1` (patch ahead of the `0.39.0` pin; rides the lockstep chore). | `effect@rc` advances past `4.0.0-rc.112`. Run the re-pin chore steps 1–6 above, bumping `effect`, `@effect/atom-react`, `@effect/language-service`, and `@effect/tsgo` together, then lift the stream-backed derived-atom ban in the Workbench if the disposal fix is in the new RC. | +| 2026-09-03 | Agent Plugins specification **`1.0.0`** — `packages/agent-bundle/src/adapters/schemas/portable/{plugin,mcp}.schema.json` + `PROVENANCE.json` (spec repo `agentplugins/agent-plugins-spec` @ `ff8ab5e392cc87bd88d87c060815a87490e51003`, 2026-08-19), portable `adapterRevision` `1.8.0`, pins in `tests/adapter-metadata.test.ts`. Spec watch for #426; not an npm pin, so re-verify with `curl`/`gh api`, not `npm view`. | Live `https://agent-plugins.org/schemas/1.0.0/{plugin,mcp}.schema.json` rehash to the pinned sha256 (1805 / 3408 bytes). Repo `main` HEAD unchanged at the pinned commit; **no tags, no GitHub releases**. `spec/1.1.0.md` is "Status: Working Draft" (started 2026-08-15, `a2afd7ec`); in-repo `schemas/1.1.0/*.schema.json` differ from 1.0.0 only in the `$id`/`const`/`description` version strings; `https://agent-plugins.org/schemas/1.1.0/*.schema.json` → 404. Observed latest published version: **1.0.0**. | `spec/1.1.0.md` (or later) flips to "Published" **and** `agent-plugins.org/schemas//` serves both schemas. Re-pin under `schemas/portable/` with a dated `PROVENANCE.json` (sha/bytes/date/commit), bump the portable `adapterRevision`, refresh the metadata pins, run `pnpm test:unit` (portable adapter + plugin-validation suites) and `pnpm test:host-install:build`, and add a capability row per additive field. | diff --git a/packages/agent-bundle/src/adapters/capabilities/portable-1.0.0.json b/packages/agent-bundle/src/adapters/capabilities/portable-1.0.0.json index fbcd91bc6..8a59930e5 100644 --- a/packages/agent-bundle/src/adapters/capabilities/portable-1.0.0.json +++ b/packages/agent-bundle/src/adapters/capabilities/portable-1.0.0.json @@ -75,7 +75,8 @@ "evidence": [ "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-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); vendor report prepared in docs/audits/2026-09-03-cursor-agent-plugins-feedback.md (docs/audits/2026-09-03-agent-plugins-cursor-ide-proof.md)." ], "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).", diff --git a/packages/agent-bundle/src/adapters/schemas/portable/PROVENANCE.json b/packages/agent-bundle/src/adapters/schemas/portable/PROVENANCE.json index 2a97cfea0..6d4683261 100644 --- a/packages/agent-bundle/src/adapters/schemas/portable/PROVENANCE.json +++ b/packages/agent-bundle/src/adapters/schemas/portable/PROVENANCE.json @@ -1,8 +1,8 @@ { "normativeTextWinsOnConflict": true, "retrievedAt": "2026-09-01", - "reverifiedAt": "2026-09-02", - "reverification": "2026-09-02: live https://agent-plugins.org/schemas/1.0.0/{plugin,mcp}.schema.json bytes rehashed to the pinned sha256 values; specification repository HEAD unchanged at the pinned commit; a 1.1.0 working draft was started upstream on 2026-08-15 (commit a2afd7ec7edb916da638fc5c94640d4a7ba4480f) without published 1.1.0 schemas, so the 1.0.0 pin stands.", + "reverifiedAt": "2026-09-03", + "reverification": "2026-09-03: live https://agent-plugins.org/schemas/1.0.0/{plugin,mcp}.schema.json bytes rehashed to the pinned sha256 values (1805 and 3408 bytes); specification repository main HEAD unchanged at the pinned commit, no tags or GitHub releases; the 1.1.0 working draft started upstream on 2026-08-15 (commit a2afd7ec7edb916da638fc5c94640d4a7ba4480f) is still 'Status: Working Draft' with in-repo schemas/1.1.0/*.schema.json that differ from 1.0.0 only in $id/const/description version strings, and https://agent-plugins.org/schemas/1.1.0/{plugin,mcp}.schema.json return 404, so the 1.0.0 pin stands (previous re-verification 2026-09-02 reached the same conclusion).", "schemas": { "mcp.schema.json": { "bytes": 3408, diff --git a/packages/agent-bundle/tests/adapter-metadata.test.ts b/packages/agent-bundle/tests/adapter-metadata.test.ts index 38a86f319..fc741310e 100644 --- a/packages/agent-bundle/tests/adapter-metadata.test.ts +++ b/packages/agent-bundle/tests/adapter-metadata.test.ts @@ -237,7 +237,7 @@ it('records observed capability versions and rehashes schema snapshots against p committedAt: '2026-08-19T16:34:23Z', url: 'https://github.com/agentplugins/agent-plugins-spec', }); - expect(provenance.reverifiedAt).toBe('2026-09-02'); + expect(provenance.reverifiedAt).toBe('2026-09-03'); expect(provenance.reverification).toEqual(expect.stringContaining('a2afd7ec7edb916da638fc5c94640d4a7ba4480f')); // Every Agent Plugins 1.0.0 feature carries an honest, dated capability row. const plugin = capabilityTable.plugin as Record; diff --git a/packages/agent-bundle/tests/portable-plugin-validation.test.ts b/packages/agent-bundle/tests/portable-plugin-validation.test.ts index 6267e6228..ce5d43392 100644 --- a/packages/agent-bundle/tests/portable-plugin-validation.test.ts +++ b/packages/agent-bundle/tests/portable-plugin-validation.test.ts @@ -88,7 +88,7 @@ it('passes a conformant Agent Plugins 1.0.0 bundle and reports the pinned proven severity: 'info', target: 'portable', })]); - expect(report.diagnostics[0]?.message).toContain('re-verified 2026-09-02'); + expect(report.diagnostics[0]?.message).toContain('re-verified 2026-09-03'); }); it('requires the root plugin.json and rejects documents the pinned schemas refuse', async () => {