From 14377e46b6f215a79f5662060674f49d98a19245 Mon Sep 17 00:00:00 2001 From: alphali <5236230+alphali@users.noreply.github.com> Date: Mon, 7 Sep 2026 23:04:14 +0800 Subject: [PATCH] fix: configure Doable MCP during plugin install --- .agents/plugins/marketplace.json | 2 +- CHANGELOG.md | 10 ++++ PRIVACY.md | 2 +- README.md | 57 +++++-------------- SECURITY.md | 2 +- TESTING.md | 50 ++++++++-------- package.json | 2 +- .../.claude-plugin/plugin.json | 5 +- .../.codex-plugin/plugin.json | 9 ++- .../.cursor-plugin/plugin.json | 18 +++++- plugins/doable-code-context/.mcp.json | 11 ++++ scripts/verify-release.mjs | 54 +++++++++++++++--- 12 files changed, 137 insertions(+), 85 deletions(-) create mode 100644 plugins/doable-code-context/.mcp.json diff --git a/.agents/plugins/marketplace.json b/.agents/plugins/marketplace.json index cd2ceb7..def8d59 100644 --- a/.agents/plugins/marketplace.json +++ b/.agents/plugins/marketplace.json @@ -12,7 +12,7 @@ }, "policy": { "installation": "AVAILABLE", - "authentication": "ON_USE", + "authentication": "ON_INSTALL", "products": [ "CODEX" ] diff --git a/CHANGELOG.md b/CHANGELOG.md index b34d566..15d1b8d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,16 @@ All notable changes to Doable Agent Plugins are documented here. +## [0.2.5] - 2026-09-07 + +### Changed + +- Bundle the official remote Doable MCP connection for Codex, Claude Code, and Cursor. +- Ask for `DOABLE_API_KEY` as a required Cursor installation variable so a + first-time user authenticates while installing the plugin. +- Define the cold-start acceptance path from a TRD Editor copy prompt through + plugin approval, authentication, exact-Round preflight, and automatic resume. + ## [0.2.4] - 2026-09-07 ### Changed diff --git a/PRIVACY.md b/PRIVACY.md index f91677f..49d6f2f 100644 --- a/PRIVACY.md +++ b/PRIVACY.md @@ -4,7 +4,7 @@ Repository inspection happens inside the coding agent selected by the customer a ## Doable Code Context -`doable-code-context` performs remote operations only through the configured Doable MCP connection. The coding-agent host owns that connection's credential. During recovery, the Skill may direct the host to configure a user-supplied key in its user-scoped MCP credential/configuration store, but the bundled helper never reads or persists it and no credential may enter the project workspace or a remote Doable payload. +`doable-code-context` performs remote operations only through the Doable MCP connection loaded by the coding-agent host. The plugin package declares the official endpoint and a credential-variable reference for Cursor and Claude Code; it contains no credential value. The coding-agent host owns the resolved credential. During recovery, the Skill may direct the host to configure a user-supplied key in its user-scoped MCP credential/configuration store, but the bundled helper never reads or persists it and no credential may enter the project workspace or a remote Doable payload. Its local `.doable/workspace-candidate.json`, `.doable/workspace-private.json`, and `.doable/requests/` records may contain real repository identities, explicitly user-supplied artifact roots, local paths, Git provenance, exact evidence locators, and frozen Doable questions. They are written with private permissions and ignored by Git. Artifact roots are never serialized into the remote workspace profile. diff --git a/README.md b/README.md index 666f831..b19b7b2 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ Official agent plugins for [Doable](https://getdoable.ai), supporting Codex, Cla | Plugin | Version | Purpose | Network | | --- | --- | --- | --- | -| `doable-code-context` | `0.2.4` | Resolve context requests or start a managed feature-testing workflow | Configured Doable MCP | +| `doable-code-context` | `0.2.5` | Resolve context requests or start a managed feature-testing workflow | Doable MCP | ## Workflow @@ -44,7 +44,7 @@ change is present in the connected repositories. If that target is missing or ambiguous, it stops and asks the user to fetch, check out, or identify it instead of answering from a neighboring revision. -All remote operations use the separately configured Doable MCP connection. The bundled helper is not a service or standalone CLI: it deterministically maps local repositories, keeps exact provenance private, builds safe payloads, and validates MCP responses. +All remote operations use the Doable MCP connection. Codex, Claude Code, and Cursor load its official remote endpoint from the plugin package. The bundled helper is not a service or standalone CLI: it deterministically maps local repositories, keeps exact provenance private, builds safe payloads, and validates MCP responses. ## Requirements @@ -59,20 +59,26 @@ The MCP connection owns organization authentication; the helper never reads a cr ### Codex +Make `DOABLE_API_KEY` available to the environment that launches the next Codex task, then install the plugin: + ```bash codex plugin marketplace add getdoable/doable-agent-plugins --ref main codex plugin add doable-code-context@getdoable ``` -Install only the plugin needed for the desired workflow, then start a new task. +The plugin registers the official Doable MCP endpoint automatically. Start a new task so Codex loads the new Skill and MCP connection. ### Claude Code +Make `DOABLE_API_KEY` available to the environment that launches the next Claude Code session, then install the plugin: + ```bash claude plugin marketplace add https://github.com/getdoable/doable-agent-plugins.git claude plugin install doable-code-context@doable --scope user ``` +The plugin registers the official Doable MCP endpoint automatically. Claude Code loads the new Skill and MCP connection in the next session. + Natural-language requests activate the Skills. Explicit invocations are: - `/doable-code-context:doable-connect` @@ -87,52 +93,17 @@ In a new Cursor Agent chat, install the plugin: /add-plugin doable-code-context@https://github.com/getdoable/doable-agent-plugins ``` +Cursor asks for the target organization's `DOABLE_API_KEY` during installation and uses it only for the plugin's user-level Doable MCP connection. After the user approves installation and enters the key, continue the original DQ request in the same conversation; do not ask them to copy it again. + ## Connect Doable MCP -The plugin supplies Skills and the local privacy helper; it does not bundle or duplicate the remote MCP server. On every entry path, the Skill first verifies the active connection against Doable. A copied Round also verifies the exact `DQ-...` code and organization before any workspace inspection. If recovery is needed, the coding agent configures the user-scoped connection and resumes the original request after the connection refreshes. +The plugin supplies Skills, a declarative connection to the hosted Doable MCP endpoint for Codex, Claude Code, and Cursor, and the local privacy helper. It does not contain an MCP server implementation. On every entry path, the Skill first verifies the active connection against Doable. A copied Round also verifies the exact `DQ-...` code and organization before any workspace inspection. If recovery is needed, the coding agent configures the user-scoped connection and resumes the original request after the connection refreshes. Keep the key in the host environment or user-scoped credential store. Never commit it, add it to a project-level MCP file, save it under `.doable/`, or print it in agent output. -### Codex - -Make `DOABLE_API_KEY` available to the environment that launches Codex, then register the remote server without putting the key value in Codex configuration: - -```bash -codex mcp add doable \ - --url https://mcp.getdoable.ai/mcp \ - --bearer-token-env-var DOABLE_API_KEY -``` - -### Claude Code - -Make `DOABLE_API_KEY` available to the environment that launches Claude Code. Single quotes preserve the environment reference instead of placing the key value in shell history: - -```bash -claude mcp add doable \ - --scope user \ - --transport http \ - https://mcp.getdoable.ai/mcp \ - --header 'Authorization: Bearer ${DOABLE_API_KEY}' -``` - -### Cursor - -Make `DOABLE_API_KEY` available to the Cursor process and add the server to the global `~/.cursor/mcp.json` file, not the customer's repository: - -```json -{ - "mcpServers": { - "doable": { - "url": "https://mcp.getdoable.ai/mcp", - "headers": { - "Authorization": "Bearer ${env:DOABLE_API_KEY}" - } - } - } -} -``` +### Host credential loading -When configuring the environment ahead of time, launch the coding-agent host from that environment. If Claude Code updates an existing MCP connection during a request, open `/mcp` and reconnect `doable` once; the Skill then retries the original preflight and continues without a restart, a new session, or another copy-paste. +Cursor and Claude Code load `plugins/doable-code-context/.mcp.json`; the Codex manifest embeds the equivalent server using Codex's native bearer-token environment field. Cursor collects the required variable during installation. Codex and Claude Code expand `DOABLE_API_KEY` from the environment when the next task or session loads the plugin. If Claude Code updates an existing connection during a request, open `/mcp` and reconnect `doable` once; the Skill then retries the original preflight and continues without another copy-paste. ## Use Doable Code Context diff --git a/SECURITY.md b/SECURITY.md index e3a3fdc..4c6a7dd 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -12,6 +12,6 @@ Include the plugin version, coding-agent host, and sanitized reproduction steps. ## Security boundary -`doable-code-context` contains no bundled MCP server, credential store, standalone CLI package, runtime dependency, or telemetry. Its helper has no network or credential primitives: it validates local/private versus remote/sanitized schemas, writes private state atomically with mode `0600`, and checks content-derived payload digests around calls made through the separately configured Doable MCP connection. Connection recovery may place a user-supplied key only in the coding-agent host's user-scoped MCP credential/configuration store; it must never echo the key or put it in a workspace file, project-scoped MCP file, shell history, or command-line argument. +`doable-code-context` contains only a declarative connection to the official hosted Doable MCP endpoint; it contains no MCP server implementation, credential store, standalone CLI package, runtime dependency, or telemetry. Cursor owns the required installation variable, and Codex and Claude Code expand it from the launching environment. The helper has no network or credential primitives: it validates local/private versus remote/sanitized schemas, writes private state atomically with mode `0600`, and checks content-derived payload digests around calls made through the host-loaded Doable MCP connection. Connection recovery may place a user-supplied key only in the coding-agent host's user-scoped MCP credential/configuration store; it must never echo the key or put it in a workspace file, project-scoped MCP file, shell history, or command-line argument. Never commit `.doable/workspace-candidate.json`, `.doable/workspace-private.json`, or `.doable/requests/`. Rotate a Doable API key if it is pasted into a conversation, terminal transcript, issue, or log. diff --git a/TESTING.md b/TESTING.md index fc09937..5667f48 100644 --- a/TESTING.md +++ b/TESTING.md @@ -1,32 +1,34 @@ # Beta acceptance checklist -Run these checks from fresh agent sessions. `doable-code-context` exposes exactly three Skills (`doable-connect`, `doable-answer-questions`, and `doable-test-feature`), no bundled MCP server, and one local-only deterministic helper. The workflow expects the existing Doable MCP to be configured separately; the helper must contain no network or credential handling. +Run these checks from fresh agent sessions. `doable-code-context` exposes exactly three Skills (`doable-connect`, `doable-answer-questions`, and `doable-test-feature`), one declarative connection to the official hosted Doable MCP endpoint, and one local-only deterministic helper. The plugin contains no MCP server implementation; the helper must contain no network or credential handling. For every scenario, confirm that the agent inspects only evidence needed for the named feature, keeps exact provenance local, asks only material product-authority questions, and sends only privacy-safe findings and opaque references to Doable. ## Connected workflow -1. **Copied-Round authentication preflight** — Paste a Round prompt with no working `doable` connection. Expect `get_code_context_connection` with the exact `round_code` before the agent uses `.doable` state or scans code; it may read only an optional `workspace.clientRef` for that preflight. After the user supplies the target organization's key once, expect the agent to configure only the host's user-scoped MCP connection. In Claude Code, the only remaining user action is `/mcp` → reconnect `doable`; the agent then retries and resumes the original Round without a restart, new session, or second prompt. -2. **Coding-agent-origin authentication preflight** — Start a feature-testing request without a copied Round. Expect `get_code_context_connection` before local workspace setup or remote suite search. Recover the connection once and resume the original feature request automatically. -3. **Demand-driven setup** — Paste a round prompt in a workspace with no `.doable` state. Expect the agent to enter setup, map only routing-level repository roles/surfaces, request approval for the sanitized profile, sync it, and resume the original round without a second prompt. -4. **Organization binding** — Replace the key with one bound to another organization. Expect the copied-Round preflight to stop before local inspection, identify the mismatch, recover only the prompt organization, and never silently rebind local state. -5. **Mono-repo and multi-repo** — Confirm every independent Git root receives a stable opaque `repoRef`, while a common parent directory does not. Move one repository and explicitly reuse its `repoRef`; expect identity to survive the path change. -6. **Profile privacy** — Use repository names, paths, branches, commits, and an internal service name that differ from the safe product role. Capture the PUT body and confirm none appears remotely. The local state must retain them. -7. **Revision-only refresh** — Advance a repository without changing its role, surfaces, user-facing flag, or safe description. Expect a sync without new user approval. Change a material field and expect approval to be required. -8. **Watch one Round** — Pull a valid `DQ-...` code. Confirm `Next action: answer` while `open_for_agent` has open questions, `wait` for `ready_to_create` / `needs_attention`, and `stop` for `creating` / `consumed` / `cancelled`. A later pull may add `established_context` plus new open questions; the candidate must cover only the new open IDs. Do not treat `ready_to_create` as finished. -9. **Per-repo routing** — Give different questions frontend and backend `repoRef` hints. Expect focused evidence collection in each owner and one product-seam synthesis, not mixed whole-repo dumps. -10. **Exact observable string** — Make an action description differ from the UI literal, such as “save the form” versus `Save`. Expect the finding and anchor to use the verified literal only. -11. **Existence versus absence** — Ask whether a validation exists. Positive evidence may establish existence. A narrow failed search must produce `unknown` or `skipped`, never a confident absence claim. -12. **Descriptive versus normative** — Let code and a user clarification disagree. Expect separate implemented and desired findings with separate sources; neither overwrites the other. +1. **Cold install from the TRD Editor** — Start TRD creation, publish a fresh Code Context Round, and copy its prompt. Paste it into a Codex, Claude Code, or Cursor agent that has never installed Doable and has no Doable MCP entry. The agent detects the missing Skill from its loaded skill catalog, preserves the exact DQ and organization while installation completes, and loads the plugin-declared `doable` MCP connection. Cursor must request `DOABLE_API_KEY` during plugin installation; Codex and Claude Code must resolve it from their launch environment. The original DQ continues without another paste. No repository scan may start before the authenticated preflight succeeds. +2. **Plugin reinstall** — Install a newer plugin version over a prior release in each supported host. Expect the host to replace the packaged Skill and MCP declaration and expose the new version only after installation completes. Cursor also requests a missing required variable. A source checkout or symlink change alone is not an installed-plugin update. +3. **Copied-Round authentication preflight** — Paste a Round prompt with no working `doable` connection. Expect `get_code_context_connection` with the exact `round_code` before the agent uses `.doable` state or scans code; it may read only an optional `workspace.clientRef` for that preflight. After the user supplies the target organization's key once, expect the agent to configure only the host's user-scoped MCP connection. In Claude Code, the only remaining user action is `/mcp` → reconnect `doable`; the agent then retries and resumes the original Round without a restart, new session, or second prompt. +4. **Coding-agent-origin authentication preflight** — Start a feature-testing request without a copied Round. Expect `get_code_context_connection` before local workspace setup or remote suite search. Recover the connection once and resume the original feature request automatically. +5. **Demand-driven setup** — Paste a round prompt in a workspace with no `.doable` state. Expect the agent to enter setup, map only routing-level repository roles/surfaces, request approval for the sanitized profile, sync it, and resume the original round without a second prompt. +6. **Organization binding** — Replace the key with one bound to another organization. Expect the copied-Round preflight to stop before local inspection, identify the mismatch, recover only the prompt organization, and never silently rebind local state. +7. **Mono-repo and multi-repo** — Confirm every independent Git root receives a stable opaque `repoRef`, while a common parent directory does not. Move one repository and explicitly reuse its `repoRef`; expect identity to survive the path change. +8. **Profile privacy** — Use repository names, paths, branches, commits, and an internal service name that differ from the safe product role. Capture the PUT body and confirm none appears remotely. The local state must retain them. +9. **Revision-only refresh** — Advance a repository without changing its role, surfaces, user-facing flag, or safe description. Expect a sync without new user approval. Change a material field and expect approval to be required. +10. **Watch one Round** — Pull a valid `DQ-...` code. Confirm `Next action: answer` while `open_for_agent` has open questions, `wait` for `ready_to_create` / `needs_attention`, and `stop` for `creating` / `consumed` / `cancelled`. A later pull may add `established_context` plus new open questions; the candidate must cover only the new open IDs. Do not treat `ready_to_create` as finished. +11. **Per-repo routing** — Give different questions frontend and backend `repoRef` hints. Expect focused evidence collection in each owner and one product-seam synthesis, not mixed whole-repo dumps. +12. **Exact observable string** — Make an action description differ from the UI literal, such as “save the form” versus `Save`. Expect the finding and anchor to use the verified literal only. +13. **Existence versus absence** — Ask whether a validation exists. Positive evidence may establish existence. A narrow failed search must produce `unknown` or `skipped`, never a confident absence claim. +14. **Descriptive versus normative** — Let code and a user clarification disagree. Expect separate implemented and desired findings with separate sources; neither overwrites the other. When they make incompatible assertions about the same behavior, expect stable finding references and one explicit conflict relation. A complementary truth-plane difference must not be marked as a conflict. -13. **One clarification round** — Leave two required normative decisions and one same-scope newly discovered decision unresolved. Expect one batched customer interaction and exact question/answer pairs. Adjacent out-of-scope discoveries stay local. -14. **Agent authority** — Attempt to submit `deferred`, `waived`, or a required agent observation. Expect local validation to reject it. `skipped` remains available with a bounded reason for platform review. -15. **Reference privacy** — Confirm the remote submission includes only opaque evidence IDs, `repoRef` values, source types, and keyed fingerprints. Exact files, symbols, lines, revisions, and source content remain local. -16. **Idempotent retry** — Submit the same candidate twice. Expect one network submission and a local same-digest receipt. A later batch on the same revision (new open questions) records a second receipt. Changing the candidate without rebuilding the payload must still be rejected. -17. **Terminal server state** — Remove the local receipt after a successful response and retry. Expect the server's idempotency contract to return the prior result rather than mutate the terminal answer. -18. **No TRD side effect** — Completing an answer batch must keep watching until `Next action: stop`. The plugin must not create a TRD, generate cases, or run tests. `ready_to_create` is not completion. -19. **Supplied artifact outside Git** — Put a PRD, screenshot, Figma export, or runtime capture in a narrow directory explicitly supplied by the user and outside every mapped repository. Expect local evidence to accept `artifact` or `runtime` without `repoRef`, emit `repo_ref: null` plus an opaque fingerprint, and keep the artifact root, file identity, path, and content out of every remote payload. Code without a mapped `repoRef`, or an artifact outside the declared root, must fail validation. -20. **Wrong workspace** — Open an unrelated workspace and resolve a round for a named feature that has no material evidence in any mapped product repository. Expect the agent to stop with a concise wrong-workspace warning. It must not mark the item skipped, write/validate a candidate, turn the mismatch into many unknowns, or call submit. -21. **Executable fact granularity** — Give one source area that exposes several neighboring mutations or validations. Expect independently testable findings: each executable path closes its entry or trigger, required action or input, and observable result. A capability inventory may remain supporting context, but it must not become a generic “run/apply/submit” flow. Mixed validation families must be split when one compact anchor cannot support the whole statement. -22. **Source versus deployment boundary** — Point the round at a deployed environment without evidence that its revision matches the inspected workspace. Expect confirmed code findings plus one explicit evidence-free `unknown` for the unproved equivalence. Reachability alone must not upgrade source behavior into deployed behavior. -23. **Named checkout target** — Name a feature branch, PR, worktree, or change set that is not the current checkout. Expect the agent to stop and ask the user to fetch, check out, or identify it. It must not answer from a neighboring revision, turn the mismatch into `unknown`, or send branch/commit details remotely. +15. **One clarification round** — Leave two required normative decisions and one same-scope newly discovered decision unresolved. Expect one batched customer interaction and exact question/answer pairs. Adjacent out-of-scope discoveries stay local. +16. **Agent authority** — Attempt to submit `deferred`, `waived`, or a required agent observation. Expect local validation to reject it. `skipped` remains available with a bounded reason for platform review. +17. **Reference privacy** — Confirm the remote submission includes only opaque evidence IDs, `repoRef` values, source types, and keyed fingerprints. Exact files, symbols, lines, revisions, and source content remain local. +18. **Idempotent retry** — Submit the same candidate twice. Expect one network submission and a local same-digest receipt. A later batch on the same revision (new open questions) records a second receipt. Changing the candidate without rebuilding the payload must still be rejected. +19. **Terminal server state** — Remove the local receipt after a successful response and retry. Expect the server's idempotency contract to return the prior result rather than mutate the terminal answer. +20. **No TRD side effect** — Completing an answer batch must keep watching until `Next action: stop`. The plugin must not create a TRD, generate cases, or run tests. `ready_to_create` is not completion. +21. **Supplied artifact outside Git** — Put a PRD, screenshot, Figma export, or runtime capture in a narrow directory explicitly supplied by the user and outside every mapped repository. Expect local evidence to accept `artifact` or `runtime` without `repoRef`, emit `repo_ref: null` plus an opaque fingerprint, and keep the artifact root, file identity, path, and content out of every remote payload. Code without a mapped `repoRef`, or an artifact outside the declared root, must fail validation. +22. **Wrong workspace** — Open an unrelated workspace and resolve a round for a named feature that has no material evidence in any mapped product repository. Expect the agent to stop with a concise wrong-workspace warning. It must not mark the item skipped, write/validate a candidate, turn the mismatch into many unknowns, or call submit. +23. **Executable fact granularity** — Give one source area that exposes several neighboring mutations or validations. Expect independently testable findings: each executable path closes its entry or trigger, required action or input, and observable result. A capability inventory may remain supporting context, but it must not become a generic “run/apply/submit” flow. Mixed validation families must be split when one compact anchor cannot support the whole statement. +24. **Source versus deployment boundary** — Point the round at a deployed environment without evidence that its revision matches the inspected workspace. Expect confirmed code findings plus one explicit evidence-free `unknown` for the unproved equivalence. Reachability alone must not upgrade source behavior into deployed behavior. +25. **Named checkout target** — Name a feature branch, PR, worktree, or change set that is not the current checkout. Expect the agent to stop and ask the user to fetch, check out, or identify it. It must not answer from a neighboring revision, turn the mismatch into `unknown`, or send branch/commit details remotely. diff --git a/package.json b/package.json index c04e645..67f6695 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "doable-agent-plugins", - "version": "0.2.4", + "version": "0.2.5", "private": true, "description": "Official installable agent plugins for Doable.", "license": "MIT", diff --git a/plugins/doable-code-context/.claude-plugin/plugin.json b/plugins/doable-code-context/.claude-plugin/plugin.json index 50d5381..e8c9476 100644 --- a/plugins/doable-code-context/.claude-plugin/plugin.json +++ b/plugins/doable-code-context/.claude-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "doable-code-context", - "version": "0.2.4", + "version": "0.2.5", "description": "Connect private code to Doable through MCP, resolve grounded context requests, and start managed feature-testing workflows.", "author": { "name": "Doable AI", @@ -14,5 +14,6 @@ "testing", "trd", "code-context" - ] + ], + "mcpServers": "./.mcp.json" } diff --git a/plugins/doable-code-context/.codex-plugin/plugin.json b/plugins/doable-code-context/.codex-plugin/plugin.json index 41240e4..000b2f5 100644 --- a/plugins/doable-code-context/.codex-plugin/plugin.json +++ b/plugins/doable-code-context/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "doable-code-context", - "version": "0.2.4", + "version": "0.2.5", "description": "Connect private code to Doable through MCP, resolve grounded context requests, and start managed feature-testing workflows.", "author": { "name": "Doable AI", @@ -18,6 +18,13 @@ "coding-agent" ], "skills": "./skills/", + "mcpServers": { + "doable": { + "type": "http", + "url": "https://mcp.getdoable.ai/mcp", + "bearer_token_env_var": "DOABLE_API_KEY" + } + }, "interface": { "displayName": "Doable Code Context", "shortDescription": "Resolve Doable questions from private code", diff --git a/plugins/doable-code-context/.cursor-plugin/plugin.json b/plugins/doable-code-context/.cursor-plugin/plugin.json index a0d8c57..2b2b5cb 100644 --- a/plugins/doable-code-context/.cursor-plugin/plugin.json +++ b/plugins/doable-code-context/.cursor-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "doable-code-context", "displayName": "Doable Code Context", - "version": "0.2.4", + "version": "0.2.5", "description": "Connect private code to Doable through MCP, resolve grounded context requests, and start managed feature-testing workflows.", "author": { "name": "Doable AI" @@ -21,5 +21,19 @@ "requirements", "code-context" ], - "skills": "./skills/" + "skills": "./skills/", + "variables": { + "type": "object", + "properties": { + "DOABLE_API_KEY": { + "type": "string", + "title": "Doable organization API key", + "description": "API key from the target organization's Doable Settings." + } + }, + "required": [ + "DOABLE_API_KEY" + ] + }, + "mcpServers": "./.mcp.json" } diff --git a/plugins/doable-code-context/.mcp.json b/plugins/doable-code-context/.mcp.json new file mode 100644 index 0000000..883d653 --- /dev/null +++ b/plugins/doable-code-context/.mcp.json @@ -0,0 +1,11 @@ +{ + "mcpServers": { + "doable": { + "type": "http", + "url": "https://mcp.getdoable.ai/mcp", + "headers": { + "Authorization": "Bearer ${DOABLE_API_KEY}" + } + } + } +} diff --git a/scripts/verify-release.mjs b/scripts/verify-release.mjs index 0201b0c..c05469d 100644 --- a/scripts/verify-release.mjs +++ b/scripts/verify-release.mjs @@ -11,9 +11,9 @@ const semver = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-[0-9A-Za-z.-]+)?(?: const plugins = [ { name: "doable-code-context", - version: "0.2.4", + version: "0.2.5", skillNames: ["doable-connect", "doable-answer-questions", "doable-test-feature"], - network: "configured-doable-mcp", + network: "bundled-doable-mcp-config", }, ]; @@ -110,12 +110,31 @@ for (const plugin of plugins) { assert(manifest.name === plugin.name, `${host} plugin name must be ${plugin.name}`); assert(manifest.version === plugin.version, `${host} ${plugin.name} version must be ${plugin.version}`); assert(semver.test(manifest.version ?? ""), `${host} ${plugin.name} version must be strict semver`); - for (const forbidden of ["mcpServers", "apps", "hooks"]) { + for (const forbidden of ["apps", "hooks"]) { assert(!(forbidden in manifest), `${host} ${plugin.name} must not declare ${forbidden}`); } } assert(codex.skills === "./skills/", `${plugin.name} Codex skills path must be ./skills/`); assert(cursor.skills === "./skills/", `${plugin.name} Cursor skills path must be ./skills/`); + assert( + JSON.stringify(codex.mcpServers) === + JSON.stringify({ + doable: { + type: "http", + url: "https://mcp.getdoable.ai/mcp", + bearer_token_env_var: "DOABLE_API_KEY", + }, + }), + `${plugin.name} Codex MCP config must use the native bearer-token environment reference`, + ); + assert(claude.mcpServers === "./.mcp.json", `${plugin.name} Claude MCP config is missing`); + assert(cursor.mcpServers === "./.mcp.json", `${plugin.name} Cursor MCP config is missing`); + assert(cursor.variables?.type === "object", `${plugin.name} Cursor variables schema is missing`); + assert( + cursor.variables?.properties?.DOABLE_API_KEY?.type === "string" && + cursor.variables?.required?.includes("DOABLE_API_KEY"), + `${plugin.name} Cursor must require DOABLE_API_KEY during installation`, + ); assert(codex.license === "MIT" && cursor.license === "MIT", `${plugin.name} manifests must use MIT`); assert(codex.author?.name === "Doable AI", `${plugin.name} publisher must be Doable AI`); assert(codex.interface?.privacyPolicyURL === "https://qa.getdoable.ai/privacy-policy", `${plugin.name} must use the QA privacy policy`); @@ -127,7 +146,7 @@ for (const plugin of plugins) { const cursorEntry = cursorMarketplace.plugins?.find((entry) => entry.name === plugin.name); assert(codexEntry?.source?.path === `./plugins/${plugin.name}`, `${plugin.name} Codex marketplace source is incorrect`); assert(codexEntry?.policy?.installation === "AVAILABLE", `${plugin.name} must be available, not forced`); - assert(codexEntry?.policy?.authentication === "ON_USE", `${plugin.name} authentication policy must be ON_USE`); + assert(codexEntry?.policy?.authentication === "ON_INSTALL", `${plugin.name} authentication policy must be ON_INSTALL`); assert(JSON.stringify(codexEntry?.policy?.products) === JSON.stringify(["CODEX"]), `${plugin.name} must be gated to CODEX`); assert(claudeEntry?.source === `./plugins/${plugin.name}`, `${plugin.name} Claude marketplace source is incorrect`); assert(cursorEntry?.source === `./plugins/${plugin.name}`, `${plugin.name} Cursor marketplace source is incorrect`); @@ -256,24 +275,41 @@ const readme = readFileSync(join(root, "README.md"), "utf8"); assert(!/private during beta|private[- ]beta/i.test(readme), "README must not describe the release as private beta"); assert(!readme.includes("github.com/getdoable/doable-mcp"), "README must not depend on private MCP documentation"); for (const requiredSetup of [ - "codex mcp add doable", - "claude mcp add doable", - '"Authorization": "Bearer ${env:DOABLE_API_KEY}"', + "The plugin registers the official Doable MCP endpoint automatically", + "/add-plugin doable-code-context@https://github.com/getdoable/doable-agent-plugins", + "Cursor asks for the target organization's `DOABLE_API_KEY` during installation", + "plugins/doable-code-context/.mcp.json", ]) { assert(readme.includes(requiredSetup), `README is missing public MCP setup: ${requiredSetup}`); } +const allowedMcpConfig = join(root, "plugins", "doable-code-context", ".mcp.json"); const forbiddenReleaseFiles = allPaths.filter((path) => { const name = path.split(sep).at(-1); - return name === ".mcp.json" || name === ".app.json" || name === ".env"; + return (name === ".mcp.json" && path !== allowedMcpConfig) || name === ".app.json" || name === ".env"; }); assert( forbiddenReleaseFiles.length === 0, `forbidden integration files found: ${forbiddenReleaseFiles.map((path) => relative(root, path)).join(", ")}`, ); +const bundledMcp = readJson(allowedMcpConfig); +assert( + JSON.stringify(bundledMcp) === + JSON.stringify({ + mcpServers: { + doable: { + type: "http", + url: "https://mcp.getdoable.ai/mcp", + headers: { Authorization: "Bearer ${DOABLE_API_KEY}" }, + }, + }, + }), + "bundled Doable MCP config must contain only the official endpoint and variable reference", +); + // The helper is a deterministic local boundary. All remote work belongs to -// the separately configured Doable MCP connection. +// the host-loaded Doable MCP connection declared by the plugin where supported. const connectedHelperPath = join(root, "plugins", "doable-code-context", "scripts", "doable-code-context.mjs"); assert(existsSync(connectedHelperPath), "connected plugin is missing its deterministic helper"); const connectedHelper = readFileSync(connectedHelperPath, "utf8");