You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Deliver the foundation of the vMCP domain/transport split: a public,
identity-parameterized VMCP domain interface and core Config in the root pkg/vmcp package, a New(cfg) -> VMCP constructor that relocates (does not
rewrite) server.New's domain wiring, and the two deliberate bounded rewrites
— the Cedar-backed admission seam (R1) and the domain-typed ElicitationRequester
(R4). This story maps to RFC Phase 1 and moves the domain seam (aggregation,
routing, admission, elicitation) out from behind the transport god-object — while server.New stays completely untouched.
This is the first story and the entry point for the whole refactor: it
establishes the VMCP contract that Serve (Phase 2) and the server.New
wrapper reduction (Phase 3) build on. Per architecture.md ("PR-Sized
Decomposition Guidance → Phase 1"), the new core is constructed by relocating the
collaborator wiring that lives in server.New today — aggregator, router, backend
registry, backend client, composer, the per-session sessionComposerFactory/NewSessionRouter pattern, and workflow validation —
plus an injected health.StatusProvider so the core runs filterHealthyBackends
internally (core filters, Serve caches). Identity is an explicit *auth.Identity
on every method and is never read from context (anti-pattern #1); no mcp-go types
cross the VMCP boundary (anti-pattern #5).
Crucially, this story carries the two highest-risk items in the entire epic —
the two bounded rewrites that re-platform existing behavior rather than merely
relocating it:
Admission seam (P1.5 Core admission seam (bounded rewrite) #5438, R1) — HIGHEST regression risk. Moves the
authorization decision from the HTTP AuthzMiddleware into a core Admission
seam that wraps the existing Cedar authorizers.Authorizer (no new policy
model). It must produce the same decision in List* (filter) and Call/Read/Get (deny), re-inject the explicit identity into ctx for Cedar's AuthorizeWithJWTClaims, source Tool.Annotations directly from the core
(replacing AnnotationEnrichmentMiddleware), and be a no-op/allow-all when
authz is unconfigured.
Domain-typed ElicitationRequester (P1.3 Domain-typed ElicitationRequester (bounded rewrite) #5436, R4) — high risk. Replaces composer.SDKElicitationRequester (which leaks mcp.ElicitationRequest/Result)
with a domain-typed requester, confining mcp-go translation to sdk_elicitation_adapter.go. A one-to-one type mirror only — no elicitation
semantics are redesigned.
Both rewrites get dedicated parity/security tests (see their child tasks).
Define the identity-parameterized VMCP interface
(ListTools/CallTool/ListResources/ReadResource/ListPrompts/GetPrompt/ LookupTool/LookupResource/LookupPrompt/Close) and the core Config in
the root pkg/vmcp package (P1.1 Define VMCP interface + core Config #5434).
Implement New(cfg) -> VMCP (*coreVMCP) by relocating server.New's domain
wiring; inject a health.StatusProvider and run filterHealthyBackends
internally (P1.4 New(cfg) -> VMCP core constructor #5437).
Add the Cedar-backed Admission seam and wire it into the core's List*/Call/Read/Get so list-omit and call-deny enforce one decision —
bounded rewrite R1 (P1.5 Core admission seam (bounded rewrite) #5438).
Removing the authz / annotation-enrichment HTTP middleware from the live chain.
In Phase 1 the new core exists but is not wired into server.New, so the
live authz middleware still runs in the shipped path; any temporary
double-enforcement exists only inside tests that exercise the core directly.
server.New signature and observable behavior unchanged
The new core is NOT yet wired into server.New in Phase 1 — the core
exists as additive public API (VMCP/New) but the server.New body is
untouched; the live authz HTTP middleware still runs in the shipped path
VMCP interface and New(cfg) -> VMCP exist as additive public API in the
root pkg/vmcp package, with identity as an explicit *auth.Identity and no
mcp-go types crossing the boundary
Bounded rewrite R4 (elicitation): the domain ElicitationRequester carries
no mcp-go types and its dedicated parity tests pass (accept/decline/cancel/
timeout; security validation still fires)
Bounded rewrite R1 (admission): list-omit and call-deny enforce the same
Cedar decision, no-op/allow-all when unconfigured, passThroughTools exempt,
identity never logged — dedicated R1 security parity tests pass
Existing thv vmcp serve E2E suite passes unchanged
Description
Deliver the foundation of the vMCP domain/transport split: a public,
identity-parameterized
VMCPdomain interface and coreConfigin the rootpkg/vmcppackage, aNew(cfg) -> VMCPconstructor that relocates (does notrewrite)
server.New's domain wiring, and the two deliberate bounded rewrites— the Cedar-backed admission seam (R1) and the domain-typed
ElicitationRequester(R4). This story maps to RFC Phase 1 and moves the domain seam (aggregation,
routing, admission, elicitation) out from behind the transport god-object — while
server.Newstays completely untouched.Context
See RFC THV-0076: vMCP Core Interface
for full design details. Part of the vMCP interface refactor (epic #5419).
This is the first story and the entry point for the whole refactor: it
establishes the
VMCPcontract thatServe(Phase 2) and theserver.Newwrapper reduction (Phase 3) build on. Per
architecture.md("PR-SizedDecomposition Guidance → Phase 1"), the new core is constructed by relocating the
collaborator wiring that lives in
server.Newtoday — aggregator, router, backendregistry, backend client, composer, the per-session
sessionComposerFactory/NewSessionRouterpattern, and workflow validation —plus an injected
health.StatusProviderso the core runsfilterHealthyBackendsinternally (core filters, Serve caches). Identity is an explicit
*auth.Identityon every method and is never read from context (anti-pattern #1); no mcp-go types
cross the
VMCPboundary (anti-pattern #5).Crucially, this story carries the two highest-risk items in the entire epic —
the two bounded rewrites that re-platform existing behavior rather than merely
relocating it:
authorization decision from the HTTP
AuthzMiddlewareinto a coreAdmissionseam that wraps the existing Cedar
authorizers.Authorizer(no new policymodel). It must produce the same decision in
List*(filter) andCall/Read/Get(deny), re-inject the explicit identity into ctx for Cedar'sAuthorizeWithJWTClaims, sourceTool.Annotationsdirectly from the core(replacing
AnnotationEnrichmentMiddleware), and be a no-op/allow-all whenauthz is unconfigured.
ElicitationRequester(P1.3 Domain-typed ElicitationRequester (bounded rewrite) #5436, R4) — high risk. Replacescomposer.SDKElicitationRequester(which leaksmcp.ElicitationRequest/Result)with a domain-typed requester, confining mcp-go translation to
sdk_elicitation_adapter.go. A one-to-one type mirror only — no elicitationsemantics are redesigned.
Both rewrites get dedicated parity/security tests (see their child tasks).
RFC Phase(s): Phase 1
Dependencies: None (first story)
Scope
In scope
VMCPinterface(
ListTools/CallTool/ListResources/ReadResource/ListPrompts/GetPrompt/LookupTool/LookupResource/LookupPrompt/Close) and the coreConfiginthe root
pkg/vmcppackage (P1.1 Define VMCP interface + core Config #5434).Tool/Resource/Prompt.BackendIDpopulation through the advertising filteras a first-class, test-enforced contract across all conflict strategies
(P1.2 Pin BackendID through the advertising filter (tests) #5435).
ElicitationRequester+ElicitationRequest/ElicitationResultvalue types and confine mcp-go translation to the SDKelicitation adapter — bounded rewrite R4 (P1.3 Domain-typed ElicitationRequester (bounded rewrite) #5436).
New(cfg) -> VMCP(*coreVMCP) by relocatingserver.New's domainwiring; inject a
health.StatusProviderand runfilterHealthyBackendsinternally (P1.4 New(cfg) -> VMCP core constructor #5437).
Admissionseam and wire it into the core'sList*/Call/Read/Getso list-omit and call-deny enforce one decision —bounded rewrite R1 (P1.5 Core admission seam (bounded rewrite) #5438).
Out of scope
Serve(ctx, VMCP, *ServerConfig), theServerConfigtype, and re-homing thetransport (mcp-go server, hooks, middleware chain, AS runner, status reporter,
optimizer, health monitor) — that is Phase 2: Serve transport helper, re-home transport, replace discovery #5431 (Phase 2).
server.New's body to the wrapper and thederiveCoreConfig/deriveServerConfigsplit — Phase 3: Reduce server.New to wrapper + config split #5432 (Phase 3).In Phase 1 the new core exists but is not wired into
server.New, so thelive authz middleware still runs in the shipped path; any temporary
double-enforcement exists only inside tests that exercise the core directly.
Child Tasks
PR-sized tasks under this story (each ≤ 400 LOC, ≤ 10 files changed excluding
tests/docs/generated, one logical change):
VMCPinterface + coreConfigBackendIDthrough the advertising filter (tests)ElicitationRequester(bounded rewrite, R4) — HIGH RISKNew(cfg) -> VMCPcore constructorAcceptance Criteria
server.Newsignature and observable behavior unchangedserver.Newin Phase 1 — the coreexists as additive public API (
VMCP/New) but theserver.Newbody isuntouched; the live authz HTTP middleware still runs in the shipped path
VMCPinterface andNew(cfg) -> VMCPexist as additive public API in theroot
pkg/vmcppackage, with identity as an explicit*auth.Identityand nomcp-go types crossing the boundary
Tool/Resource/Prompt.BackendIDis pinned non-empty on every advertisedcapability across all conflict strategies (P1.2 Pin BackendID through the advertising filter (tests) #5435)
ElicitationRequestercarriesno mcp-go types and its dedicated parity tests pass (accept/decline/cancel/
timeout; security validation still fires)
Cedar decision, no-op/allow-all when unconfigured,
passThroughToolsexempt,identity never logged — dedicated R1 security parity tests pass
thv vmcp serveE2E suite passes unchangedReferences