Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add specific error handling for client connect #64

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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
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
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
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