Skip to content

feat(dev): epoch-aware host MCP proxy with tools/list_changed on rebuild (#179 stage 1) - #339

Merged
ScriptedAlchemy merged 1 commit into
mainfrom
feat/179-s1-host-proxy
Sep 2, 2026
Merged

feat(dev): epoch-aware host MCP proxy with tools/list_changed on rebuild (#179 stage 1)#339
ScriptedAlchemy merged 1 commit into
mainfrom
feat/179-s1-host-proxy

Conversation

@ScriptedAlchemy

Copy link
Copy Markdown
Owner

Summary

Stage 1 of #179 (dev-server HMR into live agent hosts): a host keeps ONE stdio MCP process connected while agent-bundle dev hot-swaps the generated plugin server behind it — the webpack-HMR analog for MCP.

  • Host-facing endpoint POST /mcp/host/:serverName?target=<target> on the foreground dev server: a stateful Streamable HTTP MCP server (SDK Server, tools/resources/prompts listChanged: true) that serves the DEVELOPED plugin's MCP surface (not the workbench control tools) from the active artifact epoch.
  • Pooled per-epoch sessions: each host connection holds one live 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).
  • Epoch swap on rebuild (artifact.available): open + prime the new epoch's session, promote it, emit notifications/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).
  • Fail-closed drift (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.
  • Stable stdio proxy 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 with AB8025 (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)

  1. Real proxy child process + real dev server + real fixture plugin: list/call over stdio; source edit → rebuild → tools/list_changed received 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.
  2. Session-lifetime state persists across calls within an epoch (counter 1 → 2).
  3. Active epoch physically removed → AB8024 fail-closed, session invalidated.
  4. Dev server stopped mid-session → MCP error + [AB8025] stderr.
  5. No dev server at all → initialize rejected with AB8025, exit code 1.

Plus discoverDevServerUrl unit coverage (live/dead owner) and CLI parse tests for dev proxy.

Gates (local, on fb25f47)

  • pnpm build / pnpm typecheck / pnpm lint (1022 files, 0/0) — pass
  • pnpm test:unit 2615/2616 — the 1 failure is the pre-existing timing-flaky mcp-probe-service teardown test (passes 9/9 in isolation; also flakes on clean main)
  • pnpm test:route-unit 24/24, pnpm test:projection 59/59
  • host-mcp-proxy.test.ts 5/5 (integration config, prebuilt)
  • test:packed not run: no build-codegen or packed-output changes
  • Pre-existing on clean origin/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.

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-bot

changeset-bot Bot commented Sep 2, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: af344a8

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
agent-bundle Minor

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

@ScriptedAlchemy
ScriptedAlchemy merged commit 27a0d59 into main Sep 2, 2026
4 checks passed
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 2, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-02T23:39:25.789972Z af344a8 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@ScriptedAlchemy

Copy link
Copy Markdown
Owner Author

Post-merge verification found the clean-main regression boundary here:

  • parent 3c3daba2fc3bb4345443d89914a7abee41a847f7: targeted Runtime MCP App restart E2E passed
  • merge 27a0d592e8d234e6f16c770b5db92d365d7caba2: same E2E failed at overview.e2e.test.ts:670 (expected 2 to be 1)

Root cause: HostMcpRoutes correctly adds a second long-lived ProjectEventHub subscription, but the existing E2E still pinned the server-owned baseline to one. The honest ground-truth update (12) landed concurrently in #320 / merge 134353ec777a41e4abbcd14bdeb969d4ac267c3e.

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