Support stateless MCP connections with cache TTL and statefulness tracking (#254)
## Summary This PR adds support for stateless MCP connections (the 2026-07-28 model where any request may hit any server instance) by introducing connection statefulness tracking, time-based cache expiry for stateless connections, and improved session state management. ## Key Changes - **Statefulness Detection**: Added `SessionStatefulness` type and `deriveStatefulness()` method to distinguish between: - `stateful`: stdio processes or HTTP servers with a session ID (resumable) - `stateless`: HTTP servers without a session ID (not resumable, any request may hit any instance) - `unknown`: not yet determined - **Tools Cache TTL**: Implemented time-based expiry (`STATELESS_TOOLS_CACHE_TTL_MS = 60s`) for stateless connections as a fallback since they don't receive `tools/list_changed` push notifications. Stateful connections continue to rely on notification-driven invalidation with no expiry. - **Client Capabilities Extraction**: Moved hardcoded capabilities into a new `buildClientCapabilities()` function in `src/core/capabilities.ts` as a single source of truth for protocol generation evolution. - **Tasks API Accessor**: Added `tasksApi` getter to centralize access to the SDK's experimental tasks API, simplifying future migration to the 2026-07-28 Tasks extension. - **Session State Persistence**: Updated `SessionData` to persist `statefulness` alongside `mcpSessionId` so the session list can display connection mode without extra round-trips. - **Session Expiry Logic**: Refined session expiry detection to only treat bare HTTP 404 as expiry when the connection actually has a server-assigned session ID (stateless connections cannot expire). - **CLI Output**: Enhanced `formatServerDetails()` to display protocol version with statefulness suffix (e.g., "2026-07-28 (stateless)"), omitting the suffix when statefulness is unknown. - **Tests**: Added unit tests for statefulness display in server details formatting. ## Implementation Details - Statefulness is derived at connect time from the transport type (presence of `terminateSession()` method indicates HTTP) and session ID presence - Cache expiry check is performed before returning cached tools in `listAllTools()` - The `cachedToolsExpiresAt` field is set when caching tools and cleared on explicit invalidation - Session resumption logic now guards against marking stateless sessions as "expired" since they have no persistent state to lose https://claude.ai/code/session_012C98WbuwkvUPipfaeXf5ym Co-authored-by: Claude <noreply@anthropic.com>
J
Jan Curn committed
0fd565c345eed6ae387f45a9e0cfd67d64d59991
Parent: c89da28
Committed by GitHub <noreply@github.com>
on 5/30/2026, 9:26:53 PM