Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .idea/artifacts/kotlin_sdk_jvm_0_4_0.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions .idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.JsonNull
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.JsonPrimitive
import kotlin.coroutines.cancellation.CancellationException

/**
* Options for configuring the MCP client.
Expand Down Expand Up @@ -100,7 +101,12 @@ public open class Client(
notification(InitializedNotification())
} catch (error: Throwable) {
close()
if (error !is CancellationException) {
throw IllegalStateException("Error connecting to transport: ${error.message}")
}

throw error

}
}

Expand Down
63 changes: 60 additions & 3 deletions src/jvmTest/kotlin/client/ClientTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import io.modelcontextprotocol.kotlin.sdk.CreateMessageResult
import io.modelcontextprotocol.kotlin.sdk.EmptyJsonObject
import io.modelcontextprotocol.kotlin.sdk.Implementation
import InMemoryTransport
import io.mockk.coEvery
import io.mockk.spyk
import io.modelcontextprotocol.kotlin.sdk.InitializeRequest
import io.modelcontextprotocol.kotlin.sdk.InitializeResult
import io.modelcontextprotocol.kotlin.sdk.JSONRPCMessage
Expand Down Expand Up @@ -49,13 +51,13 @@ import kotlin.test.fail
class ClientTest {
@Test
fun `should initialize with matching protocol version`() = runTest {
var initialied = false
var initialised = false
val clientTransport = object : AbstractTransport() {
override suspend fun start() {}

override suspend fun send(message: JSONRPCMessage) {
if (message !is JSONRPCRequest) return
initialied = true
initialised = true
val result = InitializeResult(
protocolVersion = LATEST_PROTOCOL_VERSION,
capabilities = ServerCapabilities(),
Expand Down Expand Up @@ -90,7 +92,7 @@ class ClientTest {
)

client.connect(clientTransport)
assertTrue(initialied)
assertTrue(initialised)
}

@Test
Expand Down Expand Up @@ -189,6 +191,61 @@ class ClientTest {
assertTrue(closed)
}

@Test
fun `should reject due to non cancellation exception`() = runTest {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add test for the case when a Cancellation exception is thrown?

Copy link
Contributor Author

@SeanChinJunKai SeanChinJunKai Apr 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ribhavpahuja can i ask how will cancellation exception be thrown in this case? wanted to clarify before adding a test

var closed = false
val clientTransport = object : AbstractTransport() {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see clientTransport is very similar to the one in the previous test. should we create a test utility?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i will look into this

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @e5l ,I havent had much time to look into it but maybe we can have a separate issue to take a look at all tests?

override suspend fun start() {}

override suspend fun send(message: JSONRPCMessage) {
if (message !is JSONRPCRequest) return
check(message.method == Method.Defined.Initialize.value)

val result = InitializeResult(
protocolVersion = LATEST_PROTOCOL_VERSION,
capabilities = ServerCapabilities(),
serverInfo = Implementation(
name = "test",
version = "1.0"
)
)

val response = JSONRPCResponse(
id = message.id,
result = result
)

_onMessage.invoke(response)
}

override suspend fun close() {
closed = true
}
}

val mockClient = spyk(
Client(
clientInfo = Implementation(
name = "test client",
version = "1.0"
),
options = ClientOptions()
)
)

coEvery{
mockClient.request<InitializeResult>(any())
} throws IllegalStateException("Test error")

val exception = assertFailsWith<IllegalStateException> {
mockClient.connect(clientTransport)
}

assertEquals("Error connecting to transport: Test error", exception.message)

assertTrue(closed)
}

@Test
fun `should respect server capabilities`() = runTest {
val serverOptions = ServerOptions(
Expand Down