Skip to content

feat(mcp): expose plan / verify / list_projects on wizard-mcp-server#258

Closed
kelsonpw wants to merge 2 commits intokelsonpw/agent-ui-hintsfrom
kelsonpw/wizard-mcp-plan-verify-list
Closed

feat(mcp): expose plan / verify / list_projects on wizard-mcp-server#258
kelsonpw wants to merge 2 commits intokelsonpw/agent-ui-hintsfrom
kelsonpw/wizard-mcp-plan-verify-list

Conversation

@kelsonpw
Copy link
Copy Markdown
Collaborator

@kelsonpw kelsonpw commented Apr 25, 2026

Summary

The external MCP server (amplitude-wizard mcp serve, src/lib/wizard-mcp-server.ts) already exposes the read-only agent-ops functions over stdio so third-party AI coding agents (Claude Code, Cursor, Codex) can call them as typed tools instead of shelling out to the CLI and parsing NDJSON. This PR registers the three new agent-ops landed in #255 (plan/verify) and #257 (projects list) on that surface so they're MCP-callable too.

Stacked on #257. Read-only by designapply is intentionally NOT exposed. The sub-agent contract is "outer agent inspects → re-invokes the CLI to write," which keeps responsibility for destructive operations on the user-facing CLI where flags + exit codes can gate them properly.

New tools

Tool Wraps Description
plan_setup runPlan(installDir) Detect framework + build + persist a WizardPlan. No writes. Returns { plan, detected }.
verify_setup runVerify(installDir) Cheap, no-network SDK / API-key / framework check. Returns { outcome: 'pass' | 'fail', failures: [...] }.
list_projects runProjectsList({ query?, limit?, offset? }) Paginated, search-filtered project list. Surfaces a warning field on the auth-required path so MCP clients can route to a login flow instead of treating it as an error.

What an outer agent sees

// Claude Code / Cursor / Codex spawns the wizard MCP server and lists tools:
[
  'detect_framework',
  'get_auth_status',
  'get_auth_token',
  'get_project_status',
  'list_projects',     // NEW
  'plan_setup',        // NEW
  'verify_setup',      // NEW
]

The plan_setup description explicitly flags "no files are touched" so the LLM picking the tool doesn't assume calling it executes the install.

Test plan

  • pnpm test1311 passed, 17 skipped (8 new tests in wizard-mcp-server.test.ts, 19 total)
  • pnpm tsc --noEmit clean
  • pnpm lint clean
  • Smoke: npx amplitude-wizard mcp serve lists 7 tools via MCP list_tools
  • Smoke from Claude Code: invoke mcp__amplitude-wizard__list_projects with a query, get back a paginated result

Why not also expose apply?

Keeping the MCP surface read-only is deliberate:

  1. Capability flags don't compose well across MCP — the wizard's --yes / --force matrix gates writes today. Exposing apply over MCP would mean re-encoding that gate inside the tool's input schema, plus a parallel "are you sure" dance in the protocol. That's a regression in clarity.
  2. The CLI is already the right shape for writesapply --plan-id <id> --yes is idempotent, scriptable, and emits structured NDJSON. MCP clients just spawn(['amplitude-wizard', 'apply', '--plan-id', planId, '--yes']) and stream the events. No new protocol needed.
  3. Read-only invariant is enforceable — the MCP server today literally has no path that writes to disk; future contributors won't accidentally introduce one if apply isn't on the surface.

cc @amplitude/growth

🤖 Generated with Claude Code


Note

Medium Risk
Medium risk because it adds a new resume path that threads session IDs through CLI env vars into the Claude SDK and persists them into on-disk plans; mistakes could cause confusing apply behavior or stale-session loops.

Overview
Adds three new read-only MCP tools on the external wizard mcp serve surface: plan_setup, verify_setup, and list_projects, each wrapping the corresponding agent-ops function with typed input schemas.

Enhances wizard apply with an optional --resume flag and plan persistence updates so apply runs can capture the Claude SDK session_id into the stored plan and later resume an interrupted agent session by forwarding AMPLITUDE_WIZARD_RESUME_SESSION_ID into the agent runner/SDK.

Updates the agent interface/runner to accept resumeSessionId, emit onSessionStart, and patch the plan on session start; expands unit tests to cover MCP tool registration/forwarding and plan patching/env apply context helpers.

Reviewed by Cursor Bugbot for commit a90692a. Bugbot is set up for automated code reviews on this repo. Configure here.

The external MCP server (`amplitude-wizard mcp serve`) already exposes
the read-only agent-ops functions over stdio so third-party AI coding
agents can call them directly instead of shelling out and parsing NDJSON.
This PR adds the three new agent-ops landed in #255 / #257 to that
surface so they're MCP-callable too.

