From 0e50346d564aa8f902b02169e8d8ffa9ac2c03e6 Mon Sep 17 00:00:00 2001 From: dstibbe Date: Mon, 25 Aug 2025 16:32:38 +0200 Subject: [PATCH 01/19] Refactored the model to have the ContentBlock types match the defined schema (#158) --- kotlin-sdk-core/api/kotlin-sdk-core.api | 84 ++++++++++--------- .../modelcontextprotocol/kotlin/sdk/types.kt | 49 +++++++---- .../kotlin/sdk/types.util.kt | 22 +++-- .../kotlin/sdk/TypesTest.kt | 18 ++-- .../kotlin/sdk/TypesUtilTest.kt | 8 +- 5 files changed, 107 insertions(+), 74 deletions(-) diff --git a/kotlin-sdk-core/api/kotlin-sdk-core.api b/kotlin-sdk-core/api/kotlin-sdk-core.api index ed9a268c..8b7b246c 100644 --- a/kotlin-sdk-core/api/kotlin-sdk-core.api +++ b/kotlin-sdk-core/api/kotlin-sdk-core.api @@ -29,7 +29,7 @@ public final class io/modelcontextprotocol/kotlin/sdk/Annotations$Companion { public final fun serializer ()Lkotlinx/serialization/KSerializer; } -public final class io/modelcontextprotocol/kotlin/sdk/AudioContent : io/modelcontextprotocol/kotlin/sdk/PromptMessageContentMultimodal { +public final class io/modelcontextprotocol/kotlin/sdk/AudioContent : io/modelcontextprotocol/kotlin/sdk/ContentBlock, io/modelcontextprotocol/kotlin/sdk/CreateMessageResultContent, io/modelcontextprotocol/kotlin/sdk/SamplingMessageContent { public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/AudioContent$Companion; public static final field TYPE Ljava/lang/String; public fun (Ljava/lang/String;Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/Annotations;)V @@ -450,6 +450,15 @@ public final class io/modelcontextprotocol/kotlin/sdk/CompleteResult$Completion$ public final fun serializer ()Lkotlinx/serialization/KSerializer; } +public abstract interface class io/modelcontextprotocol/kotlin/sdk/ContentBlock { + public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/ContentBlock$Companion; + public abstract fun getType ()Ljava/lang/String; +} + +public final class io/modelcontextprotocol/kotlin/sdk/ContentBlock$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + public final class io/modelcontextprotocol/kotlin/sdk/CreateElicitationRequest : io/modelcontextprotocol/kotlin/sdk/ServerRequest, io/modelcontextprotocol/kotlin/sdk/WithMeta { public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/CreateElicitationRequest$Companion; public fun (Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/CreateElicitationRequest$RequestedSchema;Lkotlinx/serialization/json/JsonObject;)V @@ -622,17 +631,17 @@ public final class io/modelcontextprotocol/kotlin/sdk/CreateMessageRequest$Inclu public final class io/modelcontextprotocol/kotlin/sdk/CreateMessageResult : io/modelcontextprotocol/kotlin/sdk/ClientResult { public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/CreateMessageResult$Companion; - public fun (Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/StopReason;Lio/modelcontextprotocol/kotlin/sdk/Role;Lio/modelcontextprotocol/kotlin/sdk/PromptMessageContentMultimodal;Lkotlinx/serialization/json/JsonObject;)V - public synthetic fun (Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/StopReason;Lio/modelcontextprotocol/kotlin/sdk/Role;Lio/modelcontextprotocol/kotlin/sdk/PromptMessageContentMultimodal;Lkotlinx/serialization/json/JsonObject;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/StopReason;Lio/modelcontextprotocol/kotlin/sdk/Role;Lio/modelcontextprotocol/kotlin/sdk/CreateMessageResultContent;Lkotlinx/serialization/json/JsonObject;)V + public synthetic fun (Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/StopReason;Lio/modelcontextprotocol/kotlin/sdk/Role;Lio/modelcontextprotocol/kotlin/sdk/CreateMessageResultContent;Lkotlinx/serialization/json/JsonObject;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun component1 ()Ljava/lang/String; public final fun component2 ()Lio/modelcontextprotocol/kotlin/sdk/StopReason; public final fun component3 ()Lio/modelcontextprotocol/kotlin/sdk/Role; - public final fun component4 ()Lio/modelcontextprotocol/kotlin/sdk/PromptMessageContentMultimodal; + public final fun component4 ()Lio/modelcontextprotocol/kotlin/sdk/CreateMessageResultContent; public final fun component5 ()Lkotlinx/serialization/json/JsonObject; - public final fun copy (Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/StopReason;Lio/modelcontextprotocol/kotlin/sdk/Role;Lio/modelcontextprotocol/kotlin/sdk/PromptMessageContentMultimodal;Lkotlinx/serialization/json/JsonObject;)Lio/modelcontextprotocol/kotlin/sdk/CreateMessageResult; - public static synthetic fun copy$default (Lio/modelcontextprotocol/kotlin/sdk/CreateMessageResult;Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/StopReason;Lio/modelcontextprotocol/kotlin/sdk/Role;Lio/modelcontextprotocol/kotlin/sdk/PromptMessageContentMultimodal;Lkotlinx/serialization/json/JsonObject;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/CreateMessageResult; + public final fun copy (Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/StopReason;Lio/modelcontextprotocol/kotlin/sdk/Role;Lio/modelcontextprotocol/kotlin/sdk/CreateMessageResultContent;Lkotlinx/serialization/json/JsonObject;)Lio/modelcontextprotocol/kotlin/sdk/CreateMessageResult; + public static synthetic fun copy$default (Lio/modelcontextprotocol/kotlin/sdk/CreateMessageResult;Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/StopReason;Lio/modelcontextprotocol/kotlin/sdk/Role;Lio/modelcontextprotocol/kotlin/sdk/CreateMessageResultContent;Lkotlinx/serialization/json/JsonObject;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/CreateMessageResult; public fun equals (Ljava/lang/Object;)Z - public final fun getContent ()Lio/modelcontextprotocol/kotlin/sdk/PromptMessageContentMultimodal; + public final fun getContent ()Lio/modelcontextprotocol/kotlin/sdk/CreateMessageResultContent; public final fun getModel ()Ljava/lang/String; public final fun getRole ()Lio/modelcontextprotocol/kotlin/sdk/Role; public final fun getStopReason ()Lio/modelcontextprotocol/kotlin/sdk/StopReason; @@ -656,6 +665,14 @@ public final class io/modelcontextprotocol/kotlin/sdk/CreateMessageResult$Compan public final fun serializer ()Lkotlinx/serialization/KSerializer; } +public abstract interface class io/modelcontextprotocol/kotlin/sdk/CreateMessageResultContent : io/modelcontextprotocol/kotlin/sdk/ContentBlock { + public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/CreateMessageResultContent$Companion; +} + +public final class io/modelcontextprotocol/kotlin/sdk/CreateMessageResultContent$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + public final class io/modelcontextprotocol/kotlin/sdk/CustomMeta : io/modelcontextprotocol/kotlin/sdk/WithMeta { public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/CustomMeta$Companion; public fun ()V @@ -702,7 +719,7 @@ public final class io/modelcontextprotocol/kotlin/sdk/CustomRequest$Companion { public final fun serializer ()Lkotlinx/serialization/KSerializer; } -public final class io/modelcontextprotocol/kotlin/sdk/EmbeddedResource : io/modelcontextprotocol/kotlin/sdk/PromptMessageContent { +public final class io/modelcontextprotocol/kotlin/sdk/EmbeddedResource : io/modelcontextprotocol/kotlin/sdk/ContentBlock { public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/EmbeddedResource$Companion; public static final field TYPE Ljava/lang/String; public fun (Lio/modelcontextprotocol/kotlin/sdk/ResourceContents;Lio/modelcontextprotocol/kotlin/sdk/Annotations;)V @@ -875,7 +892,7 @@ public final class io/modelcontextprotocol/kotlin/sdk/GetPromptResult$Companion public final fun serializer ()Lkotlinx/serialization/KSerializer; } -public final class io/modelcontextprotocol/kotlin/sdk/ImageContent : io/modelcontextprotocol/kotlin/sdk/PromptMessageContentMultimodal { +public final class io/modelcontextprotocol/kotlin/sdk/ImageContent : io/modelcontextprotocol/kotlin/sdk/ContentBlock, io/modelcontextprotocol/kotlin/sdk/CreateMessageResultContent, io/modelcontextprotocol/kotlin/sdk/SamplingMessageContent { public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/ImageContent$Companion; public static final field TYPE Ljava/lang/String; public fun (Ljava/lang/String;Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/Annotations;)V @@ -1994,13 +2011,13 @@ public final class io/modelcontextprotocol/kotlin/sdk/PromptListChangedNotificat public final class io/modelcontextprotocol/kotlin/sdk/PromptMessage { public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/PromptMessage$Companion; - public fun (Lio/modelcontextprotocol/kotlin/sdk/Role;Lio/modelcontextprotocol/kotlin/sdk/PromptMessageContent;)V + public fun (Lio/modelcontextprotocol/kotlin/sdk/Role;Lio/modelcontextprotocol/kotlin/sdk/ContentBlock;)V public final fun component1 ()Lio/modelcontextprotocol/kotlin/sdk/Role; - public final fun component2 ()Lio/modelcontextprotocol/kotlin/sdk/PromptMessageContent; - public final fun copy (Lio/modelcontextprotocol/kotlin/sdk/Role;Lio/modelcontextprotocol/kotlin/sdk/PromptMessageContent;)Lio/modelcontextprotocol/kotlin/sdk/PromptMessage; - public static synthetic fun copy$default (Lio/modelcontextprotocol/kotlin/sdk/PromptMessage;Lio/modelcontextprotocol/kotlin/sdk/Role;Lio/modelcontextprotocol/kotlin/sdk/PromptMessageContent;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/PromptMessage; + public final fun component2 ()Lio/modelcontextprotocol/kotlin/sdk/ContentBlock; + public final fun copy (Lio/modelcontextprotocol/kotlin/sdk/Role;Lio/modelcontextprotocol/kotlin/sdk/ContentBlock;)Lio/modelcontextprotocol/kotlin/sdk/PromptMessage; + public static synthetic fun copy$default (Lio/modelcontextprotocol/kotlin/sdk/PromptMessage;Lio/modelcontextprotocol/kotlin/sdk/Role;Lio/modelcontextprotocol/kotlin/sdk/ContentBlock;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/PromptMessage; public fun equals (Ljava/lang/Object;)Z - public final fun getContent ()Lio/modelcontextprotocol/kotlin/sdk/PromptMessageContent; + public final fun getContent ()Lio/modelcontextprotocol/kotlin/sdk/ContentBlock; public final fun getRole ()Lio/modelcontextprotocol/kotlin/sdk/Role; public fun hashCode ()I public fun toString ()Ljava/lang/String; @@ -2021,23 +2038,6 @@ public final class io/modelcontextprotocol/kotlin/sdk/PromptMessage$Companion { public final fun serializer ()Lkotlinx/serialization/KSerializer; } -public abstract interface class io/modelcontextprotocol/kotlin/sdk/PromptMessageContent { - public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/PromptMessageContent$Companion; - public abstract fun getType ()Ljava/lang/String; -} - -public final class io/modelcontextprotocol/kotlin/sdk/PromptMessageContent$Companion { - public final fun serializer ()Lkotlinx/serialization/KSerializer; -} - -public abstract interface class io/modelcontextprotocol/kotlin/sdk/PromptMessageContentMultimodal : io/modelcontextprotocol/kotlin/sdk/PromptMessageContent { - public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/PromptMessageContentMultimodal$Companion; -} - -public final class io/modelcontextprotocol/kotlin/sdk/PromptMessageContentMultimodal$Companion { - public final fun serializer ()Lkotlinx/serialization/KSerializer; -} - public final class io/modelcontextprotocol/kotlin/sdk/PromptReference : io/modelcontextprotocol/kotlin/sdk/Reference { public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/PromptReference$Companion; public static final field TYPE Ljava/lang/String; @@ -2558,13 +2558,13 @@ public final class io/modelcontextprotocol/kotlin/sdk/RootsListChangedNotificati public final class io/modelcontextprotocol/kotlin/sdk/SamplingMessage { public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/SamplingMessage$Companion; - public fun (Lio/modelcontextprotocol/kotlin/sdk/Role;Lio/modelcontextprotocol/kotlin/sdk/PromptMessageContentMultimodal;)V + public fun (Lio/modelcontextprotocol/kotlin/sdk/Role;Lio/modelcontextprotocol/kotlin/sdk/SamplingMessageContent;)V public final fun component1 ()Lio/modelcontextprotocol/kotlin/sdk/Role; - public final fun component2 ()Lio/modelcontextprotocol/kotlin/sdk/PromptMessageContentMultimodal; - public final fun copy (Lio/modelcontextprotocol/kotlin/sdk/Role;Lio/modelcontextprotocol/kotlin/sdk/PromptMessageContentMultimodal;)Lio/modelcontextprotocol/kotlin/sdk/SamplingMessage; - public static synthetic fun copy$default (Lio/modelcontextprotocol/kotlin/sdk/SamplingMessage;Lio/modelcontextprotocol/kotlin/sdk/Role;Lio/modelcontextprotocol/kotlin/sdk/PromptMessageContentMultimodal;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/SamplingMessage; + public final fun component2 ()Lio/modelcontextprotocol/kotlin/sdk/SamplingMessageContent; + public final fun copy (Lio/modelcontextprotocol/kotlin/sdk/Role;Lio/modelcontextprotocol/kotlin/sdk/SamplingMessageContent;)Lio/modelcontextprotocol/kotlin/sdk/SamplingMessage; + public static synthetic fun copy$default (Lio/modelcontextprotocol/kotlin/sdk/SamplingMessage;Lio/modelcontextprotocol/kotlin/sdk/Role;Lio/modelcontextprotocol/kotlin/sdk/SamplingMessageContent;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/SamplingMessage; public fun equals (Ljava/lang/Object;)Z - public final fun getContent ()Lio/modelcontextprotocol/kotlin/sdk/PromptMessageContentMultimodal; + public final fun getContent ()Lio/modelcontextprotocol/kotlin/sdk/SamplingMessageContent; public final fun getRole ()Lio/modelcontextprotocol/kotlin/sdk/Role; public fun hashCode ()I public fun toString ()Ljava/lang/String; @@ -2585,6 +2585,14 @@ public final class io/modelcontextprotocol/kotlin/sdk/SamplingMessage$Companion public final fun serializer ()Lkotlinx/serialization/KSerializer; } +public abstract interface class io/modelcontextprotocol/kotlin/sdk/SamplingMessageContent : io/modelcontextprotocol/kotlin/sdk/ContentBlock { + public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/SamplingMessageContent$Companion; +} + +public final class io/modelcontextprotocol/kotlin/sdk/SamplingMessageContent$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + public final class io/modelcontextprotocol/kotlin/sdk/ServerCapabilities { public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/ServerCapabilities$Companion; public fun ()V @@ -2823,7 +2831,7 @@ public final class io/modelcontextprotocol/kotlin/sdk/SubscribeRequest$Companion public final fun serializer ()Lkotlinx/serialization/KSerializer; } -public final class io/modelcontextprotocol/kotlin/sdk/TextContent : io/modelcontextprotocol/kotlin/sdk/PromptMessageContentMultimodal { +public final class io/modelcontextprotocol/kotlin/sdk/TextContent : io/modelcontextprotocol/kotlin/sdk/ContentBlock, io/modelcontextprotocol/kotlin/sdk/CreateMessageResultContent, io/modelcontextprotocol/kotlin/sdk/SamplingMessageContent { public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/TextContent$Companion; public static final field TYPE Ljava/lang/String; public fun ()V @@ -3100,7 +3108,7 @@ public final class io/modelcontextprotocol/kotlin/sdk/Types_utilKt { public static synthetic fun ok$default (Lio/modelcontextprotocol/kotlin/sdk/CallToolResult$Companion;Ljava/lang/String;Lkotlinx/serialization/json/JsonObject;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/CallToolResult; } -public final class io/modelcontextprotocol/kotlin/sdk/UnknownContent : io/modelcontextprotocol/kotlin/sdk/PromptMessageContentMultimodal { +public final class io/modelcontextprotocol/kotlin/sdk/UnknownContent : io/modelcontextprotocol/kotlin/sdk/ContentBlock, io/modelcontextprotocol/kotlin/sdk/CreateMessageResultContent, io/modelcontextprotocol/kotlin/sdk/SamplingMessageContent { public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/UnknownContent$Companion; public fun (Ljava/lang/String;)V public final fun component1 ()Ljava/lang/String; diff --git a/kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/types.kt b/kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/types.kt index b6879241..53e86077 100644 --- a/kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/types.kt +++ b/kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/types.kt @@ -971,18 +971,24 @@ public data class GetPromptRequest( } /** - * Represents the content of a prompt message. + * Represents the types of a ContentBlock */ -@Serializable(with = PromptMessageContentPolymorphicSerializer::class) -public sealed interface PromptMessageContent { +@Serializable(with = ContentBlockPolymorphicSerializer::class) +public sealed interface ContentBlock { public val type: String } /** - * Represents prompt message content that is either text, image or audio. + * Represents content for the CreateMessageResult */ -@Serializable(with = PromptMessageContentMultimodalPolymorphicSerializer::class) -public sealed interface PromptMessageContentMultimodal : PromptMessageContent +@Serializable(with = CreateMessageResultContentMultimodalPolymorphicSerializer::class) +public sealed interface CreateMessageResultContent : ContentBlock + +/** + * Represents content for the SamplingMessage + */ +@Serializable(with = SamplingMessageContentMultimodalPolymorphicSerializer::class) +public sealed interface SamplingMessageContent : ContentBlock /** * Text provided to or from an LLM. @@ -998,7 +1004,9 @@ public data class TextContent( * Optional annotations for the client. */ val annotations: Annotations? = null, -) : PromptMessageContentMultimodal { +) : ContentBlock, + CreateMessageResultContent, + SamplingMessageContent { override val type: String = TYPE public companion object { @@ -1025,7 +1033,9 @@ public data class ImageContent( * Optional annotations for the client. */ val annotations: Annotations? = null, -) : PromptMessageContentMultimodal { +) : ContentBlock, + CreateMessageResultContent, + SamplingMessageContent { override val type: String = TYPE public companion object { @@ -1052,7 +1062,9 @@ public data class AudioContent( * Optional annotations for the client. */ val annotations: Annotations? = null, -) : PromptMessageContentMultimodal { +) : ContentBlock, + CreateMessageResultContent, + SamplingMessageContent { override val type: String = TYPE public companion object { @@ -1064,7 +1076,10 @@ public data class AudioContent( * Unknown content provided to or from an LLM. */ @Serializable -public data class UnknownContent(override val type: String) : PromptMessageContentMultimodal +public data class UnknownContent(override val type: String) : + ContentBlock, + CreateMessageResultContent, + SamplingMessageContent /** * The contents of a resource, embedded into a prompt or tool call result. @@ -1080,7 +1095,7 @@ public data class EmbeddedResource( * Optional annotations for the client. */ val annotations: Annotations? = null, -) : PromptMessageContent { +) : ContentBlock { override val type: String = TYPE public companion object { @@ -1130,7 +1145,7 @@ public data class Annotations( * Describes a message returned as part of a prompt. */ @Serializable -public data class PromptMessage(val role: Role, val content: PromptMessageContent) +public data class PromptMessage(val role: Role, val content: ContentBlock) /** * The server's response to a prompts/get request from the client. @@ -1282,7 +1297,7 @@ public class ListToolsResult( */ @Serializable public sealed interface CallToolResultBase : ServerResult { - public val content: List + public val content: List public val structuredContent: JsonObject? public val isError: Boolean? get() = false } @@ -1292,7 +1307,7 @@ public sealed interface CallToolResultBase : ServerResult { */ @Serializable public class CallToolResult( - override val content: List, + override val content: List, override val structuredContent: JsonObject? = null, override val isError: Boolean? = false, override val _meta: JsonObject = EmptyJsonObject, @@ -1303,7 +1318,7 @@ public class CallToolResult( */ @Serializable public class CompatibilityCallToolResult( - override val content: List, + override val content: List, override val structuredContent: JsonObject? = null, override val isError: Boolean? = false, override val _meta: JsonObject = EmptyJsonObject, @@ -1448,7 +1463,7 @@ public class ModelPreferences( * Describes a message issued to or received from an LLM API. */ @Serializable -public data class SamplingMessage(val role: Role, val content: PromptMessageContentMultimodal) +public data class SamplingMessage(val role: Role, val content: SamplingMessageContent) /** * A request from the server to sample an LLM via the client. @@ -1530,7 +1545,7 @@ public data class CreateMessageResult( */ val stopReason: StopReason? = null, val role: Role, - val content: PromptMessageContentMultimodal, + val content: CreateMessageResultContent, override val _meta: JsonObject = EmptyJsonObject, ) : ClientResult diff --git a/kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/types.util.kt b/kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/types.util.kt index 07a0bf4c..14129e6b 100644 --- a/kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/types.util.kt +++ b/kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/types.util.kt @@ -81,9 +81,9 @@ internal object ReferencePolymorphicSerializer : JsonContentPolymorphicSerialize } } -internal object PromptMessageContentPolymorphicSerializer : - JsonContentPolymorphicSerializer(PromptMessageContent::class) { - override fun selectDeserializer(element: JsonElement): DeserializationStrategy = +internal object ContentBlockPolymorphicSerializer : + JsonContentPolymorphicSerializer(ContentBlock::class) { + override fun selectDeserializer(element: JsonElement): DeserializationStrategy = when (element.jsonObject.getValue("type").jsonPrimitive.content) { ImageContent.TYPE -> ImageContent.serializer() TextContent.TYPE -> TextContent.serializer() @@ -93,9 +93,19 @@ internal object PromptMessageContentPolymorphicSerializer : } } -internal object PromptMessageContentMultimodalPolymorphicSerializer : - JsonContentPolymorphicSerializer(PromptMessageContentMultimodal::class) { - override fun selectDeserializer(element: JsonElement): DeserializationStrategy = +internal object CreateMessageResultContentMultimodalPolymorphicSerializer : + JsonContentPolymorphicSerializer(CreateMessageResultContent::class) { + override fun selectDeserializer(element: JsonElement): DeserializationStrategy = + when (element.jsonObject.getValue("type").jsonPrimitive.content) { + ImageContent.TYPE -> ImageContent.serializer() + TextContent.TYPE -> TextContent.serializer() + AudioContent.TYPE -> AudioContent.serializer() + else -> UnknownContent.serializer() + } +} +internal object SamplingMessageContentMultimodalPolymorphicSerializer : + JsonContentPolymorphicSerializer(SamplingMessageContent::class) { + override fun selectDeserializer(element: JsonElement): DeserializationStrategy = when (element.jsonObject.getValue("type").jsonPrimitive.content) { ImageContent.TYPE -> ImageContent.serializer() TextContent.TYPE -> TextContent.serializer() diff --git a/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/TypesTest.kt b/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/TypesTest.kt index 23e2ffb7..cd47852a 100644 --- a/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/TypesTest.kt +++ b/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/TypesTest.kt @@ -94,8 +94,8 @@ class TypesTest { fun `should serialize and deserialize text content correctly`() { val textContent = TextContent(text = "Test message") - val json = McpJson.encodeToString(textContent) - val decoded = McpJson.decodeFromString(json) + val json = McpJson.encodeToString(textContent) + val decoded = McpJson.decodeFromString(json) assertIs(decoded) assertEquals("text", decoded.type) @@ -121,8 +121,8 @@ class TypesTest { mimeType = "image/jpeg", ) - val json = McpJson.encodeToString(imageContent) - val decoded = McpJson.decodeFromString(json) + val json = McpJson.encodeToString(imageContent) + val decoded = McpJson.decodeFromString(json) assertIs(decoded) assertEquals("image", decoded.type) @@ -149,8 +149,8 @@ class TypesTest { mimeType = "audio/wav", ) - val json = McpJson.encodeToString(audioContent) - val decoded = McpJson.decodeFromString(json) + val json = McpJson.encodeToString(audioContent) + val decoded = McpJson.decodeFromString(json) assertIs(decoded) assertEquals("audio", decoded.type) @@ -180,8 +180,8 @@ class TypesTest { ) val embeddedResource = EmbeddedResource(resource = resource) - val json = McpJson.encodeToString(embeddedResource) - val decoded = McpJson.decodeFromString(json) + val json = McpJson.encodeToString(embeddedResource) + val decoded = McpJson.decodeFromString(json) assertIs(decoded) assertEquals("resource", decoded.type) @@ -196,7 +196,7 @@ class TypesTest { fun `should handle unknown content type`() { val unknownJson = """{"type": "unknown_type"}""" - val decoded = McpJson.decodeFromString(unknownJson) + val decoded = McpJson.decodeFromString(unknownJson) assertIs(decoded) assertEquals("unknown_type", decoded.type) diff --git a/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/TypesUtilTest.kt b/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/TypesUtilTest.kt index 5207af35..04e9d769 100644 --- a/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/TypesUtilTest.kt +++ b/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/TypesUtilTest.kt @@ -116,7 +116,7 @@ class TypesUtilTest { fun `should deserialize TextContent polymorphically`() { val json = """{"type": "text", "text": "Hello world"}""" - val decoded = McpJson.decodeFromString(json) + val decoded = McpJson.decodeFromString(json) assertIs(decoded) assertEquals("text", decoded.type) @@ -127,7 +127,7 @@ class TypesUtilTest { fun `should deserialize ImageContent polymorphically`() { val json = """{"type": "image", "data": "aW1hZ2U=", "mimeType": "image/png"}""" - val decoded = McpJson.decodeFromString(json) + val decoded = McpJson.decodeFromString(json) assertIs(decoded) assertEquals("image", decoded.type) @@ -139,7 +139,7 @@ class TypesUtilTest { fun `should deserialize AudioContent polymorphically`() { val json = """{"type": "audio", "data": "YXVkaW8=", "mimeType": "audio/mp3"}""" - val decoded = McpJson.decodeFromString(json) + val decoded = McpJson.decodeFromString(json) assertIs(decoded) assertEquals("audio", decoded.type) @@ -152,7 +152,7 @@ class TypesUtilTest { val json = """{"type": "resource", "resource": {"uri": "file:///test.txt", "mimeType": "text/plain", "text": "content"}}""" - val decoded = McpJson.decodeFromString(json) + val decoded = McpJson.decodeFromString(json) assertIs(decoded) assertEquals("resource", decoded.type) From 610b9d6bfd7ca7cedc00397a60354f18a51526cb Mon Sep 17 00:00:00 2001 From: dstibbe Date: Thu, 4 Sep 2025 11:12:06 +0200 Subject: [PATCH 02/19] Updated API (#158) --- kotlin-sdk-core/api/kotlin-sdk-core.api | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/kotlin-sdk-core/api/kotlin-sdk-core.api b/kotlin-sdk-core/api/kotlin-sdk-core.api index 8b7b246c..4b3d29a9 100644 --- a/kotlin-sdk-core/api/kotlin-sdk-core.api +++ b/kotlin-sdk-core/api/kotlin-sdk-core.api @@ -2038,6 +2038,23 @@ public final class io/modelcontextprotocol/kotlin/sdk/PromptMessage$Companion { public final fun serializer ()Lkotlinx/serialization/KSerializer; } +public abstract interface class io/modelcontextprotocol/kotlin/sdk/PromptMessageContent { + public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/PromptMessageContent$Companion; + public abstract fun getType ()Ljava/lang/String; +} + +public final class io/modelcontextprotocol/kotlin/sdk/PromptMessageContent$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + +public abstract interface class io/modelcontextprotocol/kotlin/sdk/PromptMessageContentMultimodal : io/modelcontextprotocol/kotlin/sdk/PromptMessageContent { + public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/PromptMessageContentMultimodal$Companion; +} + +public final class io/modelcontextprotocol/kotlin/sdk/PromptMessageContentMultimodal$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + public final class io/modelcontextprotocol/kotlin/sdk/PromptReference : io/modelcontextprotocol/kotlin/sdk/Reference { public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/PromptReference$Companion; public static final field TYPE Ljava/lang/String; From 3d182bcf3e2f9e34b8b0ad4cc21b4fc997d0fddd Mon Sep 17 00:00:00 2001 From: dstibbe Date: Thu, 4 Sep 2025 11:58:27 +0200 Subject: [PATCH 03/19] Added ResourceLink type (#158) --- kotlin-sdk-core/api/kotlin-sdk-core.api | 42 ++++++++++++++ .../modelcontextprotocol/kotlin/sdk/types.kt | 56 +++++++++++++++++++ 2 files changed, 98 insertions(+) diff --git a/kotlin-sdk-core/api/kotlin-sdk-core.api b/kotlin-sdk-core/api/kotlin-sdk-core.api index 4b3d29a9..2e1afd5b 100644 --- a/kotlin-sdk-core/api/kotlin-sdk-core.api +++ b/kotlin-sdk-core/api/kotlin-sdk-core.api @@ -2285,6 +2285,48 @@ public final class io/modelcontextprotocol/kotlin/sdk/ResourceContents$Companion public final fun serializer ()Lkotlinx/serialization/KSerializer; } +public final class io/modelcontextprotocol/kotlin/sdk/ResourceLink : io/modelcontextprotocol/kotlin/sdk/ContentBlock { + public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/ResourceLink$Companion; + public static final field TYPE Ljava/lang/String; + public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/Long;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/Annotations;)V + public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/Long;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/Annotations;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Ljava/lang/String; + public final fun component2 ()Ljava/lang/String; + public final fun component3 ()Ljava/lang/Long; + public final fun component4 ()Ljava/lang/String; + public final fun component5 ()Ljava/lang/String; + public final fun component6 ()Ljava/lang/String; + public final fun component7 ()Lio/modelcontextprotocol/kotlin/sdk/Annotations; + public final fun copy (Ljava/lang/String;Ljava/lang/String;Ljava/lang/Long;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/Annotations;)Lio/modelcontextprotocol/kotlin/sdk/ResourceLink; + public static synthetic fun copy$default (Lio/modelcontextprotocol/kotlin/sdk/ResourceLink;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Long;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/Annotations;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/ResourceLink; + public fun equals (Ljava/lang/Object;)Z + public final fun getAnnotations ()Lio/modelcontextprotocol/kotlin/sdk/Annotations; + public final fun getDescription ()Ljava/lang/String; + public final fun getMimeType ()Ljava/lang/String; + public final fun getName ()Ljava/lang/String; + public final fun getSize ()Ljava/lang/Long; + public final fun getTitle ()Ljava/lang/String; + public fun getType ()Ljava/lang/String; + public final fun getUri ()Ljava/lang/String; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final synthetic class io/modelcontextprotocol/kotlin/sdk/ResourceLink$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lio/modelcontextprotocol/kotlin/sdk/ResourceLink$$serializer; + public final fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lio/modelcontextprotocol/kotlin/sdk/ResourceLink; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lio/modelcontextprotocol/kotlin/sdk/ResourceLink;)V + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class io/modelcontextprotocol/kotlin/sdk/ResourceLink$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + public final class io/modelcontextprotocol/kotlin/sdk/ResourceListChangedNotification : io/modelcontextprotocol/kotlin/sdk/ServerNotification { public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/ResourceListChangedNotification$Companion; public fun ()V diff --git a/kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/types.kt b/kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/types.kt index 53e86077..edf3001c 100644 --- a/kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/types.kt +++ b/kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/types.kt @@ -1072,6 +1072,62 @@ public data class AudioContent( } } +/** + * A Resource Link provided to or from an LLM. + */ +@Serializable +public data class ResourceLink( + /** + * A description of what this resource represents. + * + * This can be used by clients to improve the LLM’s understanding of available resources. It can be thought of like a “hint” to the model. + * + */ + val description: String? = null, + + /** + * Intended for programmatic or logical use, but used as a display name in past specs or fallback (if title isn’t present). + */ + val name: String, + + /** + * The size of the raw resource content, in bytes (i.e., before base64 encoding or any tokenization), if known. + * + * This can be used by Hosts to display file sizes and estimate context window usage. + * + */ + val size: Long? = null, + + /** + * Intended for UI and end-user contexts — optimized to be human-readable and easily understood, even by those unfamiliar with domain-specific terminology. + * + * If not provided, the name should be used for display (except for Tool, where annotations.title should be given precedence over using name, if present). + * + */ + val title: String? = null, + + /** + * The URI of this resource. + */ + val uri: String, + + /** + * The MIME type of this resource, if known. + */ + val mimeType: String, + + /** + * Optional annotations for the client. + */ + val annotations: Annotations? = null, +) : ContentBlock { + override val type: String = TYPE + + public companion object { + public const val TYPE: String = "resource_link" + } +} + /** * Unknown content provided to or from an LLM. */ From f6c8843bdcb09a4544d2dab4ff2d494f4397de48 Mon Sep 17 00:00:00 2001 From: dstibbe Date: Thu, 4 Sep 2025 12:09:47 +0200 Subject: [PATCH 04/19] Added ResourceLink serializer (#158) --- .../kotlin/io/modelcontextprotocol/kotlin/sdk/types.util.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/types.util.kt b/kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/types.util.kt index 14129e6b..3cad0891 100644 --- a/kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/types.util.kt +++ b/kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/types.util.kt @@ -89,6 +89,7 @@ internal object ContentBlockPolymorphicSerializer : TextContent.TYPE -> TextContent.serializer() EmbeddedResource.TYPE -> EmbeddedResource.serializer() AudioContent.TYPE -> AudioContent.serializer() + ResourceLink.TYPE -> ResourceLink.serializer() else -> UnknownContent.serializer() } } From cf7976d3159f9fe41bf9763818f20ede7db65a0e Mon Sep 17 00:00:00 2001 From: dstibbe Date: Thu, 4 Sep 2025 22:53:39 +0200 Subject: [PATCH 05/19] Added tests (#158) --- .../kotlin/sdk/TypesTest.kt | 46 +++++++++++++++++++ .../kotlin/sdk/TypesUtilTest.kt | 22 +++++++++ 2 files changed, 68 insertions(+) diff --git a/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/TypesTest.kt b/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/TypesTest.kt index cd47852a..e29d95df 100644 --- a/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/TypesTest.kt +++ b/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/TypesTest.kt @@ -158,6 +158,52 @@ class TypesTest { assertEquals("audio/wav", decoded.mimeType) } + @Test + fun `should validate resource link content`() { + val resourceLink = ResourceLink( + mimeType = "application/pdf", + description = "This pdf is meant to be a resource link test", + name = "file01", + size = 76859L, + title = "This is a pdf", + uri = "file:///path/to/my_file.pdf", + ) + + with(resourceLink) { + assertEquals("application/pdf", mimeType) + assertEquals("This pdf is meant to be a resource link test", description) + assertEquals("file01", name) + assertEquals(76859L, size) + assertEquals("This is a pdf", title) + assertEquals("file:///path/to/my_file.pdf", uri) + } + } + + @Test + fun `should serialize and deserialize resource link correctly`() { + val resourceLink = ResourceLink( + mimeType = "application/pdf", + description = "This pdf is meant to be a resource link test", + name = "file01", + size = 76859L, + title = "This is a pdf", + uri = "file:///path/to/my_file.pdf", + ) + + val json = McpJson.encodeToString(resourceLink) + val decoded = McpJson.decodeFromString(json) + + assertIs(decoded) + with(resourceLink) { + assertEquals("application/pdf", mimeType) + assertEquals("This pdf is meant to be a resource link test", description) + assertEquals("file01", name) + assertEquals(76859L, size) + assertEquals("This is a pdf", title) + assertEquals("file:///path/to/my_file.pdf", uri) + } + } + @Test fun `should validate embedded resource content`() { val resource = TextResourceContents( diff --git a/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/TypesUtilTest.kt b/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/TypesUtilTest.kt index 04e9d769..b2536e91 100644 --- a/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/TypesUtilTest.kt +++ b/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/TypesUtilTest.kt @@ -147,6 +147,28 @@ class TypesUtilTest { assertEquals("audio/mp3", decoded.mimeType) } + @Test + fun `should deserialize ResourceLink polymorphically`() { + val json = """ + { + "type": "resource_link", + "uri": "file:///project/src/main.rs", + "name": "main.rs", + "description": "Primary application entry point", + "mimeType": "text/x-rust" + } + """.trimIndent() + + val decoded = McpJson.decodeFromString(json) + + assertIs(decoded) + assertEquals("resource_link", decoded.type) + assertEquals("file:///project/src/main.rs", decoded.uri) + assertEquals("main.rs", decoded.name) + assertEquals("Primary application entry point", decoded.description) + assertEquals("text/x-rust", decoded.mimeType) + } + @Test fun `should deserialize EmbeddedResource polymorphically`() { val json = From 2e7c450bcb8f71ff32e8870f026fbac591001b77 Mon Sep 17 00:00:00 2001 From: dstibbe Date: Thu, 4 Sep 2025 23:06:12 +0200 Subject: [PATCH 06/19] Updated doc (#158) --- CONTRIBUTING.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b1e985ea..6ed5ace2 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -81,6 +81,8 @@ See [How to Write a Git Commit Message](https://chris.beams.io/posts/git-commit/ * Run `./gradlew test` to test the module and speed up development. * Run `./gradlew build` to build the project, which also runs all the tests. +*note*: when you change the data model, you might need to regenerate the .api files by running `./gradlew apiDump`. + ## Contacting maintainers * If something cannot be done, not convenient, or does not work — submit an [issue](#submitting-issues). From 8f00711049bc1138a2b83ebd3d17e7f552f2ccb5 Mon Sep 17 00:00:00 2001 From: dstibbe Date: Thu, 4 Sep 2025 23:57:29 +0200 Subject: [PATCH 07/19] Updated documentation (#158) --- CONTRIBUTING.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6ed5ace2..3cd40e99 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -80,6 +80,7 @@ See [How to Write a Git Commit Message](https://chris.beams.io/posts/git-commit/ * Run `./gradlew assemble` to build the project and produce the corresponding artifacts. * Run `./gradlew test` to test the module and speed up development. * Run `./gradlew build` to build the project, which also runs all the tests. +* Run `./gradlew allTests` to run all tests. *note*: when you change the data model, you might need to regenerate the .api files by running `./gradlew apiDump`. From c3746a4dc0b0e7856557ec57c56e016cf3cbd4df Mon Sep 17 00:00:00 2001 From: dstibbe Date: Sat, 6 Sep 2025 23:41:06 +0200 Subject: [PATCH 08/19] Re-added old types for backwards compatibility (#158) --- kotlin-sdk-core/api/kotlin-sdk-core.api | 13 +------------ .../io/modelcontextprotocol/kotlin/sdk/types.kt | 15 ++++++++++++--- 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/kotlin-sdk-core/api/kotlin-sdk-core.api b/kotlin-sdk-core/api/kotlin-sdk-core.api index 2e1afd5b..d2299dc2 100644 --- a/kotlin-sdk-core/api/kotlin-sdk-core.api +++ b/kotlin-sdk-core/api/kotlin-sdk-core.api @@ -450,9 +450,8 @@ public final class io/modelcontextprotocol/kotlin/sdk/CompleteResult$Completion$ public final fun serializer ()Lkotlinx/serialization/KSerializer; } -public abstract interface class io/modelcontextprotocol/kotlin/sdk/ContentBlock { +public abstract interface class io/modelcontextprotocol/kotlin/sdk/ContentBlock : io/modelcontextprotocol/kotlin/sdk/PromptMessageContent { public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/ContentBlock$Companion; - public abstract fun getType ()Ljava/lang/String; } public final class io/modelcontextprotocol/kotlin/sdk/ContentBlock$Companion { @@ -2039,20 +2038,10 @@ public final class io/modelcontextprotocol/kotlin/sdk/PromptMessage$Companion { } public abstract interface class io/modelcontextprotocol/kotlin/sdk/PromptMessageContent { - public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/PromptMessageContent$Companion; public abstract fun getType ()Ljava/lang/String; } -public final class io/modelcontextprotocol/kotlin/sdk/PromptMessageContent$Companion { - public final fun serializer ()Lkotlinx/serialization/KSerializer; -} - public abstract interface class io/modelcontextprotocol/kotlin/sdk/PromptMessageContentMultimodal : io/modelcontextprotocol/kotlin/sdk/PromptMessageContent { - public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/PromptMessageContentMultimodal$Companion; -} - -public final class io/modelcontextprotocol/kotlin/sdk/PromptMessageContentMultimodal$Companion { - public final fun serializer ()Lkotlinx/serialization/KSerializer; } public final class io/modelcontextprotocol/kotlin/sdk/PromptReference : io/modelcontextprotocol/kotlin/sdk/Reference { diff --git a/kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/types.kt b/kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/types.kt index edf3001c..4f14930b 100644 --- a/kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/types.kt +++ b/kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/types.kt @@ -970,13 +970,22 @@ public data class GetPromptRequest( override val method: Method = Method.Defined.PromptsGet } +@Deprecated("For backwards compatibility; use ContentBlock instead", ReplaceWith("ContentBlock")) +public sealed interface PromptMessageContent { + public val type: String +} + +@Deprecated( + "For backwards compatibility; use CreateMessageResultContent or SamplingMessageContent instead", + ReplaceWith("CreateMessageResultContent"), +) +public sealed interface PromptMessageContentMultimodal : PromptMessageContent + /** * Represents the types of a ContentBlock */ @Serializable(with = ContentBlockPolymorphicSerializer::class) -public sealed interface ContentBlock { - public val type: String -} +public sealed interface ContentBlock : PromptMessageContent /** * Represents content for the CreateMessageResult From f2b04d339eaa2d68798f091020615571d58d4178 Mon Sep 17 00:00:00 2001 From: dstibbe Date: Fri, 12 Sep 2025 15:57:37 +0200 Subject: [PATCH 09/19] Updated tests and updated api (#158) --- .../kotlin/io/modelcontextprotocol/kotlin/sdk/TypesTest.kt | 2 +- .../kotlin/sdk/integration/kotlin/PromptIntegrationTest.kt | 4 ++-- .../kotlin/sdk/integration/kotlin/ToolIntegrationTest.kt | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/TypesTest.kt b/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/TypesTest.kt index e29d95df..3a355dcd 100644 --- a/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/TypesTest.kt +++ b/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/TypesTest.kt @@ -194,7 +194,7 @@ class TypesTest { val decoded = McpJson.decodeFromString(json) assertIs(decoded) - with(resourceLink) { + with(decoded) { assertEquals("application/pdf", mimeType) assertEquals("This pdf is meant to be a resource link test", description) assertEquals("file01", name) diff --git a/kotlin-sdk-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/integration/kotlin/PromptIntegrationTest.kt b/kotlin-sdk-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/integration/kotlin/PromptIntegrationTest.kt index 54fd5fc8..b9572f6c 100644 --- a/kotlin-sdk-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/integration/kotlin/PromptIntegrationTest.kt +++ b/kotlin-sdk-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/integration/kotlin/PromptIntegrationTest.kt @@ -1,11 +1,11 @@ package io.modelcontextprotocol.kotlin.sdk.integration.kotlin +import io.modelcontextprotocol.kotlin.sdk.ContentBlock import io.modelcontextprotocol.kotlin.sdk.GetPromptRequest import io.modelcontextprotocol.kotlin.sdk.GetPromptResult import io.modelcontextprotocol.kotlin.sdk.ImageContent import io.modelcontextprotocol.kotlin.sdk.PromptArgument import io.modelcontextprotocol.kotlin.sdk.PromptMessage -import io.modelcontextprotocol.kotlin.sdk.PromptMessageContent import io.modelcontextprotocol.kotlin.sdk.Role import io.modelcontextprotocol.kotlin.sdk.ServerCapabilities import io.modelcontextprotocol.kotlin.sdk.TextContent @@ -93,7 +93,7 @@ class PromptIntegrationTest : KotlinTestBase() { ), ) - val assistantContents = mutableListOf() + val assistantContents = mutableListOf() assistantContents.add(TextContent(text = "I'd be happy to discuss $topic with you.")) if (includeImage) { diff --git a/kotlin-sdk-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/integration/kotlin/ToolIntegrationTest.kt b/kotlin-sdk-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/integration/kotlin/ToolIntegrationTest.kt index 84ae233d..35029594 100644 --- a/kotlin-sdk-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/integration/kotlin/ToolIntegrationTest.kt +++ b/kotlin-sdk-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/integration/kotlin/ToolIntegrationTest.kt @@ -3,8 +3,8 @@ package io.modelcontextprotocol.kotlin.sdk.integration.kotlin import io.kotest.assertions.json.shouldEqualJson import io.modelcontextprotocol.kotlin.sdk.CallToolResult import io.modelcontextprotocol.kotlin.sdk.CallToolResultBase +import io.modelcontextprotocol.kotlin.sdk.ContentBlock import io.modelcontextprotocol.kotlin.sdk.ImageContent -import io.modelcontextprotocol.kotlin.sdk.PromptMessageContent import io.modelcontextprotocol.kotlin.sdk.ServerCapabilities import io.modelcontextprotocol.kotlin.sdk.TextContent import io.modelcontextprotocol.kotlin.sdk.Tool @@ -276,7 +276,7 @@ class ToolIntegrationTest : KotlinTestBase() { val text = (request.arguments["text"] as? JsonPrimitive)?.content ?: "Default text" val includeImage = (request.arguments["includeImage"] as? JsonPrimitive)?.content?.toBoolean() ?: true - val content = mutableListOf( + val content = mutableListOf( TextContent(text = "Text content: $text"), ) From ae1ead07521ee09fc1896eb794b60722c15a7021 Mon Sep 17 00:00:00 2001 From: dstibbe Date: Mon, 25 Aug 2025 16:32:38 +0200 Subject: [PATCH 10/19] Refactored the model to have the ContentBlock types match the defined schema (#158) --- kotlin-sdk-core/api/kotlin-sdk-core.api | 84 ++++++++++--------- .../modelcontextprotocol/kotlin/sdk/types.kt | 49 +++++++---- .../kotlin/sdk/types.util.kt | 22 +++-- .../kotlin/sdk/TypesTest.kt | 18 ++-- .../kotlin/sdk/TypesUtilTest.kt | 8 +- 5 files changed, 107 insertions(+), 74 deletions(-) diff --git a/kotlin-sdk-core/api/kotlin-sdk-core.api b/kotlin-sdk-core/api/kotlin-sdk-core.api index ed9a268c..8b7b246c 100644 --- a/kotlin-sdk-core/api/kotlin-sdk-core.api +++ b/kotlin-sdk-core/api/kotlin-sdk-core.api @@ -29,7 +29,7 @@ public final class io/modelcontextprotocol/kotlin/sdk/Annotations$Companion { public final fun serializer ()Lkotlinx/serialization/KSerializer; } -public final class io/modelcontextprotocol/kotlin/sdk/AudioContent : io/modelcontextprotocol/kotlin/sdk/PromptMessageContentMultimodal { +public final class io/modelcontextprotocol/kotlin/sdk/AudioContent : io/modelcontextprotocol/kotlin/sdk/ContentBlock, io/modelcontextprotocol/kotlin/sdk/CreateMessageResultContent, io/modelcontextprotocol/kotlin/sdk/SamplingMessageContent { public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/AudioContent$Companion; public static final field TYPE Ljava/lang/String; public fun (Ljava/lang/String;Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/Annotations;)V @@ -450,6 +450,15 @@ public final class io/modelcontextprotocol/kotlin/sdk/CompleteResult$Completion$ public final fun serializer ()Lkotlinx/serialization/KSerializer; } +public abstract interface class io/modelcontextprotocol/kotlin/sdk/ContentBlock { + public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/ContentBlock$Companion; + public abstract fun getType ()Ljava/lang/String; +} + +public final class io/modelcontextprotocol/kotlin/sdk/ContentBlock$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + public final class io/modelcontextprotocol/kotlin/sdk/CreateElicitationRequest : io/modelcontextprotocol/kotlin/sdk/ServerRequest, io/modelcontextprotocol/kotlin/sdk/WithMeta { public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/CreateElicitationRequest$Companion; public fun (Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/CreateElicitationRequest$RequestedSchema;Lkotlinx/serialization/json/JsonObject;)V @@ -622,17 +631,17 @@ public final class io/modelcontextprotocol/kotlin/sdk/CreateMessageRequest$Inclu public final class io/modelcontextprotocol/kotlin/sdk/CreateMessageResult : io/modelcontextprotocol/kotlin/sdk/ClientResult { public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/CreateMessageResult$Companion; - public fun (Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/StopReason;Lio/modelcontextprotocol/kotlin/sdk/Role;Lio/modelcontextprotocol/kotlin/sdk/PromptMessageContentMultimodal;Lkotlinx/serialization/json/JsonObject;)V - public synthetic fun (Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/StopReason;Lio/modelcontextprotocol/kotlin/sdk/Role;Lio/modelcontextprotocol/kotlin/sdk/PromptMessageContentMultimodal;Lkotlinx/serialization/json/JsonObject;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/StopReason;Lio/modelcontextprotocol/kotlin/sdk/Role;Lio/modelcontextprotocol/kotlin/sdk/CreateMessageResultContent;Lkotlinx/serialization/json/JsonObject;)V + public synthetic fun (Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/StopReason;Lio/modelcontextprotocol/kotlin/sdk/Role;Lio/modelcontextprotocol/kotlin/sdk/CreateMessageResultContent;Lkotlinx/serialization/json/JsonObject;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun component1 ()Ljava/lang/String; public final fun component2 ()Lio/modelcontextprotocol/kotlin/sdk/StopReason; public final fun component3 ()Lio/modelcontextprotocol/kotlin/sdk/Role; - public final fun component4 ()Lio/modelcontextprotocol/kotlin/sdk/PromptMessageContentMultimodal; + public final fun component4 ()Lio/modelcontextprotocol/kotlin/sdk/CreateMessageResultContent; public final fun component5 ()Lkotlinx/serialization/json/JsonObject; - public final fun copy (Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/StopReason;Lio/modelcontextprotocol/kotlin/sdk/Role;Lio/modelcontextprotocol/kotlin/sdk/PromptMessageContentMultimodal;Lkotlinx/serialization/json/JsonObject;)Lio/modelcontextprotocol/kotlin/sdk/CreateMessageResult; - public static synthetic fun copy$default (Lio/modelcontextprotocol/kotlin/sdk/CreateMessageResult;Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/StopReason;Lio/modelcontextprotocol/kotlin/sdk/Role;Lio/modelcontextprotocol/kotlin/sdk/PromptMessageContentMultimodal;Lkotlinx/serialization/json/JsonObject;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/CreateMessageResult; + public final fun copy (Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/StopReason;Lio/modelcontextprotocol/kotlin/sdk/Role;Lio/modelcontextprotocol/kotlin/sdk/CreateMessageResultContent;Lkotlinx/serialization/json/JsonObject;)Lio/modelcontextprotocol/kotlin/sdk/CreateMessageResult; + public static synthetic fun copy$default (Lio/modelcontextprotocol/kotlin/sdk/CreateMessageResult;Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/StopReason;Lio/modelcontextprotocol/kotlin/sdk/Role;Lio/modelcontextprotocol/kotlin/sdk/CreateMessageResultContent;Lkotlinx/serialization/json/JsonObject;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/CreateMessageResult; public fun equals (Ljava/lang/Object;)Z - public final fun getContent ()Lio/modelcontextprotocol/kotlin/sdk/PromptMessageContentMultimodal; + public final fun getContent ()Lio/modelcontextprotocol/kotlin/sdk/CreateMessageResultContent; public final fun getModel ()Ljava/lang/String; public final fun getRole ()Lio/modelcontextprotocol/kotlin/sdk/Role; public final fun getStopReason ()Lio/modelcontextprotocol/kotlin/sdk/StopReason; @@ -656,6 +665,14 @@ public final class io/modelcontextprotocol/kotlin/sdk/CreateMessageResult$Compan public final fun serializer ()Lkotlinx/serialization/KSerializer; } +public abstract interface class io/modelcontextprotocol/kotlin/sdk/CreateMessageResultContent : io/modelcontextprotocol/kotlin/sdk/ContentBlock { + public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/CreateMessageResultContent$Companion; +} + +public final class io/modelcontextprotocol/kotlin/sdk/CreateMessageResultContent$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + public final class io/modelcontextprotocol/kotlin/sdk/CustomMeta : io/modelcontextprotocol/kotlin/sdk/WithMeta { public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/CustomMeta$Companion; public fun ()V @@ -702,7 +719,7 @@ public final class io/modelcontextprotocol/kotlin/sdk/CustomRequest$Companion { public final fun serializer ()Lkotlinx/serialization/KSerializer; } -public final class io/modelcontextprotocol/kotlin/sdk/EmbeddedResource : io/modelcontextprotocol/kotlin/sdk/PromptMessageContent { +public final class io/modelcontextprotocol/kotlin/sdk/EmbeddedResource : io/modelcontextprotocol/kotlin/sdk/ContentBlock { public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/EmbeddedResource$Companion; public static final field TYPE Ljava/lang/String; public fun (Lio/modelcontextprotocol/kotlin/sdk/ResourceContents;Lio/modelcontextprotocol/kotlin/sdk/Annotations;)V @@ -875,7 +892,7 @@ public final class io/modelcontextprotocol/kotlin/sdk/GetPromptResult$Companion public final fun serializer ()Lkotlinx/serialization/KSerializer; } -public final class io/modelcontextprotocol/kotlin/sdk/ImageContent : io/modelcontextprotocol/kotlin/sdk/PromptMessageContentMultimodal { +public final class io/modelcontextprotocol/kotlin/sdk/ImageContent : io/modelcontextprotocol/kotlin/sdk/ContentBlock, io/modelcontextprotocol/kotlin/sdk/CreateMessageResultContent, io/modelcontextprotocol/kotlin/sdk/SamplingMessageContent { public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/ImageContent$Companion; public static final field TYPE Ljava/lang/String; public fun (Ljava/lang/String;Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/Annotations;)V @@ -1994,13 +2011,13 @@ public final class io/modelcontextprotocol/kotlin/sdk/PromptListChangedNotificat public final class io/modelcontextprotocol/kotlin/sdk/PromptMessage { public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/PromptMessage$Companion; - public fun (Lio/modelcontextprotocol/kotlin/sdk/Role;Lio/modelcontextprotocol/kotlin/sdk/PromptMessageContent;)V + public fun (Lio/modelcontextprotocol/kotlin/sdk/Role;Lio/modelcontextprotocol/kotlin/sdk/ContentBlock;)V public final fun component1 ()Lio/modelcontextprotocol/kotlin/sdk/Role; - public final fun component2 ()Lio/modelcontextprotocol/kotlin/sdk/PromptMessageContent; - public final fun copy (Lio/modelcontextprotocol/kotlin/sdk/Role;Lio/modelcontextprotocol/kotlin/sdk/PromptMessageContent;)Lio/modelcontextprotocol/kotlin/sdk/PromptMessage; - public static synthetic fun copy$default (Lio/modelcontextprotocol/kotlin/sdk/PromptMessage;Lio/modelcontextprotocol/kotlin/sdk/Role;Lio/modelcontextprotocol/kotlin/sdk/PromptMessageContent;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/PromptMessage; + public final fun component2 ()Lio/modelcontextprotocol/kotlin/sdk/ContentBlock; + public final fun copy (Lio/modelcontextprotocol/kotlin/sdk/Role;Lio/modelcontextprotocol/kotlin/sdk/ContentBlock;)Lio/modelcontextprotocol/kotlin/sdk/PromptMessage; + public static synthetic fun copy$default (Lio/modelcontextprotocol/kotlin/sdk/PromptMessage;Lio/modelcontextprotocol/kotlin/sdk/Role;Lio/modelcontextprotocol/kotlin/sdk/ContentBlock;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/PromptMessage; public fun equals (Ljava/lang/Object;)Z - public final fun getContent ()Lio/modelcontextprotocol/kotlin/sdk/PromptMessageContent; + public final fun getContent ()Lio/modelcontextprotocol/kotlin/sdk/ContentBlock; public final fun getRole ()Lio/modelcontextprotocol/kotlin/sdk/Role; public fun hashCode ()I public fun toString ()Ljava/lang/String; @@ -2021,23 +2038,6 @@ public final class io/modelcontextprotocol/kotlin/sdk/PromptMessage$Companion { public final fun serializer ()Lkotlinx/serialization/KSerializer; } -public abstract interface class io/modelcontextprotocol/kotlin/sdk/PromptMessageContent { - public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/PromptMessageContent$Companion; - public abstract fun getType ()Ljava/lang/String; -} - -public final class io/modelcontextprotocol/kotlin/sdk/PromptMessageContent$Companion { - public final fun serializer ()Lkotlinx/serialization/KSerializer; -} - -public abstract interface class io/modelcontextprotocol/kotlin/sdk/PromptMessageContentMultimodal : io/modelcontextprotocol/kotlin/sdk/PromptMessageContent { - public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/PromptMessageContentMultimodal$Companion; -} - -public final class io/modelcontextprotocol/kotlin/sdk/PromptMessageContentMultimodal$Companion { - public final fun serializer ()Lkotlinx/serialization/KSerializer; -} - public final class io/modelcontextprotocol/kotlin/sdk/PromptReference : io/modelcontextprotocol/kotlin/sdk/Reference { public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/PromptReference$Companion; public static final field TYPE Ljava/lang/String; @@ -2558,13 +2558,13 @@ public final class io/modelcontextprotocol/kotlin/sdk/RootsListChangedNotificati public final class io/modelcontextprotocol/kotlin/sdk/SamplingMessage { public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/SamplingMessage$Companion; - public fun (Lio/modelcontextprotocol/kotlin/sdk/Role;Lio/modelcontextprotocol/kotlin/sdk/PromptMessageContentMultimodal;)V + public fun (Lio/modelcontextprotocol/kotlin/sdk/Role;Lio/modelcontextprotocol/kotlin/sdk/SamplingMessageContent;)V public final fun component1 ()Lio/modelcontextprotocol/kotlin/sdk/Role; - public final fun component2 ()Lio/modelcontextprotocol/kotlin/sdk/PromptMessageContentMultimodal; - public final fun copy (Lio/modelcontextprotocol/kotlin/sdk/Role;Lio/modelcontextprotocol/kotlin/sdk/PromptMessageContentMultimodal;)Lio/modelcontextprotocol/kotlin/sdk/SamplingMessage; - public static synthetic fun copy$default (Lio/modelcontextprotocol/kotlin/sdk/SamplingMessage;Lio/modelcontextprotocol/kotlin/sdk/Role;Lio/modelcontextprotocol/kotlin/sdk/PromptMessageContentMultimodal;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/SamplingMessage; + public final fun component2 ()Lio/modelcontextprotocol/kotlin/sdk/SamplingMessageContent; + public final fun copy (Lio/modelcontextprotocol/kotlin/sdk/Role;Lio/modelcontextprotocol/kotlin/sdk/SamplingMessageContent;)Lio/modelcontextprotocol/kotlin/sdk/SamplingMessage; + public static synthetic fun copy$default (Lio/modelcontextprotocol/kotlin/sdk/SamplingMessage;Lio/modelcontextprotocol/kotlin/sdk/Role;Lio/modelcontextprotocol/kotlin/sdk/SamplingMessageContent;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/SamplingMessage; public fun equals (Ljava/lang/Object;)Z - public final fun getContent ()Lio/modelcontextprotocol/kotlin/sdk/PromptMessageContentMultimodal; + public final fun getContent ()Lio/modelcontextprotocol/kotlin/sdk/SamplingMessageContent; public final fun getRole ()Lio/modelcontextprotocol/kotlin/sdk/Role; public fun hashCode ()I public fun toString ()Ljava/lang/String; @@ -2585,6 +2585,14 @@ public final class io/modelcontextprotocol/kotlin/sdk/SamplingMessage$Companion public final fun serializer ()Lkotlinx/serialization/KSerializer; } +public abstract interface class io/modelcontextprotocol/kotlin/sdk/SamplingMessageContent : io/modelcontextprotocol/kotlin/sdk/ContentBlock { + public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/SamplingMessageContent$Companion; +} + +public final class io/modelcontextprotocol/kotlin/sdk/SamplingMessageContent$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + public final class io/modelcontextprotocol/kotlin/sdk/ServerCapabilities { public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/ServerCapabilities$Companion; public fun ()V @@ -2823,7 +2831,7 @@ public final class io/modelcontextprotocol/kotlin/sdk/SubscribeRequest$Companion public final fun serializer ()Lkotlinx/serialization/KSerializer; } -public final class io/modelcontextprotocol/kotlin/sdk/TextContent : io/modelcontextprotocol/kotlin/sdk/PromptMessageContentMultimodal { +public final class io/modelcontextprotocol/kotlin/sdk/TextContent : io/modelcontextprotocol/kotlin/sdk/ContentBlock, io/modelcontextprotocol/kotlin/sdk/CreateMessageResultContent, io/modelcontextprotocol/kotlin/sdk/SamplingMessageContent { public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/TextContent$Companion; public static final field TYPE Ljava/lang/String; public fun ()V @@ -3100,7 +3108,7 @@ public final class io/modelcontextprotocol/kotlin/sdk/Types_utilKt { public static synthetic fun ok$default (Lio/modelcontextprotocol/kotlin/sdk/CallToolResult$Companion;Ljava/lang/String;Lkotlinx/serialization/json/JsonObject;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/CallToolResult; } -public final class io/modelcontextprotocol/kotlin/sdk/UnknownContent : io/modelcontextprotocol/kotlin/sdk/PromptMessageContentMultimodal { +public final class io/modelcontextprotocol/kotlin/sdk/UnknownContent : io/modelcontextprotocol/kotlin/sdk/ContentBlock, io/modelcontextprotocol/kotlin/sdk/CreateMessageResultContent, io/modelcontextprotocol/kotlin/sdk/SamplingMessageContent { public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/UnknownContent$Companion; public fun (Ljava/lang/String;)V public final fun component1 ()Ljava/lang/String; diff --git a/kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/types.kt b/kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/types.kt index b6879241..53e86077 100644 --- a/kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/types.kt +++ b/kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/types.kt @@ -971,18 +971,24 @@ public data class GetPromptRequest( } /** - * Represents the content of a prompt message. + * Represents the types of a ContentBlock */ -@Serializable(with = PromptMessageContentPolymorphicSerializer::class) -public sealed interface PromptMessageContent { +@Serializable(with = ContentBlockPolymorphicSerializer::class) +public sealed interface ContentBlock { public val type: String } /** - * Represents prompt message content that is either text, image or audio. + * Represents content for the CreateMessageResult */ -@Serializable(with = PromptMessageContentMultimodalPolymorphicSerializer::class) -public sealed interface PromptMessageContentMultimodal : PromptMessageContent +@Serializable(with = CreateMessageResultContentMultimodalPolymorphicSerializer::class) +public sealed interface CreateMessageResultContent : ContentBlock + +/** + * Represents content for the SamplingMessage + */ +@Serializable(with = SamplingMessageContentMultimodalPolymorphicSerializer::class) +public sealed interface SamplingMessageContent : ContentBlock /** * Text provided to or from an LLM. @@ -998,7 +1004,9 @@ public data class TextContent( * Optional annotations for the client. */ val annotations: Annotations? = null, -) : PromptMessageContentMultimodal { +) : ContentBlock, + CreateMessageResultContent, + SamplingMessageContent { override val type: String = TYPE public companion object { @@ -1025,7 +1033,9 @@ public data class ImageContent( * Optional annotations for the client. */ val annotations: Annotations? = null, -) : PromptMessageContentMultimodal { +) : ContentBlock, + CreateMessageResultContent, + SamplingMessageContent { override val type: String = TYPE public companion object { @@ -1052,7 +1062,9 @@ public data class AudioContent( * Optional annotations for the client. */ val annotations: Annotations? = null, -) : PromptMessageContentMultimodal { +) : ContentBlock, + CreateMessageResultContent, + SamplingMessageContent { override val type: String = TYPE public companion object { @@ -1064,7 +1076,10 @@ public data class AudioContent( * Unknown content provided to or from an LLM. */ @Serializable -public data class UnknownContent(override val type: String) : PromptMessageContentMultimodal +public data class UnknownContent(override val type: String) : + ContentBlock, + CreateMessageResultContent, + SamplingMessageContent /** * The contents of a resource, embedded into a prompt or tool call result. @@ -1080,7 +1095,7 @@ public data class EmbeddedResource( * Optional annotations for the client. */ val annotations: Annotations? = null, -) : PromptMessageContent { +) : ContentBlock { override val type: String = TYPE public companion object { @@ -1130,7 +1145,7 @@ public data class Annotations( * Describes a message returned as part of a prompt. */ @Serializable -public data class PromptMessage(val role: Role, val content: PromptMessageContent) +public data class PromptMessage(val role: Role, val content: ContentBlock) /** * The server's response to a prompts/get request from the client. @@ -1282,7 +1297,7 @@ public class ListToolsResult( */ @Serializable public sealed interface CallToolResultBase : ServerResult { - public val content: List + public val content: List public val structuredContent: JsonObject? public val isError: Boolean? get() = false } @@ -1292,7 +1307,7 @@ public sealed interface CallToolResultBase : ServerResult { */ @Serializable public class CallToolResult( - override val content: List, + override val content: List, override val structuredContent: JsonObject? = null, override val isError: Boolean? = false, override val _meta: JsonObject = EmptyJsonObject, @@ -1303,7 +1318,7 @@ public class CallToolResult( */ @Serializable public class CompatibilityCallToolResult( - override val content: List, + override val content: List, override val structuredContent: JsonObject? = null, override val isError: Boolean? = false, override val _meta: JsonObject = EmptyJsonObject, @@ -1448,7 +1463,7 @@ public class ModelPreferences( * Describes a message issued to or received from an LLM API. */ @Serializable -public data class SamplingMessage(val role: Role, val content: PromptMessageContentMultimodal) +public data class SamplingMessage(val role: Role, val content: SamplingMessageContent) /** * A request from the server to sample an LLM via the client. @@ -1530,7 +1545,7 @@ public data class CreateMessageResult( */ val stopReason: StopReason? = null, val role: Role, - val content: PromptMessageContentMultimodal, + val content: CreateMessageResultContent, override val _meta: JsonObject = EmptyJsonObject, ) : ClientResult diff --git a/kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/types.util.kt b/kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/types.util.kt index 07a0bf4c..14129e6b 100644 --- a/kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/types.util.kt +++ b/kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/types.util.kt @@ -81,9 +81,9 @@ internal object ReferencePolymorphicSerializer : JsonContentPolymorphicSerialize } } -internal object PromptMessageContentPolymorphicSerializer : - JsonContentPolymorphicSerializer(PromptMessageContent::class) { - override fun selectDeserializer(element: JsonElement): DeserializationStrategy = +internal object ContentBlockPolymorphicSerializer : + JsonContentPolymorphicSerializer(ContentBlock::class) { + override fun selectDeserializer(element: JsonElement): DeserializationStrategy = when (element.jsonObject.getValue("type").jsonPrimitive.content) { ImageContent.TYPE -> ImageContent.serializer() TextContent.TYPE -> TextContent.serializer() @@ -93,9 +93,19 @@ internal object PromptMessageContentPolymorphicSerializer : } } -internal object PromptMessageContentMultimodalPolymorphicSerializer : - JsonContentPolymorphicSerializer(PromptMessageContentMultimodal::class) { - override fun selectDeserializer(element: JsonElement): DeserializationStrategy = +internal object CreateMessageResultContentMultimodalPolymorphicSerializer : + JsonContentPolymorphicSerializer(CreateMessageResultContent::class) { + override fun selectDeserializer(element: JsonElement): DeserializationStrategy = + when (element.jsonObject.getValue("type").jsonPrimitive.content) { + ImageContent.TYPE -> ImageContent.serializer() + TextContent.TYPE -> TextContent.serializer() + AudioContent.TYPE -> AudioContent.serializer() + else -> UnknownContent.serializer() + } +} +internal object SamplingMessageContentMultimodalPolymorphicSerializer : + JsonContentPolymorphicSerializer(SamplingMessageContent::class) { + override fun selectDeserializer(element: JsonElement): DeserializationStrategy = when (element.jsonObject.getValue("type").jsonPrimitive.content) { ImageContent.TYPE -> ImageContent.serializer() TextContent.TYPE -> TextContent.serializer() diff --git a/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/TypesTest.kt b/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/TypesTest.kt index 23e2ffb7..cd47852a 100644 --- a/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/TypesTest.kt +++ b/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/TypesTest.kt @@ -94,8 +94,8 @@ class TypesTest { fun `should serialize and deserialize text content correctly`() { val textContent = TextContent(text = "Test message") - val json = McpJson.encodeToString(textContent) - val decoded = McpJson.decodeFromString(json) + val json = McpJson.encodeToString(textContent) + val decoded = McpJson.decodeFromString(json) assertIs(decoded) assertEquals("text", decoded.type) @@ -121,8 +121,8 @@ class TypesTest { mimeType = "image/jpeg", ) - val json = McpJson.encodeToString(imageContent) - val decoded = McpJson.decodeFromString(json) + val json = McpJson.encodeToString(imageContent) + val decoded = McpJson.decodeFromString(json) assertIs(decoded) assertEquals("image", decoded.type) @@ -149,8 +149,8 @@ class TypesTest { mimeType = "audio/wav", ) - val json = McpJson.encodeToString(audioContent) - val decoded = McpJson.decodeFromString(json) + val json = McpJson.encodeToString(audioContent) + val decoded = McpJson.decodeFromString(json) assertIs(decoded) assertEquals("audio", decoded.type) @@ -180,8 +180,8 @@ class TypesTest { ) val embeddedResource = EmbeddedResource(resource = resource) - val json = McpJson.encodeToString(embeddedResource) - val decoded = McpJson.decodeFromString(json) + val json = McpJson.encodeToString(embeddedResource) + val decoded = McpJson.decodeFromString(json) assertIs(decoded) assertEquals("resource", decoded.type) @@ -196,7 +196,7 @@ class TypesTest { fun `should handle unknown content type`() { val unknownJson = """{"type": "unknown_type"}""" - val decoded = McpJson.decodeFromString(unknownJson) + val decoded = McpJson.decodeFromString(unknownJson) assertIs(decoded) assertEquals("unknown_type", decoded.type) diff --git a/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/TypesUtilTest.kt b/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/TypesUtilTest.kt index 5207af35..04e9d769 100644 --- a/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/TypesUtilTest.kt +++ b/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/TypesUtilTest.kt @@ -116,7 +116,7 @@ class TypesUtilTest { fun `should deserialize TextContent polymorphically`() { val json = """{"type": "text", "text": "Hello world"}""" - val decoded = McpJson.decodeFromString(json) + val decoded = McpJson.decodeFromString(json) assertIs(decoded) assertEquals("text", decoded.type) @@ -127,7 +127,7 @@ class TypesUtilTest { fun `should deserialize ImageContent polymorphically`() { val json = """{"type": "image", "data": "aW1hZ2U=", "mimeType": "image/png"}""" - val decoded = McpJson.decodeFromString(json) + val decoded = McpJson.decodeFromString(json) assertIs(decoded) assertEquals("image", decoded.type) @@ -139,7 +139,7 @@ class TypesUtilTest { fun `should deserialize AudioContent polymorphically`() { val json = """{"type": "audio", "data": "YXVkaW8=", "mimeType": "audio/mp3"}""" - val decoded = McpJson.decodeFromString(json) + val decoded = McpJson.decodeFromString(json) assertIs(decoded) assertEquals("audio", decoded.type) @@ -152,7 +152,7 @@ class TypesUtilTest { val json = """{"type": "resource", "resource": {"uri": "file:///test.txt", "mimeType": "text/plain", "text": "content"}}""" - val decoded = McpJson.decodeFromString(json) + val decoded = McpJson.decodeFromString(json) assertIs(decoded) assertEquals("resource", decoded.type) From 0bc0304f06588404a463f788a30b9c9c05850f01 Mon Sep 17 00:00:00 2001 From: dstibbe Date: Thu, 4 Sep 2025 11:12:06 +0200 Subject: [PATCH 11/19] Updated API (#158) --- kotlin-sdk-core/api/kotlin-sdk-core.api | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/kotlin-sdk-core/api/kotlin-sdk-core.api b/kotlin-sdk-core/api/kotlin-sdk-core.api index 8b7b246c..4b3d29a9 100644 --- a/kotlin-sdk-core/api/kotlin-sdk-core.api +++ b/kotlin-sdk-core/api/kotlin-sdk-core.api @@ -2038,6 +2038,23 @@ public final class io/modelcontextprotocol/kotlin/sdk/PromptMessage$Companion { public final fun serializer ()Lkotlinx/serialization/KSerializer; } +public abstract interface class io/modelcontextprotocol/kotlin/sdk/PromptMessageContent { + public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/PromptMessageContent$Companion; + public abstract fun getType ()Ljava/lang/String; +} + +public final class io/modelcontextprotocol/kotlin/sdk/PromptMessageContent$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + +public abstract interface class io/modelcontextprotocol/kotlin/sdk/PromptMessageContentMultimodal : io/modelcontextprotocol/kotlin/sdk/PromptMessageContent { + public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/PromptMessageContentMultimodal$Companion; +} + +public final class io/modelcontextprotocol/kotlin/sdk/PromptMessageContentMultimodal$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + public final class io/modelcontextprotocol/kotlin/sdk/PromptReference : io/modelcontextprotocol/kotlin/sdk/Reference { public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/PromptReference$Companion; public static final field TYPE Ljava/lang/String; From c210a3a9220cbe5da14eed792fdbd52cea2bbb33 Mon Sep 17 00:00:00 2001 From: dstibbe Date: Thu, 4 Sep 2025 11:58:27 +0200 Subject: [PATCH 12/19] Added ResourceLink type (#158) --- kotlin-sdk-core/api/kotlin-sdk-core.api | 42 ++++++++++++++ .../modelcontextprotocol/kotlin/sdk/types.kt | 56 +++++++++++++++++++ 2 files changed, 98 insertions(+) diff --git a/kotlin-sdk-core/api/kotlin-sdk-core.api b/kotlin-sdk-core/api/kotlin-sdk-core.api index 4b3d29a9..2e1afd5b 100644 --- a/kotlin-sdk-core/api/kotlin-sdk-core.api +++ b/kotlin-sdk-core/api/kotlin-sdk-core.api @@ -2285,6 +2285,48 @@ public final class io/modelcontextprotocol/kotlin/sdk/ResourceContents$Companion public final fun serializer ()Lkotlinx/serialization/KSerializer; } +public final class io/modelcontextprotocol/kotlin/sdk/ResourceLink : io/modelcontextprotocol/kotlin/sdk/ContentBlock { + public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/ResourceLink$Companion; + public static final field TYPE Ljava/lang/String; + public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/Long;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/Annotations;)V + public synthetic fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/Long;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/Annotations;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Ljava/lang/String; + public final fun component2 ()Ljava/lang/String; + public final fun component3 ()Ljava/lang/Long; + public final fun component4 ()Ljava/lang/String; + public final fun component5 ()Ljava/lang/String; + public final fun component6 ()Ljava/lang/String; + public final fun component7 ()Lio/modelcontextprotocol/kotlin/sdk/Annotations; + public final fun copy (Ljava/lang/String;Ljava/lang/String;Ljava/lang/Long;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/Annotations;)Lio/modelcontextprotocol/kotlin/sdk/ResourceLink; + public static synthetic fun copy$default (Lio/modelcontextprotocol/kotlin/sdk/ResourceLink;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Long;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/Annotations;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/ResourceLink; + public fun equals (Ljava/lang/Object;)Z + public final fun getAnnotations ()Lio/modelcontextprotocol/kotlin/sdk/Annotations; + public final fun getDescription ()Ljava/lang/String; + public final fun getMimeType ()Ljava/lang/String; + public final fun getName ()Ljava/lang/String; + public final fun getSize ()Ljava/lang/Long; + public final fun getTitle ()Ljava/lang/String; + public fun getType ()Ljava/lang/String; + public final fun getUri ()Ljava/lang/String; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final synthetic class io/modelcontextprotocol/kotlin/sdk/ResourceLink$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lio/modelcontextprotocol/kotlin/sdk/ResourceLink$$serializer; + public final fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lio/modelcontextprotocol/kotlin/sdk/ResourceLink; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lio/modelcontextprotocol/kotlin/sdk/ResourceLink;)V + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class io/modelcontextprotocol/kotlin/sdk/ResourceLink$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + public final class io/modelcontextprotocol/kotlin/sdk/ResourceListChangedNotification : io/modelcontextprotocol/kotlin/sdk/ServerNotification { public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/ResourceListChangedNotification$Companion; public fun ()V diff --git a/kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/types.kt b/kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/types.kt index 53e86077..edf3001c 100644 --- a/kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/types.kt +++ b/kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/types.kt @@ -1072,6 +1072,62 @@ public data class AudioContent( } } +/** + * A Resource Link provided to or from an LLM. + */ +@Serializable +public data class ResourceLink( + /** + * A description of what this resource represents. + * + * This can be used by clients to improve the LLM’s understanding of available resources. It can be thought of like a “hint” to the model. + * + */ + val description: String? = null, + + /** + * Intended for programmatic or logical use, but used as a display name in past specs or fallback (if title isn’t present). + */ + val name: String, + + /** + * The size of the raw resource content, in bytes (i.e., before base64 encoding or any tokenization), if known. + * + * This can be used by Hosts to display file sizes and estimate context window usage. + * + */ + val size: Long? = null, + + /** + * Intended for UI and end-user contexts — optimized to be human-readable and easily understood, even by those unfamiliar with domain-specific terminology. + * + * If not provided, the name should be used for display (except for Tool, where annotations.title should be given precedence over using name, if present). + * + */ + val title: String? = null, + + /** + * The URI of this resource. + */ + val uri: String, + + /** + * The MIME type of this resource, if known. + */ + val mimeType: String, + + /** + * Optional annotations for the client. + */ + val annotations: Annotations? = null, +) : ContentBlock { + override val type: String = TYPE + + public companion object { + public const val TYPE: String = "resource_link" + } +} + /** * Unknown content provided to or from an LLM. */ From 72cd5647f59181522c466ec228587d164119ca75 Mon Sep 17 00:00:00 2001 From: dstibbe Date: Thu, 4 Sep 2025 12:09:47 +0200 Subject: [PATCH 13/19] Added ResourceLink serializer (#158) --- .../kotlin/io/modelcontextprotocol/kotlin/sdk/types.util.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/types.util.kt b/kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/types.util.kt index 14129e6b..3cad0891 100644 --- a/kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/types.util.kt +++ b/kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/types.util.kt @@ -89,6 +89,7 @@ internal object ContentBlockPolymorphicSerializer : TextContent.TYPE -> TextContent.serializer() EmbeddedResource.TYPE -> EmbeddedResource.serializer() AudioContent.TYPE -> AudioContent.serializer() + ResourceLink.TYPE -> ResourceLink.serializer() else -> UnknownContent.serializer() } } From 56d86810779d821e0615ab3f6d2cf75667f6743c Mon Sep 17 00:00:00 2001 From: dstibbe Date: Thu, 4 Sep 2025 22:53:39 +0200 Subject: [PATCH 14/19] Added tests (#158) --- .../kotlin/sdk/TypesTest.kt | 46 +++++++++++++++++++ .../kotlin/sdk/TypesUtilTest.kt | 22 +++++++++ 2 files changed, 68 insertions(+) diff --git a/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/TypesTest.kt b/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/TypesTest.kt index cd47852a..e29d95df 100644 --- a/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/TypesTest.kt +++ b/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/TypesTest.kt @@ -158,6 +158,52 @@ class TypesTest { assertEquals("audio/wav", decoded.mimeType) } + @Test + fun `should validate resource link content`() { + val resourceLink = ResourceLink( + mimeType = "application/pdf", + description = "This pdf is meant to be a resource link test", + name = "file01", + size = 76859L, + title = "This is a pdf", + uri = "file:///path/to/my_file.pdf", + ) + + with(resourceLink) { + assertEquals("application/pdf", mimeType) + assertEquals("This pdf is meant to be a resource link test", description) + assertEquals("file01", name) + assertEquals(76859L, size) + assertEquals("This is a pdf", title) + assertEquals("file:///path/to/my_file.pdf", uri) + } + } + + @Test + fun `should serialize and deserialize resource link correctly`() { + val resourceLink = ResourceLink( + mimeType = "application/pdf", + description = "This pdf is meant to be a resource link test", + name = "file01", + size = 76859L, + title = "This is a pdf", + uri = "file:///path/to/my_file.pdf", + ) + + val json = McpJson.encodeToString(resourceLink) + val decoded = McpJson.decodeFromString(json) + + assertIs(decoded) + with(resourceLink) { + assertEquals("application/pdf", mimeType) + assertEquals("This pdf is meant to be a resource link test", description) + assertEquals("file01", name) + assertEquals(76859L, size) + assertEquals("This is a pdf", title) + assertEquals("file:///path/to/my_file.pdf", uri) + } + } + @Test fun `should validate embedded resource content`() { val resource = TextResourceContents( diff --git a/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/TypesUtilTest.kt b/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/TypesUtilTest.kt index 04e9d769..b2536e91 100644 --- a/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/TypesUtilTest.kt +++ b/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/TypesUtilTest.kt @@ -147,6 +147,28 @@ class TypesUtilTest { assertEquals("audio/mp3", decoded.mimeType) } + @Test + fun `should deserialize ResourceLink polymorphically`() { + val json = """ + { + "type": "resource_link", + "uri": "file:///project/src/main.rs", + "name": "main.rs", + "description": "Primary application entry point", + "mimeType": "text/x-rust" + } + """.trimIndent() + + val decoded = McpJson.decodeFromString(json) + + assertIs(decoded) + assertEquals("resource_link", decoded.type) + assertEquals("file:///project/src/main.rs", decoded.uri) + assertEquals("main.rs", decoded.name) + assertEquals("Primary application entry point", decoded.description) + assertEquals("text/x-rust", decoded.mimeType) + } + @Test fun `should deserialize EmbeddedResource polymorphically`() { val json = From cebb28dfc9977e54cadc123c689627063fa753e4 Mon Sep 17 00:00:00 2001 From: dstibbe Date: Thu, 4 Sep 2025 23:06:12 +0200 Subject: [PATCH 15/19] Updated doc (#158) --- CONTRIBUTING.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b1e985ea..6ed5ace2 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -81,6 +81,8 @@ See [How to Write a Git Commit Message](https://chris.beams.io/posts/git-commit/ * Run `./gradlew test` to test the module and speed up development. * Run `./gradlew build` to build the project, which also runs all the tests. +*note*: when you change the data model, you might need to regenerate the .api files by running `./gradlew apiDump`. + ## Contacting maintainers * If something cannot be done, not convenient, or does not work — submit an [issue](#submitting-issues). From c821241597e8ddd72cbe00995011d500f6a350d5 Mon Sep 17 00:00:00 2001 From: dstibbe Date: Thu, 4 Sep 2025 23:57:29 +0200 Subject: [PATCH 16/19] Updated documentation (#158) --- CONTRIBUTING.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6ed5ace2..3cd40e99 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -80,6 +80,7 @@ See [How to Write a Git Commit Message](https://chris.beams.io/posts/git-commit/ * Run `./gradlew assemble` to build the project and produce the corresponding artifacts. * Run `./gradlew test` to test the module and speed up development. * Run `./gradlew build` to build the project, which also runs all the tests. +* Run `./gradlew allTests` to run all tests. *note*: when you change the data model, you might need to regenerate the .api files by running `./gradlew apiDump`. From 7a2ae41045ff95b9c1a1ca62e675fcd520b8a3e0 Mon Sep 17 00:00:00 2001 From: dstibbe Date: Sat, 6 Sep 2025 23:41:06 +0200 Subject: [PATCH 17/19] Re-added old types for backwards compatibility (#158) --- kotlin-sdk-core/api/kotlin-sdk-core.api | 13 +------------ .../io/modelcontextprotocol/kotlin/sdk/types.kt | 15 ++++++++++++--- 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/kotlin-sdk-core/api/kotlin-sdk-core.api b/kotlin-sdk-core/api/kotlin-sdk-core.api index 2e1afd5b..d2299dc2 100644 --- a/kotlin-sdk-core/api/kotlin-sdk-core.api +++ b/kotlin-sdk-core/api/kotlin-sdk-core.api @@ -450,9 +450,8 @@ public final class io/modelcontextprotocol/kotlin/sdk/CompleteResult$Completion$ public final fun serializer ()Lkotlinx/serialization/KSerializer; } -public abstract interface class io/modelcontextprotocol/kotlin/sdk/ContentBlock { +public abstract interface class io/modelcontextprotocol/kotlin/sdk/ContentBlock : io/modelcontextprotocol/kotlin/sdk/PromptMessageContent { public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/ContentBlock$Companion; - public abstract fun getType ()Ljava/lang/String; } public final class io/modelcontextprotocol/kotlin/sdk/ContentBlock$Companion { @@ -2039,20 +2038,10 @@ public final class io/modelcontextprotocol/kotlin/sdk/PromptMessage$Companion { } public abstract interface class io/modelcontextprotocol/kotlin/sdk/PromptMessageContent { - public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/PromptMessageContent$Companion; public abstract fun getType ()Ljava/lang/String; } -public final class io/modelcontextprotocol/kotlin/sdk/PromptMessageContent$Companion { - public final fun serializer ()Lkotlinx/serialization/KSerializer; -} - public abstract interface class io/modelcontextprotocol/kotlin/sdk/PromptMessageContentMultimodal : io/modelcontextprotocol/kotlin/sdk/PromptMessageContent { - public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/PromptMessageContentMultimodal$Companion; -} - -public final class io/modelcontextprotocol/kotlin/sdk/PromptMessageContentMultimodal$Companion { - public final fun serializer ()Lkotlinx/serialization/KSerializer; } public final class io/modelcontextprotocol/kotlin/sdk/PromptReference : io/modelcontextprotocol/kotlin/sdk/Reference { diff --git a/kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/types.kt b/kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/types.kt index edf3001c..4f14930b 100644 --- a/kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/types.kt +++ b/kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/types.kt @@ -970,13 +970,22 @@ public data class GetPromptRequest( override val method: Method = Method.Defined.PromptsGet } +@Deprecated("For backwards compatibility; use ContentBlock instead", ReplaceWith("ContentBlock")) +public sealed interface PromptMessageContent { + public val type: String +} + +@Deprecated( + "For backwards compatibility; use CreateMessageResultContent or SamplingMessageContent instead", + ReplaceWith("CreateMessageResultContent"), +) +public sealed interface PromptMessageContentMultimodal : PromptMessageContent + /** * Represents the types of a ContentBlock */ @Serializable(with = ContentBlockPolymorphicSerializer::class) -public sealed interface ContentBlock { - public val type: String -} +public sealed interface ContentBlock : PromptMessageContent /** * Represents content for the CreateMessageResult From 51365484239a4b3b20a4c6d8e07fc2bbcec3958e Mon Sep 17 00:00:00 2001 From: dstibbe Date: Fri, 12 Sep 2025 15:57:37 +0200 Subject: [PATCH 18/19] Updated tests and updated api (#158) --- .../kotlin/io/modelcontextprotocol/kotlin/sdk/TypesTest.kt | 2 +- .../kotlin/sdk/integration/kotlin/PromptIntegrationTest.kt | 4 ++-- .../kotlin/sdk/integration/kotlin/ToolIntegrationTest.kt | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/TypesTest.kt b/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/TypesTest.kt index e29d95df..3a355dcd 100644 --- a/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/TypesTest.kt +++ b/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/TypesTest.kt @@ -194,7 +194,7 @@ class TypesTest { val decoded = McpJson.decodeFromString(json) assertIs(decoded) - with(resourceLink) { + with(decoded) { assertEquals("application/pdf", mimeType) assertEquals("This pdf is meant to be a resource link test", description) assertEquals("file01", name) diff --git a/kotlin-sdk-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/integration/kotlin/PromptIntegrationTest.kt b/kotlin-sdk-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/integration/kotlin/PromptIntegrationTest.kt index 54fd5fc8..b9572f6c 100644 --- a/kotlin-sdk-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/integration/kotlin/PromptIntegrationTest.kt +++ b/kotlin-sdk-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/integration/kotlin/PromptIntegrationTest.kt @@ -1,11 +1,11 @@ package io.modelcontextprotocol.kotlin.sdk.integration.kotlin +import io.modelcontextprotocol.kotlin.sdk.ContentBlock import io.modelcontextprotocol.kotlin.sdk.GetPromptRequest import io.modelcontextprotocol.kotlin.sdk.GetPromptResult import io.modelcontextprotocol.kotlin.sdk.ImageContent import io.modelcontextprotocol.kotlin.sdk.PromptArgument import io.modelcontextprotocol.kotlin.sdk.PromptMessage -import io.modelcontextprotocol.kotlin.sdk.PromptMessageContent import io.modelcontextprotocol.kotlin.sdk.Role import io.modelcontextprotocol.kotlin.sdk.ServerCapabilities import io.modelcontextprotocol.kotlin.sdk.TextContent @@ -93,7 +93,7 @@ class PromptIntegrationTest : KotlinTestBase() { ), ) - val assistantContents = mutableListOf() + val assistantContents = mutableListOf() assistantContents.add(TextContent(text = "I'd be happy to discuss $topic with you.")) if (includeImage) { diff --git a/kotlin-sdk-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/integration/kotlin/ToolIntegrationTest.kt b/kotlin-sdk-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/integration/kotlin/ToolIntegrationTest.kt index 84ae233d..35029594 100644 --- a/kotlin-sdk-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/integration/kotlin/ToolIntegrationTest.kt +++ b/kotlin-sdk-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/integration/kotlin/ToolIntegrationTest.kt @@ -3,8 +3,8 @@ package io.modelcontextprotocol.kotlin.sdk.integration.kotlin import io.kotest.assertions.json.shouldEqualJson import io.modelcontextprotocol.kotlin.sdk.CallToolResult import io.modelcontextprotocol.kotlin.sdk.CallToolResultBase +import io.modelcontextprotocol.kotlin.sdk.ContentBlock import io.modelcontextprotocol.kotlin.sdk.ImageContent -import io.modelcontextprotocol.kotlin.sdk.PromptMessageContent import io.modelcontextprotocol.kotlin.sdk.ServerCapabilities import io.modelcontextprotocol.kotlin.sdk.TextContent import io.modelcontextprotocol.kotlin.sdk.Tool @@ -276,7 +276,7 @@ class ToolIntegrationTest : KotlinTestBase() { val text = (request.arguments["text"] as? JsonPrimitive)?.content ?: "Default text" val includeImage = (request.arguments["includeImage"] as? JsonPrimitive)?.content?.toBoolean() ?: true - val content = mutableListOf( + val content = mutableListOf( TextContent(text = "Text content: $text"), ) From 8858a6c5b1be68e44c04fb4b9387c46945f742b8 Mon Sep 17 00:00:00 2001 From: dstibbe Date: Wed, 17 Sep 2025 15:37:16 +0200 Subject: [PATCH 19/19] Merged master (#158) --- .../kotlin/io/modelcontextprotocol/kotlin/sdk/TypesTest.kt | 2 +- .../kotlin/io/modelcontextprotocol/kotlin/sdk/TypesUtilTest.kt | 2 +- .../sdk/integration/kotlin/AbstractPromptIntegrationTest.kt | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/TypesTest.kt b/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/TypesTest.kt index 3a355dcd..ffadc846 100644 --- a/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/TypesTest.kt +++ b/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/TypesTest.kt @@ -81,7 +81,7 @@ class TypesTest { assertEquals("invalid_type", decoded.type) } - // PromptMessageContent Tests + // ContentBlock Tests @Test fun `should validate text content`() { val textContent = TextContent(text = "Hello, world!") diff --git a/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/TypesUtilTest.kt b/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/TypesUtilTest.kt index b2536e91..d0a1f184 100644 --- a/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/TypesUtilTest.kt +++ b/kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/TypesUtilTest.kt @@ -111,7 +111,7 @@ class TypesUtilTest { assertEquals("unknown_ref", decoded.type) } - // PromptMessageContent Polymorphic Serializer Tests + // ContentBlock Polymorphic Serializer Tests @Test fun `should deserialize TextContent polymorphically`() { val json = """{"type": "text", "text": "Hello world"}""" diff --git a/kotlin-sdk-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/integration/kotlin/AbstractPromptIntegrationTest.kt b/kotlin-sdk-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/integration/kotlin/AbstractPromptIntegrationTest.kt index 7101e829..d5644bbc 100644 --- a/kotlin-sdk-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/integration/kotlin/AbstractPromptIntegrationTest.kt +++ b/kotlin-sdk-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/integration/kotlin/AbstractPromptIntegrationTest.kt @@ -1,6 +1,5 @@ package io.modelcontextprotocol.kotlin.sdk.integration.kotlin -import io.modelcontextprotocol.kotlin.sdk.ContentBlock import io.modelcontextprotocol.kotlin.sdk.GetPromptRequest import io.modelcontextprotocol.kotlin.sdk.GetPromptResult import io.modelcontextprotocol.kotlin.sdk.PromptArgument