feat(dev): epoch-aware host MCP proxy with tools/list_changed on rebuild (#179 stage 1) - #339
Conversation
Add an epoch-aware host endpoint and stable stdio proxy so development hosts receive catalog changes without reinstalling while preserving session state and in-flight calls.
🦋 Changeset detectedLatest commit: af344a8 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
Post-merge verification found the clean-main regression boundary here:
Root cause: |
Summary
Stage 1 of #179 (dev-server HMR into live agent hosts): a host keeps ONE stdio MCP process connected while
agent-bundle devhot-swaps the generated plugin server behind it — the webpack-HMR analog for MCP.POST /mcp/host/:serverName?target=<target>on the foreground dev server: a stateful Streamable HTTP MCP server (SDKServer,tools/resources/prompts listChanged: true) that serves the DEVELOPED plugin's MCP surface (not the workbench control tools) from the active artifact epoch.McpSession(spawned generated server) per epoch, reused across calls — session-lifetime state accumulates across calls exactly like a packed host process (pinned by a regression test: an in-memory counter tool returns 1 then 2 over the same connection).artifact.available): open + prime the new epoch's session, promote it, emitnotifications/tools/list_changed(+ resources/prompts) on the SAME connection; the old session drains in-flight calls to completion against their original epoch, then closes. Failed builds keep last-good serving (no event, no swap).AB8024): if the pinned/active epoch physically vanishes (external retention — the PR fix(dev): fail MCP tool calls closed when the pinned epoch vanishes #134 scenario), in-flight and subsequent calls fail with a typed MCP error carrying{ code: 'AB8024', epochId }, and the connection is invalidated; never silent success from a dead artifact.agent-bundle dev proxy --root <projectRoot> --server <name> [--target <t>] [--url <origin>]: the thin process a host spawns as the plugin's MCP server. Discovers the running dev server through the dev-lock sidecar (discoverDevServerUrl, new export validating owner liveness + published loopback origin), bridges stdio↔HTTP transparently including notifications, and fails closed withAB8025(stderr diagnostic + JSON-RPC error + exit 1) when no dev server is running or it goes away. Lazy-imported in the CLI action per the existing convention.Tests (
tests/host-mcp-proxy.test.ts, integration pool)tools/list_changedreceived and new tool visible/callable on the same connection (no disconnect); in-flight call started pre-swap completes against its original epoch (v1) while new calls serve v2.AB8024fail-closed, session invalidated.[AB8025]stderr.AB8025, exit code 1.Plus
discoverDevServerUrlunit coverage (live/dead owner) and CLI parse tests fordev proxy.Gates (local, on fb25f47)
pnpm build/pnpm typecheck/pnpm lint(1022 files, 0/0) — passpnpm test:unit2615/2616 — the 1 failure is the pre-existing timing-flakymcp-probe-serviceteardown test (passes 9/9 in isolation; also flakes on clean main)pnpm test:route-unit24/24,pnpm test:projection59/59host-mcp-proxy.test.ts5/5 (integration config, prebuilt)test:packednot run: no build-codegen or packed-output changesorigin/main(verified at fb25f47 in a pristine worktree):cli.test.ts"keeps inspect JSON stable and validates only the supplied artifact" fails from the Adopt pinned schemas and ecosystem validators for generated plugin conformance (build-time + doctor) #133 Codex-validator landing (AB6030/AB6031 info diagnostics in validate JSON). Not touched by this diff.Part of #179 (stage 1 of 4). Changeset: minor.