From 5636faf60c21b5b879dd83b6acfc45ae4bd1b69c Mon Sep 17 00:00:00 2001 From: Konstantin Pavlov <1517853+kpavlov@users.noreply.github.com> Date: Wed, 12 Nov 2025 14:40:33 +0200 Subject: [PATCH] Updated ServerPromptsTest, move AbstractTransport to it's own file (#376) # Refactor and enhance prompt handling logic and tests, update Kotest version and modularize `AbstractTransport` - Added more post-conditions/scenarios to ServerPromptsTest - Extracted `AbstractTransport` to a dedicated file for modularity. - Improved Protocol connection state validation: When disconnected, the Protocol will throw `IllegalStateException` instead of `Error`. - Minor Kotlin fixes and suppressions to improve code quality. ## Motivation and Context Improve test scenarios in ServerPromptsTest ## How Has This Been Tested? Integration tests updated ## Breaking Changes When disconnected, the Protocol will throw `IllegalStateException` instead of `Error`. ## Types of changes - [ ] Bug fix (non-breaking change which fixes an issue) - [ ] New feature (non-breaking change which adds functionality) - [x] Breaking change (fix or feature that would cause existing functionality to change) - [ ] Documentation update - [x] Test update - [x] Refactoring ## Checklist - [x] I have read the [MCP Documentation](https://modelcontextprotocol.io) - [x] My code follows the repository's style guidelines - [x] New and existing tests pass locally - [ ] I have added appropriate error handling - [ ] I have added or updated documentation as needed ## Additional context --- kotlin-sdk-core/api/kotlin-sdk-core.api | 74 +++++++++ .../kotlin/sdk/shared/AbstractTransport.kt | 54 +++++++ .../kotlin/sdk/shared/Transport.kt | 51 ------- .../kotlin/sdk/types/common.kt | 10 ++ .../kotlin/sdk/types/prompts.kt | 16 ++ kotlin-sdk-test/build.gradle.kts | 1 + .../sdk/server/OldSchemaServerPromptsTest.kt | 143 ++++++++++++++++-- 7 files changed, 282 insertions(+), 67 deletions(-) create mode 100644 kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/shared/AbstractTransport.kt diff --git a/kotlin-sdk-core/api/kotlin-sdk-core.api b/kotlin-sdk-core/api/kotlin-sdk-core.api index 7c051ee..3c1aec4 100644 --- a/kotlin-sdk-core/api/kotlin-sdk-core.api +++ b/kotlin-sdk-core/api/kotlin-sdk-core.api @@ -558,6 +558,7 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/AudioContent : io/mo public fun getMeta ()Lkotlinx/serialization/json/JsonObject; public final fun getMimeType ()Ljava/lang/String; public fun getType ()Lio/modelcontextprotocol/kotlin/sdk/types/ContentTypes; + public fun get_meta ()Lkotlinx/serialization/json/JsonObject; public fun hashCode ()I public fun toString ()Ljava/lang/String; } @@ -587,6 +588,7 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/BaseNotificationPara public static synthetic fun copy$default (Lio/modelcontextprotocol/kotlin/sdk/types/BaseNotificationParams;Lkotlinx/serialization/json/JsonObject;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/types/BaseNotificationParams; public fun equals (Ljava/lang/Object;)Z public fun getMeta ()Lkotlinx/serialization/json/JsonObject; + public fun get_meta ()Lkotlinx/serialization/json/JsonObject; public fun hashCode ()I public fun toString ()Ljava/lang/String; } @@ -649,6 +651,7 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/BlobResourceContents public fun getMeta ()Lkotlinx/serialization/json/JsonObject; public fun getMimeType ()Ljava/lang/String; public fun getUri ()Ljava/lang/String; + public fun get_meta ()Lkotlinx/serialization/json/JsonObject; public fun hashCode ()I public fun toString ()Ljava/lang/String; } @@ -746,6 +749,7 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/CallToolResult : io/ public final fun getContent ()Ljava/util/List; public fun getMeta ()Lkotlinx/serialization/json/JsonObject; public final fun getStructuredContent ()Lkotlinx/serialization/json/JsonObject; + public fun get_meta ()Lkotlinx/serialization/json/JsonObject; public fun hashCode ()I public final fun isError ()Ljava/lang/Boolean; public fun toString ()Ljava/lang/String; @@ -811,6 +815,7 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/CancelledNotificatio public fun getMeta ()Lkotlinx/serialization/json/JsonObject; public final fun getReason ()Ljava/lang/String; public final fun getRequestId ()Lio/modelcontextprotocol/kotlin/sdk/types/RequestId; + public fun get_meta ()Lkotlinx/serialization/json/JsonObject; public fun hashCode ()I public fun toString ()Ljava/lang/String; } @@ -920,6 +925,10 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/ClientResult$Compani public final fun serializer ()Lkotlinx/serialization/KSerializer; } +public final class io/modelcontextprotocol/kotlin/sdk/types/ClientResult$DefaultImpls { + public static fun get_meta (Lio/modelcontextprotocol/kotlin/sdk/types/ClientResult;)Lkotlinx/serialization/json/JsonObject; +} + public final class io/modelcontextprotocol/kotlin/sdk/types/CommonKt { public static final field LATEST_PROTOCOL_VERSION Ljava/lang/String; public static final fun ProgressToken (J)Lio/modelcontextprotocol/kotlin/sdk/types/RequestId; @@ -1063,6 +1072,7 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/CompleteResult : io/ public fun equals (Ljava/lang/Object;)Z public final fun getCompletion ()Lio/modelcontextprotocol/kotlin/sdk/types/CompleteResult$Completion; public fun getMeta ()Lkotlinx/serialization/json/JsonObject; + public fun get_meta ()Lkotlinx/serialization/json/JsonObject; public fun hashCode ()I public fun toString ()Ljava/lang/String; } @@ -1123,6 +1133,10 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/ContentBlock$Compani public final fun serializer ()Lkotlinx/serialization/KSerializer; } +public final class io/modelcontextprotocol/kotlin/sdk/types/ContentBlock$DefaultImpls { + public static fun get_meta (Lio/modelcontextprotocol/kotlin/sdk/types/ContentBlock;)Lkotlinx/serialization/json/JsonObject; +} + public final class io/modelcontextprotocol/kotlin/sdk/types/ContentTypes : java/lang/Enum { public static final field AUDIO Lio/modelcontextprotocol/kotlin/sdk/types/ContentTypes; public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/types/ContentTypes$Companion; @@ -1239,6 +1253,7 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/CreateMessageResult public final fun getModel ()Ljava/lang/String; public final fun getRole ()Lio/modelcontextprotocol/kotlin/sdk/types/Role; public final fun getStopReason-6olV-UY ()Ljava/lang/String; + public fun get_meta ()Lkotlinx/serialization/json/JsonObject; public fun hashCode ()I public fun toString ()Ljava/lang/String; } @@ -1423,6 +1438,7 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/ElicitResult : io/mo public final fun getAction ()Lio/modelcontextprotocol/kotlin/sdk/types/ElicitResult$Action; public final fun getContent ()Lkotlinx/serialization/json/JsonObject; public fun getMeta ()Lkotlinx/serialization/json/JsonObject; + public fun get_meta ()Lkotlinx/serialization/json/JsonObject; public fun hashCode ()I public fun toString ()Ljava/lang/String; } @@ -1470,6 +1486,7 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/EmbeddedResource : i public fun getMeta ()Lkotlinx/serialization/json/JsonObject; public final fun getResource ()Lio/modelcontextprotocol/kotlin/sdk/types/ResourceContents; public fun getType ()Lio/modelcontextprotocol/kotlin/sdk/types/ContentTypes; + public fun get_meta ()Lkotlinx/serialization/json/JsonObject; public fun hashCode ()I public fun toString ()Ljava/lang/String; } @@ -1499,6 +1516,7 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/EmptyResult : io/mod public static synthetic fun copy$default (Lio/modelcontextprotocol/kotlin/sdk/types/EmptyResult;Lkotlinx/serialization/json/JsonObject;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/types/EmptyResult; public fun equals (Ljava/lang/Object;)Z public fun getMeta ()Lkotlinx/serialization/json/JsonObject; + public fun get_meta ()Lkotlinx/serialization/json/JsonObject; public fun hashCode ()I public fun toString ()Ljava/lang/String; } @@ -1521,6 +1539,8 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/EmptyResult$Companio public final class io/modelcontextprotocol/kotlin/sdk/types/GetPromptRequest : io/modelcontextprotocol/kotlin/sdk/types/ClientRequest { public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/types/GetPromptRequest$Companion; public fun (Lio/modelcontextprotocol/kotlin/sdk/types/GetPromptRequestParams;)V + public synthetic fun (Ljava/lang/String;Ljava/util/Map;Lkotlinx/serialization/json/JsonObject;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public synthetic fun (Ljava/lang/String;Ljava/util/Map;Lkotlinx/serialization/json/JsonObject;Lkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun component1 ()Lio/modelcontextprotocol/kotlin/sdk/types/GetPromptRequestParams; public final fun copy (Lio/modelcontextprotocol/kotlin/sdk/types/GetPromptRequestParams;)Lio/modelcontextprotocol/kotlin/sdk/types/GetPromptRequest; public static synthetic fun copy$default (Lio/modelcontextprotocol/kotlin/sdk/types/GetPromptRequest;Lio/modelcontextprotocol/kotlin/sdk/types/GetPromptRequestParams;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/types/GetPromptRequest; @@ -1595,6 +1615,7 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/GetPromptResult : io public final fun getDescription ()Ljava/lang/String; public final fun getMessages ()Ljava/util/List; public fun getMeta ()Lkotlinx/serialization/json/JsonObject; + public fun get_meta ()Lkotlinx/serialization/json/JsonObject; public fun hashCode ()I public fun toString ()Ljava/lang/String; } @@ -1677,6 +1698,7 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/ImageContent : io/mo public fun getMeta ()Lkotlinx/serialization/json/JsonObject; public final fun getMimeType ()Ljava/lang/String; public fun getType ()Lio/modelcontextprotocol/kotlin/sdk/types/ContentTypes; + public fun get_meta ()Lkotlinx/serialization/json/JsonObject; public fun hashCode ()I public fun toString ()Ljava/lang/String; } @@ -1830,6 +1852,7 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/InitializeResult : i public fun getMeta ()Lkotlinx/serialization/json/JsonObject; public final fun getProtocolVersion ()Ljava/lang/String; public final fun getServerInfo ()Lio/modelcontextprotocol/kotlin/sdk/types/Implementation; + public fun get_meta ()Lkotlinx/serialization/json/JsonObject; public fun hashCode ()I public fun toString ()Ljava/lang/String; } @@ -2075,6 +2098,7 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/ListPromptsResult : public fun getMeta ()Lkotlinx/serialization/json/JsonObject; public fun getNextCursor ()Ljava/lang/String; public final fun getPrompts ()Ljava/util/List; + public fun get_meta ()Lkotlinx/serialization/json/JsonObject; public fun hashCode ()I public fun toString ()Ljava/lang/String; } @@ -2140,6 +2164,7 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/ListResourceTemplate public fun getMeta ()Lkotlinx/serialization/json/JsonObject; public fun getNextCursor ()Ljava/lang/String; public final fun getResourceTemplates ()Ljava/util/List; + public fun get_meta ()Lkotlinx/serialization/json/JsonObject; public fun hashCode ()I public fun toString ()Ljava/lang/String; } @@ -2205,6 +2230,7 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/ListResourcesResult public fun getMeta ()Lkotlinx/serialization/json/JsonObject; public fun getNextCursor ()Ljava/lang/String; public final fun getResources ()Ljava/util/List; + public fun get_meta ()Lkotlinx/serialization/json/JsonObject; public fun hashCode ()I public fun toString ()Ljava/lang/String; } @@ -2267,6 +2293,7 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/ListRootsResult : io public fun equals (Ljava/lang/Object;)Z public fun getMeta ()Lkotlinx/serialization/json/JsonObject; public final fun getRoots ()Ljava/util/List; + public fun get_meta ()Lkotlinx/serialization/json/JsonObject; public fun hashCode ()I public fun toString ()Ljava/lang/String; } @@ -2332,6 +2359,7 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/ListToolsResult : io public fun getMeta ()Lkotlinx/serialization/json/JsonObject; public fun getNextCursor ()Ljava/lang/String; public final fun getTools ()Ljava/util/List; + public fun get_meta ()Lkotlinx/serialization/json/JsonObject; public fun hashCode ()I public fun toString ()Ljava/lang/String; } @@ -2414,6 +2442,7 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/LoggingMessageNotifi public final fun getLevel ()Lio/modelcontextprotocol/kotlin/sdk/types/LoggingLevel; public final fun getLogger ()Ljava/lang/String; public fun getMeta ()Lkotlinx/serialization/json/JsonObject; + public fun get_meta ()Lkotlinx/serialization/json/JsonObject; public fun hashCode ()I public fun toString ()Ljava/lang/String; } @@ -2449,6 +2478,10 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/MediaContent$Compani public final fun serializer ()Lkotlinx/serialization/KSerializer; } +public final class io/modelcontextprotocol/kotlin/sdk/types/MediaContent$DefaultImpls { + public static fun get_meta (Lio/modelcontextprotocol/kotlin/sdk/types/MediaContent;)Lkotlinx/serialization/json/JsonObject; +} + public abstract interface class io/modelcontextprotocol/kotlin/sdk/types/Method { public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/types/Method$Companion; public abstract fun getValue ()Ljava/lang/String; @@ -2602,6 +2635,10 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/NotificationParams$C public final fun serializer ()Lkotlinx/serialization/KSerializer; } +public final class io/modelcontextprotocol/kotlin/sdk/types/NotificationParams$DefaultImpls { + public static fun get_meta (Lio/modelcontextprotocol/kotlin/sdk/types/NotificationParams;)Lkotlinx/serialization/json/JsonObject; +} + public abstract interface class io/modelcontextprotocol/kotlin/sdk/types/PaginatedRequest : io/modelcontextprotocol/kotlin/sdk/types/Request { public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/types/PaginatedRequest$Companion; public abstract fun getParams ()Lio/modelcontextprotocol/kotlin/sdk/types/PaginatedRequestParams; @@ -2650,6 +2687,10 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/PaginatedResult$Comp public final fun serializer ()Lkotlinx/serialization/KSerializer; } +public final class io/modelcontextprotocol/kotlin/sdk/types/PaginatedResult$DefaultImpls { + public static fun get_meta (Lio/modelcontextprotocol/kotlin/sdk/types/PaginatedResult;)Lkotlinx/serialization/json/JsonObject; +} + public final class io/modelcontextprotocol/kotlin/sdk/types/PingRequest : io/modelcontextprotocol/kotlin/sdk/types/ClientRequest, io/modelcontextprotocol/kotlin/sdk/types/ServerRequest { public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/types/PingRequest$Companion; public fun ()V @@ -2752,6 +2793,7 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/ProgressNotification public final fun getProgress ()D public final fun getProgressToken ()Lio/modelcontextprotocol/kotlin/sdk/types/RequestId; public final fun getTotal ()Ljava/lang/Double; + public fun get_meta ()Lkotlinx/serialization/json/JsonObject; public fun hashCode ()I public fun toString ()Ljava/lang/String; } @@ -2790,6 +2832,7 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/Prompt : io/modelcon public fun getMeta ()Lkotlinx/serialization/json/JsonObject; public final fun getName ()Ljava/lang/String; public final fun getTitle ()Ljava/lang/String; + public fun get_meta ()Lkotlinx/serialization/json/JsonObject; public fun hashCode ()I public fun toString ()Ljava/lang/String; } @@ -3049,6 +3092,7 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/ReadResourceResult : public fun equals (Ljava/lang/Object;)Z public final fun getContents ()Ljava/util/List; public fun getMeta ()Lkotlinx/serialization/json/JsonObject; + public fun get_meta ()Lkotlinx/serialization/json/JsonObject; public fun hashCode ()I public fun toString ()Ljava/lang/String; } @@ -3218,6 +3262,10 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/RequestResult$Compan public final fun serializer ()Lkotlinx/serialization/KSerializer; } +public final class io/modelcontextprotocol/kotlin/sdk/types/RequestResult$DefaultImpls { + public static fun get_meta (Lio/modelcontextprotocol/kotlin/sdk/types/RequestResult;)Lkotlinx/serialization/json/JsonObject; +} + public final class io/modelcontextprotocol/kotlin/sdk/types/Resource : io/modelcontextprotocol/kotlin/sdk/types/ResourceLike { public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/types/Resource$Companion; public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Long;Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/types/Annotations;Ljava/util/List;Lkotlinx/serialization/json/JsonObject;)V @@ -3243,6 +3291,7 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/Resource : io/modelc public final fun getSize ()Ljava/lang/Long; public final fun getTitle ()Ljava/lang/String; public final fun getUri ()Ljava/lang/String; + public fun get_meta ()Lkotlinx/serialization/json/JsonObject; public fun hashCode ()I public fun toString ()Ljava/lang/String; } @@ -3272,6 +3321,10 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/ResourceContents$Com public final fun serializer ()Lkotlinx/serialization/KSerializer; } +public final class io/modelcontextprotocol/kotlin/sdk/types/ResourceContents$DefaultImpls { + public static fun get_meta (Lio/modelcontextprotocol/kotlin/sdk/types/ResourceContents;)Lkotlinx/serialization/json/JsonObject; +} + public abstract interface class io/modelcontextprotocol/kotlin/sdk/types/ResourceLike : io/modelcontextprotocol/kotlin/sdk/types/WithMeta { public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/types/ResourceLike$Companion; } @@ -3280,6 +3333,10 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/ResourceLike$Compani public final fun serializer ()Lkotlinx/serialization/KSerializer; } +public final class io/modelcontextprotocol/kotlin/sdk/types/ResourceLike$DefaultImpls { + public static fun get_meta (Lio/modelcontextprotocol/kotlin/sdk/types/ResourceLike;)Lkotlinx/serialization/json/JsonObject; +} + public final class io/modelcontextprotocol/kotlin/sdk/types/ResourceLink : io/modelcontextprotocol/kotlin/sdk/types/ContentBlock, io/modelcontextprotocol/kotlin/sdk/types/ResourceLike { public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/types/ResourceLink$Companion; public fun (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Long;Ljava/lang/String;Ljava/util/List;Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/types/Annotations;Lkotlinx/serialization/json/JsonObject;)V @@ -3306,6 +3363,7 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/ResourceLink : io/mo public final fun getTitle ()Ljava/lang/String; public fun getType ()Lio/modelcontextprotocol/kotlin/sdk/types/ContentTypes; public final fun getUri ()Ljava/lang/String; + public fun get_meta ()Lkotlinx/serialization/json/JsonObject; public fun hashCode ()I public fun toString ()Ljava/lang/String; } @@ -3379,6 +3437,7 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/ResourceTemplate : i public final fun getName ()Ljava/lang/String; public final fun getTitle ()Ljava/lang/String; public final fun getUriTemplate ()Ljava/lang/String; + public fun get_meta ()Lkotlinx/serialization/json/JsonObject; public fun hashCode ()I public fun toString ()Ljava/lang/String; } @@ -3466,6 +3525,7 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/ResourceUpdatedNotif public fun equals (Ljava/lang/Object;)Z public fun getMeta ()Lkotlinx/serialization/json/JsonObject; public final fun getUri ()Ljava/lang/String; + public fun get_meta ()Lkotlinx/serialization/json/JsonObject; public fun hashCode ()I public fun toString ()Ljava/lang/String; } @@ -3511,6 +3571,7 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/Root : io/modelconte public fun getMeta ()Lkotlinx/serialization/json/JsonObject; public final fun getName ()Ljava/lang/String; public final fun getUri ()Ljava/lang/String; + public fun get_meta ()Lkotlinx/serialization/json/JsonObject; public fun hashCode ()I public fun toString ()Ljava/lang/String; } @@ -3744,6 +3805,10 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/ServerResult$Compani public final fun serializer ()Lkotlinx/serialization/KSerializer; } +public final class io/modelcontextprotocol/kotlin/sdk/types/ServerResult$DefaultImpls { + public static fun get_meta (Lio/modelcontextprotocol/kotlin/sdk/types/ServerResult;)Lkotlinx/serialization/json/JsonObject; +} + public final class io/modelcontextprotocol/kotlin/sdk/types/SetLevelRequest : io/modelcontextprotocol/kotlin/sdk/types/ClientRequest { public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/types/SetLevelRequest$Companion; public fun (Lio/modelcontextprotocol/kotlin/sdk/types/SetLevelRequestParams;)V @@ -3913,6 +3978,7 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/TextContent : io/mod public fun getMeta ()Lkotlinx/serialization/json/JsonObject; public final fun getText ()Ljava/lang/String; public fun getType ()Lio/modelcontextprotocol/kotlin/sdk/types/ContentTypes; + public fun get_meta ()Lkotlinx/serialization/json/JsonObject; public fun hashCode ()I public fun toString ()Ljava/lang/String; } @@ -3947,6 +4013,7 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/TextResourceContents public fun getMimeType ()Ljava/lang/String; public final fun getText ()Ljava/lang/String; public fun getUri ()Ljava/lang/String; + public fun get_meta ()Lkotlinx/serialization/json/JsonObject; public fun hashCode ()I public fun toString ()Ljava/lang/String; } @@ -3989,6 +4056,7 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/Tool : io/modelconte public final fun getName ()Ljava/lang/String; public final fun getOutputSchema ()Lio/modelcontextprotocol/kotlin/sdk/types/ToolSchema; public final fun getTitle ()Ljava/lang/String; + public fun get_meta ()Lkotlinx/serialization/json/JsonObject; public fun hashCode ()I public fun toString ()Ljava/lang/String; } @@ -4128,6 +4196,7 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/UnknownResourceConte public fun getMeta ()Lkotlinx/serialization/json/JsonObject; public fun getMimeType ()Ljava/lang/String; public fun getUri ()Ljava/lang/String; + public fun get_meta ()Lkotlinx/serialization/json/JsonObject; public fun hashCode ()I public fun toString ()Ljava/lang/String; } @@ -4211,9 +4280,14 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/UnsubscribeRequestPa public abstract interface class io/modelcontextprotocol/kotlin/sdk/types/WithMeta { public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/types/WithMeta$Companion; public abstract fun getMeta ()Lkotlinx/serialization/json/JsonObject; + public fun get_meta ()Lkotlinx/serialization/json/JsonObject; } public final class io/modelcontextprotocol/kotlin/sdk/types/WithMeta$Companion { public final fun serializer ()Lkotlinx/serialization/KSerializer; } +public final class io/modelcontextprotocol/kotlin/sdk/types/WithMeta$DefaultImpls { + public static fun get_meta (Lio/modelcontextprotocol/kotlin/sdk/types/WithMeta;)Lkotlinx/serialization/json/JsonObject; +} + diff --git a/kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/shared/AbstractTransport.kt b/kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/shared/AbstractTransport.kt new file mode 100644 index 0000000..55e09da --- /dev/null +++ b/kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/shared/AbstractTransport.kt @@ -0,0 +1,54 @@ +package io.modelcontextprotocol.kotlin.sdk.shared + +import io.modelcontextprotocol.kotlin.sdk.JSONRPCMessage +import kotlinx.coroutines.CompletableDeferred + +/** + * Implements [onClose], [onError] and [onMessage] functions of [Transport] providing + * corresponding [_onClose], [_onError] and [_onMessage] properties to use for an implementation. + */ +@Suppress("PropertyName") +public abstract class AbstractTransport : Transport { + protected var _onClose: (() -> Unit) = {} + private set + protected var _onError: ((Throwable) -> Unit) = {} + private set + + // to not skip messages + private val _onMessageInitialized = CompletableDeferred() + protected var _onMessage: (suspend ((JSONRPCMessage) -> Unit)) = { + _onMessageInitialized.await() + _onMessage.invoke(it) + } + private set + + override fun onClose(block: () -> Unit) { + val old = _onClose + _onClose = { + old() + block() + } + } + + override fun onError(block: (Throwable) -> Unit) { + val old = _onError + _onError = { e -> + old(e) + block(e) + } + } + + override fun onMessage(block: suspend (JSONRPCMessage) -> Unit) { + val old: suspend (JSONRPCMessage) -> Unit = when (_onMessageInitialized.isCompleted) { + true -> _onMessage + false -> { _ -> } + } + + _onMessage = { message -> + old(message) + block(message) + } + + _onMessageInitialized.complete(Unit) + } +} diff --git a/kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/shared/Transport.kt b/kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/shared/Transport.kt index 0ce13ec..aa698b4 100644 --- a/kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/shared/Transport.kt +++ b/kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/shared/Transport.kt @@ -1,7 +1,6 @@ package io.modelcontextprotocol.kotlin.sdk.shared import io.modelcontextprotocol.kotlin.sdk.types.JSONRPCMessage -import kotlinx.coroutines.CompletableDeferred /** * Describes the minimal contract for MCP transport that a client or server can communicate over. @@ -47,53 +46,3 @@ public interface Transport { */ public fun onMessage(block: suspend (JSONRPCMessage) -> Unit) } - -/** - * Implements [onClose], [onError] and [onMessage] functions of [Transport] providing - * corresponding [_onClose], [_onError] and [_onMessage] properties to use for an implementation. - */ -@Suppress("PropertyName") -public abstract class AbstractTransport : Transport { - protected var _onClose: (() -> Unit) = {} - private set - protected var _onError: ((Throwable) -> Unit) = {} - private set - - // to not skip messages - private val _onMessageInitialized = CompletableDeferred() - protected var _onMessage: (suspend ((JSONRPCMessage) -> Unit)) = { - _onMessageInitialized.await() - _onMessage.invoke(it) - } - private set - - override fun onClose(block: () -> Unit) { - val old = _onClose - _onClose = { - old() - block() - } - } - - override fun onError(block: (Throwable) -> Unit) { - val old = _onError - _onError = { e -> - old(e) - block(e) - } - } - - override fun onMessage(block: suspend (JSONRPCMessage) -> Unit) { - val old: suspend (JSONRPCMessage) -> Unit = when (_onMessageInitialized.isCompleted) { - true -> _onMessage - false -> { _ -> } - } - - _onMessage = { message -> - old(message) - block(message) - } - - _onMessageInitialized.complete(Unit) - } -} diff --git a/kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/types/common.kt b/kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/types/common.kt index 1066843..f715be7 100644 --- a/kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/types/common.kt +++ b/kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/types/common.kt @@ -1,5 +1,7 @@ package io.modelcontextprotocol.kotlin.sdk.types +import io.modelcontextprotocol.kotlin.sdk.types.Icon.Theme.Dark +import io.modelcontextprotocol.kotlin.sdk.types.Icon.Theme.Light import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import kotlinx.serialization.json.JsonObject @@ -27,6 +29,14 @@ public val SUPPORTED_PROTOCOL_VERSIONS: List = listOf( public sealed interface WithMeta { @SerialName("_meta") public val meta: JsonObject? + + @Deprecated( + message = "Use 'meta' instead.", + replaceWith = ReplaceWith("meta"), + ) + @Suppress("PropertyName", "VariableNaming") + public val _meta: JsonObject + get() = meta ?: EmptyJsonObject } // ============================================================================ diff --git a/kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/types/prompts.kt b/kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/types/prompts.kt index f1d352e..fcaa22a 100644 --- a/kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/types/prompts.kt +++ b/kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/types/prompts.kt @@ -113,6 +113,22 @@ public data class GetPromptRequest(override val params: GetPromptRequestParams) @EncodeDefault override val method: Method = Method.Defined.PromptsGet + @Deprecated( + message = "Use constructor with GetPromptRequestParams instead", + replaceWith = ReplaceWith("GetPromptRequest(GetPromptRequestParams(name, arguments, meta))"), + ) + public constructor( + name: String, + arguments: Map? = null, + meta: RequestMeta? = null, + ) : this( + GetPromptRequestParams( + name = name, + arguments = arguments, + meta = meta, + ), + ) + /** * The name of the prompt or prompt template to retrieve. */ diff --git a/kotlin-sdk-test/build.gradle.kts b/kotlin-sdk-test/build.gradle.kts index b8532e1..ec44904 100644 --- a/kotlin-sdk-test/build.gradle.kts +++ b/kotlin-sdk-test/build.gradle.kts @@ -14,6 +14,7 @@ kotlin { implementation(dependencies.platform(libs.ktor.bom)) implementation(project(":kotlin-sdk")) implementation(kotlin("test")) + implementation(libs.kotest.assertions.core) implementation(libs.kotest.assertions.json) implementation(libs.kotlin.logging) implementation(libs.kotlinx.coroutines.test) diff --git a/kotlin-sdk-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/server/OldSchemaServerPromptsTest.kt b/kotlin-sdk-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/server/OldSchemaServerPromptsTest.kt index fe0826b..1721bec 100644 --- a/kotlin-sdk-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/server/OldSchemaServerPromptsTest.kt +++ b/kotlin-sdk-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/server/OldSchemaServerPromptsTest.kt @@ -1,12 +1,24 @@ package io.modelcontextprotocol.kotlin.sdk.server +import io.kotest.assertions.throwables.shouldThrow +import io.kotest.matchers.collections.shouldBeEmpty +import io.kotest.matchers.collections.shouldContain +import io.kotest.matchers.collections.shouldContainExactly +import io.kotest.matchers.collections.shouldHaveSize +import io.kotest.matchers.nulls.shouldNotBeNull +import io.kotest.matchers.shouldBe +import io.kotest.matchers.throwable.shouldHaveMessage +import io.modelcontextprotocol.kotlin.sdk.EmptyJsonObject +import io.modelcontextprotocol.kotlin.sdk.GetPromptRequest import io.modelcontextprotocol.kotlin.sdk.GetPromptResult import io.modelcontextprotocol.kotlin.sdk.Implementation import io.modelcontextprotocol.kotlin.sdk.Method import io.modelcontextprotocol.kotlin.sdk.Prompt import io.modelcontextprotocol.kotlin.sdk.ServerCapabilities +import io.modelcontextprotocol.kotlin.sdk.types.McpException import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.test.runTest +import org.junit.jupiter.api.Nested import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertThrows import kotlin.test.assertEquals @@ -21,9 +33,51 @@ class OldSchemaServerPromptsTest : OldSchemaAbstractServerFeaturesTest() { ) @Test - fun `removePrompt should remove a prompt`() = runTest { + fun `Should list no prompts by default`() = runTest { + client.listPrompts() shouldNotBeNull { + prompts.shouldBeEmpty() + } + } + + @Test + fun `Should add a prompt`() = runTest { // Add a prompt - val testPrompt = Prompt("test-prompt", "Test Prompt", null) + val testPrompt = Prompt( + name = "test-prompt-with-custom-handler", + description = "Test Prompt", + arguments = null, + ) + val expectedPromptResult = GetPromptResult( + description = "Test prompt description", + messages = listOf(), + ) + + server.addPrompt(testPrompt) { + expectedPromptResult + } + + client.getPrompt( + GetPromptRequest( + name = "test-prompt-with-custom-handler", + arguments = null, + ), + ) shouldBe expectedPromptResult + + client.listPrompts() shouldNotBeNull { + prompts shouldContainExactly listOf(testPrompt) + nextCursor shouldBe null + _meta shouldBe EmptyJsonObject + } + } + + @Test + fun `Should remove a prompt`() = runTest { + // given + val testPrompt = Prompt( + name = "test-prompt-to-remove", + description = "Test Prompt", + arguments = null, + ) server.addPrompt(testPrompt) { GetPromptResult( description = "Test prompt description", @@ -31,15 +85,32 @@ class OldSchemaServerPromptsTest : OldSchemaAbstractServerFeaturesTest() { ) } - // Remove the prompt + client.listPrompts() shouldNotBeNull { + prompts shouldContain testPrompt + } + + // when val result = server.removePrompt(testPrompt.name) - // Verify the prompt was removed + // then assertTrue(result, "Prompt should be removed successfully") + val mcpException = shouldThrow { + client.getPrompt( + GetPromptRequest( + name = testPrompt.name, + arguments = null, + ), + ) + } + mcpException shouldHaveMessage "MCP error -32603: Prompt not found: ${testPrompt.name}" + + client.listPrompts() shouldNotBeNull { + prompts.firstOrNull { it.name == testPrompt.name } shouldBe null + } } @Test - fun `removePrompts should remove multiple prompts and send notification`() = runTest { + fun `Should remove multiple prompts and send notification`() = runTest { // Add prompts val testPrompt1 = Prompt("test-prompt-1", "Test Prompt 1", null) val testPrompt2 = Prompt("test-prompt-2", "Test Prompt 2", null) @@ -56,11 +127,17 @@ class OldSchemaServerPromptsTest : OldSchemaAbstractServerFeaturesTest() { ) } + client.listPrompts() shouldNotBeNull { + prompts shouldHaveSize 2 + } // Remove the prompts val result = server.removePrompts(listOf(testPrompt1.name, testPrompt2.name)) // Verify the prompts were removed assertEquals(2, result, "Both prompts should be removed") + client.listPrompts() shouldNotBeNull { + prompts.shouldBeEmpty() + } } @Test @@ -82,21 +159,55 @@ class OldSchemaServerPromptsTest : OldSchemaAbstractServerFeaturesTest() { assertFalse(promptListChangedNotificationReceived, "No notification should be sent when prompt doesn't exist") } - @Test - fun `removePrompt should throw when prompts capability is not supported`() = runTest { + @Nested + inner class NoPromptsCapabilitiesTests { // Create server without prompts capability - val serverOptions = ServerOptions( - capabilities = ServerCapabilities(), - ) - val server = Server( + val serverWithoutPrompts = Server( Implementation(name = "test server", version = "1.0"), - serverOptions, + ServerOptions( + capabilities = ServerCapabilities(), + ), ) - // Verify that removing a prompt throws an exception - val exception = assertThrows { - server.removePrompt("test-prompt") + @Test + fun `RemovePrompt should throw when prompts capability is not supported`() = runTest { + // Verify that removing a prompt throws an exception + val exception = assertThrows { + serverWithoutPrompts.removePrompt("test-prompt") + } + assertEquals("Server does not support prompts capability.", exception.message) + } + + @Test + fun `Remove Prompts should throw when prompts capability is not supported`() = runTest { + // Verify that removing a prompt throws an exception + val exception = assertThrows { + serverWithoutPrompts.removePrompts(emptyList()) + } + assertEquals("Server does not support prompts capability.", exception.message) + } + + @Test + fun `Add Prompt should throw when prompts capability is not supported`() = runTest { + // Verify that removing a prompt throws an exception + val exception = assertThrows { + serverWithoutPrompts.addPrompt(name = "test-prompt") { + GetPromptResult( + description = "Test prompt description", + messages = listOf(), + ) + } + } + assertEquals("Server does not support prompts capability.", exception.message) + } + + @Test + fun `Add Prompts should throw when prompts capability is not supported`() = runTest { + // Verify that removing a prompt throws an exception + val exception = assertThrows { + serverWithoutPrompts.addPrompts(emptyList()) + } + assertEquals("Server does not support prompts capability.", exception.message) } - assertEquals("Server does not support prompts capability.", exception.message) } }