From 3f727de90ba6e8fc7be09e289fe07d6a9213759a Mon Sep 17 00:00:00 2001 From: devcrocod Date: Wed, 17 Jun 2026 21:22:07 +0200 Subject: [PATCH] chore(deprecation): promote deprecated APIs from WARNING to ERROR Raise @Deprecated level to ERROR for the legacy ClientCapabilities constructor, ElicitRequest.requestedSchema, the ElicitRequestParams(...) factory, and the StdioServerTransport(inputStream, outputStream) constructor. Migrate the internal and test call sites that consumed these now-ERROR APIs so everything still compiles: - elicitation DSL builds ElicitRequestFormParams directly - StreamableHttpServerTransport drops the in-band DNS-rebinding check (now handled by the DnsRebindingProtection plugin, as its handleRequest KDoc already documents) - integration test bases use the input/output stdio constructor - drop the deprecated-compat ElicitationTest cases --- .../sdk/integration/kotlin/KotlinTestBase.kt | 4 +- .../sdk/integration/typescript/TsTestBase.kt | 4 +- .../kotlin/sdk/types/capabilities.kt | 2 +- .../kotlin/sdk/types/elicitation.dsl.kt | 3 +- .../kotlin/sdk/types/elicitation.kt | 4 +- .../kotlin/sdk/types/ElicitationTest.kt | 30 -------------- .../kotlin/sdk/server/StdioServerTransport.kt | 2 +- .../server/StreamableHttpServerTransport.kt | 40 ++----------------- 8 files changed, 13 insertions(+), 76 deletions(-) diff --git a/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/integration/kotlin/KotlinTestBase.kt b/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/integration/kotlin/KotlinTestBase.kt index 2f8b3872f..3c7fc4b22 100644 --- a/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/integration/kotlin/KotlinTestBase.kt +++ b/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/integration/kotlin/KotlinTestBase.kt @@ -182,8 +182,8 @@ abstract class KotlinTestBase { // Server transport reads from client and writes to client val serverTransport = StdioServerTransport( - inputStream = clientToServerIn.asSource().buffered(), - outputStream = serverToClientOut.asSink().buffered(), + input = clientToServerIn.asSource().buffered(), + output = serverToClientOut.asSink().buffered(), ) stdioServerTransport = serverTransport diff --git a/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/integration/typescript/TsTestBase.kt b/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/integration/typescript/TsTestBase.kt index 29a6de734..6898004ed 100644 --- a/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/integration/typescript/TsTestBase.kt +++ b/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/integration/typescript/TsTestBase.kt @@ -198,8 +198,8 @@ abstract class TsTestBase { // Create Kotlin server and attach stdio transport to the process streams val server: Server = KotlinServerForTsClient().createMcpServer() val transport = StdioServerTransport( - inputStream = process.inputStream.asSource().buffered(), - outputStream = process.outputStream.asSink().buffered(), + input = process.inputStream.asSource().buffered(), + output = process.outputStream.asSink().buffered(), ) // Connect server in a background thread to avoid blocking diff --git a/kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/types/capabilities.kt b/kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/types/capabilities.kt index cd78b582a..e6602975c 100644 --- a/kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/types/capabilities.kt +++ b/kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/types/capabilities.kt @@ -82,7 +82,7 @@ public data class ClientCapabilities( "experimental = experimental, " + "extensions = extensions)", ), - level = DeprecationLevel.WARNING, + level = DeprecationLevel.ERROR, ) public constructor( sampling: JsonObject?, diff --git a/kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/types/elicitation.dsl.kt b/kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/types/elicitation.dsl.kt index 12309fad7..9eb5481c6 100644 --- a/kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/types/elicitation.dsl.kt +++ b/kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/types/elicitation.dsl.kt @@ -1,6 +1,7 @@ package io.modelcontextprotocol.kotlin.sdk.types import io.modelcontextprotocol.kotlin.sdk.ExperimentalMcpApi +import io.modelcontextprotocol.kotlin.sdk.types.ElicitRequestFormParams import kotlinx.serialization.json.JsonObject import kotlinx.serialization.json.JsonObjectBuilder import kotlinx.serialization.json.buildJsonObject @@ -153,7 +154,7 @@ public class ElicitRequestBuilder @PublishedApi internal constructor() : Request "Missing required field 'requestedSchema'. Use requestedSchema { properties { ... } }" } - val params = ElicitRequestParams(message = message, requestedSchema = requestedSchema, meta = meta) + val params = ElicitRequestFormParams(message = message, requestedSchema = requestedSchema, meta = meta) return ElicitRequest(params) } } diff --git a/kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/types/elicitation.kt b/kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/types/elicitation.kt index a0ee74c9a..a90e77243 100644 --- a/kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/types/elicitation.kt +++ b/kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/types/elicitation.kt @@ -30,7 +30,7 @@ public data class ElicitRequest(override val params: ElicitRequestParams) : Serv @Deprecated( "Use (params as ElicitRequestFormParams).requestedSchema", ReplaceWith("(params as ElicitRequestFormParams).requestedSchema"), - DeprecationLevel.WARNING, + DeprecationLevel.ERROR, ) public val requestedSchema: ElicitRequestParams.RequestedSchema? get() = (params as? ElicitRequestFormParams)?.requestedSchema @@ -88,7 +88,7 @@ public sealed interface ElicitRequestParams : RequestParams { @Deprecated( "Use ElicitRequestFormParams instead", ReplaceWith("ElicitRequestFormParams(message, requestedSchema = requestedSchema, meta = meta)"), - DeprecationLevel.WARNING, + DeprecationLevel.ERROR, ) @Suppress("FunctionName") public fun ElicitRequestParams( diff --git a/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/types/ElicitationTest.kt b/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/types/ElicitationTest.kt index 1a418639a..c6debcb51 100644 --- a/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/types/ElicitationTest.kt +++ b/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/types/ElicitationTest.kt @@ -173,36 +173,6 @@ class ElicitationTest { params.url shouldBe "https://example.com/auth" } - // ── Deprecated compat ─────────────────────────────────────────────── - - @Test - fun `deprecated factory function should create ElicitRequestFormParams`() { - val schema = ElicitRequestParams.RequestedSchema( - properties = mapOf("name" to StringSchema()), - ) - val params = ElicitRequestParams(message = "Test", requestedSchema = schema) - params.shouldBeInstanceOf() - params.message shouldBe "Test" - params.requestedSchema shouldBe schema - } - - @Test - fun `deprecated requestedSchema should return schema for form mode`() { - val schema = ElicitRequestParams.RequestedSchema( - properties = mapOf("x" to BooleanSchema()), - ) - val request = ElicitRequest(ElicitRequestFormParams(message = "m", requestedSchema = schema)) - request.requestedSchema shouldBe schema - } - - @Test - fun `deprecated requestedSchema should return null for URL mode`() { - val request = ElicitRequest( - ElicitRequestURLParams(message = "m", elicitationId = "id", url = "https://example.com"), - ) - request.requestedSchema.shouldBeNull() - } - // ── ElicitResult ──────────────────────────────────────────────────── @Test diff --git a/kotlin-sdk-server/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/server/StdioServerTransport.kt b/kotlin-sdk-server/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/server/StdioServerTransport.kt index 9261be257..bfe02a69f 100644 --- a/kotlin-sdk-server/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/server/StdioServerTransport.kt +++ b/kotlin-sdk-server/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/server/StdioServerTransport.kt @@ -112,7 +112,7 @@ public class StdioServerTransport private constructor( @Deprecated( message = "Use StdioServerTransport(input, output) { ... } instead.", replaceWith = ReplaceWith("StdioServerTransport(input = inputStream, output = outputStream)"), - level = DeprecationLevel.WARNING, + level = DeprecationLevel.ERROR, ) public constructor(inputStream: Source, outputStream: Sink) : this( input = inputStream, diff --git a/kotlin-sdk-server/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/server/StreamableHttpServerTransport.kt b/kotlin-sdk-server/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/server/StreamableHttpServerTransport.kt index ae8a39d1d..3238a90e6 100644 --- a/kotlin-sdk-server/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/server/StreamableHttpServerTransport.kt +++ b/kotlin-sdk-server/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/server/StreamableHttpServerTransport.kt @@ -138,17 +138,17 @@ public class StreamableHttpServerTransport(private val configuration: Configurat public val enableJsonResponse: Boolean = false, @Deprecated( message = "Use install(DnsRebindingProtection) on your Ktor route instead", - level = DeprecationLevel.WARNING, + level = DeprecationLevel.ERROR, ) public val enableDnsRebindingProtection: Boolean = false, @Deprecated( message = "Use install(DnsRebindingProtection) on your Ktor route instead", - level = DeprecationLevel.WARNING, + level = DeprecationLevel.ERROR, ) public val allowedHosts: List? = null, @Deprecated( message = "Use install(DnsRebindingProtection) on your Ktor route instead", - level = DeprecationLevel.WARNING, + level = DeprecationLevel.ERROR, ) public val allowedOrigins: List? = null, public val eventStore: EventStore? = null, @@ -320,12 +320,6 @@ public class StreamableHttpServerTransport(private val configuration: Configurat * (or apply equivalent middleware) to validate `Host` / `Origin` headers. */ public suspend fun handleRequest(session: ServerSSESession?, call: ApplicationCall) { - validateHeaders(call)?.let { reason -> - call.reject(HttpStatusCode.Forbidden, RPCError.ErrorCode.CONNECTION_CLOSED, reason) - _onError(Error(reason)) - return - } - when (call.request.httpMethod) { HttpMethod.Post -> handlePostRequest(session, call) @@ -664,34 +658,6 @@ public class StreamableHttpServerTransport(private val configuration: Configurat } } - private fun validateHeaders(call: ApplicationCall): String? { - if (!configuration.enableDnsRebindingProtection) return null - - configuration.allowedHosts?.let { hosts -> - val hostHeader = call.request.headers[HttpHeaders.Host] - val hostname = hostHeader?.let { extractHostname(it) }?.lowercase() - val allowedHostsLowercase = hosts.map { - extractHostname(it)?.lowercase() ?: it.lowercase() - } - - if (hostname == null || hostname !in allowedHostsLowercase) { - return "Invalid Host header: $hostHeader" - } - } - - configuration.allowedOrigins?.let { origins -> - val originHeader = call.request.headers[HttpHeaders.Origin]?.lowercase() - val allowedOriginsLowercase = origins.map { it.lowercase() } - - // Allow requests without Origin (non-browser clients cannot perform DNS rebinding) - if (originHeader != null && originHeader !in allowedOriginsLowercase) { - return "Invalid Origin header: $originHeader" - } - } - - return null - } - private suspend fun flushSse(session: ServerSSESession?) { try { session?.send(data = "")