Skip to content

Commit 6882306

Browse files
committed
#355 Handle coroutine cancellation and exceptions in server transports
1 parent 7681de1 commit 6882306

File tree

2 files changed

+12
-4
lines changed

2 files changed

+12
-4
lines changed

kotlin-sdk-server/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/server/KtorServer.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import kotlinx.atomicfu.atomic
1919
import kotlinx.atomicfu.update
2020
import kotlinx.collections.immutable.PersistentMap
2121
import kotlinx.collections.immutable.toPersistentMap
22+
import kotlinx.coroutines.awaitCancellation
2223

2324
private val logger = KotlinLogging.logger {}
2425

@@ -91,6 +92,8 @@ internal suspend fun ServerSSESession.mcpSseEndpoint(
9192
server.createSession(transport)
9293

9394
logger.debug { "Server connected to transport for sessionId: ${transport.sessionId}" }
95+
96+
awaitCancellation()
9497
}
9598

9699
internal fun ServerSSESession.mcpSseTransport(

kotlin-sdk-server/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/server/SSEServerTransport.kt

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,11 @@ import io.ktor.server.sse.ServerSSESession
1111
import io.modelcontextprotocol.kotlin.sdk.JSONRPCMessage
1212
import io.modelcontextprotocol.kotlin.sdk.shared.AbstractTransport
1313
import io.modelcontextprotocol.kotlin.sdk.shared.McpJson
14+
import kotlinx.coroutines.InternalCoroutinesApi
1415
import kotlinx.coroutines.job
1516
import kotlin.concurrent.atomics.AtomicBoolean
1617
import kotlin.concurrent.atomics.ExperimentalAtomicApi
18+
import kotlin.coroutines.cancellation.CancellationException
1719
import kotlin.uuid.ExperimentalUuidApi
1820
import kotlin.uuid.Uuid
1921

@@ -53,10 +55,13 @@ public class SseServerTransport(private val endpoint: String, private val sessio
5355
data = "${endpoint.encodeURLPath()}?$SESSION_ID_PARAM=$sessionId",
5456
)
5557

56-
try {
57-
session.coroutineContext.job.join()
58-
} finally {
59-
_onClose.invoke()
58+
@OptIn(InternalCoroutinesApi::class)
59+
session.coroutineContext.job.invokeOnCompletion {
60+
if (it != null && it !is CancellationException) {
61+
_onError.invoke(it)
62+
} else {
63+
_onClose.invoke()
64+
}
6065
}
6166
}
6267

0 commit comments

Comments
 (0)