feat(studio): provide a trusted host bridge for the local MCP server - #96
Merged
Conversation
Studio had a typed MCP transport interface but nothing implementing it against a real session, because a browser cannot open a stdio pipe. Add a narrow allowlisted bridge inside the trusted runtime that owns the serialized session, plus the Studio-side transport. The deterministic fixture client stays the default. Closes #59. Co-Authored-By: Claude <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Studio had a typed client and an MCP transport interface but nothing that actually implemented it against a real session — browsers cannot open a stdio pipe. This adds the trusted host bridge.
docs/STUDIO_HOST_BRIDGE.mddocuments the architecture and trust boundary, and is the thing to read first.docs/STUDIO.mdnow points at it from the transport section.Trust boundary
The bridge lives inside the runtime host that already owns generation, materialization, builds, and previews, so it inherits loopback binding, one-time bootstrap, the
HttpOnlysession cookie,Host/Originchecks, the body quota, and structuredRuntimeErrorresponses. On top of that:__proto__is not an operation.{ command, path }reaches the MCP tool as nothing at all.STALE_ARTIFACTrather than a silent replay over newer decisions.This is not a general MCP proxy, and adding an operation is a deliberate reviewable change to the allowlist.
Failure handling
Reconnect (one retry after closing a dead stdio session; MCP operations are idempotent by request id), timeout, cancellation, and structured errors. A session-engine rejection like
INVALID_SESSIONis reported unchanged and never replayed — it is an answer, not an outage. The bridge does not assume the transport honours an abort signal; it stops waiting either way and discards the session so nothing arrives late on a connection a later request would reuse.The fixture client is preserved
pnpm devis unchanged. Studio only switches to the real session when the runtime advertises a bridge atGET /api/v1/art-director/operations; otherwise it keepscreateLocalArtDirectorClient(), so the four-stage workflow still needs no runtime, no stdio session, and no credentials. The component tests keep injecting their own fake.Tests
packages/local-runtime/test/art-director-bridge.test.ts— 11 tests, no subprocess, via an injectedArtDirectorMcpSession:happy path across the workflow · the allowlist · rejected operation / tool name / prototype key / non-object body · dropped extra fields · operation before any session · stale session · transport failure with successful reconnect · persistent transport failure · domain rejection that must not be replayed · response missing the session · timeout · cancellation · serialized concurrent calls.
Validation
node --test test/art-director-bridge.test.ts test/http-server.test.ts— 12 passingpnpm --filter @universal/studio test— 3 passingpnpm typecheck,pnpm lint,pnpm format:checkpnpm --filter @universal/studio buildNot done: no end-to-end run against a live stdio MCP server. Everything here is exercised through the injected session port, and step 2 of the setup guide is the wiring a runtime host would add. Worth one manual pass before merge.
Closes #59.
🤖 Generated with Claude Code