diff --git a/build.gradle.kts b/build.gradle.kts
index 09e9adb..f4ddd44 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -61,7 +61,7 @@ kover {
}
verify {
rule {
- minBound(65)
+ minBound(73)
}
}
}
diff --git a/kotlin-sdk-core/build.gradle.kts b/kotlin-sdk-core/build.gradle.kts
index 40848b1..0e702c4 100644
--- a/kotlin-sdk-core/build.gradle.kts
+++ b/kotlin-sdk-core/build.gradle.kts
@@ -121,10 +121,8 @@ kotlin {
commonTest {
dependencies {
+ implementation(project(":test-utils"))
implementation(kotlin("test"))
- implementation(libs.kotlinx.coroutines.test)
- implementation(libs.kotest.assertions.core)
- implementation(libs.kotest.assertions.json)
}
}
diff --git a/kotlin-sdk-core/detekt-baseline-commonTestSourceSet.xml b/kotlin-sdk-core/detekt-baseline-commonTestSourceSet.xml
deleted file mode 100644
index 0a4c838..0000000
--- a/kotlin-sdk-core/detekt-baseline-commonTestSourceSet.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-
- EmptyFunctionBlock:ProtocolTest.kt:RecordingTransport${}
- EmptyFunctionBlock:ProtocolTest.kt:TestProtocol${}
- LongMethod:ElicitationTest.kt:ElicitationTest$@Test fun `should serialize ElicitRequest with requested schema`
- LongMethod:SamplingDslTest.kt:SamplingDslTest$@Test fun `buildCreateMessageRequest should build with all fields`
- LongMethod:SamplingTest.kt:SamplingTest$@Test fun `should serialize CreateMessageRequest with all fields`
- LongMethod:ToolsTest.kt:ToolsTest$@Test fun `should serialize Tool with annotations and schemas`
-
-
diff --git a/kotlin-sdk-core/detekt-baseline-test.xml b/kotlin-sdk-core/detekt-baseline-test.xml
deleted file mode 100644
index 0a4c838..0000000
--- a/kotlin-sdk-core/detekt-baseline-test.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-
- EmptyFunctionBlock:ProtocolTest.kt:RecordingTransport${}
- EmptyFunctionBlock:ProtocolTest.kt:TestProtocol${}
- LongMethod:ElicitationTest.kt:ElicitationTest$@Test fun `should serialize ElicitRequest with requested schema`
- LongMethod:SamplingDslTest.kt:SamplingDslTest$@Test fun `buildCreateMessageRequest should build with all fields`
- LongMethod:SamplingTest.kt:SamplingTest$@Test fun `should serialize CreateMessageRequest with all fields`
- LongMethod:ToolsTest.kt:ToolsTest$@Test fun `should serialize Tool with annotations and schemas`
-
-
diff --git a/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/shared/ProtocolTest.kt b/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/shared/ProtocolTest.kt
index 142c2af..b228071 100644
--- a/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/shared/ProtocolTest.kt
+++ b/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/shared/ProtocolTest.kt
@@ -154,9 +154,15 @@ class ProtocolTest {
}
private class TestProtocol : Protocol(null) {
- override fun assertCapabilityForMethod(method: Method) {}
- override fun assertNotificationCapability(method: Method) {}
- override fun assertRequestHandlerCapability(method: Method) {}
+ override fun assertCapabilityForMethod(method: Method) {
+ // noop
+ }
+ override fun assertNotificationCapability(method: Method) {
+ // noop
+ }
+ override fun assertRequestHandlerCapability(method: Method) {
+ // noop
+ }
}
private class RecordingTransport : Transport {
@@ -164,7 +170,9 @@ private class RecordingTransport : Transport {
private var onMessageCallback: (suspend (JSONRPCMessage) -> Unit)? = null
private var onCloseCallback: (() -> Unit)? = null
- override suspend fun start() {}
+ override suspend fun start() {
+ // noop
+ }
override suspend fun send(message: JSONRPCMessage, options: TransportSendOptions?) {
sentMessages.send(message)
@@ -178,7 +186,9 @@ private class RecordingTransport : Transport {
onCloseCallback = block
}
- override fun onError(block: (Throwable) -> Unit) {}
+ override fun onError(block: (Throwable) -> Unit) {
+ // noop
+ }
override fun onMessage(block: suspend (JSONRPCMessage) -> Unit) {
onMessageCallback = block
diff --git a/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/types/CapabilitiesTest.kt b/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/types/CapabilitiesTest.kt
index c288cca..33d5653 100644
--- a/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/types/CapabilitiesTest.kt
+++ b/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/types/CapabilitiesTest.kt
@@ -1,6 +1,9 @@
package io.modelcontextprotocol.kotlin.sdk.types
import io.kotest.assertions.json.shouldEqualJson
+import io.modelcontextprotocol.kotlin.test.utils.verifyDeserialization
+import io.modelcontextprotocol.kotlin.test.utils.verifySerialization
+import io.modelcontextprotocol.kotlin.test.utils.verifySerializationRoundTrip
import kotlinx.serialization.json.buildJsonObject
import kotlinx.serialization.json.put
import kotlin.test.Test
@@ -15,14 +18,16 @@ class CapabilitiesTest {
name = "test-server",
version = "1.0.0",
)
- val json = McpJson.encodeToString(implementation)
-
- json shouldEqualJson """
+ verifySerialization(
+ implementation,
+ McpJson,
+ """
{
"name": "test-server",
"version": "1.0.0"
}
- """.trimIndent()
+ """.trimIndent(),
+ )
}
@Test
@@ -36,9 +41,10 @@ class CapabilitiesTest {
Icon(src = "https://example.com/icon.png"),
),
)
- val json = McpJson.encodeToString(implementation)
-
- json shouldEqualJson """
+ verifySerialization(
+ implementation,
+ McpJson,
+ """
{
"name": "test-server",
"version": "1.0.0",
@@ -48,7 +54,8 @@ class CapabilitiesTest {
{"src": "https://example.com/icon.png"}
]
}
- """.trimIndent()
+ """.trimIndent(),
+ )
}
@Test
@@ -62,7 +69,7 @@ class CapabilitiesTest {
}
""".trimIndent()
- val implementation = McpJson.decodeFromString(json)
+ val implementation = verifyDeserialization(McpJson, json)
assertEquals("test-server", implementation.name)
assertEquals("2.1.3-beta", implementation.version)
@@ -79,7 +86,7 @@ class CapabilitiesTest {
}
""".trimIndent()
- val implementation = McpJson.decodeFromString(json)
+ val implementation = verifyDeserialization(McpJson, json)
assertEquals("minimal-server", implementation.name)
assertEquals("0.1.0", implementation.version)
@@ -97,13 +104,7 @@ class CapabilitiesTest {
websiteUrl = "https://test.com",
)
- val json = McpJson.encodeToString(original)
- val decoded = McpJson.decodeFromString(json)
-
- assertEquals(original.name, decoded.name)
- assertEquals(original.version, decoded.version)
- assertEquals(original.title, decoded.title)
- assertEquals(original.websiteUrl, decoded.websiteUrl)
+ verifySerializationRoundTrip(original, McpJson)
}
// ClientCapabilities tests
@@ -120,13 +121,15 @@ class CapabilitiesTest {
val capabilities = ClientCapabilities(
sampling = ClientCapabilities.sampling,
)
- val json = McpJson.encodeToString(capabilities)
-
- json shouldEqualJson """
+ verifySerialization(
+ capabilities,
+ McpJson,
+ """
{
"sampling": {}
}
- """.trimIndent()
+ """.trimIndent(),
+ )
}
@Test
@@ -134,13 +137,15 @@ class CapabilitiesTest {
val capabilities = ClientCapabilities(
roots = ClientCapabilities.Roots(),
)
- val json = McpJson.encodeToString(capabilities)
-
- json shouldEqualJson """
+ verifySerialization(
+ capabilities,
+ McpJson,
+ """
{
"roots": {}
}
- """.trimIndent()
+ """.trimIndent(),
+ )
}
@Test
@@ -148,15 +153,17 @@ class CapabilitiesTest {
val capabilities = ClientCapabilities(
roots = ClientCapabilities.Roots(listChanged = true),
)
- val json = McpJson.encodeToString(capabilities)
-
- json shouldEqualJson """
+ verifySerialization(
+ capabilities,
+ McpJson,
+ """
{
"roots": {
"listChanged": true
}
}
- """.trimIndent()
+ """.trimIndent(),
+ )
}
@Test
@@ -164,13 +171,15 @@ class CapabilitiesTest {
val capabilities = ClientCapabilities(
elicitation = ClientCapabilities.elicitation,
)
- val json = McpJson.encodeToString(capabilities)
-
- json shouldEqualJson """
+ verifySerialization(
+ capabilities,
+ McpJson,
+ """
{
"elicitation": {}
}
- """.trimIndent()
+ """.trimIndent(),
+ )
}
@Test
@@ -186,9 +195,10 @@ class CapabilitiesTest {
val capabilities = ClientCapabilities(
experimental = experimental,
)
- val json = McpJson.encodeToString(capabilities)
-
- json shouldEqualJson """
+ verifySerialization(
+ capabilities,
+ McpJson,
+ """
{
"experimental": {
"customFeature": {
@@ -196,7 +206,8 @@ class CapabilitiesTest {
}
}
}
- """.trimIndent()
+ """.trimIndent(),
+ )
}
@Test
@@ -210,9 +221,10 @@ class CapabilitiesTest {
elicitation = ClientCapabilities.elicitation,
experimental = experimental,
)
- val json = McpJson.encodeToString(capabilities)
-
- json shouldEqualJson """
+ verifySerialization(
+ capabilities,
+ McpJson,
+ """
{
"sampling": {},
"roots": {
@@ -225,7 +237,8 @@ class CapabilitiesTest {
}
}
}
- """.trimIndent()
+ """.trimIndent(),
+ )
}
@Test
@@ -240,7 +253,7 @@ class CapabilitiesTest {
}
""".trimIndent()
- val capabilities = McpJson.decodeFromString(json)
+ val capabilities = verifyDeserialization(McpJson, json)
assertEquals(EmptyJsonObject, capabilities.sampling)
assertEquals(true, capabilities.roots?.listChanged)
@@ -251,7 +264,7 @@ class CapabilitiesTest {
fun `should deserialize empty ClientCapabilities from JSON`() {
val json = "{}"
- val capabilities = McpJson.decodeFromString(json)
+ val capabilities = verifyDeserialization(McpJson, json)
assertNull(capabilities.sampling)
assertNull(capabilities.roots)
@@ -267,12 +280,7 @@ class CapabilitiesTest {
elicitation = ClientCapabilities.elicitation,
)
- val json = McpJson.encodeToString(original)
- val decoded = McpJson.decodeFromString(json)
-
- assertEquals(original.sampling, decoded.sampling)
- assertEquals(original.roots?.listChanged, decoded.roots?.listChanged)
- assertEquals(original.elicitation, decoded.elicitation)
+ verifySerializationRoundTrip(original, McpJson)
}
// ServerCapabilities tests
@@ -289,13 +297,15 @@ class CapabilitiesTest {
val capabilities = ServerCapabilities(
tools = ServerCapabilities.Tools(),
)
- val json = McpJson.encodeToString(capabilities)
-
- json shouldEqualJson """
+ verifySerialization(
+ capabilities,
+ McpJson,
+ """
{
"tools": {}
}
- """.trimIndent()
+ """.trimIndent(),
+ )
}
@Test
@@ -303,15 +313,17 @@ class CapabilitiesTest {
val capabilities = ServerCapabilities(
tools = ServerCapabilities.Tools(listChanged = true),
)
- val json = McpJson.encodeToString(capabilities)
-
- json shouldEqualJson """
+ verifySerialization(
+ capabilities,
+ McpJson,
+ """
{
"tools": {
"listChanged": true
}
}
- """.trimIndent()
+ """.trimIndent(),
+ )
}
@Test
@@ -319,13 +331,15 @@ class CapabilitiesTest {
val capabilities = ServerCapabilities(
resources = ServerCapabilities.Resources(),
)
- val json = McpJson.encodeToString(capabilities)
-
- json shouldEqualJson """
+ verifySerialization(
+ capabilities,
+ McpJson,
+ """
{
"resources": {}
}
- """.trimIndent()
+ """.trimIndent(),
+ )
}
@Test
@@ -333,15 +347,17 @@ class CapabilitiesTest {
val capabilities = ServerCapabilities(
resources = ServerCapabilities.Resources(listChanged = true),
)
- val json = McpJson.encodeToString(capabilities)
-
- json shouldEqualJson """
+ verifySerialization(
+ capabilities,
+ McpJson,
+ """
{
"resources": {
"listChanged": true
}
}
- """.trimIndent()
+ """.trimIndent(),
+ )
}
@Test
@@ -349,15 +365,17 @@ class CapabilitiesTest {
val capabilities = ServerCapabilities(
resources = ServerCapabilities.Resources(subscribe = true),
)
- val json = McpJson.encodeToString(capabilities)
-
- json shouldEqualJson """
+ verifySerialization(
+ capabilities,
+ McpJson,
+ """
{
"resources": {
"subscribe": true
}
}
- """.trimIndent()
+ """.trimIndent(),
+ )
}
@Test
@@ -368,16 +386,18 @@ class CapabilitiesTest {
subscribe = false,
),
)
- val json = McpJson.encodeToString(capabilities)
-
- json shouldEqualJson """
+ verifySerialization(
+ capabilities,
+ McpJson,
+ """
{
"resources": {
"listChanged": true,
"subscribe": false
}
}
- """.trimIndent()
+ """.trimIndent(),
+ )
}
@Test
@@ -385,13 +405,15 @@ class CapabilitiesTest {
val capabilities = ServerCapabilities(
prompts = ServerCapabilities.Prompts(),
)
- val json = McpJson.encodeToString(capabilities)
-
- json shouldEqualJson """
+ verifySerialization(
+ capabilities,
+ McpJson,
+ """
{
"prompts": {}
}
- """.trimIndent()
+ """.trimIndent(),
+ )
}
@Test
@@ -399,15 +421,17 @@ class CapabilitiesTest {
val capabilities = ServerCapabilities(
prompts = ServerCapabilities.Prompts(listChanged = false),
)
- val json = McpJson.encodeToString(capabilities)
-
- json shouldEqualJson """
+ verifySerialization(
+ capabilities,
+ McpJson,
+ """
{
"prompts": {
"listChanged": false
}
}
- """.trimIndent()
+ """.trimIndent(),
+ )
}
@Test
@@ -415,13 +439,15 @@ class CapabilitiesTest {
val capabilities = ServerCapabilities(
logging = ServerCapabilities.Logging,
)
- val json = McpJson.encodeToString(capabilities)
-
- json shouldEqualJson """
+ verifySerialization(
+ capabilities,
+ McpJson,
+ """
{
"logging": {}
}
- """.trimIndent()
+ """.trimIndent(),
+ )
}
@Test
@@ -429,13 +455,15 @@ class CapabilitiesTest {
val capabilities = ServerCapabilities(
completions = ServerCapabilities.Completions,
)
- val json = McpJson.encodeToString(capabilities)
-
- json shouldEqualJson """
+ verifySerialization(
+ capabilities,
+ McpJson,
+ """
{
"completions": {}
}
- """.trimIndent()
+ """.trimIndent(),
+ )
}
@Test
@@ -451,9 +479,10 @@ class CapabilitiesTest {
val capabilities = ServerCapabilities(
experimental = experimental,
)
- val json = McpJson.encodeToString(capabilities)
-
- json shouldEqualJson """
+ verifySerialization(
+ capabilities,
+ McpJson,
+ """
{
"experimental": {
"customCapability": {
@@ -461,7 +490,8 @@ class CapabilitiesTest {
}
}
}
- """.trimIndent()
+ """.trimIndent(),
+ )
}
@Test
@@ -480,9 +510,10 @@ class CapabilitiesTest {
completions = ServerCapabilities.Completions,
experimental = experimental,
)
- val json = McpJson.encodeToString(capabilities)
-
- json shouldEqualJson """
+ verifySerialization(
+ capabilities,
+ McpJson,
+ """
{
"tools": {
"listChanged": true
@@ -502,7 +533,8 @@ class CapabilitiesTest {
}
}
}
- """.trimIndent()
+ """.trimIndent(),
+ )
}
@Test
@@ -524,7 +556,7 @@ class CapabilitiesTest {
}
""".trimIndent()
- val capabilities = McpJson.decodeFromString(json)
+ val capabilities = verifyDeserialization(McpJson, json)
assertEquals(true, capabilities.tools?.listChanged)
assertEquals(false, capabilities.resources?.listChanged)
@@ -538,7 +570,7 @@ class CapabilitiesTest {
fun `should deserialize empty ServerCapabilities from JSON`() {
val json = "{}"
- val capabilities = McpJson.decodeFromString(json)
+ val capabilities = verifyDeserialization(McpJson, json)
assertNull(capabilities.tools)
assertNull(capabilities.resources)
@@ -560,14 +592,7 @@ class CapabilitiesTest {
logging = ServerCapabilities.Logging,
)
- val json = McpJson.encodeToString(original)
- val decoded = McpJson.decodeFromString(json)
-
- assertEquals(original.tools?.listChanged, decoded.tools?.listChanged)
- assertEquals(original.resources?.listChanged, decoded.resources?.listChanged)
- assertEquals(original.resources?.subscribe, decoded.resources?.subscribe)
- assertEquals(original.prompts?.listChanged, decoded.prompts?.listChanged)
- assertEquals(original.logging, decoded.logging)
+ verifySerializationRoundTrip(original, McpJson)
}
// Additional nested type tests
@@ -579,7 +604,7 @@ class CapabilitiesTest {
}
""".trimIndent()
- val capabilities = McpJson.decodeFromString(json)
+ val capabilities = verifyDeserialization(McpJson, json)
assertNull(capabilities.roots?.listChanged)
}
@@ -594,7 +619,7 @@ class CapabilitiesTest {
}
""".trimIndent()
- val capabilities = McpJson.decodeFromString(json)
+ val capabilities = verifyDeserialization(McpJson, json)
assertNull(capabilities.tools?.listChanged)
assertNull(capabilities.resources?.listChanged)
@@ -612,7 +637,7 @@ class CapabilitiesTest {
}
""".trimIndent()
- val capabilities = McpJson.decodeFromString(json)
+ val capabilities = verifyDeserialization(McpJson, json)
// Should not fail - additionalProperties are allowed
assertEquals("customValue", capabilities.sampling?.get("customProperty")?.toString()?.trim('"'))
@@ -628,7 +653,7 @@ class CapabilitiesTest {
}
""".trimIndent()
- val capabilities = McpJson.decodeFromString(json)
+ val capabilities = verifyDeserialization(McpJson, json)
// Should not fail - additionalProperties are allowed
assertEquals("debug", capabilities.logging?.get("level")?.toString()?.trim('"'))
diff --git a/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/types/CommonTypeTest.kt b/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/types/CommonTypeTest.kt
index 679d14a..44e88ac 100644
--- a/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/types/CommonTypeTest.kt
+++ b/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/types/CommonTypeTest.kt
@@ -1,10 +1,12 @@
package io.modelcontextprotocol.kotlin.sdk.types
-import io.kotest.assertions.json.shouldEqualJson
+import io.kotest.matchers.collections.shouldContainExactlyInAnyOrder
+import io.kotest.matchers.shouldBe
+import io.modelcontextprotocol.kotlin.test.utils.verifyDeserialization
+import io.modelcontextprotocol.kotlin.test.utils.verifySerialization
+import io.modelcontextprotocol.kotlin.test.utils.verifySerializationRoundTrip
import kotlin.test.Test
import kotlin.test.assertEquals
-import kotlin.test.assertIs
-import kotlin.test.assertNotEquals
import kotlin.test.assertNull
import kotlin.test.assertTrue
@@ -12,29 +14,30 @@ class CommonTypeTest {
@Test
fun `should have correct latest protocol version`() {
- assertNotEquals("", LATEST_PROTOCOL_VERSION)
- assertEquals("2025-06-18", LATEST_PROTOCOL_VERSION)
+ LATEST_PROTOCOL_VERSION shouldBe "2025-06-18"
}
@Test
fun `should have correct supported protocol versions`() {
- assertIs>(SUPPORTED_PROTOCOL_VERSIONS)
- assertTrue(SUPPORTED_PROTOCOL_VERSIONS.contains(LATEST_PROTOCOL_VERSION))
- assertTrue(SUPPORTED_PROTOCOL_VERSIONS.contains("2025-03-26"))
- assertTrue(SUPPORTED_PROTOCOL_VERSIONS.contains("2024-11-05"))
- assertEquals(3, SUPPORTED_PROTOCOL_VERSIONS.size)
+ SUPPORTED_PROTOCOL_VERSIONS shouldContainExactlyInAnyOrder listOf(
+ LATEST_PROTOCOL_VERSION,
+ "2025-03-26",
+ "2024-11-05",
+ )
}
@Test
fun `should serialize Icon with minimal fields`() {
val icon = Icon(src = "https://example.com/icon.png")
- val json = McpJson.encodeToString(icon)
-
- json shouldEqualJson """
+ verifySerialization(
+ icon,
+ McpJson,
+ """
{
"src": "https://example.com/icon.png"
}
- """.trimIndent()
+ """.trimIndent(),
+ )
}
@Test
@@ -45,16 +48,18 @@ class CommonTypeTest {
sizes = listOf("48x48", "96x96"),
theme = Icon.Theme.Light,
)
- val json = McpJson.encodeToString(icon)
-
- json shouldEqualJson """
+ verifySerialization(
+ icon,
+ McpJson,
+ """
{
"src": "https://example.com/icon.png",
"mimeType": "image/png",
"sizes": ["48x48", "96x96"],
"theme": "light"
}
- """.trimIndent()
+ """.trimIndent(),
+ )
}
@Test
@@ -68,7 +73,7 @@ class CommonTypeTest {
}
""".trimIndent()
- val icon = McpJson.decodeFromString(json)
+ val icon = verifyDeserialization(McpJson, json)
assertEquals("https://example.com/icon.png", icon.src)
assertEquals("image/png", icon.mimeType)
@@ -80,7 +85,7 @@ class CommonTypeTest {
fun `should deserialize Icon with minimal fields from JSON`() {
val json = """{"src": "https://example.com/icon.png"}"""
- val icon = McpJson.decodeFromString(json)
+ val icon = verifyDeserialization(McpJson, json)
assertEquals("https://example.com/icon.png", icon.src)
assertNull(icon.mimeType)
@@ -111,8 +116,8 @@ class CommonTypeTest {
@Test
fun `should deserialize Role from lowercase strings`() {
- val user = McpJson.decodeFromString("\"user\"")
- val assistant = McpJson.decodeFromString("\"assistant\"")
+ val user = verifyDeserialization(McpJson, "\"user\"")
+ val assistant = verifyDeserialization(McpJson, "\"assistant\"")
assertEquals(Role.User, user)
assertEquals(Role.Assistant, assistant)
@@ -126,28 +131,32 @@ class CommonTypeTest {
lastModified = "2025-01-12T15:00:58Z",
)
- val json = McpJson.encodeToString(annotations)
-
- json shouldEqualJson """
+ verifySerialization(
+ annotations,
+ McpJson,
+ """
{
"audience": ["user", "assistant"],
"priority": 0.8,
"lastModified": "2025-01-12T15:00:58Z"
}
- """.trimIndent()
+ """.trimIndent(),
+ )
}
@Test
fun `should serialize Annotations with minimal fields`() {
val annotations = Annotations(priority = 0.5)
- val json = McpJson.encodeToString(annotations)
-
- json shouldEqualJson """
+ verifySerialization(
+ annotations,
+ McpJson,
+ """
{
"priority": 0.5
}
- """.trimIndent()
+ """.trimIndent(),
+ )
}
@Test
@@ -160,7 +169,7 @@ class CommonTypeTest {
}
""".trimIndent()
- val annotations = McpJson.decodeFromString(json)
+ val annotations = verifyDeserialization(McpJson, json)
assertEquals(listOf(Role.User), annotations.audience)
assertEquals(0.9, annotations.priority)
@@ -175,11 +184,6 @@ class CommonTypeTest {
lastModified = "2025-01-12T15:00:58Z",
)
- val json = McpJson.encodeToString(original)
- val decoded = McpJson.decodeFromString(json)
-
- assertEquals(original.audience, decoded.audience)
- assertEquals(original.priority, decoded.priority)
- assertEquals(original.lastModified, decoded.lastModified)
+ verifySerializationRoundTrip(original, McpJson)
}
}
diff --git a/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/types/CompletionTest.kt b/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/types/CompletionTest.kt
index 9a27ac5..03a76e4 100644
--- a/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/types/CompletionTest.kt
+++ b/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/types/CompletionTest.kt
@@ -1,6 +1,8 @@
package io.modelcontextprotocol.kotlin.sdk.types
import io.kotest.assertions.json.shouldEqualJson
+import io.modelcontextprotocol.kotlin.test.utils.verifyDeserialization
+import io.modelcontextprotocol.kotlin.test.utils.verifySerialization
import kotlinx.serialization.json.buildJsonObject
import kotlinx.serialization.json.jsonPrimitive
import kotlinx.serialization.json.put
@@ -25,9 +27,10 @@ class CompletionTest {
),
)
- val json = McpJson.encodeToString(request)
-
- json shouldEqualJson """
+ verifySerialization(
+ request,
+ McpJson,
+ """
{
"method": "completion/complete",
"params": {
@@ -41,7 +44,8 @@ class CompletionTest {
}
}
}
- """.trimIndent()
+ """.trimIndent(),
+ )
}
@Test
@@ -67,9 +71,10 @@ class CompletionTest {
),
)
- val json = McpJson.encodeToString(request)
-
- json shouldEqualJson """
+ verifySerialization(
+ request,
+ McpJson,
+ """
{
"method": "completion/complete",
"params": {
@@ -92,7 +97,8 @@ class CompletionTest {
}
}
}
- """.trimIndent()
+ """.trimIndent(),
+ )
}
@Test
@@ -121,7 +127,7 @@ class CompletionTest {
}
""".trimIndent()
- val request = McpJson.decodeFromString(json)
+ val request = verifyDeserialization(McpJson, json)
assertEquals(Method.Defined.CompletionComplete, request.method)
val params = request.params
@@ -148,9 +154,10 @@ class CompletionTest {
},
)
- val json = McpJson.encodeToString(result)
-
- json shouldEqualJson """
+ verifySerialization(
+ result,
+ McpJson,
+ """
{
"completion": {
"values": [
@@ -164,7 +171,8 @@ class CompletionTest {
"source": "cache"
}
}
- """.trimIndent()
+ """.trimIndent(),
+ )
}
@Test
@@ -182,7 +190,7 @@ class CompletionTest {
}
""".trimIndent()
- val result = McpJson.decodeFromString(json)
+ val result = verifyDeserialization(McpJson, json)
assertEquals(listOf("README.md", "CONTRIBUTING.md"), result.completion.values)
assertEquals(2, result.completion.total)
assertEquals(false, result.completion.hasMore)
@@ -209,7 +217,7 @@ class CompletionTest {
}
""".trimIndent()
- val decoded = McpJson.decodeFromString(json)
+ val decoded = verifyDeserialization(McpJson, json)
assertEquals(listOf("foo"), decoded.completion.values)
assertNull(decoded.completion.total)
assertNull(decoded.completion.hasMore)
diff --git a/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/types/ContentTest.kt b/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/types/ContentTest.kt
index 2290dce..f110326 100644
--- a/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/types/ContentTest.kt
+++ b/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/types/ContentTest.kt
@@ -1,6 +1,7 @@
package io.modelcontextprotocol.kotlin.sdk.types
-import io.kotest.assertions.json.shouldEqualJson
+import io.modelcontextprotocol.kotlin.test.utils.verifyDeserialization
+import io.modelcontextprotocol.kotlin.test.utils.verifySerialization
import kotlinx.serialization.json.buildJsonObject
import kotlinx.serialization.json.jsonPrimitive
import kotlinx.serialization.json.put
@@ -15,14 +16,16 @@ class ContentTest {
fun `should serialize TextContent with minimal fields`() {
val content = TextContent(text = "Hello, MCP!")
- val json = McpJson.encodeToString(content)
-
- json shouldEqualJson """
+ verifySerialization(
+ content,
+ McpJson,
+ """
{
"type": "text",
"text": "Hello, MCP!"
}
- """.trimIndent()
+ """.trimIndent(),
+ )
}
@Test
@@ -33,9 +36,10 @@ class ContentTest {
meta = buildJsonObject { put("origin", "assistant") },
)
- val json = McpJson.encodeToString(content)
-
- json shouldEqualJson """
+ verifySerialization(
+ content,
+ McpJson,
+ """
{
"type": "text",
"text": "Need help?",
@@ -46,12 +50,8 @@ class ContentTest {
"origin": "assistant"
}
}
- """.trimIndent()
-
- val decoded = McpJson.decodeFromString(json) as TextContent
- assertEquals("Need help?", decoded.text)
- assertEquals(listOf(Role.User), decoded.annotations?.audience)
- assertEquals("assistant", decoded.meta?.get("origin")?.jsonPrimitive?.content)
+ """.trimIndent(),
+ )
}
@Test
@@ -61,15 +61,17 @@ class ContentTest {
mimeType = "image/png",
)
- val json = McpJson.encodeToString(content)
-
- json shouldEqualJson """
+ verifySerialization(
+ content,
+ McpJson,
+ """
{
"type": "image",
"data": "aW1hZ2VEYXRh",
"mimeType": "image/png"
}
- """.trimIndent()
+ """.trimIndent(),
+ )
}
@Test
@@ -83,7 +85,7 @@ class ContentTest {
}
""".trimIndent()
- val content = McpJson.decodeFromString(json) as ImageContent
+ val content = verifyDeserialization(McpJson, json) as ImageContent
assertEquals("Zm9vYmFy", content.data)
assertEquals("image/jpeg", content.mimeType)
assertEquals(0.6, content.annotations?.priority)
@@ -98,9 +100,10 @@ class ContentTest {
meta = buildJsonObject { put("durationMs", 1200) },
)
- val json = McpJson.encodeToString(content)
-
- json shouldEqualJson """
+ verifySerialization(
+ content,
+ McpJson,
+ """
{
"type": "audio",
"data": "YXVkaW9kYXRh",
@@ -109,7 +112,8 @@ class ContentTest {
"durationMs": 1200
}
}
- """.trimIndent()
+ """.trimIndent(),
+ )
}
@Test
@@ -123,7 +127,7 @@ class ContentTest {
}
""".trimIndent()
- val content = McpJson.decodeFromString(json) as AudioContent
+ val content = verifyDeserialization(McpJson, json) as AudioContent
assertEquals("audio/mpeg", content.mimeType)
assertEquals("YmF6", content.data)
assertEquals("user", content.meta?.get("speaker")?.jsonPrimitive?.content)
@@ -136,15 +140,17 @@ class ContentTest {
uri = "file:///workspace/README.md",
)
- val json = McpJson.encodeToString(resource)
-
- json shouldEqualJson """
+ verifySerialization(
+ resource,
+ McpJson,
+ """
{
"type": "resource_link",
"name": "README",
"uri": "file:///workspace/README.md"
}
- """.trimIndent()
+ """.trimIndent(),
+ )
}
@Test
@@ -161,9 +167,10 @@ class ContentTest {
meta = buildJsonObject { put("etag", "1234") },
)
- val json = McpJson.encodeToString(resource)
-
- json shouldEqualJson """
+ verifySerialization(
+ resource,
+ McpJson,
+ """
{
"type": "resource_link",
"name": "README",
@@ -184,7 +191,8 @@ class ContentTest {
"etag": "1234"
}
}
- """.trimIndent()
+ """.trimIndent(),
+ )
}
@Test
@@ -199,7 +207,7 @@ class ContentTest {
}
""".trimIndent()
- val resource = McpJson.decodeFromString(json) as ResourceLink
+ val resource = verifyDeserialization(McpJson, json) as ResourceLink
assertEquals("config", resource.name)
assertEquals("file:///config.yaml", resource.uri)
assertEquals(512, resource.size)
@@ -220,9 +228,10 @@ class ContentTest {
meta = buildJsonObject { put("source", "analysis") },
)
- val json = McpJson.encodeToString(embedded)
-
- json shouldEqualJson """
+ verifySerialization(
+ embedded,
+ McpJson,
+ """
{
"type": "resource",
"resource": {
@@ -240,7 +249,8 @@ class ContentTest {
"source": "analysis"
}
}
- """.trimIndent()
+ """.trimIndent(),
+ )
}
@Test
@@ -257,7 +267,7 @@ class ContentTest {
}
""".trimIndent()
- val embedded = McpJson.decodeFromString(json) as EmbeddedResource
+ val embedded = verifyDeserialization(McpJson, json) as EmbeddedResource
assertEquals(ContentTypes.EMBEDDED_RESOURCE, embedded.type)
assertNull(embedded.annotations)
assertEquals("base64", embedded.meta?.get("encoding")?.jsonPrimitive?.content)
@@ -292,7 +302,7 @@ class ContentTest {
]
""".trimIndent()
- val content = McpJson.decodeFromString>(json)
+ val content = verifyDeserialization>(McpJson, json)
assertEquals(3, content.size)
assertIs(content[0])
assertIs(content[1])
diff --git a/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/types/ElicitationTest.kt b/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/types/ElicitationTest.kt
index c59c23f..0026ea0 100644
--- a/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/types/ElicitationTest.kt
+++ b/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/types/ElicitationTest.kt
@@ -1,6 +1,8 @@
package io.modelcontextprotocol.kotlin.sdk.types
import io.kotest.assertions.json.shouldEqualJson
+import io.modelcontextprotocol.kotlin.test.utils.verifyDeserialization
+import io.modelcontextprotocol.kotlin.test.utils.verifySerialization
import kotlinx.serialization.json.boolean
import kotlinx.serialization.json.buildJsonObject
import kotlinx.serialization.json.int
@@ -16,6 +18,7 @@ import kotlin.test.assertNull
class ElicitationTest {
@Test
+ @Suppress("LongMethod")
fun `should serialize ElicitRequest with requested schema`() {
val request = ElicitRequest(
ElicitRequestParams(
@@ -54,9 +57,10 @@ class ElicitationTest {
),
)
- val json = McpJson.encodeToString(request)
-
- json shouldEqualJson """
+ verifySerialization(
+ request,
+ McpJson,
+ """
{
"method": "elicitation/create",
"params": {
@@ -84,7 +88,8 @@ class ElicitationTest {
}
}
}
- """.trimIndent()
+ """.trimIndent(),
+ )
}
@Test
@@ -111,7 +116,7 @@ class ElicitationTest {
}
""".trimIndent()
- val request = McpJson.decodeFromString(json)
+ val request = verifyDeserialization(McpJson, json)
assertEquals(Method.Defined.ElicitationCreate, request.method)
val params = request.params
@@ -163,7 +168,7 @@ class ElicitationTest {
}
""".trimIndent()
- val decoded = McpJson.decodeFromString(json)
+ val decoded = verifyDeserialization(McpJson, json)
assertEquals(ElicitResult.Action.Accept, decoded.action)
val content = decoded.content
@@ -188,7 +193,7 @@ class ElicitationTest {
}
""".trimIndent()
- val result = McpJson.decodeFromString(json)
+ val result = verifyDeserialization(McpJson, json)
assertEquals(ElicitResult.Action.Decline, result.action)
assertNull(result.content)
assertEquals("User skipped", result.meta?.get("reason")?.jsonPrimitive?.content)
diff --git a/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/types/InitializeTest.kt b/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/types/InitializeTest.kt
index 6af7c4e..69c0878 100644
--- a/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/types/InitializeTest.kt
+++ b/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/types/InitializeTest.kt
@@ -1,6 +1,7 @@
package io.modelcontextprotocol.kotlin.sdk.types
-import io.kotest.assertions.json.shouldEqualJson
+import io.modelcontextprotocol.kotlin.test.utils.verifyDeserialization
+import io.modelcontextprotocol.kotlin.test.utils.verifySerialization
import kotlinx.serialization.json.boolean
import kotlinx.serialization.json.buildJsonObject
import kotlinx.serialization.json.jsonObject
@@ -14,6 +15,7 @@ import kotlin.test.assertNull
class InitializeTest {
@Test
+ @Suppress("LongMethod")
fun `should serialize InitializeRequest with capabilities and meta`() {
val request = InitializeRequest(
InitializeRequestParams(
@@ -41,9 +43,10 @@ class InitializeTest {
),
)
- val json = McpJson.encodeToString(request)
-
- json shouldEqualJson """
+ verifySerialization(
+ request,
+ McpJson,
+ """
{
"method": "initialize",
"params": {
@@ -73,7 +76,8 @@ class InitializeTest {
}
}
}
- """.trimIndent()
+ """.trimIndent(),
+ )
}
@Test
@@ -106,7 +110,7 @@ class InitializeTest {
}
""".trimIndent()
- val request = McpJson.decodeFromString(json)
+ val request = verifyDeserialization(McpJson, json)
assertEquals(Method.Defined.Initialize, request.method)
val params = request.params
@@ -149,9 +153,10 @@ class InitializeTest {
meta = buildJsonObject { put("issuedAt", "2025-01-12T15:00:58Z") },
)
- val json = McpJson.encodeToString(result)
-
- json shouldEqualJson """
+ verifySerialization(
+ result,
+ McpJson,
+ """
{
"protocolVersion": "2024-11-05",
"capabilities": {
@@ -180,7 +185,8 @@ class InitializeTest {
"issuedAt": "2025-01-12T15:00:58Z"
}
}
- """.trimIndent()
+ """.trimIndent(),
+ )
}
@Test
@@ -201,7 +207,7 @@ class InitializeTest {
}
""".trimIndent()
- val result = McpJson.decodeFromString(json)
+ val result = verifyDeserialization(McpJson, json)
assertEquals("2025-03-26", result.protocolVersion)
assertEquals("result-server", result.serverInfo.name)
diff --git a/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/types/JsonRpcTest.kt b/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/types/JsonRpcTest.kt
index ce02cc0..db111fe 100644
--- a/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/types/JsonRpcTest.kt
+++ b/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/types/JsonRpcTest.kt
@@ -3,6 +3,8 @@ package io.modelcontextprotocol.kotlin.sdk.types
import io.kotest.assertions.json.shouldEqualJson
import io.kotest.matchers.shouldBe
import io.kotest.matchers.types.shouldBeSameInstanceAs
+import io.modelcontextprotocol.kotlin.test.utils.verifyDeserialization
+import io.modelcontextprotocol.kotlin.test.utils.verifySerialization
import kotlinx.serialization.json.boolean
import kotlinx.serialization.json.buildJsonObject
import kotlinx.serialization.json.int
@@ -43,7 +45,8 @@ class JsonRpcTest {
@Test
fun `should convert JSONRPCRequest to Request`() {
- val jsonRpc = McpJson.decodeFromString(
+ val jsonRpc = verifyDeserialization(
+ McpJson,
"""
{
"id": 17,
@@ -101,7 +104,8 @@ class JsonRpcTest {
@Test
fun `should convert JSONRPCNotification to Notification`() {
- val jsonRpc = McpJson.decodeFromString(
+ val jsonRpc = verifyDeserialization(
+ McpJson,
"""
{
"method": "notifications/message",
@@ -147,9 +151,10 @@ class JsonRpcTest {
},
)
- val json = McpJson.encodeToString(request)
-
- json shouldEqualJson """
+ verifySerialization(
+ request,
+ McpJson,
+ """
{
"id": "req-1",
"method": "tools/list",
@@ -159,7 +164,8 @@ class JsonRpcTest {
},
"jsonrpc": "2.0"
}
- """.trimIndent()
+ """.trimIndent(),
+ )
}
@Test
@@ -175,7 +181,7 @@ class JsonRpcTest {
}
""".trimIndent()
- val request = McpJson.decodeFromString(json)
+ val request = verifyDeserialization(McpJson, json)
val id = request.id
assertIs(id)
@@ -196,9 +202,10 @@ class JsonRpcTest {
},
)
- val json = McpJson.encodeToString(notification)
-
- json shouldEqualJson """
+ verifySerialization(
+ notification,
+ McpJson,
+ """
{
"method": "notifications/log",
"params": {
@@ -207,7 +214,8 @@ class JsonRpcTest {
},
"jsonrpc": "2.0"
}
- """.trimIndent()
+ """.trimIndent(),
+ )
}
@Test
@@ -223,7 +231,7 @@ class JsonRpcTest {
}
""".trimIndent()
- val notification = McpJson.decodeFromString(json)
+ val notification = verifyDeserialization(McpJson, json)
assertEquals("notifications/progress", notification.method)
val params = notification.params?.jsonObject
assertNotNull(params)
@@ -240,9 +248,10 @@ class JsonRpcTest {
),
)
- val json = McpJson.encodeToString(response)
-
- json shouldEqualJson """
+ verifySerialization(
+ response,
+ McpJson,
+ """
{
"id": "call-1",
"result": {
@@ -252,7 +261,8 @@ class JsonRpcTest {
},
"jsonrpc": "2.0"
}
- """.trimIndent()
+ """.trimIndent(),
+ )
}
@Test
@@ -269,7 +279,7 @@ class JsonRpcTest {
}
""".trimIndent()
- val response = McpJson.decodeFromString(json)
+ val response = verifyDeserialization(McpJson, json)
val result = response.result
assertIs(result)
assertEquals(RequestId.NumberId(7L), response.id)
@@ -289,9 +299,10 @@ class JsonRpcTest {
),
)
- val json = McpJson.encodeToString(error)
-
- json shouldEqualJson """
+ verifySerialization(
+ error,
+ McpJson,
+ """
{
"id": 99,
"error": {
@@ -303,7 +314,8 @@ class JsonRpcTest {
},
"jsonrpc": "2.0"
}
- """.trimIndent()
+ """.trimIndent(),
+ )
}
@Test
@@ -323,7 +335,7 @@ class JsonRpcTest {
}
""".trimIndent()
- val error = McpJson.decodeFromString(json)
+ val error = verifyDeserialization(McpJson, json)
assertEquals(RequestId("req-404"), error.id)
assertEquals(RPCError.ErrorCode.INVALID_PARAMS, error.error.code)
assertEquals("Invalid params", error.error.message)
@@ -348,7 +360,7 @@ class JsonRpcTest {
}
""".trimIndent()
- val message = McpJson.decodeFromString(json)
+ val message = verifyDeserialization(McpJson, json)
val request = assertIs(message)
assertEquals("sampling/create", request.method)
val params = request.params?.jsonObject
@@ -373,7 +385,7 @@ class JsonRpcTest {
}
""".trimIndent()
- val message = McpJson.decodeFromString(json)
+ val message = verifyDeserialization(McpJson, json)
val error = assertIs(message)
assertEquals(RPCError.ErrorCode.REQUEST_TIMEOUT, error.error.code)
val data = error.error.data?.jsonObject
@@ -388,9 +400,10 @@ class JsonRpcTest {
params = buildJsonObject { put("message", "Polymorphic") },
)
- val json = McpJson.encodeToString(message)
-
- json shouldEqualJson """
+ verifySerialization(
+ message,
+ McpJson,
+ """
{
"method": "notifications/log",
"params": {
@@ -398,7 +411,8 @@ class JsonRpcTest {
},
"jsonrpc": "2.0"
}
- """.trimIndent()
+ """.trimIndent(),
+ )
}
@Test
diff --git a/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/types/LoggingTest.kt b/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/types/LoggingTest.kt
index 2b8c61a..bc03a47 100644
--- a/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/types/LoggingTest.kt
+++ b/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/types/LoggingTest.kt
@@ -1,6 +1,7 @@
package io.modelcontextprotocol.kotlin.sdk.types
-import io.kotest.assertions.json.shouldEqualJson
+import io.modelcontextprotocol.kotlin.test.utils.verifyDeserialization
+import io.modelcontextprotocol.kotlin.test.utils.verifySerialization
import kotlinx.serialization.json.boolean
import kotlinx.serialization.json.buildJsonObject
import kotlinx.serialization.json.jsonObject
@@ -47,9 +48,10 @@ class LoggingTest {
),
)
- val json = McpJson.encodeToString(request)
-
- json shouldEqualJson """
+ verifySerialization(
+ request,
+ McpJson,
+ """
{
"method": "logging/setLevel",
"params": {
@@ -59,7 +61,8 @@ class LoggingTest {
}
}
}
- """.trimIndent()
+ """.trimIndent(),
+ )
}
@Test
@@ -76,7 +79,7 @@ class LoggingTest {
}
""".trimIndent()
- val request = McpJson.decodeFromString(json)
+ val request = verifyDeserialization(McpJson, json)
assertEquals(Method.Defined.LoggingSetLevel, request.method)
assertEquals(LoggingLevel.Alert, request.params.level)
assertEquals(ProgressToken(1001), request.params.meta?.progressToken)
@@ -93,9 +96,10 @@ class LoggingTest {
),
)
- val json = McpJson.encodeToString(notification)
-
- json shouldEqualJson """
+ verifySerialization(
+ notification,
+ McpJson,
+ """
{
"method": "notifications/message",
"params": {
@@ -109,7 +113,8 @@ class LoggingTest {
}
}
}
- """.trimIndent()
+ """.trimIndent(),
+ )
}
@Test
@@ -124,7 +129,7 @@ class LoggingTest {
}
""".trimIndent()
- val notification = McpJson.decodeFromString(json)
+ val notification = verifyDeserialization(McpJson, json)
assertEquals(Method.Defined.NotificationsMessage, notification.method)
val params = notification.params
@@ -153,7 +158,7 @@ class LoggingTest {
}
""".trimIndent()
- val notification = McpJson.decodeFromString(json)
+ val notification = verifyDeserialization(McpJson, json)
val params = notification.params
assertEquals(LoggingLevel.Critical, params.level)
val data = params.data.jsonObject
diff --git a/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/types/NotificationTest.kt b/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/types/NotificationTest.kt
index bbd209d..b55a441 100644
--- a/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/types/NotificationTest.kt
+++ b/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/types/NotificationTest.kt
@@ -1,6 +1,7 @@
package io.modelcontextprotocol.kotlin.sdk.types
-import io.kotest.assertions.json.shouldEqualJson
+import io.modelcontextprotocol.kotlin.test.utils.verifyDeserialization
+import io.modelcontextprotocol.kotlin.test.utils.verifySerialization
import kotlinx.serialization.json.buildJsonObject
import kotlinx.serialization.json.jsonPrimitive
import kotlinx.serialization.json.put
@@ -20,9 +21,10 @@ class NotificationTest {
),
)
- val json = McpJson.encodeToString(notification)
-
- json shouldEqualJson """
+ verifySerialization(
+ notification,
+ McpJson,
+ """
{
"method": "notifications/cancelled",
"params": {
@@ -33,7 +35,8 @@ class NotificationTest {
}
}
}
- """.trimIndent()
+ """.trimIndent(),
+ )
}
@Test
@@ -48,7 +51,7 @@ class NotificationTest {
}
""".trimIndent()
- val notification = McpJson.decodeFromString(json)
+ val notification = verifyDeserialization(McpJson, json)
val params = notification.params
assertEquals(Method.Defined.NotificationsCancelled, notification.method)
@@ -65,9 +68,10 @@ class NotificationTest {
),
)
- val json = McpJson.encodeToString(notification)
-
- json shouldEqualJson """
+ verifySerialization(
+ notification,
+ McpJson,
+ """
{
"method": "notifications/initialized",
"params": {
@@ -76,7 +80,8 @@ class NotificationTest {
}
}
}
- """.trimIndent()
+ """.trimIndent(),
+ )
}
@Test
@@ -87,7 +92,7 @@ class NotificationTest {
}
""".trimIndent()
- val notification = McpJson.decodeFromString(json)
+ val notification = verifyDeserialization(McpJson, json)
assertEquals(Method.Defined.NotificationsInitialized, notification.method)
assertNull(notification.params)
@@ -105,9 +110,10 @@ class NotificationTest {
),
)
- val json = McpJson.encodeToString(notification)
-
- json shouldEqualJson """
+ verifySerialization(
+ notification,
+ McpJson,
+ """
{
"method": "notifications/progress",
"params": {
@@ -120,7 +126,8 @@ class NotificationTest {
}
}
}
- """.trimIndent()
+ """.trimIndent(),
+ )
}
@Test
@@ -135,7 +142,7 @@ class NotificationTest {
}
""".trimIndent()
- val notification = McpJson.decodeFromString(json)
+ val notification = verifyDeserialization(McpJson, json)
val params = notification.params
assertEquals(Method.Defined.NotificationsProgress, notification.method)
@@ -155,9 +162,10 @@ class NotificationTest {
),
)
- val json = McpJson.encodeToString(notification)
-
- json shouldEqualJson """
+ verifySerialization(
+ notification,
+ McpJson,
+ """
{
"method": "notifications/resources/updated",
"params": {
@@ -167,7 +175,8 @@ class NotificationTest {
}
}
}
- """.trimIndent()
+ """.trimIndent(),
+ )
}
@Test
@@ -184,7 +193,7 @@ class NotificationTest {
}
""".trimIndent()
- val notification = McpJson.decodeFromString(json)
+ val notification = verifyDeserialization(McpJson, json)
val params = notification.params
assertEquals(Method.Defined.NotificationsResourcesUpdated, notification.method)
@@ -200,9 +209,10 @@ class NotificationTest {
),
)
- val json = McpJson.encodeToString(notification)
-
- json shouldEqualJson """
+ verifySerialization(
+ notification,
+ McpJson,
+ """
{
"method": "notifications/prompts/list_changed",
"params": {
@@ -211,7 +221,8 @@ class NotificationTest {
}
}
}
- """.trimIndent()
+ """.trimIndent(),
+ )
}
@Test
@@ -222,7 +233,7 @@ class NotificationTest {
}
""".trimIndent()
- val notification = McpJson.decodeFromString(json)
+ val notification = verifyDeserialization(McpJson, json)
assertEquals(Method.Defined.NotificationsPromptsListChanged, notification.method)
assertNull(notification.params)
@@ -236,9 +247,10 @@ class NotificationTest {
),
)
- val json = McpJson.encodeToString(notification)
-
- json shouldEqualJson """
+ verifySerialization(
+ notification,
+ McpJson,
+ """
{
"method": "notifications/resources/list_changed",
"params": {
@@ -247,7 +259,8 @@ class NotificationTest {
}
}
}
- """.trimIndent()
+ """.trimIndent(),
+ )
}
@Test
@@ -258,9 +271,10 @@ class NotificationTest {
),
)
- val json = McpJson.encodeToString(notification)
-
- json shouldEqualJson """
+ verifySerialization(
+ notification,
+ McpJson,
+ """
{
"method": "notifications/roots/list_changed",
"params": {
@@ -269,7 +283,8 @@ class NotificationTest {
}
}
}
- """.trimIndent()
+ """.trimIndent(),
+ )
}
@Test
@@ -280,9 +295,10 @@ class NotificationTest {
),
)
- val json = McpJson.encodeToString(notification)
-
- json shouldEqualJson """
+ verifySerialization(
+ notification,
+ McpJson,
+ """
{
"method": "notifications/tools/list_changed",
"params": {
@@ -291,6 +307,7 @@ class NotificationTest {
}
}
}
- """.trimIndent()
+ """.trimIndent(),
+ )
}
}
diff --git a/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/types/PingRequestTest.kt b/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/types/PingRequestTest.kt
index 070d58e..72bddb0 100644
--- a/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/types/PingRequestTest.kt
+++ b/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/types/PingRequestTest.kt
@@ -1,6 +1,7 @@
package io.modelcontextprotocol.kotlin.sdk.types
-import io.kotest.assertions.json.shouldEqualJson
+import io.modelcontextprotocol.kotlin.test.utils.verifyDeserialization
+import io.modelcontextprotocol.kotlin.test.utils.verifySerialization
import kotlinx.serialization.json.buildJsonObject
import kotlinx.serialization.json.int
import kotlinx.serialization.json.jsonObject
@@ -17,13 +18,15 @@ class PingRequestTest {
fun `should serialize PingRequest without params`() {
val request = PingRequest()
- val json = McpJson.encodeToString(request)
-
- json shouldEqualJson """
+ verifySerialization(
+ request,
+ McpJson,
+ """
{
"method": "ping"
}
- """.trimIndent()
+ """.trimIndent(),
+ )
}
@Test
@@ -36,9 +39,10 @@ class PingRequestTest {
),
)
- val json = McpJson.encodeToString(request)
-
- json shouldEqualJson """
+ verifySerialization(
+ request,
+ McpJson,
+ """
{
"method": "ping",
"params": {
@@ -47,7 +51,8 @@ class PingRequestTest {
}
}
}
- """.trimIndent()
+ """.trimIndent(),
+ )
}
@Test
@@ -84,7 +89,7 @@ class PingRequestTest {
}
""".trimIndent()
- val jsonRpc = McpJson.decodeFromString(json)
+ val jsonRpc = verifyDeserialization(McpJson, json)
val request = jsonRpc.fromJSON()
val pingRequest = assertIs(request)
diff --git a/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/types/PromptsTest.kt b/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/types/PromptsTest.kt
index 01eb01c..d7101d3 100644
--- a/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/types/PromptsTest.kt
+++ b/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/types/PromptsTest.kt
@@ -1,6 +1,8 @@
package io.modelcontextprotocol.kotlin.sdk.types
import io.kotest.assertions.json.shouldEqualJson
+import io.modelcontextprotocol.kotlin.test.utils.verifyDeserialization
+import io.modelcontextprotocol.kotlin.test.utils.verifySerialization
import kotlinx.serialization.json.buildJsonObject
import kotlinx.serialization.json.double
import kotlinx.serialization.json.int
@@ -18,13 +20,15 @@ class PromptsTest {
fun `should serialize PromptArgument with minimal fields`() {
val argument = PromptArgument(name = "repository")
- val json = McpJson.encodeToString(argument)
-
- json shouldEqualJson """
+ verifySerialization(
+ argument,
+ McpJson,
+ """
{
"name": "repository"
}
- """.trimIndent()
+ """.trimIndent(),
+ )
}
@Test
@@ -36,16 +40,18 @@ class PromptsTest {
title = "Language",
)
- val json = McpJson.encodeToString(argument)
-
- json shouldEqualJson """
+ verifySerialization(
+ argument,
+ McpJson,
+ """
{
"name": "language",
"description": "Programming language to use",
"required": true,
"title": "Language"
}
- """.trimIndent()
+ """.trimIndent(),
+ )
}
@Test
@@ -69,9 +75,10 @@ class PromptsTest {
meta = buildJsonObject { put("category", "status-report") },
)
- val json = McpJson.encodeToString(prompt)
-
- json shouldEqualJson """
+ verifySerialization(
+ prompt,
+ McpJson,
+ """
{
"name": "summarize_update",
"description": "Summarize the latest repository changes.",
@@ -97,7 +104,8 @@ class PromptsTest {
"category": "status-report"
}
}
- """.trimIndent()
+ """.trimIndent(),
+ )
}
@Test
@@ -127,7 +135,7 @@ class PromptsTest {
}
""".trimIndent()
- val prompt = McpJson.decodeFromString(json)
+ val prompt = verifyDeserialization(McpJson, json)
assertEquals("code_review", prompt.name)
assertEquals("Generate a succinct code review.", prompt.description)
@@ -151,9 +159,10 @@ class PromptsTest {
content = TextContent(text = "Provide a concise summary of the changes."),
)
- val json = McpJson.encodeToString(message)
-
- json shouldEqualJson """
+ verifySerialization(
+ message,
+ McpJson,
+ """
{
"role": "assistant",
"content": {
@@ -161,7 +170,8 @@ class PromptsTest {
"text": "Provide a concise summary of the changes."
}
}
- """.trimIndent()
+ """.trimIndent(),
+ )
}
@Test
@@ -180,7 +190,7 @@ class PromptsTest {
}
""".trimIndent()
- val message = McpJson.decodeFromString(json)
+ val message = verifyDeserialization(McpJson, json)
assertEquals(Role.User, message.role)
val content = message.content
@@ -203,7 +213,7 @@ class PromptsTest {
}
""".trimIndent()
- val decoded = McpJson.decodeFromString(json)
+ val decoded = verifyDeserialization(McpJson, json)
val promptReference = assertIs(decoded)
assertEquals("daily-summary", promptReference.name)
assertEquals("Daily Summary", promptReference.title)
@@ -222,9 +232,10 @@ class PromptsTest {
),
)
- val json = McpJson.encodeToString(request)
-
- json shouldEqualJson """
+ verifySerialization(
+ request,
+ McpJson,
+ """
{
"method": "prompts/get",
"params": {
@@ -237,7 +248,8 @@ class PromptsTest {
}
}
}
- """.trimIndent()
+ """.trimIndent(),
+ )
}
@Test
@@ -258,7 +270,7 @@ class PromptsTest {
}
""".trimIndent()
- val request = McpJson.decodeFromString(json)
+ val request = verifyDeserialization(McpJson, json)
assertEquals(Method.Defined.PromptsGet, request.method)
val params = request.params
@@ -287,9 +299,10 @@ class PromptsTest {
meta = buildJsonObject { put("generatedAt", "2025-01-12T15:00:58Z") },
)
- val json = McpJson.encodeToString(result)
-
- json shouldEqualJson """
+ verifySerialization(
+ result,
+ McpJson,
+ """
{
"messages": [
{
@@ -312,7 +325,8 @@ class PromptsTest {
"generatedAt": "2025-01-12T15:00:58Z"
}
}
- """.trimIndent()
+ """.trimIndent(),
+ )
}
@Test
@@ -332,7 +346,7 @@ class PromptsTest {
}
""".trimIndent()
- val result = McpJson.decodeFromString(json)
+ val result = verifyDeserialization(McpJson, json)
val messages = result.messages
assertEquals(1, messages.size)
@@ -354,9 +368,10 @@ class PromptsTest {
),
)
- val json = McpJson.encodeToString(request)
-
- json shouldEqualJson """
+ verifySerialization(
+ request,
+ McpJson,
+ """
{
"method": "prompts/list",
"params": {
@@ -366,20 +381,23 @@ class PromptsTest {
}
}
}
- """.trimIndent()
+ """.trimIndent(),
+ )
}
@Test
fun `should serialize ListPromptsRequest without params`() {
val request = ListPromptsRequest()
- val json = McpJson.encodeToString(request)
-
- json shouldEqualJson """
+ verifySerialization(
+ request,
+ McpJson,
+ """
{
"method": "prompts/list"
}
- """.trimIndent()
+ """.trimIndent(),
+ )
}
@Test
@@ -393,9 +411,10 @@ class PromptsTest {
meta = buildJsonObject { put("page", 1) },
)
- val json = McpJson.encodeToString(result)
-
- json shouldEqualJson """
+ verifySerialization(
+ result,
+ McpJson,
+ """
{
"prompts": [
{"name": "morning-briefing"},
@@ -406,7 +425,8 @@ class PromptsTest {
"page": 1
}
}
- """.trimIndent()
+ """.trimIndent(),
+ )
}
@Test
@@ -433,7 +453,7 @@ class PromptsTest {
}
""".trimIndent()
- val result = McpJson.decodeFromString(json)
+ val result = verifyDeserialization(McpJson, json)
val prompts = result.prompts
assertEquals(1, prompts.size)
diff --git a/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/types/RequestTest.kt b/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/types/RequestTest.kt
index c42589e..1d6da19 100644
--- a/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/types/RequestTest.kt
+++ b/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/types/RequestTest.kt
@@ -2,6 +2,8 @@ package io.modelcontextprotocol.kotlin.sdk.types
import io.kotest.assertions.json.shouldEqualJson
import io.kotest.matchers.shouldBe
+import io.modelcontextprotocol.kotlin.test.utils.verifyDeserialization
+import io.modelcontextprotocol.kotlin.test.utils.verifySerialization
import kotlinx.serialization.json.buildJsonObject
import kotlinx.serialization.json.double
import kotlinx.serialization.json.jsonPrimitive
@@ -41,16 +43,18 @@ class RequestTest {
),
)
- val json = McpJson.encodeToString(params)
-
- json shouldEqualJson """
+ verifySerialization(
+ params,
+ McpJson,
+ """
{
"_meta": {
"progressToken": "base-42",
"origin": "test-suite"
}
}
- """.trimIndent()
+ """.trimIndent(),
+ )
}
@Test
@@ -64,7 +68,7 @@ class RequestTest {
}
""".trimIndent()
- val params = McpJson.decodeFromString(json)
+ val params = verifyDeserialization(McpJson, json)
val meta = params.meta
assertNotNull(meta)
@@ -79,16 +83,18 @@ class RequestTest {
meta = RequestMeta(buildJsonObject { put("progressToken", "page-req") }),
)
- val json = McpJson.encodeToString(params)
-
- json shouldEqualJson """
+ verifySerialization(
+ params,
+ McpJson,
+ """
{
"cursor": "cursor-1",
"_meta": {
"progressToken": "page-req"
}
}
- """.trimIndent()
+ """.trimIndent(),
+ )
}
@Test
@@ -102,7 +108,7 @@ class RequestTest {
}
""".trimIndent()
- val params = McpJson.decodeFromString(json)
+ val params = verifyDeserialization(McpJson, json)
assertEquals("cursor-2", params.cursor)
assertEquals(ProgressToken(99), params.meta?.progressToken)
@@ -163,22 +169,24 @@ class RequestTest {
},
)
- val json = McpJson.encodeToString(result)
-
- json shouldEqualJson """
+ verifySerialization(
+ result,
+ McpJson,
+ """
{
"_meta": {
"processedBy": "worker-1"
}
}
- """.trimIndent()
+ """.trimIndent(),
+ )
}
@Test
fun `should deserialize EmptyResult without meta`() {
val json = "{}"
- val result = McpJson.decodeFromString(json)
+ val result = verifyDeserialization(McpJson, json)
assertNull(result.meta)
}
diff --git a/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/types/ResourcesTest.kt b/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/types/ResourcesTest.kt
index 0313b75..e47a0f5 100644
--- a/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/types/ResourcesTest.kt
+++ b/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/types/ResourcesTest.kt
@@ -1,6 +1,8 @@
package io.modelcontextprotocol.kotlin.sdk.types
import io.kotest.assertions.json.shouldEqualJson
+import io.modelcontextprotocol.kotlin.test.utils.verifyDeserialization
+import io.modelcontextprotocol.kotlin.test.utils.verifySerialization
import kotlinx.serialization.json.buildJsonObject
import kotlinx.serialization.json.int
import kotlinx.serialization.json.jsonPrimitive
@@ -20,14 +22,16 @@ class ResourcesTest {
name = "README",
)
- val json = McpJson.encodeToString(resource)
-
- json shouldEqualJson """
+ verifySerialization(
+ resource,
+ McpJson,
+ """
{
"uri": "file:///workspace/README.md",
"name": "README"
}
- """.trimIndent()
+ """.trimIndent(),
+ )
}
@Test
@@ -46,9 +50,10 @@ class ResourcesTest {
meta = buildJsonObject { put("etag", "abc123") },
)
- val json = McpJson.encodeToString(resource)
-
- json shouldEqualJson """
+ verifySerialization(
+ resource,
+ McpJson,
+ """
{
"uri": "file:///workspace/CHANGELOG.md",
"name": "CHANGELOG",
@@ -67,7 +72,8 @@ class ResourcesTest {
"etag": "abc123"
}
}
- """.trimIndent()
+ """.trimIndent(),
+ )
}
@Test
@@ -92,7 +98,7 @@ class ResourcesTest {
}
""".trimIndent()
- val resource = McpJson.decodeFromString(json)
+ val resource = verifyDeserialization(McpJson, json)
assertEquals("file:///config/settings.json", resource.uri)
assertEquals("config-settings", resource.name)
@@ -122,9 +128,10 @@ class ResourcesTest {
meta = buildJsonObject { put("requiresAuth", true) },
)
- val json = McpJson.encodeToString(template)
-
- json shouldEqualJson """
+ verifySerialization(
+ template,
+ McpJson,
+ """
{
"uriTemplate": "file:///workspace/{path}",
"name": "workspace-file",
@@ -145,7 +152,8 @@ class ResourcesTest {
"requiresAuth": true
}
}
- """.trimIndent()
+ """.trimIndent(),
+ )
}
@Test
@@ -161,7 +169,7 @@ class ResourcesTest {
}
""".trimIndent()
- val template = McpJson.decodeFromString(json)
+ val template = verifyDeserialization(McpJson, json)
assertEquals("db://users/{id}", template.uriTemplate)
assertEquals("user-record", template.name)
@@ -183,7 +191,7 @@ class ResourcesTest {
}
""".trimIndent()
- val decoded = McpJson.decodeFromString(json)
+ val decoded = verifyDeserialization(McpJson, json)
val templateReference = assertIs(decoded)
assertEquals("file:///workspace/{path}", templateReference.uri)
assertEquals(ReferenceType.ResourceTemplate, templateReference.type)
@@ -198,9 +206,10 @@ class ResourcesTest {
meta = buildJsonObject { put("encoding", "utf-8") },
)
- val json = McpJson.encodeToString(contents)
-
- json shouldEqualJson """
+ verifySerialization(
+ contents,
+ McpJson,
+ """
{
"text": "Hello, MCP!",
"uri": "file:///workspace/hello.txt",
@@ -209,7 +218,8 @@ class ResourcesTest {
"encoding": "utf-8"
}
}
- """.trimIndent()
+ """.trimIndent(),
+ )
}
@Test
@@ -225,7 +235,7 @@ class ResourcesTest {
}
""".trimIndent()
- val contents = McpJson.decodeFromString(json)
+ val contents = verifyDeserialization(McpJson, json)
val blobContents = assertIs(contents)
assertEquals("YmluYXJ5ZGF0YQ==", blobContents.blob)
assertEquals("file:///workspace/logo.png", blobContents.uri)
@@ -240,14 +250,16 @@ class ResourcesTest {
mimeType = "application/octet-stream",
)
- val json = McpJson.encodeToString(contents)
-
- json shouldEqualJson """
+ verifySerialization(
+ contents,
+ McpJson,
+ """
{
"uri": "custom://resource/42",
"mimeType": "application/octet-stream"
}
- """.trimIndent()
+ """.trimIndent(),
+ )
}
@Test
@@ -259,9 +271,10 @@ class ResourcesTest {
),
)
- val json = McpJson.encodeToString(request)
-
- json shouldEqualJson """
+ verifySerialization(
+ request,
+ McpJson,
+ """
{
"method": "resources/list",
"params": {
@@ -271,20 +284,23 @@ class ResourcesTest {
}
}
}
- """.trimIndent()
+ """.trimIndent(),
+ )
}
@Test
fun `should serialize ListResourcesRequest without params`() {
val request = ListResourcesRequest()
- val json = McpJson.encodeToString(request)
-
- json shouldEqualJson """
+ verifySerialization(
+ request,
+ McpJson,
+ """
{
"method": "resources/list"
}
- """.trimIndent()
+ """.trimIndent(),
+ )
}
@Test
@@ -298,9 +314,10 @@ class ResourcesTest {
meta = buildJsonObject { put("page", 1) },
)
- val json = McpJson.encodeToString(result)
-
- json shouldEqualJson """
+ verifySerialization(
+ result,
+ McpJson,
+ """
{
"resources": [
{"uri": "file:///workspace/README.md", "name": "README"},
@@ -311,7 +328,8 @@ class ResourcesTest {
"page": 1
}
}
- """.trimIndent()
+ """.trimIndent(),
+ )
}
@Test
@@ -332,7 +350,7 @@ class ResourcesTest {
}
""".trimIndent()
- val result = McpJson.decodeFromString(json)
+ val result = verifyDeserialization(McpJson, json)
assertEquals("cursor-next", result.nextCursor)
val resources = result.resources
@@ -353,9 +371,10 @@ class ResourcesTest {
),
)
- val json = McpJson.encodeToString(request)
-
- json shouldEqualJson """
+ verifySerialization(
+ request,
+ McpJson,
+ """
{
"method": "resources/read",
"params": {
@@ -365,7 +384,8 @@ class ResourcesTest {
}
}
}
- """.trimIndent()
+ """.trimIndent(),
+ )
}
@Test
@@ -390,7 +410,7 @@ class ResourcesTest {
}
""".trimIndent()
- val result = McpJson.decodeFromString(json)
+ val result = verifyDeserialization(McpJson, json)
val contents = result.contents
assertEquals(2, contents.size)
@@ -410,9 +430,10 @@ class ResourcesTest {
),
)
- val json = McpJson.encodeToString(request)
-
- json shouldEqualJson """
+ verifySerialization(
+ request,
+ McpJson,
+ """
{
"method": "resources/subscribe",
"params": {
@@ -422,7 +443,8 @@ class ResourcesTest {
}
}
}
- """.trimIndent()
+ """.trimIndent(),
+ )
}
@Test
@@ -434,9 +456,10 @@ class ResourcesTest {
),
)
- val json = McpJson.encodeToString(request)
-
- json shouldEqualJson """
+ verifySerialization(
+ request,
+ McpJson,
+ """
{
"method": "resources/unsubscribe",
"params": {
@@ -446,7 +469,8 @@ class ResourcesTest {
}
}
}
- """.trimIndent()
+ """.trimIndent(),
+ )
}
@Test
@@ -458,9 +482,10 @@ class ResourcesTest {
),
)
- val json = McpJson.encodeToString(request)
-
- json shouldEqualJson """
+ verifySerialization(
+ request,
+ McpJson,
+ """
{
"method": "resources/templates/list",
"params": {
@@ -470,7 +495,8 @@ class ResourcesTest {
}
}
}
- """.trimIndent()
+ """.trimIndent(),
+ )
}
@Test
@@ -483,9 +509,10 @@ class ResourcesTest {
meta = buildJsonObject { put("page", 3) },
)
- val json = McpJson.encodeToString(result)
-
- json shouldEqualJson """
+ verifySerialization(
+ result,
+ McpJson,
+ """
{
"resourceTemplates": [
{
@@ -498,7 +525,8 @@ class ResourcesTest {
"page": 3
}
}
- """.trimIndent()
+ """.trimIndent(),
+ )
}
@Test
@@ -522,7 +550,7 @@ class ResourcesTest {
}
""".trimIndent()
- val result = McpJson.decodeFromString(json)
+ val result = verifyDeserialization(McpJson, json)
assertEquals("cursor-next", result.nextCursor)
val templates = result.resourceTemplates
diff --git a/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/types/RootsTest.kt b/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/types/RootsTest.kt
index 3f1b8d6..2350c25 100644
--- a/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/types/RootsTest.kt
+++ b/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/types/RootsTest.kt
@@ -1,6 +1,7 @@
package io.modelcontextprotocol.kotlin.sdk.types
-import io.kotest.assertions.json.shouldEqualJson
+import io.modelcontextprotocol.kotlin.test.utils.verifyDeserialization
+import io.modelcontextprotocol.kotlin.test.utils.verifySerialization
import kotlinx.serialization.json.boolean
import kotlinx.serialization.json.buildJsonObject
import kotlinx.serialization.json.jsonPrimitive
@@ -15,13 +16,15 @@ class RootsTest {
fun `should serialize Root with minimal fields`() {
val root = Root(uri = "file:///workspace/project")
- val json = McpJson.encodeToString(root)
-
- json shouldEqualJson """
+ verifySerialization(
+ root,
+ McpJson,
+ """
{
"uri": "file:///workspace/project"
}
- """.trimIndent()
+ """.trimIndent(),
+ )
}
@Test
@@ -32,9 +35,10 @@ class RootsTest {
meta = buildJsonObject { put("writable", true) },
)
- val json = McpJson.encodeToString(root)
-
- json shouldEqualJson """
+ verifySerialization(
+ root,
+ McpJson,
+ """
{
"uri": "file:///workspace/docs",
"name": "Docs",
@@ -42,7 +46,8 @@ class RootsTest {
"writable": true
}
}
- """.trimIndent()
+ """.trimIndent(),
+ )
}
@Test
@@ -57,7 +62,7 @@ class RootsTest {
}
""".trimIndent()
- val root = McpJson.decodeFromString(json)
+ val root = verifyDeserialization(McpJson, json)
assertEquals("file:///workspace/code", root.uri)
assertEquals("Code", root.name)
@@ -74,9 +79,10 @@ class RootsTest {
),
)
- val json = McpJson.encodeToString(request)
-
- json shouldEqualJson """
+ verifySerialization(
+ request,
+ McpJson,
+ """
{
"method": "roots/list",
"params": {
@@ -85,20 +91,23 @@ class RootsTest {
}
}
}
- """.trimIndent()
+ """.trimIndent(),
+ )
}
@Test
fun `should serialize ListRootsRequest without params`() {
val request = ListRootsRequest()
- val json = McpJson.encodeToString(request)
-
- json shouldEqualJson """
+ verifySerialization(
+ request,
+ McpJson,
+ """
{
"method": "roots/list"
}
- """.trimIndent()
+ """.trimIndent(),
+ )
}
@Test
@@ -111,9 +120,10 @@ class RootsTest {
meta = buildJsonObject { put("issuedAt", "2025-01-12T15:00:58Z") },
)
- val json = McpJson.encodeToString(result)
-
- json shouldEqualJson """
+ verifySerialization(
+ result,
+ McpJson,
+ """
{
"roots": [
{"uri": "file:///workspace/project", "name": "Project"},
@@ -123,7 +133,8 @@ class RootsTest {
"issuedAt": "2025-01-12T15:00:58Z"
}
}
- """.trimIndent()
+ """.trimIndent(),
+ )
}
@Test
@@ -142,7 +153,7 @@ class RootsTest {
}
""".trimIndent()
- val result = McpJson.decodeFromString(json)
+ val result = verifyDeserialization(McpJson, json)
val roots = result.roots
assertEquals(1, roots.size)
diff --git a/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/types/SamplingTest.kt b/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/types/SamplingTest.kt
index 7dc3fe6..696f322 100644
--- a/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/types/SamplingTest.kt
+++ b/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/types/SamplingTest.kt
@@ -1,6 +1,7 @@
package io.modelcontextprotocol.kotlin.sdk.types
-import io.kotest.assertions.json.shouldEqualJson
+import io.modelcontextprotocol.kotlin.test.utils.verifyDeserialization
+import io.modelcontextprotocol.kotlin.test.utils.verifySerialization
import kotlinx.serialization.json.buildJsonObject
import kotlinx.serialization.json.double
import kotlinx.serialization.json.jsonPrimitive
@@ -17,13 +18,15 @@ class SamplingTest {
fun `should serialize ModelHint`() {
val hint = ModelHint(name = "claude-3-5-sonnet")
- val json = McpJson.encodeToString(hint)
-
- json shouldEqualJson """
+ verifySerialization(
+ hint,
+ McpJson,
+ """
{
"name": "claude-3-5-sonnet"
}
- """.trimIndent()
+ """.trimIndent(),
+ )
}
@Test
@@ -35,9 +38,10 @@ class SamplingTest {
intelligencePriority = 1.0,
)
- val json = McpJson.encodeToString(preferences)
-
- json shouldEqualJson """
+ verifySerialization(
+ preferences,
+ McpJson,
+ """
{
"hints": [
{"name": "haiku"},
@@ -47,7 +51,8 @@ class SamplingTest {
"speedPriority": 0.75,
"intelligencePriority": 1.0
}
- """.trimIndent()
+ """.trimIndent(),
+ )
}
@Test
@@ -64,9 +69,10 @@ class SamplingTest {
content = TextContent(text = "Summarize the latest release."),
)
- val json = McpJson.encodeToString(message)
-
- json shouldEqualJson """
+ verifySerialization(
+ message,
+ McpJson,
+ """
{
"role": "user",
"content": {
@@ -74,10 +80,12 @@ class SamplingTest {
"text": "Summarize the latest release."
}
}
- """.trimIndent()
+ """.trimIndent(),
+ )
}
@Test
+ @Suppress("LongMethod")
fun `should serialize CreateMessageRequest with all fields`() {
val request = CreateMessageRequest(
CreateMessageRequestParams(
@@ -105,9 +113,10 @@ class SamplingTest {
),
)
- val json = McpJson.encodeToString(request)
-
- json shouldEqualJson """
+ verifySerialization(
+ request,
+ McpJson,
+ """
{
"method": "sampling/createMessage",
"params": {
@@ -146,7 +155,8 @@ class SamplingTest {
}
}
}
- """.trimIndent()
+ """.trimIndent(),
+ )
}
@Test
@@ -181,7 +191,7 @@ class SamplingTest {
}
""".trimIndent()
- val request = McpJson.decodeFromString(json)
+ val request = verifyDeserialization(McpJson, json)
assertEquals(Method.Defined.SamplingCreateMessage, request.method)
val params = request.params
@@ -209,9 +219,10 @@ class SamplingTest {
meta = buildJsonObject { put("latencyMs", 850) },
)
- val json = McpJson.encodeToString(result)
-
- json shouldEqualJson """
+ verifySerialization(
+ result,
+ McpJson,
+ """
{
"role": "assistant",
"content": {
@@ -224,7 +235,8 @@ class SamplingTest {
"latencyMs": 850
}
}
- """.trimIndent()
+ """.trimIndent(),
+ )
}
@Test
@@ -244,7 +256,7 @@ class SamplingTest {
}
""".trimIndent()
- val result = McpJson.decodeFromString(json)
+ val result = verifyDeserialization(McpJson, json)
assertEquals(Role.Assistant, result.role)
val text = assertIs(result.content)
diff --git a/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/types/ToolsTest.kt b/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/types/ToolsTest.kt
index 447df4f..4663fbd 100644
--- a/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/types/ToolsTest.kt
+++ b/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/types/ToolsTest.kt
@@ -1,6 +1,7 @@
package io.modelcontextprotocol.kotlin.sdk.types
-import io.kotest.assertions.json.shouldEqualJson
+import io.modelcontextprotocol.kotlin.test.utils.verifyDeserialization
+import io.modelcontextprotocol.kotlin.test.utils.verifySerialization
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.buildJsonObject
import kotlinx.serialization.json.int
@@ -21,19 +22,22 @@ class ToolsTest {
inputSchema = ToolSchema(),
)
- val json = McpJson.encodeToString(tool)
-
- json shouldEqualJson """
+ verifySerialization(
+ tool,
+ McpJson,
+ """
{
"name": "search",
"inputSchema": {
"type": "object"
}
}
- """.trimIndent()
+ """.trimIndent(),
+ )
}
@Test
+ @Suppress("LongMethod")
fun `should serialize Tool with annotations and schemas`() {
val inputSchema = ToolSchema(
properties = buildJsonObject {
@@ -74,9 +78,10 @@ class ToolsTest {
meta = buildJsonObject { put("category", "search") },
)
- val json = McpJson.encodeToString(tool)
-
- json shouldEqualJson """
+ verifySerialization(
+ tool,
+ McpJson,
+ """
{
"name": "web-search",
"inputSchema": {
@@ -113,7 +118,8 @@ class ToolsTest {
"category": "search"
}
}
- """.trimIndent()
+ """.trimIndent(),
+ )
}
@Test
@@ -139,7 +145,7 @@ class ToolsTest {
}
""".trimIndent()
- val tool = McpJson.decodeFromString(json)
+ val tool = verifyDeserialization(McpJson, json)
assertEquals("translate", tool.name)
assertEquals("Translate Text", tool.annotations?.title)
@@ -156,9 +162,7 @@ class ToolsTest {
fun `should serialize ToolSchema with defs`() {
val tool = toolWithDefs()
- val json = McpJson.encodeToString(tool)
-
- json shouldEqualJson toolWithDefsJson()
+ verifySerialization(tool, McpJson, toolWithDefsJson())
}
@Test
@@ -188,7 +192,7 @@ class ToolsTest {
}
""".trimIndent()
- val tool = McpJson.decodeFromString(json)
+ val tool = verifyDeserialization(McpJson, json)
val schema = tool.inputSchema
val defs = schema.defs
@@ -215,9 +219,10 @@ class ToolsTest {
),
)
- val json = McpJson.encodeToString(request)
-
- json shouldEqualJson """
+ verifySerialization(
+ request,
+ McpJson,
+ """
{
"method": "tools/call",
"params": {
@@ -230,7 +235,8 @@ class ToolsTest {
}
}
}
- """.trimIndent()
+ """.trimIndent(),
+ )
}
@Test
@@ -250,7 +256,7 @@ class ToolsTest {
}
""".trimIndent()
- val request = McpJson.decodeFromString(json)
+ val request = verifyDeserialization(McpJson, json)
assertEquals(Method.Defined.ToolsCall, request.method)
val params = request.params
@@ -273,9 +279,10 @@ class ToolsTest {
meta = buildJsonObject { put("elapsedMs", 1200) },
)
- val json = McpJson.encodeToString(result)
-
- json shouldEqualJson """
+ verifySerialization(
+ result,
+ McpJson,
+ """
{
"content": [
{
@@ -294,7 +301,8 @@ class ToolsTest {
"elapsedMs": 1200
}
}
- """.trimIndent()
+ """.trimIndent(),
+ )
}
@Test
@@ -318,7 +326,7 @@ class ToolsTest {
}
""".trimIndent()
- val result = McpJson.decodeFromString(json)
+ val result = verifyDeserialization(McpJson, json)
assertEquals(true, result.isError)
val text = assertIs(result.content.first())
@@ -338,9 +346,10 @@ class ToolsTest {
),
)
- val json = McpJson.encodeToString(request)
-
- json shouldEqualJson """
+ verifySerialization(
+ request,
+ McpJson,
+ """
{
"method": "tools/list",
"params": {
@@ -350,20 +359,23 @@ class ToolsTest {
}
}
}
- """.trimIndent()
+ """.trimIndent(),
+ )
}
@Test
fun `should serialize ListToolsRequest without params`() {
val request = ListToolsRequest()
- val json = McpJson.encodeToString(request)
-
- json shouldEqualJson """
+ verifySerialization(
+ request,
+ McpJson,
+ """
{
"method": "tools/list"
}
- """.trimIndent()
+ """.trimIndent(),
+ )
}
@Test
@@ -377,9 +389,10 @@ class ToolsTest {
meta = buildJsonObject { put("page", 1) },
)
- val json = McpJson.encodeToString(result)
-
- json shouldEqualJson """
+ verifySerialization(
+ result,
+ McpJson,
+ """
{
"tools": [
{
@@ -400,7 +413,8 @@ class ToolsTest {
"page": 1
}
}
- """.trimIndent()
+ """.trimIndent(),
+ )
}
@Test
@@ -423,7 +437,7 @@ class ToolsTest {
}
""".trimIndent()
- val result = McpJson.decodeFromString(json)
+ val result = verifyDeserialization(McpJson, json)
assertEquals("cursor-next", result.nextCursor)
val tools = result.tools
@@ -483,7 +497,7 @@ class ToolsTest {
@Test
fun `should deserialize complex tool definition`() {
val expected = weatherTool()
- val actual = McpJson.decodeFromString(weatherToolJson())
+ val actual = verifyDeserialization(McpJson, weatherToolJson())
assertEquals(expected, actual)
}
diff --git a/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/types/dsl/SamplingDslTest.kt b/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/types/dsl/SamplingDslTest.kt
index 5b60cfb..c53a400 100644
--- a/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/types/dsl/SamplingDslTest.kt
+++ b/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/types/dsl/SamplingDslTest.kt
@@ -28,6 +28,7 @@ import kotlin.test.Test
@OptIn(ExperimentalMcpApi::class)
class SamplingDslTest {
@Test
+ @Suppress("LongMethod")
fun `buildCreateMessageRequest should build with all fields`() {
val request = buildCreateMessageRequest {
maxTokens = 1000
diff --git a/test-utils/build.gradle.kts b/test-utils/build.gradle.kts
index 346001a..91574e0 100644
--- a/test-utils/build.gradle.kts
+++ b/test-utils/build.gradle.kts
@@ -1,24 +1,56 @@
+@file:OptIn(ExperimentalWasmDsl::class)
+
+import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
+
plugins {
- kotlin("jvm")
+ id("mcp.multiplatform")
}
kotlin {
- explicitApi()
- dependencies {
- api(kotlin("test"))
- api(libs.kotest.assertions.core)
- api(libs.kotest.assertions.json)
- api(libs.kotlinx.coroutines.test)
- api(libs.ktor.server.core)
- api(libs.mockk)
- api(libs.awaitility)
- api(libs.junit.jupiter.params)
- api(libs.kotlin.logging)
- runtimeOnly(libs.slf4j.simple)
+ iosArm64()
+ iosX64()
+ iosSimulatorArm64()
+ watchosX64()
+ watchosArm64()
+ watchosSimulatorArm64()
+ tvosX64()
+ tvosArm64()
+ tvosSimulatorArm64()
+ js {
+ browser()
+ nodejs()
+ }
+ wasmJs {
+ browser()
+ nodejs()
}
- tasks.withType {
- useJUnitPlatform()
+ sourceSets {
+ commonMain {
+ dependencies {
+
+ api(kotlin("test"))
+ api(libs.kotest.assertions.core)
+ api(libs.kotest.assertions.json)
+ api(libs.kotlinx.serialization.json)
+ api(libs.kotlinx.coroutines.test)
+ api(libs.ktor.server.core)
+ api(libs.kotlin.logging)
+ }
+ }
+
+ jvmMain {
+ dependencies {
+ api(libs.mockk)
+ api(libs.awaitility)
+ api(libs.junit.jupiter.params)
+ runtimeOnly(libs.slf4j.simple)
+ }
+ }
}
}
+
+tasks.withType {
+ useJUnitPlatform()
+}
diff --git a/test-utils/detekt-baseline.xml b/test-utils/detekt-baseline.xml
index 390ccd0..5abd0b4 100644
--- a/test-utils/detekt-baseline.xml
+++ b/test-utils/detekt-baseline.xml
@@ -3,7 +3,6 @@
CyclomaticComplexMethod:Retry.kt:RetryExtension$private fun executeWithRetry
- ReturnCount:TypeScriptRunner.kt:TypeScriptRunner$public fun installDependencies
ThrowsCount:Retry.kt:RetryExtension$private fun executeWithRetry
diff --git a/test-utils/src/main/kotlin/io/modelcontextprotocol/kotlin/test/utils/ktor.kt b/test-utils/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/test/utils/ktor.kt
similarity index 100%
rename from test-utils/src/main/kotlin/io/modelcontextprotocol/kotlin/test/utils/ktor.kt
rename to test-utils/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/test/utils/ktor.kt
diff --git a/test-utils/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/test/utils/serializationUtils.kt b/test-utils/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/test/utils/serializationUtils.kt
new file mode 100644
index 0000000..8f3d33a
--- /dev/null
+++ b/test-utils/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/test/utils/serializationUtils.kt
@@ -0,0 +1,88 @@
+package io.modelcontextprotocol.kotlin.test.utils
+
+import io.kotest.assertions.json.shouldEqualJson
+import io.kotest.assertions.withClue
+import io.kotest.matchers.shouldBe
+import kotlinx.serialization.SerializationException
+import kotlinx.serialization.json.Json
+import org.intellij.lang.annotations.Language
+import kotlin.test.fail
+
+/**
+ * Verifies the serialization and deserialization process for a given object, ensuring:
+ * - The object can be serialized to JSON.
+ * - The serialized JSON matches the expected JSON.
+ * - The serialized JSON can be deserialized back into the original object.
+ *
+ * This is especially useful for round-trip serialization tests.
+ *
+ * @param T The type of the object to be verified.
+ * @param value The object to be serialized and deserialized.
+ * @param json The JSON serializer/deserializer used for the serialization process.
+ * @param expectedJson A lambda returning the expected JSON string for comparison purposes.
+ */
+public inline fun verifySerialization(value: T, json: Json, expectedJson: () -> String) {
+ verifySerialization(value, json, expectedJson.invoke())
+}
+
+/**
+ * Verifies the serialization and deserialization process for a given object, ensuring:
+ * - The object can be serialized to JSON.
+ * - The serialized JSON matches the expected JSON.
+ * - The serialized JSON can be deserialized back into the original object.
+ *
+ * This is especially useful for round-trip serialization tests.
+ *
+ * @param T The type of the object being tested for serialization and deserialization.
+ * @param value The object to be serialized and deserialized.
+ * @param json The JSON serialization/deserialization processor.
+ * @param expectedJson The expected JSON string to compare against the serialized output.
+ */
+public inline fun verifySerialization(value: T, json: Json, @Language("json") expectedJson: String) {
+ val jsonString = json.encodeToString(value)
+
+ jsonString shouldEqualJson expectedJson
+
+ val parsedObject: T = try {
+ json.decodeFromString(jsonString)
+ } catch (e: SerializationException) {
+ fail("Failed to parse generated json string: ${e.message}:\n$value", e)
+ }
+ withClue("Parsed object is not equal to original value") {
+ parsedObject shouldBe value
+ }
+}
+
+/**
+ * Verifies the deserialization of a JSON payload into an object of the specified type,
+ * and ensures that the reverse serialization process returns the same JSON payload.
+ *
+ * This is especially useful for round-trip serialization tests.
+ *
+ * @param T The type of the object being deserialized from the JSON payload.
+ * @param json The JSON processor used for deserialization and serialization.
+ * @param payload The JSON string to deserialize into an object and verify against.
+ * @return The deserialized object of type T.
+ */
+public inline fun verifyDeserialization(json: Json, @Language("json") payload: String): T {
+ val value: T = json.decodeFromString(payload)
+ verifySerialization(value = value, json = json, expectedJson = payload)
+ return value
+}
+
+/**
+ * Verifies the round-trip serialization and deserialization process for a given object.
+ * This method ensures that:
+ * - The object can be serialized into a JSON string.
+ * - The serialized JSON string can be deserialized back into an equivalent object.
+ *
+ * This is particularly useful for testing the integrity of serialization logic.
+ *
+ * @param T The type of the object being tested for serialization and deserialization.
+ * @param value The object to be serialized and deserialized.
+ * @param json The JSON processor used for the serialization and deserialization process.
+ */
+public inline fun verifySerializationRoundTrip(value: T, json: Json) {
+ val jsonString: String = json.encodeToString(value)
+ verifySerialization(value = value, json = json, expectedJson = jsonString)
+}
diff --git a/test-utils/src/main/kotlin/io/modelcontextprotocol/kotlin/test/utils/DisabledOnCI.kt b/test-utils/src/jvmMain/kotlin/io/modelcontextprotocol/kotlin/test/utils/DisabledOnCI.kt
similarity index 100%
rename from test-utils/src/main/kotlin/io/modelcontextprotocol/kotlin/test/utils/DisabledOnCI.kt
rename to test-utils/src/jvmMain/kotlin/io/modelcontextprotocol/kotlin/test/utils/DisabledOnCI.kt
diff --git a/test-utils/src/main/kotlin/io/modelcontextprotocol/kotlin/test/utils/Retry.kt b/test-utils/src/jvmMain/kotlin/io/modelcontextprotocol/kotlin/test/utils/Retry.kt
similarity index 96%
rename from test-utils/src/main/kotlin/io/modelcontextprotocol/kotlin/test/utils/Retry.kt
rename to test-utils/src/jvmMain/kotlin/io/modelcontextprotocol/kotlin/test/utils/Retry.kt
index e173f3f..a37e6e3 100644
--- a/test-utils/src/main/kotlin/io/modelcontextprotocol/kotlin/test/utils/Retry.kt
+++ b/test-utils/src/jvmMain/kotlin/io/modelcontextprotocol/kotlin/test/utils/Retry.kt
@@ -16,6 +16,7 @@ import java.util.Optional
@ExtendWith(RetryExtension::class)
public annotation class Retry(val times: Int = 3, val delayMs: Long = 1000)
+@Suppress("ForbiddenVoid") // needed for Java API interop
public class RetryExtension : InvocationInterceptor {
override fun interceptTestMethod(
invocation: Invocation,
@@ -87,7 +88,7 @@ public class RetryExtension : InvocationInterceptor {
}
}
- throw lastError ?: IllegalStateException("Unexpected state in retry logic")
+ throw lastError ?: error("Unexpected state in retry logic")
}
private fun describeTest(ctx: ExtensionContext): String {
diff --git a/test-utils/src/main/kotlin/io/modelcontextprotocol/kotlin/test/utils/TypeScriptRunner.kt b/test-utils/src/jvmMain/kotlin/io/modelcontextprotocol/kotlin/test/utils/TypeScriptRunner.kt
similarity index 98%
rename from test-utils/src/main/kotlin/io/modelcontextprotocol/kotlin/test/utils/TypeScriptRunner.kt
rename to test-utils/src/jvmMain/kotlin/io/modelcontextprotocol/kotlin/test/utils/TypeScriptRunner.kt
index ff9292e..e7f5798 100644
--- a/test-utils/src/main/kotlin/io/modelcontextprotocol/kotlin/test/utils/TypeScriptRunner.kt
+++ b/test-utils/src/jvmMain/kotlin/io/modelcontextprotocol/kotlin/test/utils/TypeScriptRunner.kt
@@ -18,6 +18,7 @@ public object TypeScriptRunner {
* @param logPrefix Prefix for log lines
* @return The started Process
*/
+ @Suppress("LongParameterList")
public fun run(
typescriptDir: File,
scriptPath: String,
@@ -53,6 +54,7 @@ public object TypeScriptRunner {
* @param typescriptDir The directory containing package.json
* @throws RuntimeException if npm install fails
*/
+ @Suppress("ReturnCount")
public fun installDependencies(typescriptDir: File) {
require(typescriptDir.isDirectory()) { "Type script directory does not exist: ${typescriptDir.absolutePath}" }
println("Installing TypeScript dependencies in ${typescriptDir.absolutePath}")
diff --git a/test-utils/src/main/kotlin/io/modelcontextprotocol/kotlin/test/utils/platform.kt b/test-utils/src/jvmMain/kotlin/io/modelcontextprotocol/kotlin/test/utils/platform.jvm.kt
similarity index 100%
rename from test-utils/src/main/kotlin/io/modelcontextprotocol/kotlin/test/utils/platform.kt
rename to test-utils/src/jvmMain/kotlin/io/modelcontextprotocol/kotlin/test/utils/platform.jvm.kt
diff --git a/test-utils/src/main/kotlin/io/modelcontextprotocol/kotlin/test/utils/processBuilders.kt b/test-utils/src/jvmMain/kotlin/io/modelcontextprotocol/kotlin/test/utils/processBuilders.kt
similarity index 100%
rename from test-utils/src/main/kotlin/io/modelcontextprotocol/kotlin/test/utils/processBuilders.kt
rename to test-utils/src/jvmMain/kotlin/io/modelcontextprotocol/kotlin/test/utils/processBuilders.kt
diff --git a/test-utils/src/main/kotlin/io/modelcontextprotocol/kotlin/test/utils/processUtils.kt b/test-utils/src/jvmMain/kotlin/io/modelcontextprotocol/kotlin/test/utils/processUtils.kt
similarity index 100%
rename from test-utils/src/main/kotlin/io/modelcontextprotocol/kotlin/test/utils/processUtils.kt
rename to test-utils/src/jvmMain/kotlin/io/modelcontextprotocol/kotlin/test/utils/processUtils.kt