Repository navigation
Description
This release closes memory-exhaustion holes in the WebSocket and Streamable HTTP servers, deprecates the SSE transport in favor of Streamable HTTP, moves APIs deprecated in 0.9.0–0.13.0 to ERROR, and adds experimental SEP-2575 discovery types.
Breaking Changes
Streamable HTTP sessions expire when idle and are capped by @devcrocod in #1032
mcpStreamableHttp kept every session until the client sent DELETE, so an initialize flood could exhaust server memory.
- A session expires after
sessionIdleTimeoutwithout requests (default 30 minutes; an openGETstream does not count), and its id then answers404 Not Found.Duration.INFINITEdisables expiry. - At most
maxSessionssessions are open at once (default 10,000); a new session beyond that gets503 Service Unavailable. GETwithout anMcp-Session-Idheader returns400 Bad Request.StreamableHttpClientTransportreports a404on itsGETstream throughonError. It does not start a new session by itself; the application has to.- The JVM signature of
mcpStreamableHttpchanged; recompile code built against earlier versions.
application.mcpStreamableHttp(
path = "/mcp",
sessionIdleTimeout = 10.minutes,
maxSessions = 1_000,
) { server }Deprecated APIs moved to ERROR by @devcrocod in #1128
APIs deprecated in 0.9.0–0.13.0 no longer compile; existing binaries keep working.
StreamableHttpServerTransport()andStreamableHttpServerTransport(enableJsonResponse, …)→StreamableHttpServerTransport(Configuration(...))StreamableHttpClientTransport,HttpClient.mcpStreamableHttpTransportandHttpClient.mcpStreamableHttpwithreconnectionTime→ the overloads takingReconnectionOptions(initialReconnectionDelay = ...)ElicitRequest.requestedSchema→(params as ElicitRequestFormParams).requestedSchemaElicitRequestParams(message, requestedSchema, meta)→ElicitRequestFormParams(...)ClientCapabilities(sampling: JsonObject?, …)→ the constructor with typedClientCapabilities.SamplingandClientCapabilities.ElicitationStreamableHttpServerTransport.Configuration.enableDnsRebindingProtection,allowedHostsandallowedOrigins→install(DnsRebindingProtection)on the Ktor route
StdioServerTransport(inputStream, outputStream) is now hidden: rename the named arguments to input and output. Positional calls keep compiling.
The mcpStatelessStreamableHttp overload with eventStore, at ERROR since 0.15.0, is removed. Binaries built against 0.14.0 or earlier that call it fail with NoSuchMethodError until recompiled.
- StdioServerTransport(inputStream = source, outputStream = sink)
+ StdioServerTransport(input = source, output = sink)server/discover types extend sealed hierarchies by @Yusufihsangorgel in #893
DiscoverRequest is a new ClientRequest, DiscoverResult a new ServerResult, and Method.Defined gains ServerDiscover. Exhaustive when expressions over these types need a new branch or an else.
Features
- Experimental (
@ExperimentalMcpApi) SEP-2575 types: theserver/discoverrequest and result (DiscoverRequest,DiscoverResult,CacheScope), request-scoped metadata accessors onRequestMeta(protocolVersion,clientInfo,clientCapabilities,logLevel), andUnsupportedProtocolVersionDatafor the newUNSUPPORTED_PROTOCOL_VERSION(-32022) error code. These are types and codecs only; the client and server do not use them yet, by @Yusufihsangorgel in #893 - Custom transports can report errors through the new protected
AbstractTransport.invokeOnErrorCallback, which swallows exceptions thrown by theonErrorcallback and lets cancellation propagate, by @devcrocod in #998
Changed
- The SDK is now compiled with Kotlin language and API version 2.2 (previously 2.1), by @devcrocod in #1026
Deprecated
- The SSE transport is deprecated in favor of Streamable HTTP, following SEP-2596; behavior is unchanged, by @devcrocod in #1130
SseClientTransport→StreamableHttpClientTransportHttpClient.mcpSseTransport→mcpStreamableHttpTransport;HttpClient.mcpSse→mcpStreamableHttpSseServerTransportandRoute.mcp→StreamableHttpServerTransportApplication.mcp→Application.mcpStreamableHttp
Security
Application.mcpWebSocketnow caps inbound WebSocket messages at 16 MiB; before, a single large message could exhaust server memory. WithRoute.mcpWebSocket, setmaxFrameSizewhen you installWebSockets.WebSocketMcpTransportnow keeps reading after a message it cannot handle instead of stalling while frames pile up, and reports such messages and failed sessions throughonError, by @devcrocod in #1129mcpStreamableHttpexpires idle sessions and caps their number, so aninitializeflood can no longer exhaust server memory (see Breaking Changes), by @devcrocod in #1032SseClientTransportrefuses to start when the server'sendpointevent points to a different origin, so a malicious server can no longer redirect requests, and their auth headers, to another host, by @ez-lbz in #958
Fixed
SseClientTransportresolves relativeendpointURLs per RFC 3986, so connecting to a server mounted under a sub-path such asmcp("/api/mcp/sse")no longer fails with404, by @gibkim in #1100Application.mcpandApplication.mcpStreamableHttpno longer fail withDuplicatePluginExceptionwhen the SSE plugin is already installed, so one application can host several Streamable HTTP endpoints, by @soulkeykim in #1092ServerSession.onInitializedcallbacks registered after the handshake now run immediately, and concurrent registrations are no longer lost; a duplicatenotifications/initializedno longer runs the callbacks again, by @taekop in #1019prompts/getfor an unknown prompt returnsINVALID_PARAMS(-32602) instead ofINTERNAL_ERROR(-32603), by @latent-9 in #955- Tasks with unlimited retention are sent with
"ttl": null, as the schema requires, instead of omitting the key, by @devcrocod in #1089 CancellationExceptionno longer reachesonErrorcallbacks or the log, by @devcrocod in #998
Maintenance
- ABI checks use the Kotlin Gradle plugin's built-in validation (
checkKotlinAbi/updateKotlinAbi) instead of binary-compatibility-validator, and every module gains a klib ABI dump, by @devcrocod in #1027 - Trimmed the test suite to tests that guard real SDK behavior and fixed tests that could not fail; a local
jvmTestrun went from about 40 s to 5 s, by @devcrocod in #1054 - Removed log-message assertion tests and the Logback test dependency from the server module, by @devcrocod in #1053
- Fixed the flaky
ChannelTransportTest, by @devcrocod in #1028
Dependencies
- Kotlin to v2.4.21 in #901, #986, #1111
- Ktor to v3.5.2 in #941
- kotlinx-collections-immutable to v0.5.2 in #981
- Gradle to v9.8.1 in #949, #969, #1023, #1112
- Kotest to v6.2.6 in #960, #992, #1125
- slf4j-simple to v2.0.20 in #993, #1022
- detekt-gradle-plugin to v2.0.0-alpha.6 in #950
- Kover to v0.9.11 in #1097
- logback-classic and binary-compatibility-validator updates, both since removed from the build, in #916, #970, #982
- Sample projects updated to Kotlin 2.4.21, Ktor BOM 3.6.0, mcp-kotlin 0.15.0, Gradle 9.8.1 and anthropic-java 2.73.0 in #900, #903, #904, #905, #924, #925, #926, #927, #928, #929, #942, #944, #945, #946, #947, #948, #968, #974, #980, #988, #989, #994, #995, #996, #997, #1011, #1012, #1013, #1014, #1015, #1021, #1031, #1106, #1108, #1110, #1113, #1114, #1116, #1124
- Test harness and CI updates (TypeScript integration-test dependencies, CodeQL,
gradle/actions,actions/setup-javav6,actions/stalev11) in #932, #933, #934, #935, #937, #939, #940, #952, #954, #962, #963, #971, #975, #977, #978, #979, #983, #987, #990, #991, #1017, #1018, #1025, #1029, #1030, #1055, #1098, #1099, #1104, #1109, #1115, #1126, #1127, #1133, #1140, #1141
New Contributors
- @Yusufihsangorgel made their first contribution in #893
- @latent-9 made their first contribution in #955
- @ez-lbz made their first contribution in #958
- @taekop made their first contribution in #1019
- @soulkeykim made their first contribution in #1092
- @gibkim made their first contribution in #1100
Full Changelog: 0.15.0...0.16.0