fix(server): drop inert eventStore from stateless Streamable HTTP (#909)

Removes the `eventStore` parameter from `mcpStatelessStreamableHttp`,
where it was never read or written.

closes #908 

## Breaking Changes
Source-breaking only

## Types of changes
- [x] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing
functionality to change)
- [ ] Documentation update

## Checklist
- [x] I have read the [MCP
Documentation](https://modelcontextprotocol.io)
- [x] My code follows the repository's style guidelines
- [x] New and existing tests pass locally
- [x] I have added appropriate error handling
- [x] I have added or updated documentation as needed
This commit is contained in:
Pavel Gorgulov 2026-07-28 15:43:24 +02:00 • committed by GitHub
commit dab340f9bf
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 35 additions and 6 deletions

View file

@ -58,7 +58,9 @@ public final class io/modelcontextprotocol/kotlin/sdk/server/KtorServerKt {
public static synthetic fun mcp$default (Lio/ktor/server/routing/Route;Ljava/lang/String;ZLjava/util/List;Ljava/util/List;JLkotlin/jvm/functions/Function1;ILjava/lang/Object;)V
public static synthetic fun mcp$default (Lio/ktor/server/routing/Route;ZLjava/util/List;Ljava/util/List;JLkotlin/jvm/functions/Function1;ILjava/lang/Object;)V
public static final fun mcpStatelessStreamableHttp (Lio/ktor/server/application/Application;Ljava/lang/String;ZLjava/util/List;Ljava/util/List;Lio/modelcontextprotocol/kotlin/sdk/server/EventStore;Lkotlin/jvm/functions/Function1;)V
public static final fun mcpStatelessStreamableHttp (Lio/ktor/server/application/Application;Ljava/lang/String;ZLjava/util/List;Ljava/util/List;Lkotlin/jvm/functions/Function1;)V
public static synthetic fun mcpStatelessStreamableHttp$default (Lio/ktor/server/application/Application;Ljava/lang/String;ZLjava/util/List;Ljava/util/List;Lio/modelcontextprotocol/kotlin/sdk/server/EventStore;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)V
public static synthetic fun mcpStatelessStreamableHttp$default (Lio/ktor/server/application/Application;Ljava/lang/String;ZLjava/util/List;Ljava/util/List;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)V
public static final fun mcpStreamableHttp (Lio/ktor/server/application/Application;Ljava/lang/String;ZLjava/util/List;Ljava/util/List;Lio/modelcontextprotocol/kotlin/sdk/server/EventStore;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)V
public static synthetic fun mcpStreamableHttp$default (Lio/ktor/server/application/Application;Ljava/lang/String;ZLjava/util/List;Ljava/util/List;Lio/modelcontextprotocol/kotlin/sdk/server/EventStore;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)V
}

View file

@ -270,7 +270,6 @@ private fun Application.mcpStatelessStreamableHttp(
block: RoutingContext.() -> Server,
) {
installMcpContentNegotiation()
install(SSE)
routing {
route(path) {
@ -297,10 +296,11 @@ private fun Application.mcpStatelessStreamableHttp(
* over _stateless_ [Streamable HTTP Transport](https://modelcontextprotocol.io/specification/2025-11-25/basic/transports#streamable-http)
*
* Sets up an HTTP POST endpoint at [path]. GET and DELETE requests return 405 Method Not Allowed.
* Simple request/response pairs are returned as JSON (not SSE streams).
* Every request/response pair is returned as JSON, so this endpoint opens no SSE stream and
* offers no resumability. Use [mcpStreamableHttp] when either is required.
*
* Automatically installs [ContentNegotiation][io.ktor.server.plugins.contentnegotiation.ContentNegotiation]
* with [McpJson][io.modelcontextprotocol.kotlin.sdk.types.McpJson] and [SSE].
* with [McpJson][io.modelcontextprotocol.kotlin.sdk.types.McpJson].
*
* @param path The URL path where the server listens for incoming JSON-RPC requests. Defaults to "/mcp".
* @param enableDnsRebindingProtection Determines whether DNS rebinding protection is enabled. Defaults to `true`.
@ -309,7 +309,6 @@ private fun Application.mcpStatelessStreamableHttp(
* @param allowedOrigins A list of allowed `Origin` header values, compared by hostname only
* (scheme and port are ignored). Requests without an `Origin` header are allowed.
* If `null`, origin validation is disabled.
* @param eventStore An optional [EventStore] implementation to provide resumability and event replay support.
* @param block factory block with access to the [RoutingContext] (for reading request headers)
* that creates and returns the [Server] to handle the connection.
*/
@ -319,7 +318,6 @@ public fun Application.mcpStatelessStreamableHttp(
enableDnsRebindingProtection: Boolean = true,
allowedHosts: List<String>? = null,
allowedOrigins: List<String>? = null,
eventStore: EventStore? = null,
block: RoutingContext.() -> Server,
) {
mcpStatelessStreamableHttp(
@ -328,13 +326,42 @@ public fun Application.mcpStatelessStreamableHttp(
allowedHosts = allowedHosts,
allowedOrigins = allowedOrigins,
configuration = StreamableHttpServerTransport.Configuration(
eventStore = eventStore,
enableJsonResponse = true,
),
block = block,
)
}
/**
* Retained only so that existing call sites get a migration hint instead of an unresolved parameter name.
*
* @param eventStore never consulted by a stateless endpoint. Resumption is driven by a `GET` carrying
* `Last-Event-ID`, and this endpoint answers `GET` with `405 Method Not Allowed`, so no stream
* exists to store events on or replay them to. Use [mcpStreamableHttp] when you need resumability.
*/
@Deprecated(
"Use mcpStatelessStreamableHttp without eventStore.",
ReplaceWith("mcpStatelessStreamableHttp(path, enableDnsRebindingProtection, allowedHosts, allowedOrigins, block)"),
DeprecationLevel.ERROR,
)
@Suppress("UnusedParameter")
public fun Application.mcpStatelessStreamableHttp(
path: String = "/mcp",
enableDnsRebindingProtection: Boolean = true,
allowedHosts: List<String>? = null,
allowedOrigins: List<String>? = null,
eventStore: EventStore?,
block: RoutingContext.() -> Server,
) {
mcpStatelessStreamableHttp(
path = path,
enableDnsRebindingProtection = enableDnsRebindingProtection,
allowedHosts = allowedHosts,
allowedOrigins = allowedOrigins,
block = block,
)
}
private suspend fun ServerSSESession.mcpSseEndpoint(
postEndpoint: String,
transportManager: TransportManager<SseServerTransport>,