Description
This release makes inbound message handling concurrent after the initialization handshake, adds SSE heartbeats and elicitation schema validation, and fixes several Streamable HTTP response and session-lifecycle bugs.
Breaking Changes
Concurrent inbound dispatch by @devcrocod in #884
Inbound messages were processed serially on the transport read loop, so one slow or peer-awaiting handler (sampling, elicitation, roots) blocked every later message on the connection, including the responses and notifications/cancelled those handlers depend on. After the handshake, handlers may now run concurrently; processing stays serial during initialization.
ProtocolOptions/ClientOptions/ServerOptionsgain ahandlerCoroutineContextparameter (defaults toDispatchers.Default). Concurrency is bounded internally; there is no opt-out flag by design.RequestHandlerExtrais now aCoroutineContext.Elementand is no longer user-constructible. It exposesrequestId,method,sendRequest, andsendNotification, and can be read inside a handler via the newcurrentRequestHandlerExtra().
- val extra = RequestHandlerExtra()
+ val extra = currentRequestHandlerExtra()Duplicate feature names are rejected at registration by @devcrocod in #883
addTool/addPrompt/addResource/addResourceTemplate silently replaced an existing entry and emitted a spurious list_changed notification. They now throw IllegalArgumentException and leave the existing registration intact. The batch variants (addTools/addPrompts/addResources) are all-or-nothing. To replace a feature, remove it first.
- server.addTool(name = "search", ...) // silently overwrote
+ server.removeTool("search")
+ server.addTool(name = "search", ...)eventStore removed from mcpStatelessStreamableHttp by @devcrocod in #909
The parameter was never read or written: a stateless endpoint answers GET with 405, so no stream exists to store or replay events on. The old overload is retained at DeprecationLevel.ERROR with a ReplaceWith migration hint. Use mcpStreamableHttp when you need resumability.
application.mcpStatelessStreamableHttp(
path = "/mcp",
- eventStore = myEventStore,
) { server }Features
- Optional SSE heartbeats for Streamable HTTP servers via
mcpStreamableHttp(sseHeartbeatConfig = { ... }), keeping long-lived streams alive against clients that disconnect on idle. Heartbeats stay off by default, by @UnscientificJsZhai in #761 - Accepted form-mode elicitation content is now validated against
requestedSchema; a mismatch failscreateElicitationwithMcpException(INVALID_PARAMS) instead of reaching server code unchecked, by @rea9r in #896 - The Streamable HTTP client now sends the standard
Mcp-MethodandMcp-NamePOST headers, by @AndreKalberer in #894
Fixed
- Stateless Streamable HTTP now closes its per-request session, fixing unbounded growth of the session registry and notification subscriptions, by @KlyneChrysler in #872
- Streamable HTTP responses keep their status and body when the client accepts only
text/event-stream, by @devcrocod in #911 - The
Acceptheader is parsed as media ranges, so clients sending*/*orapplication/*are matched correctly, by @devcrocod in #912 - Malformed request params are reported as
Invalid params(-32602) instead ofInternal error, by @jstar0 in #886 - Duplicate
initializeerrors preserve the JSON-RPC request id, by @jstar0 in #868 - Connection teardown clears the recently-cancelled request id buffer, by @devcrocod in #892
Maintenance
- Create the server session before connecting the client in tests, avoiding a
ChannelTransportstartup race, by @devcrocod in #885
Dependencies
- Kotlin to v2.4.0 in #834
- Ktor to v3.5.1 in #832
- kotlinx-collections-immutable to v0.5.1 in #873
- Netty BOM to v4.2.16.Final in #889
- Kotest to v6.2.3 in #888, #915
- logback-classic to v1.6.0 in #890, #897, #914
- detekt-gradle-plugin to v2.0.0-alpha.5 in #849
- Kover to v0.9.9 in #902
- junit-jupiter to v6.1.2 in #897
- Shadow plugin to v9.6.1 and anthropic-java to v2.52.0 in #882, #914
- Sample projects updated to Kotlin 2.4.x, Ktor BOM 3.5.1, and mcp-kotlin 0.14.0 in #819, #822, #825, #827, #829, #859, #860, #861, #862, #863, #875, #876, #877, #878, #879, #899
- Test harness and CI updates (TypeScript integration-test dependencies,
actions/setup-nodev7) in #880, #891, #898, #906, #907, #910
New Contributors
- @UnscientificJsZhai made their first contribution in #761
- @KlyneChrysler made their first contribution in #872
- @AndreKalberer made their first contribution in #894
Full Changelog: 0.14.0...0.15.0