diff --git a/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/client/SSEClientTransport.kt b/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/client/SSEClientTransport.kt index 9e21347..b52fb7a 100644 --- a/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/client/SSEClientTransport.kt +++ b/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/client/SSEClientTransport.kt @@ -39,7 +39,18 @@ public class SseClientTransport( private var job: Job? = null private val baseUrl by lazy { - session.call.request.url.toString().removeSuffix("/") + val requestUrl = session.call.request.url.toString() + val url = Url(requestUrl) + var path = url.encodedPath + if (path.isEmpty()) { + url.protocolWithAuthority + } else if (path.endsWith("/")) { + url.protocolWithAuthority + path.removeSuffix("/") + } else { + // the last item is not a directory, so will not be taken into account + path = path.substring(0, path.lastIndexOf("/")) + url.protocolWithAuthority + path + } } override suspend fun start() { @@ -79,8 +90,7 @@ public class SseClientTransport( val eventData = event.data ?: "" // check url correctness - val maybeEndpoint = Url("$baseUrl/$eventData") - + val maybeEndpoint = Url("$baseUrl/${if (eventData.startsWith("/")) eventData.substring(1) else eventData}") endpoint.complete(maybeEndpoint.toString()) } catch (e: Exception) { _onError(e) diff --git a/src/jvmTest/kotlin/client/SseTransportTest.kt b/src/jvmTest/kotlin/client/SseTransportTest.kt index b056893..5d7094c 100644 --- a/src/jvmTest/kotlin/client/SseTransportTest.kt +++ b/src/jvmTest/kotlin/client/SseTransportTest.kt @@ -82,4 +82,68 @@ class SseTransportTest : BaseTransportTest() { testClientRead(client) server.stop() } + + @Test + fun `test sse path not root path`() = runTest { + val server = embeddedServer(CIO, port = PORT) { + install(io.ktor.server.sse.SSE) + val transports = ConcurrentMap() + routing { + sse("/sse") { + mcpSseTransport("/messages", transports).apply { + onMessage { + send(it) + } + + start() + } + } + + post("/messages") { + + mcpPostEndpoint(transports) + } + } + }.start(wait = false) + + val client = HttpClient { + install(SSE) + }.mcpSseTransport { + url { + host = "localhost" + port = PORT + pathSegments = listOf("sse") + } + } + + testClientRead(client) + server.stop() + } + + @Test + fun `test sse path not root path`() = runTest { + val server = embeddedServer(CIO, port = PORT) { + install(io.ktor.server.sse.SSE) + routing { + mcpSseTransport(path = "/sse", incomingPath = "/messages") { + onMessage = { + send(it) + } + } + } + }.start(wait = false) + + val client = HttpClient { + install(SSE) + }.mcpSseTransport { + url { + host = "localhost" + port = PORT + pathSegments = listOf("sse") + } + } + + testClientRead(client) + server.stop() + } }