refactor: Refactor serialization tests (#527)

1. Migrate test-utils module to multiplatform, add serialization
utilities

- Migrated `test-utils` to support Kotlin Multiplatform (JVM, JS,
Native).
- Added `verifySerialization`, `verifyDeserialization`,
`verifySerializationRoundTrip` utilities to streamline and validate JSON
serialization/deserialization

2. Refactor serialization tests to use test serialization utilities

- Updated serialization tests across the project to use
`verifySerialization` and `verifyDeserialization` utilities.
- Improved consistency and ensured proper round-trip validation for JSON
deserialization.

3. Remove some detekt baseline files and suppress `LongMethod` findings

    - Deleted detekt baseline XML files for test sources.
- Suppressed `LongMethod` findings in relevant tests using `@Suppress`
annotations.
- Replaced empty method implementations with explicit `// noop` comments
for clarity.
    - Adjusted min test coverage threshold in `build.gradle.kts`.

## Motivation and Context
To improve serialization testing

## How Has This Been Tested?
Unit tests

## Breaking Changes
No

## Types of changes

- [ ] Bug fix (non-breaking change which fixes an issue)
- [x] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing
functionality to change)
- [ ] Documentation update
- [x] Refactoring

## Checklist
<!-- Go over all the following points, and put an `x` in all the boxes
that apply. -->
- [x] I have read the [MCP
Documentation](https://modelcontextprotocol.io)
- [x] My code follows the repository's style guidelines
- [x] New and existing tests pass locally
- [ ] I have added appropriate error handling
- [ ] I have added or updated documentation as needed
This commit is contained in:
Konstantin Pavlov 2026-02-17 12:16:02 +02:00 • committed by GitHub
commit 367618d109
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
21 changed files with 858 additions and 559 deletions

View file

@ -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)
}
}

View file

@ -1,12 +0,0 @@
<?xml version='1.0' encoding='UTF-8'?>
<SmellBaseline>
<ManuallySuppressedIssues/>
<CurrentIssues>
<ID>EmptyFunctionBlock:ProtocolTest.kt:RecordingTransport${}</ID>
<ID>EmptyFunctionBlock:ProtocolTest.kt:TestProtocol${}</ID>
<ID>LongMethod:ElicitationTest.kt:ElicitationTest$@Test fun `should serialize ElicitRequest with requested schema`</ID>
<ID>LongMethod:SamplingDslTest.kt:SamplingDslTest$@Test fun `buildCreateMessageRequest should build with all fields`</ID>
<ID>LongMethod:SamplingTest.kt:SamplingTest$@Test fun `should serialize CreateMessageRequest with all fields`</ID>
<ID>LongMethod:ToolsTest.kt:ToolsTest$@Test fun `should serialize Tool with annotations and schemas`</ID>
</CurrentIssues>
</SmellBaseline>

View file

@ -1,12 +0,0 @@
<?xml version='1.0' encoding='UTF-8'?>
<SmellBaseline>
<ManuallySuppressedIssues/>
<CurrentIssues>
<ID>EmptyFunctionBlock:ProtocolTest.kt:RecordingTransport${}</ID>
<ID>EmptyFunctionBlock:ProtocolTest.kt:TestProtocol${}</ID>
<ID>LongMethod:ElicitationTest.kt:ElicitationTest$@Test fun `should serialize ElicitRequest with requested schema`</ID>
<ID>LongMethod:SamplingDslTest.kt:SamplingDslTest$@Test fun `buildCreateMessageRequest should build with all fields`</ID>
<ID>LongMethod:SamplingTest.kt:SamplingTest$@Test fun `should serialize CreateMessageRequest with all fields`</ID>
<ID>LongMethod:ToolsTest.kt:ToolsTest$@Test fun `should serialize Tool with annotations and schemas`</ID>
</CurrentIssues>
</SmellBaseline>

View file

@ -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

View file

