From 7937390b4787f758c82c369541374d050fbdcd9b Mon Sep 17 00:00:00 2001 From: Pavel Gorgulov Date: Thu, 9 Jul 2026 17:03:01 +0200 Subject: [PATCH] test: create the server session before connecting the client (#885) `ChannelTransportTest` connected both sides concurrently, which is racy. `ChannelTransport` starts delivering inbound messages when its read loop launches, which happens before `start()` marks the transport `Operational`. The two run on different dispatchers, so the client's `initialize` could reach the server read loop first and the session's `InitializeResult` was then rejected by the send state gate with `Transport is not ready`. Measured ~3 failures in 600 rounds. `createSession()` does not await the handshake, so creating the session first is enough to order the two. This also matches the usage shown in `createLinkedPair()`'s KDoc. Not addressed here: the transport itself should not deliver inbound messages before it is `Operational`. Fixing that needs a post-`Operational` hook on `AbstractClientTransport` that the read loop awaits. --- .../kotlin/sdk/testing/ChannelTransportTest.kt | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/testing/ChannelTransportTest.kt b/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/testing/ChannelTransportTest.kt index ea246db..4d798ce 100644 --- a/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/testing/ChannelTransportTest.kt +++ b/integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/testing/ChannelTransportTest.kt @@ -6,7 +6,6 @@ import io.modelcontextprotocol.kotlin.sdk.ExperimentalMcpApi import io.modelcontextprotocol.kotlin.sdk.client.Client import io.modelcontextprotocol.kotlin.sdk.server.Server import io.modelcontextprotocol.kotlin.sdk.server.ServerOptions -import io.modelcontextprotocol.kotlin.sdk.server.ServerSession import io.modelcontextprotocol.kotlin.sdk.types.Implementation import io.modelcontextprotocol.kotlin.sdk.types.InitializeRequest import io.modelcontextprotocol.kotlin.sdk.types.InitializeResult @@ -16,9 +15,6 @@ import io.modelcontextprotocol.kotlin.sdk.types.ListResourcesResult import io.modelcontextprotocol.kotlin.sdk.types.Method import io.modelcontextprotocol.kotlin.sdk.types.Resource import io.modelcontextprotocol.kotlin.sdk.types.ServerCapabilities -import kotlinx.coroutines.CompletableDeferred -import kotlinx.coroutines.joinAll -import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import kotlin.test.Test @@ -43,18 +39,9 @@ class ChannelTransportTest { clientInfo = Implementation(name = "test client", version = "1.0"), ) - val serverSessionResult = CompletableDeferred() + val serverSession = server.createSession(serverTransport) + client.connect(clientTransport) - listOf( - launch { - client.connect(clientTransport) - }, - launch { - serverSessionResult.complete(server.createSession(serverTransport)) - }, - ).joinAll() - - val serverSession = serverSessionResult.await() serverSession.setRequestHandler(Method.Defined.Initialize) { _, _ -> InitializeResult( protocolVersion = LATEST_PROTOCOL_VERSION,