Introduce the core interfaces and default implementations for the new session-scoped architecture described in THV-0038. This is purely additive — no changes to existing flows or existing tests.
New files:
pkg/vmcp/session/session.go — Session interface (domain object: CallTool, ReadResource, GetPrompt, Tools, Resources, Prompts, BackendSessions, Close)
pkg/vmcp/session/factory.go — SessionFactory interface and defaultSessionFactory
pkg/vmcp/session/default_session.go — defaultSession embedding transportsession.Session for metadata, owning backend clients in an internal map, tracking backend session IDs
Key requirements:
- Factory initializes clients per backend in parallel (
errgroup, bounded concurrency configurable via max_backend_init_concurrency, default 10, per-session not global)
- Per-backend timeout so a slow backend does not block session creation
- Partial initialization: log warnings for failed backends, continue with successful ones
- Thread safety:
sync.RWMutex protecting client map; RLock released before network I/O; sync.WaitGroup in-flight counter so Close() waits for in-flight calls before tearing down clients
Close() closes all owned backend clients
Dual-layer storage model:
defaultSession must cleanly separate two layers with different lifecycles:
| Layer |
Contents |
Storage |
Lifetime |
| Metadata |
Session ID, timestamps, identity reference, backend ID list |
Serializable via transportsession.Storage interface (LocalStorage today, RedisStorage in future) |
Can persist across restarts |
| Runtime |
MCP client objects, routing table, capabilities, backend session ID map, closed flag |
In-process memory only — cannot be serialized (active TCP connections, goroutines) |
Lives only while the vMCP instance is running |
The behavior-oriented Session embeds transportsession.Session (metadata layer) and owns MCP clients (runtime layer). All sessions go through the same Storage interface — no parallel storage path.
Distributed deployment note: because MCP clients cannot be serialized, horizontal scaling requires sticky sessions (session affinity at the load balancer). Without sticky sessions, a request routed to a different vMCP instance will need to recreate backend clients (one-time cost). This is a known trade-off and must be documented in the implementation.
Acceptance Criteria
RFC: THV-0038 — Session-scoped client lifecycle
Introduce the core interfaces and default implementations for the new session-scoped architecture described in THV-0038. This is purely additive — no changes to existing flows or existing tests.
New files:
pkg/vmcp/session/session.go—Sessioninterface (domain object:CallTool,ReadResource,GetPrompt,Tools,Resources,Prompts,BackendSessions,Close)pkg/vmcp/session/factory.go—SessionFactoryinterface anddefaultSessionFactorypkg/vmcp/session/default_session.go—defaultSessionembeddingtransportsession.Sessionfor metadata, owning backend clients in an internal map, tracking backend session IDsKey requirements:
errgroup, bounded concurrency configurable viamax_backend_init_concurrency, default 10, per-session not global)sync.RWMutexprotecting client map;RLockreleased before network I/O;sync.WaitGroupin-flight counter soClose()waits for in-flight calls before tearing down clientsClose()closes all owned backend clientsDual-layer storage model:
defaultSessionmust cleanly separate two layers with different lifecycles:transportsession.Storageinterface (LocalStorage today, RedisStorage in future)The behavior-oriented
Sessionembedstransportsession.Session(metadata layer) and owns MCP clients (runtime layer). All sessions go through the sameStorageinterface — no parallel storage path.Distributed deployment note: because MCP clients cannot be serialized, horizontal scaling requires sticky sessions (session affinity at the load balancer). Without sticky sessions, a request routed to a different vMCP instance will need to recreate backend clients (one-time cost). This is a known trade-off and must be documented in the implementation.
Acceptance Criteria
Sessioninterface is defined inpkg/vmcp/session/session.gowith all required methodsSessionFactoryinterface anddefaultSessionFactoryare defined and implementeddefaultSessionimplements bothtransportsession.Sessionand the vMCPSessioninterfacedefaultSessioncleanly separates serializable metadata from non-serializable runtime state — no MCP client objects or routing tables are placed in the metadata layertransportsession.Storageinterface; no parallel storage path is introducedRLockand locks are released before network I/OClose()waits for all in-flight operations to complete before closing backend clientsCallTool,ReadResource,Close, parallel init with timeouts, partial failure, interface compositionRFC: THV-0038 — Session-scoped client lifecycle