Skip to content

Commit 15608bd

Browse files
committed
feat(core, server): improve error handling and add synthetic constructors
- Enhanced `McpException` default message logic to ensure consistency and eliminate redundant prefixes. - Simplified test assertions in integration tests using `shouldBe` for better readability. - Added synthetic constructors in `ServerOptions` and `McpException` for better Kotlin compatibility. - Updated `Protocol` to rethrow `CancellationException` explicitly, ensuring proper cancellation behavior.
1 parent dabe078 commit 15608bd

7 files changed

Lines changed: 28 additions & 16 deletions

File tree

integration-test/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/client/ClientTest.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package io.modelcontextprotocol.kotlin.sdk.client
22

3+
import io.kotest.matchers.shouldBe
34
import io.modelcontextprotocol.kotlin.sdk.server.Server
45
import io.modelcontextprotocol.kotlin.sdk.server.ServerOptions
56
import io.modelcontextprotocol.kotlin.sdk.server.ServerSession
@@ -266,8 +267,8 @@ class ClientTest {
266267
client.connect(failingTransport)
267268
}
268269

269-
assertEquals(-32600, exception.code)
270-
assertEquals("MCP error -32600: Invalid Request", exception.message)
270+
exception.code shouldBe -32600
271+
exception.message shouldBe "Invalid Request"
271272

272273
assertTrue(closed)
273274
}

integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/integration/kotlin/AbstractPromptIntegrationTest.kt

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package io.modelcontextprotocol.kotlin.sdk.integration.kotlin
22

33
import io.kotest.assertions.withClue
4+
import io.kotest.matchers.shouldBe
45
import io.kotest.matchers.string.shouldContain
56
import io.modelcontextprotocol.kotlin.sdk.types.GetPromptRequest
67
import io.modelcontextprotocol.kotlin.sdk.types.GetPromptRequestParams
@@ -688,13 +689,13 @@ abstract class AbstractPromptIntegrationTest : KotlinTestBase() {
688689
}
689690
}
690691

691-
val expectedMessage = "MCP error -32603: Prompt not found: non-existent-prompt"
692+
val expectedMessage = "Prompt not found: non-existent-prompt"
692693

693-
assertEquals(
694-
RPCError.ErrorCode.INTERNAL_ERROR,
695-
exception.code,
696-
"Exception code should be INTERNAL_ERROR: ${RPCError.ErrorCode.INTERNAL_ERROR}",
697-
)
698-
assertEquals(expectedMessage, exception.message, "Unexpected error message for non-existent prompt")
694+
withClue("Exception code should be INTERNAL_ERROR: -32603") {
695+
exception.code shouldBe -32603
696+
}
697+
withClue("Unexpected error message for non-existent prompt") {
698+
exception.message shouldBe expectedMessage
699+
}
699700
}
700701
}

kotlin-sdk-core/api/kotlin-sdk-core.api

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2713,6 +2713,7 @@ public abstract interface annotation class io/modelcontextprotocol/kotlin/sdk/ty
27132713
}
27142714

27152715
public final class io/modelcontextprotocol/kotlin/sdk/types/McpException : java/lang/Exception {
2716+
public fun <init> (I)V
27162717
public fun <init> (ILjava/lang/String;)V
27172718
public fun <init> (ILjava/lang/String;Lkotlinx/serialization/json/JsonElement;)V
27182719
public fun <init> (ILjava/lang/String;Lkotlinx/serialization/json/JsonElement;Ljava/lang/Throwable;)V

kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/shared/Protocol.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,8 @@ public abstract class Protocol(@PublishedApi internal val options: ProtocolOptio
336336
RPCError(code = RPCError.ErrorCode.INTERNAL_ERROR, message = cause.message ?: "Internal error")
337337
}
338338
transport?.send(JSONRPCError(id = request.id, error = rpcError))
339+
} catch (e: CancellationException) {
340+
throw e
339341
} catch (sendError: Throwable) {
340342
logger.error(sendError) {
341343
"Failed to send error response for request: ${request.method} (id: ${request.id})"

kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/types/McpException.kt

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,13 @@ import kotlin.jvm.JvmOverloads
88
*
99
* @property code The MCP/JSON‑RPC error code.
1010
* @property data Optional additional error payload as a JSON element; `null` when not provided.
11-
* @param message The error message.
11+
* @param message The error message. Used verbatim as [Exception.message] — no error code prefix is prepended.
12+
* Defaults to `"MCP error $code"` when not provided.
1213
* @param cause The original cause.
1314
*/
1415
public class McpException @JvmOverloads public constructor(
1516
public val code: Int,
16-
message: String?,
17+
message: String = "MCP error $code",
1718
public val data: JsonElement? = null,
1819
cause: Throwable? = null,
19-
) : Exception("MCP error $code: $message", cause)
20+
) : Exception(message, cause)

kotlin-sdk-server/api/kotlin-sdk-server.api

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@ public class io/modelcontextprotocol/kotlin/sdk/server/Server {
148148
public final class io/modelcontextprotocol/kotlin/sdk/server/ServerOptions : io/modelcontextprotocol/kotlin/sdk/shared/ProtocolOptions {
149149
public fun <init> (Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities;)V
150150
public fun <init> (Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities;Z)V
151+
public synthetic fun <init> (Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities;ZILkotlin/jvm/internal/DefaultConstructorMarker;)V
151152
public fun <init> (Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities;ZLio/modelcontextprotocol/kotlin/sdk/utils/ResourceTemplateMatcherFactory;)V
152153
public synthetic fun <init> (Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities;ZLio/modelcontextprotocol/kotlin/sdk/utils/ResourceTemplateMatcherFactory;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
153154
public final fun getCapabilities ()Lio/modelcontextprotocol/kotlin/sdk/types/ServerCapabilities;

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

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -66,12 +66,17 @@ private val logger = KotlinLogging.logger {}
6666
* @property resourceTemplateMatcherFactory The factory used to create [ResourceTemplateMatcher] instances
6767
* for matching resource URIs against registered templates. Defaults to [PathSegmentTemplateMatcher.factory].
6868
*/
69-
public class ServerOptions @JvmOverloads public constructor(
69+
public class ServerOptions(
7070
public val capabilities: ServerCapabilities,
7171
enforceStrictCapabilities: Boolean = true,
72-
public val resourceTemplateMatcherFactory: ResourceTemplateMatcherFactory =
73-
PathSegmentTemplateMatcher.factory,
74-
) : ProtocolOptions(enforceStrictCapabilities = enforceStrictCapabilities)
72+
public val resourceTemplateMatcherFactory: ResourceTemplateMatcherFactory = PathSegmentTemplateMatcher.factory,
73+
) : ProtocolOptions(enforceStrictCapabilities = enforceStrictCapabilities) {
74+
@JvmOverloads
75+
public constructor(
76+
capabilities: ServerCapabilities,
77+
enforceStrictCapabilities: Boolean = true,
78+
) : this(capabilities, enforceStrictCapabilities, PathSegmentTemplateMatcher.factory)
79+
}
7580

7681
/**
7782
* An MCP server is responsible for storing features and handling new connections.

0 commit comments

Comments
 (0)