Skip to content

SseClientTransport isn't closed when underlying sseSession is disconnected #437

@nerzhulart

Description

@nerzhulart

This makes impossible to realize when a server is stopped to shutdown the client.
E.g. in Idea we can't shutdown stdio wrapper properly when Idea itself (with MCP server) is closed.

The naive solution is something like

Index: kotlin-sdk-client/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/client/SSEClientTransport.kt
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
===================================================================
diff --git a/kotlin-sdk-client/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/client/SSEClientTransport.kt b/kotlin-sdk-client/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/client/SSEClientTransport.kt
--- a/kotlin-sdk-client/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/client/SSEClientTransport.kt	(revision 29d89abf0290b40f3f4e774e1617564f0e13e75b)
+++ b/kotlin-sdk-client/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/client/SSEClientTransport.kt	(date 1764369820391)
@@ -3,6 +3,7 @@
 import io.github.oshai.kotlinlogging.KotlinLogging
 import io.ktor.client.HttpClient
 import io.ktor.client.plugins.sse.ClientSSESession
+import io.ktor.client.plugins.sse.SSE
 import io.ktor.client.plugins.sse.sseSession
 import io.ktor.client.request.HttpRequestBuilder
 import io.ktor.client.request.post
@@ -16,6 +17,7 @@
 import io.ktor.http.protocolWithAuthority
 import io.modelcontextprotocol.kotlin.sdk.shared.AbstractTransport
 import io.modelcontextprotocol.kotlin.sdk.shared.TransportSendOptions
+import io.modelcontextprotocol.kotlin.sdk.types.Implementation
 import io.modelcontextprotocol.kotlin.sdk.types.JSONRPCMessage
 import io.modelcontextprotocol.kotlin.sdk.types.McpJson
 import kotlinx.coroutines.CancellationException
@@ -28,6 +30,7 @@
 import kotlinx.coroutines.cancel
 import kotlinx.coroutines.cancelAndJoin
 import kotlinx.coroutines.ensureActive
+import kotlinx.coroutines.job
 import kotlinx.coroutines.launch
 import kotlinx.serialization.SerializationException
 import kotlin.concurrent.atomics.AtomicBoolean
@@ -85,6 +88,13 @@
                 block = requestBuilder,
             )
             scope = CoroutineScope(session.coroutineContext + SupervisorJob())
+            session.coroutineContext.job.invokeOnCompletion {
+                if (it != null && it !is kotlin.coroutines.cancellation.CancellationException) {
+                    _onError.invoke(it)
+                } else {
+                    _onClose.invoke()
+                }
+            }
 
             job = scope.launch(CoroutineName("SseMcpClientTransport.connect#${hashCode()}")) {
                 collectMessages()

But I'm not sure if we have to cancel jobs here, or they will be cancelled by close() call outside by onClose signal

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions