modelcontextprotocol / modelcontextprotocol/kotlin-sdk

[Proposal] SEP-2567: Sessionless MCP via Explicit State Handles architecture & migration design (#814)

Open
#1,004 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Kotlin
Stars
1.5k
Forks
248
Avg merge
1d 20h
Merged PRs (30d)
23

Description

Proposal: SEP-2567 Sessionless MCP via Explicit State Handles Architecture & Migration Design (#814)
Motivation & Context

Tracking implementation of SEP-2567 in kotlin-sdk for the 2026-07-28 MCP specification release (aligned with umbrella issue #842).

In earlier versions of the MCP specification, protocol sessions were represented via the Mcp-Session-Id transport header and server-side session tables. In practice, session-scoped state created severe architectural bottlenecks:

  1. Cache Invalidation & Redundant Round-Trips: The possibility that tools/list, resources/list, or prompts/list might vary per session forced clients and multi-agent orchestrators to re-fetch the entire catalog on every new connection ($O(\text{subagents} \times \text{servers})$ re-fetches), even when virtually no servers actually altered their list outputs dynamically per connection.
  2. Rigid 1:1 Cardinality: Implicit session state prevented mixed shared/isolated state topologies (for instance, multiple subagents needing to share an authenticated workspace context while isolating execution handles or transaction sandboxes).
  3. Transport Fragility: Modern cloud load balancers and horizontal auto-scaling struggle with long-lived session affinity without sticky routing or distributed session replication.

SEP-2567 eliminates protocol-level sessions from MCP:

  • No Mcp-Session-Id header is generated or required on the wire.
  • tools/list, resources/list, and prompts/list MUST NOT depend on per-connection or prior-tool-call state; their outputs are strictly cacheable at (deployment, auth) granularity.
  • Stateful workflows transition to explicit, server-minted state handles (e.g., create_*() -> handle passed into subsequent operations).

This proposal outlines the Kotlin Multiplatform architecture to transition kotlin-sdk (kotlin-sdk-core, kotlin-sdk-server, and kotlin-sdk-client) to a sessionless model.


Proposed Architecture & Component Design
1. Transport Header & Protocol Version Gating (kotlin-sdk-core)

In kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/:

  • Protocol Version Negotiation:
    For negotiated protocol version >= 2026-07-28:
    • Mcp-Session-Id is no longer emitted by client transports or parsed/required by server transports.
    • For backwards compatibility with legacy protocol versions (< 2026-07-28), preserve optional Mcp-Session-Id extraction only when legacy protocol negotiation is explicitly active.
  • Constant Deprecation:
    @Deprecated(
        message = "Session IDs are removed in MCP spec 2026-07-28 per SEP-2567. Use explicit state handles instead.",
        level = DeprecationLevel.WARNING
    )
    public const val MCP_SESSION_ID_HEADER: String = "Mcp-Session-Id"
    
2. Streamable HTTP Transport Refactoring (kotlin-sdk-server & Ktor integration)
  • Per-Request Dispatch:
    Currently, StreamableHttpServerTransport maintains an internal session map keyed by session ID to dispatch messages to specific ServerSession instances.
    • Transition the HTTP POST endpoint (/mcp or custom path) to stateless, per-request request-response dispatch.
    • Incoming requests are executed directly against a shared ServerRequestHandler or stateless ServerSession instance without session lookup locks.
    • SSE or standalone notification streams (if negotiated for keep-alives or asynchronous task progress per SEP-2663) decouple from transport session IDs.
  • Ktor Extension Simplification:
    // Clean, stateless Ktor routing
    route("/mcp") {
        post {
            serverTransport.handlePost(call)
        }
    }
    
3. Explicit State Handle Utilities (kotlin-sdk-core / kotlin-sdk-server)

To provide first-class ergonomics for servers that maintain domain or transaction state across calls:

  • Provide an explicit state handle abstraction:
    @Serializable
    @JvmInline
    public value class StateHandle(public val value: String) {
        public companion object {
            public fun generate(): StateHandle = StateHandle(uuid4().toString())
        }
    }
    
  • State Handle Storage SPI:
    public interface StateStore<T> {
        public suspend fun get(handle: StateHandle): T?
        public suspend fun put(handle: StateHandle, state: T, ttl: Duration? = null)
        public suspend fun remove(handle: StateHandle): T?
    }
    
    public class InMemoryStateStore<T>(
        private val defaultTtl: Duration = 1.hours
    ) : StateStore<T> { ... }
    
    This makes state lifetime explicit, observable, and easily backed by Redis, Memcached, or distributed storage when running multi-instance servers behind stateless load balancers.
4. Client-Side Catalog Caching & Multi-Agent Sharing (kotlin-sdk-client)
  • Cross-Call Cache Reusability:
    Because tools/list, resources/list, and prompts/list are guaranteed not to mutate based on ephemeral connection handles, clients can safely cache catalog responses at the host/auth scope:
    public class McpCatalogCache(
        private val client: ClientSession,
        private val cacheTtl: Duration = 15.minutes
    ) {
        private val tools = atomic<ListToolsResult?>(null)
        public suspend fun getTools(forceRefresh: Boolean = false): ListToolsResult = ...
    }
    
  • Subagents and concurrent coroutines sharing the same credentials can reuse the identical cached catalog without issuing parallel tools/list RPC calls.
5. Testing & Conformance Plan
  • Unit Tests:
    • Verify that StreamableHttpClientTransport does not send Mcp-Session-Id when negotiating protocol version 2026-07-28.
    • Verify that server accepts POST requests without Mcp-Session-Id header and correctly returns JSON-RPC responses.
    • Verify that legacy clients sending Mcp-Session-Id under < 2026-07-28 continue to function without breakage.
  • Conformance Scenarios:
    • Validate against the sessionless conformance test suite in modelcontextprotocol/conformance verifying stateless HTTP POST handling and cache stability.

Next Steps

Upon review and consensus on the sessionless architecture:

  1. Deprecate Mcp-Session-Id and remove mandatory session lookups in StreamableHttpServerTransport.
  2. Add StateHandle and StateStore utilities in kotlin-sdk-server.
  3. Enable cross-connection catalog caching in kotlin-sdk-client.
  4. Update unit tests and conformance suite integration.

AI assistance disclosure: AI was used to discover this opportunity and draft the change or text. The submission was checked against the prepared artifact and recorded verification evidence.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reviewing the transport and protocol-version code under kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/, then inspect StreamableHttpServerTransport and the Ktor integration in kotlin-sdk-server. Compare the listed unit-test and conformance scenarios for 2026-07-28 and legacy versions; done means the migration design is agreed and its core, server, client, and test changes are covered.

Written by the indexing model from the issue text.

Assessment

Tech stack
kotlin
Domain
api, backend
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.