@ -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<Implementation>(json)
val implementation = verifyDeserialization<Implementation>(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<Implementation>(json)
val implementation = verifyDeserialization<Implementation>(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<Implementation>(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<ClientCapabilities>(json)
val capabilities = verifyDeserialization<ClientCapabilities>(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<ClientCapabilities>(json)
val capabilities = verifyDeserialization<ClientCapabilities>(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<ClientCapabilities>(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<ServerCapabilities>(json)
val capabilities = verifyDeserialization<ServerCapabilities>(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<ServerCapabilities>(json)
val capabilities = verifyDeserialization<ServerCapabilities>(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<ServerCapabilities>(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<ClientCapabilities>(json)
val capabilities = verifyDeserialization<ClientCapabilities>(McpJson, json)
assertNull(capabilities.roots?.listChanged)
}
@ -594,7 +619,7 @@ class CapabilitiesTest {
}
""".trimIndent()
val capabilities = McpJson.decodeFromString<ServerCapabilities>(json)
val capabilities = verifyDeserialization<ServerCapabilities>(McpJson, json)
assertNull(capabilities.tools?.listChanged)
assertNull(capabilities.resources?.listChanged)
@ -612,7 +637,7 @@ class CapabilitiesTest {
}
""".trimIndent()
val capabilities = McpJson.decodeFromString<ClientCapabilities>(json)
val capabilities = verifyDeserialization<ClientCapabilities>(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<ServerCapabilities>(json)
val capabilities = verifyDeserialization<ServerCapabilities>(McpJson, json)
// Should not fail - additionalProperties are allowed
assertEquals("debug", capabilities.logging?.get("level")?.toString()?.trim('"'))

View file

@ -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<List<String>>(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<Icon>(json)
val icon = verifyDeserialization<Icon>(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<Icon>(json)
val icon = verifyDeserialization<Icon>(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<Role>("\"user\"")
val assistant = McpJson.decodeFromString<Role>("\"assistant\"")
val user = verifyDeserialization<Role>(McpJson, "\"user\"")
val assistant = verifyDeserialization<Role>(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<Annotations>(json)
val annotations = verifyDeserialization<Annotations>(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<Annotations>(json)
assertEquals(original.audience, decoded.audience)
assertEquals(original.priority, decoded.priority)
assertEquals(original.lastModified, decoded.lastModified)
verifySerializationRoundTrip(original, McpJson)
}
}

View file

@ -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<CompleteRequest>(json)
val request = verifyDeserialization<CompleteRequest>(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<CompleteResult>(json)
val result = verifyDeserialization<CompleteResult>(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<CompleteResult>(json)
val decoded = verifyDeserialization<CompleteResult>(McpJson, json)
assertEquals(listOf("foo"), decoded.completion.values)
assertNull(decoded.completion.total)
assertNull(decoded.completion.hasMore)

View file

@ -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<MediaContent>(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<MediaContent>(json) as ImageContent
val content = verifyDeserialization<MediaContent>(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<MediaContent>(json) as AudioContent
val content = verifyDeserialization<MediaContent>(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<ContentBlock>(json) as ResourceLink
val resource = verifyDeserialization<ContentBlock>(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<ContentBlock>(json) as EmbeddedResource
val embedded = verifyDeserialization<ContentBlock>(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<List<ContentBlock>>(json)
val content = verifyDeserialization<List<ContentBlock>>(McpJson, json)
assertEquals(3, content.size)
assertIs<TextContent>(content[0])
assertIs<ResourceLink>(content[1])

View file

@ -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<ElicitRequest>(json)
val request = verifyDeserialization<ElicitRequest>(McpJson, json)
assertEquals(Method.Defined.ElicitationCreate, request.method)
val params = request.params
@ -163,7 +168,7 @@ class ElicitationTest {
}
""".trimIndent()
val decoded = McpJson.decodeFromString<ElicitResult>(json)
val decoded = verifyDeserialization<ElicitResult>(McpJson, json)
assertEquals(ElicitResult.Action.Accept, decoded.action)
val content = decoded.content
@ -188,7 +193,7 @@ class ElicitationTest {
}
""".trimIndent()
val result = McpJson.decodeFromString<ElicitResult>(json)
val result = verifyDeserialization<ElicitResult>(McpJson, json)
assertEquals(ElicitResult.Action.Decline, result.action)
assertNull(result.content)
assertEquals("User skipped", result.meta?.get("reason")?.jsonPrimitive?.content)

View file

@ -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<InitializeRequest>(json)
val request = verifyDeserialization<InitializeRequest>(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<InitializeResult>(json)
val result = verifyDeserialization<InitializeResult>(McpJson, json)
assertEquals("2025-03-26", result.protocolVersion)
assertEquals("result-server", result.serverInfo.name)

View file

@ -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<JSONRPCRequest>(
val jsonRpc = verifyDeserialization<JSONRPCRequest>(
McpJson,
"""
{
"id": 17,
@ -101,7 +104,8 @@ class JsonRpcTest {
@Test
fun `should convert JSONRPCNotification to Notification`() {
val jsonRpc = McpJson.decodeFromString<JSONRPCNotification>(
val jsonRpc = verifyDeserialization<JSONRPCNotification>(
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<JSONRPCRequest>(json)
val request = verifyDeserialization<JSONRPCRequest>(McpJson, json)
val id = request.id
assertIs<RequestId.NumberId>(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<JSONRPCNotification>(json)
val notification = verifyDeserialization<JSONRPCNotification>(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<JSONRPCResponse>(json)
val response = verifyDeserialization<JSONRPCResponse>(McpJson, json)
val result = response.result
assertIs<EmptyResult>(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<JSONRPCError>(json)
val error = verifyDeserialization<JSONRPCError>(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<JSONRPCMessage>(json)
val message = verifyDeserialization<JSONRPCMessage>(McpJson, json)
val request = assertIs<JSONRPCRequest>(message)
assertEquals("sampling/create", request.method)
val params = request.params?.jsonObject
@ -373,7 +385,7 @@ class JsonRpcTest {
}
""".trimIndent()
val message = McpJson.decodeFromString<JSONRPCMessage>(json)
val message = verifyDeserialization<JSONRPCMessage>(McpJson, json)
val error = assertIs<JSONRPCError>(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

View file

@ -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<SetLevelRequest>(json)
val request = verifyDeserialization<SetLevelRequest>(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<LoggingMessageNotification>(json)
val notification = verifyDeserialization<LoggingMessageNotification>(McpJson, json)
assertEquals(Method.Defined.NotificationsMessage, notification.method)
val params = notification.params
@ -153,7 +158,7 @@ class LoggingTest {
}
""".trimIndent()
val notification = McpJson.decodeFromString<LoggingMessageNotification>(json)
val notification = verifyDeserialization<LoggingMessageNotification>(McpJson, json)
val params = notification.params
assertEquals(LoggingLevel.Critical, params.level)
val data = params.data.jsonObject

View file

@ -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<CancelledNotification>(json)
val notification = verifyDeserialization<CancelledNotification>(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<InitializedNotification>(json)
val notification = verifyDeserialization<InitializedNotification>(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<ProgressNotification>(json)
val notification = verifyDeserialization<ProgressNotification>(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<ResourceUpdatedNotification>(json)
val notification = verifyDeserialization<ResourceUpdatedNotification>(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<PromptListChangedNotification>(json)
val notification = verifyDeserialization<PromptListChangedNotification>(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(),
)
}
}

View file

@ -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<JSONRPCRequest>(json)
val jsonRpc = verifyDeserialization<JSONRPCRequest>(McpJson, json)
val request = jsonRpc.fromJSON()
val pingRequest = assertIs<PingRequest>(request)

View file

@ -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<Prompt>(json)
val prompt = verifyDeserialization<Prompt>(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<PromptMessage>(json)
val message = verifyDeserialization<PromptMessage>(McpJson, json)
assertEquals(Role.User, message.role)
val content = message.content
@ -203,7 +213,7 @@ class PromptsTest {
}
""".trimIndent()
val decoded = McpJson.decodeFromString<Reference>(json)
val decoded = verifyDeserialization<Reference>(McpJson, json)
val promptReference = assertIs<PromptReference>(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<GetPromptRequest>(json)
val request = verifyDeserialization<GetPromptRequest>(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<GetPromptResult>(json)
val result = verifyDeserialization<GetPromptResult>(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<ListPromptsResult>(json)
val result = verifyDeserialization<ListPromptsResult>(McpJson, json)
val prompts = result.prompts
assertEquals(1, prompts.size)

View file

@ -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<BaseRequestParams>(json)
val params = verifyDeserialization<BaseRequestParams>(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<PaginatedRequestParams>(json)
val params = verifyDeserialization<PaginatedRequestParams>(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<EmptyResult>(json)
val result = verifyDeserialization<EmptyResult>(McpJson, json)
assertNull(result.meta)
}

View file

@ -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<Resource>(json)
val resource = verifyDeserialization<Resource>(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<ResourceTemplate>(json)
val template = verifyDeserialization<ResourceTemplate>(McpJson, json)
assertEquals("db://users/{id}", template.uriTemplate)
assertEquals("user-record", template.name)
@ -183,7 +191,7 @@ class ResourcesTest {
}
""".trimIndent()
val decoded = McpJson.decodeFromString<Reference>(json)
val decoded = verifyDeserialization<Reference>(McpJson, json)
val templateReference = assertIs<ResourceTemplateReference>(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<ResourceContents>(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<ResourceContents>(json)
val contents = verifyDeserialization<ResourceContents>(McpJson, json)
val blobContents = assertIs<BlobResourceContents>(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<ResourceContents>(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<ListResourcesResult>(json)
val result = verifyDeserialization<ListResourcesResult>(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<ReadResourceResult>(json)
val result = verifyDeserialization<ReadResourceResult>(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<ListResourceTemplatesResult>(json)
val result = verifyDeserialization<ListResourceTemplatesResult>(McpJson, json)
assertEquals("cursor-next", result.nextCursor)
val templates = result.resourceTemplates

View file

@ -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<Root>(json)
val root = verifyDeserialization<Root>(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<ListRootsResult>(json)
val result = verifyDeserialization<ListRootsResult>(McpJson, json)
val roots = result.roots
assertEquals(1, roots.size)

View file

@ -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<CreateMessageRequest>(json)
val request = verifyDeserialization<CreateMessageRequest>(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<CreateMessageResult>(json)
val result = verifyDeserialization<CreateMessageResult>(McpJson, json)
assertEquals(Role.Assistant, result.role)
val text = assertIs<TextContent>(result.content)

View file

@ -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<Tool>(json)
val tool = verifyDeserialization<Tool>(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<Tool>(json)
val tool = verifyDeserialization<Tool>(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<CallToolRequest>(json)
val request = verifyDeserialization<CallToolRequest>(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<CallToolResult>(json)
val result = verifyDeserialization<CallToolResult>(McpJson, json)
assertEquals(true, result.isError)
val text = assertIs<TextContent>(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<ListToolsResult>(json)
val result = verifyDeserialization<ListToolsResult>(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<Tool>(weatherToolJson())
val actual = verifyDeserialization<Tool>(McpJson, weatherToolJson())
assertEquals(expected, actual)
}

Some files were not shown because too many files have changed in this diff Show more