New tools:

  plan_setup       → runPlan(installDir)
                     Runs detection + builds + persists a WizardPlan.
                     Documented as no-writes; returns { plan, detected }.

  verify_setup     → runVerify(installDir)
                     Cheap, no-network SDK / API-key / framework check.
                     Returns { outcome: pass|fail, failures: [...] }.

  list_projects    → runProjectsList({ query?, limit?, offset? })
                     Paginated, search-filtered project list. Surfaces a
                     `warning` field on the auth-required path so MCP
                     clients can route to a login flow instead of
                     treating it as an error.

Surface stays read-only — `apply` is intentionally NOT exposed. The
sub-agent contract is "outer agent inspects → re-invokes the CLI to
write," which keeps the responsibility for destructive operations on the
user-facing CLI where flags and exit codes can gate them properly.

Tests: +8 in wizard-mcp-server.test.ts (1311 total). Suite green.

Stacked on #257.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@kelsonpw kelsonpw requested a review from a team April 25, 2026 20:27
@github-actions
Copy link
Copy Markdown
Contributor

🧙 Wizard CI

Run the Wizard CI and test your changes against wizard-workbench example apps by replying with a GitHub comment using one of the following commands:

Test all apps:

  • /wizard-ci all

Test all apps in a directory:

  • /wizard-ci django
  • /wizard-ci fastapi
  • /wizard-ci flask
  • /wizard-ci javascript-node
  • /wizard-ci javascript-web
  • /wizard-ci next-js
  • /wizard-ci python
  • /wizard-ci react-router
  • /wizard-ci vue

Test an individual app:

  • /wizard-ci django/django3-saas
  • /wizard-ci fastapi/fastapi3-ai-saas
  • /wizard-ci flask/flask3-social-media
Show more apps
  • /wizard-ci javascript-node/express-todo
  • /wizard-ci javascript-node/fastify-blog
  • /wizard-ci javascript-node/hono-links
  • /wizard-ci javascript-node/koa-notes
  • /wizard-ci javascript-node/native-http-contacts
  • /wizard-ci javascript-web/saas-dashboard
  • /wizard-ci next-js/15-app-router-saas
  • /wizard-ci next-js/15-app-router-todo
  • /wizard-ci next-js/15-pages-router-saas
  • /wizard-ci next-js/15-pages-router-todo
  • /wizard-ci python/meeting-summarizer
  • /wizard-ci react-router/react-router-v7-project
  • /wizard-ci react-router/rrv7-starter
  • /wizard-ci react-router/saas-template
  • /wizard-ci react-router/shopper
  • /wizard-ci vue/movies

Results will be posted here when complete.

…ashes (#259)

* feat(agent): apply --resume — resumable Claude SDK sessions across crashes

When `apply` runs the inner Claude SDK agent, capture the session id from
the first system/init message and persist it on the plan. A subsequent
`apply --plan-id <id> --yes --resume` passes that id to the SDK as
`resume:`, rehydrating the conversation instead of starting a fresh
agent that has to redo SDK install + package detection + file reads.

Surface area:

  WizardPlan now optionally carries:
    agentSessionId         — UUID of the prior SDK session
    agentSessionUpdatedAt  — when it was captured

  apply --resume           — opt-in flag; pulls agentSessionId from the
                             persisted plan and forwards to the spawned
                             child via AMPLITUDE_WIZARD_RESUME_SESSION_ID.
                             When the plan has no captured session yet,
                             logs a structured warning and falls through
                             to a fresh run (right default for the very
                             first apply against a plan).

  applyPlanPatch(planId, p) — partial-update helper for plans on disk;
                              best-effort, returns null on miss.

  getApplyContextFromEnv()  — agent-runner reads { planId, resumeSessionId }
                              from env vars set by `apply` so the spawn
                              boundary stays decoupled from the SDK call
                              site. Both vars optional — fresh runs work.

Wiring in agent-runner: pass `resumeSessionId` and `onSessionStart` into
`runAgent`. The latter fires once on system/init and patches the plan
with the SDK-assigned session id (handles both fresh runs AND forks from
a resumed session, which the SDK gives a new id).

Wiring in agent-interface: two new optional `runAgent` config fields
(`onSessionStart`, `resumeSessionId`). Surgical change — adds 6 lines to
the SDK message loop and 1 line to the query options.

Tests: +8 in agent-plans.test.ts (1319 total). Suite green.

Smoke tests:
  $ wizard plan --json                    → planId X (no agentSessionId)
  $ wizard apply --plan-id X --yes        → first run; captures session
  $ wizard apply --plan-id X --resume --yes → second run; resumes
  $ wizard apply --plan-id Y --resume --yes → warns "no captured session", runs fresh

Stacked on #258. Closes the design-doc gap on mid-`apply` work loss
(SIGINT, network drop, terminal crash).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix: replace vendor name 'Claude SDK' with 'agent' in CLI help text

Applied via @cursor push command

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
@kelsonpw
Copy link
Copy Markdown
Collaborator Author

Replaced by #285 — rebased onto current main, list_projects deferred (depends on closed #257). Title narrowed accordingly.

@kelsonpw kelsonpw closed this Apr 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant