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.

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