Skip to content

Serve MCP 2026-07-28 stateless requests through vMCP (server-side Modern path) #5910

Description

@jhrozek

Part of #5756. Design: RFC THV-0083 (D2, D4, Phase 2, Configuration Changes); arch doc §"Server side", §"server/discover", §"Rollback / kill switch". Security-critical.

Serve Modern (2026-07-28) stateless requests through vMCP by bypassing the Serve session layer and dispatching straight to the already-stateless core.

Changes

  • Replace var mcpHandler http.Handler = streamableServer (server.go:607) with a classifying handler: Modern → hand-rolled dispatcher, everything else → SDK server unchanged.
  • Direct core dispatch for tools/list/tools/call/resources/read/prompts/get.
  • From-scratch JSON-RPC/resultType envelope — v1.6.1 has no Modern wire shapes and v1.7's resultType is unexported, so it cannot be lifted. Reuse mcpcompat's dispatch/successResponse/errorResponse skeleton and error-code mapping; match v1.7 wire shape byte-for-byte so the eventual v1.7 swap is delete-and-repoint. Validate against the v1.7.0-pre.1 conformance testdata.
  • 404/-32601 for unimplemented methods; notifications (no id) → 202/drop; reject JSON-RPC batches with a defined error.
  • Re-home what the bypassed SDK server owned: re-invoke authzCallGate and translate Denial→HTTP 403 (preserving audit outcome:"denied", not a 200 tool-error); apply header-forward from context on the outgoing backend call.
  • Serve server/discover as a post-admission capability-flags envelope (no descriptor arrays), computed through the same admission seam as core.List*; add its authz-map entry together with that filtered response. Emit required CacheableResult fields (ttlMs:0, cacheScope:"private" interim).
  • Dedicated startup kill-switch flag, Modern-off by default.

Composite tools work in this phase with the pod-local in-memory store (composites run synchronously within one tools/call, so state is request-local).

Dependencies

#5909 (Phase 1 classifier wiring). Independent of the go-sdk v1.7 bump (#5754).

Verification

  • Per-method dispatch; envelope matches v1.7 conformance testdata; 404/-32601; notification 202/drop; batch rejection.
  • Denied tools/call → HTTP 403 and audit outcome:"denied" (not a 200 tool-error).
  • server/discover carries no descriptors; flags reflect admission (empty admitted set → no advertised flag); authz-map entry gated on the post-admission envelope.
  • Legacy path untouched.

Notes

Split into PRs: (a) dispatcher + envelope + re-homing + kill switch, (b) server/discover post-admission envelope + authz-map entry. The envelope is the main new-code and correctness surface.

Watch-item (not a blocker): computing discover flags post-admission runs the same fan-out as tools/list on a probe the spec expects to be cheap/cacheable — a short-TTL per-identity capability cache is the measure-first mitigation, tied to #5761.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions