Update notification schema and refactor tests package structure (#199)

This commit is contained in:
Pavel Gorgulov 2025-07-28 11:20:55 +02:00 • committed by GitHub
commit cb79ea7a13
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 1174 additions and 151 deletions

View file

@ -133,20 +133,16 @@ public final class io/modelcontextprotocol/kotlin/sdk/CallToolResultBase$Default
public static fun isError (Lio/modelcontextprotocol/kotlin/sdk/CallToolResultBase;)Ljava/lang/Boolean;
}
public final class io/modelcontextprotocol/kotlin/sdk/CancelledNotification : io/modelcontextprotocol/kotlin/sdk/ClientNotification, io/modelcontextprotocol/kotlin/sdk/ServerNotification, io/modelcontextprotocol/kotlin/sdk/WithMeta {
public final class io/modelcontextprotocol/kotlin/sdk/CancelledNotification : io/modelcontextprotocol/kotlin/sdk/ClientNotification, io/modelcontextprotocol/kotlin/sdk/ServerNotification {
public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/CancelledNotification$Companion;
public fun <init> (Lio/modelcontextprotocol/kotlin/sdk/RequestId;Ljava/lang/String;Lkotlinx/serialization/json/JsonObject;)V
public synthetic fun <init> (Lio/modelcontextprotocol/kotlin/sdk/RequestId;Ljava/lang/String;Lkotlinx/serialization/json/JsonObject;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun component1 ()Lio/modelcontextprotocol/kotlin/sdk/RequestId;
public final fun component2 ()Ljava/lang/String;
public final fun component3 ()Lkotlinx/serialization/json/JsonObject;
public final fun copy (Lio/modelcontextprotocol/kotlin/sdk/RequestId;Ljava/lang/String;Lkotlinx/serialization/json/JsonObject;)Lio/modelcontextprotocol/kotlin/sdk/CancelledNotification;
public static synthetic fun copy$default (Lio/modelcontextprotocol/kotlin/sdk/CancelledNotification;Lio/modelcontextprotocol/kotlin/sdk/RequestId;Ljava/lang/String;Lkotlinx/serialization/json/JsonObject;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/CancelledNotification;
public fun <init> (Lio/modelcontextprotocol/kotlin/sdk/CancelledNotification$Params;)V
public final fun component1 ()Lio/modelcontextprotocol/kotlin/sdk/CancelledNotification$Params;
public final fun copy (Lio/modelcontextprotocol/kotlin/sdk/CancelledNotification$Params;)Lio/modelcontextprotocol/kotlin/sdk/CancelledNotification;
public static synthetic fun copy$default (Lio/modelcontextprotocol/kotlin/sdk/CancelledNotification;Lio/modelcontextprotocol/kotlin/sdk/CancelledNotification$Params;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/CancelledNotification;
public fun equals (Ljava/lang/Object;)Z
public fun getMethod ()Lio/modelcontextprotocol/kotlin/sdk/Method;
public final fun getReason ()Ljava/lang/String;
public final fun getRequestId ()Lio/modelcontextprotocol/kotlin/sdk/RequestId;
public fun get_meta ()Lkotlinx/serialization/json/JsonObject;
public fun getParams ()Lio/modelcontextprotocol/kotlin/sdk/CancelledNotification$Params;
public synthetic fun getParams ()Lio/modelcontextprotocol/kotlin/sdk/NotificationParams;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}
@ -166,6 +162,38 @@ public final class io/modelcontextprotocol/kotlin/sdk/CancelledNotification$Comp
public final fun serializer ()Lkotlinx/serialization/KSerializer;
}
public final class io/modelcontextprotocol/kotlin/sdk/CancelledNotification$Params : io/modelcontextprotocol/kotlin/sdk/NotificationParams {
public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/CancelledNotification$Params$Companion;
public fun <init> (Lio/modelcontextprotocol/kotlin/sdk/RequestId;Ljava/lang/String;Lkotlinx/serialization/json/JsonObject;)V
public synthetic fun <init> (Lio/modelcontextprotocol/kotlin/sdk/RequestId;Ljava/lang/String;Lkotlinx/serialization/json/JsonObject;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun component1 ()Lio/modelcontextprotocol/kotlin/sdk/RequestId;
public final fun component2 ()Ljava/lang/String;
public final fun component3 ()Lkotlinx/serialization/json/JsonObject;
public final fun copy (Lio/modelcontextprotocol/kotlin/sdk/RequestId;Ljava/lang/String;Lkotlinx/serialization/json/JsonObject;)Lio/modelcontextprotocol/kotlin/sdk/CancelledNotification$Params;
public static synthetic fun copy$default (Lio/modelcontextprotocol/kotlin/sdk/CancelledNotification$Params;Lio/modelcontextprotocol/kotlin/sdk/RequestId;Ljava/lang/String;Lkotlinx/serialization/json/JsonObject;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/CancelledNotification$Params;
public fun equals (Ljava/lang/Object;)Z
public final fun getReason ()Ljava/lang/String;
public final fun getRequestId ()Lio/modelcontextprotocol/kotlin/sdk/RequestId;
public fun get_meta ()Lkotlinx/serialization/json/JsonObject;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}
public final synthetic class io/modelcontextprotocol/kotlin/sdk/CancelledNotification$Params$$serializer : kotlinx/serialization/internal/GeneratedSerializer {
public static final field INSTANCE Lio/modelcontextprotocol/kotlin/sdk/CancelledNotification$Params$$serializer;
public final fun childSerializers ()[Lkotlinx/serialization/KSerializer;
public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lio/modelcontextprotocol/kotlin/sdk/CancelledNotification$Params;
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/CancelledNotification$Params;)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/CancelledNotification$Params$Companion {
public final fun serializer ()Lkotlinx/serialization/KSerializer;
}
public final class io/modelcontextprotocol/kotlin/sdk/ClientCapabilities {
public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/ClientCapabilities$Companion;
public fun <init> ()V
@ -942,7 +970,17 @@ public final class io/modelcontextprotocol/kotlin/sdk/InitializeResult$Companion
public final class io/modelcontextprotocol/kotlin/sdk/InitializedNotification : io/modelcontextprotocol/kotlin/sdk/ClientNotification {
public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/InitializedNotification$Companion;
public fun <init> ()V
public fun <init> (Lio/modelcontextprotocol/kotlin/sdk/InitializedNotification$Params;)V
public synthetic fun <init> (Lio/modelcontextprotocol/kotlin/sdk/InitializedNotification$Params;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun component1 ()Lio/modelcontextprotocol/kotlin/sdk/InitializedNotification$Params;
public final fun copy (Lio/modelcontextprotocol/kotlin/sdk/InitializedNotification$Params;)Lio/modelcontextprotocol/kotlin/sdk/InitializedNotification;
public static synthetic fun copy$default (Lio/modelcontextprotocol/kotlin/sdk/InitializedNotification;Lio/modelcontextprotocol/kotlin/sdk/InitializedNotification$Params;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/InitializedNotification;
public fun equals (Ljava/lang/Object;)Z
public fun getMethod ()Lio/modelcontextprotocol/kotlin/sdk/Method;
public fun getParams ()Lio/modelcontextprotocol/kotlin/sdk/InitializedNotification$Params;
public synthetic fun getParams ()Lio/modelcontextprotocol/kotlin/sdk/NotificationParams;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}
public final synthetic class io/modelcontextprotocol/kotlin/sdk/InitializedNotification$$serializer : kotlinx/serialization/internal/GeneratedSerializer {
@ -960,6 +998,35 @@ public final class io/modelcontextprotocol/kotlin/sdk/InitializedNotification$Co
public final fun serializer ()Lkotlinx/serialization/KSerializer;
}
public final class io/modelcontextprotocol/kotlin/sdk/InitializedNotification$Params : io/modelcontextprotocol/kotlin/sdk/NotificationParams {
public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/InitializedNotification$Params$Companion;
public fun <init> ()V
public fun <init> (Lkotlinx/serialization/json/JsonObject;)V
public synthetic fun <init> (Lkotlinx/serialization/json/JsonObject;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun component1 ()Lkotlinx/serialization/json/JsonObject;
public final fun copy (Lkotlinx/serialization/json/JsonObject;)Lio/modelcontextprotocol/kotlin/sdk/InitializedNotification$Params;
public static synthetic fun copy$default (Lio/modelcontextprotocol/kotlin/sdk/InitializedNotification$Params;Lkotlinx/serialization/json/JsonObject;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/InitializedNotification$Params;
public fun equals (Ljava/lang/Object;)Z
public fun get_meta ()Lkotlinx/serialization/json/JsonObject;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}
public final synthetic class io/modelcontextprotocol/kotlin/sdk/InitializedNotification$Params$$serializer : kotlinx/serialization/internal/GeneratedSerializer {
public static final field INSTANCE Lio/modelcontextprotocol/kotlin/sdk/InitializedNotification$Params$$serializer;
public final fun childSerializers ()[Lkotlinx/serialization/KSerializer;
public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lio/modelcontextprotocol/kotlin/sdk/InitializedNotification$Params;
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/InitializedNotification$Params;)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/InitializedNotification$Params$Companion {
public final fun serializer ()Lkotlinx/serialization/KSerializer;
}
public final class io/modelcontextprotocol/kotlin/sdk/JSONRPCError : io/modelcontextprotocol/kotlin/sdk/JSONRPCMessage {
public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/JSONRPCError$Companion;
public fun <init> (Lio/modelcontextprotocol/kotlin/sdk/ErrorCode;Ljava/lang/String;Lkotlinx/serialization/json/JsonObject;)V
@ -1386,22 +1453,16 @@ public final class io/modelcontextprotocol/kotlin/sdk/LoggingLevel$Companion {
public final fun serializer ()Lkotlinx/serialization/KSerializer;
}
public final class io/modelcontextprotocol/kotlin/sdk/LoggingMessageNotification : io/modelcontextprotocol/kotlin/sdk/ServerNotification, io/modelcontextprotocol/kotlin/sdk/WithMeta {
public final class io/modelcontextprotocol/kotlin/sdk/LoggingMessageNotification : io/modelcontextprotocol/kotlin/sdk/ServerNotification {
public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/LoggingMessageNotification$Companion;
public fun <init> (Lio/modelcontextprotocol/kotlin/sdk/LoggingLevel;Ljava/lang/String;Lkotlinx/serialization/json/JsonObject;Lkotlinx/serialization/json/JsonObject;)V
public synthetic fun <init> (Lio/modelcontextprotocol/kotlin/sdk/LoggingLevel;Ljava/lang/String;Lkotlinx/serialization/json/JsonObject;Lkotlinx/serialization/json/JsonObject;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun component1 ()Lio/modelcontextprotocol/kotlin/sdk/LoggingLevel;
public final fun component2 ()Ljava/lang/String;
public final fun component3 ()Lkotlinx/serialization/json/JsonObject;
public final fun component4 ()Lkotlinx/serialization/json/JsonObject;
public final fun copy (Lio/modelcontextprotocol/kotlin/sdk/LoggingLevel;Ljava/lang/String;Lkotlinx/serialization/json/JsonObject;Lkotlinx/serialization/json/JsonObject;)Lio/modelcontextprotocol/kotlin/sdk/LoggingMessageNotification;
public static synthetic fun copy$default (Lio/modelcontextprotocol/kotlin/sdk/LoggingMessageNotification;Lio/modelcontextprotocol/kotlin/sdk/LoggingLevel;Ljava/lang/String;Lkotlinx/serialization/json/JsonObject;Lkotlinx/serialization/json/JsonObject;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/LoggingMessageNotification;
public fun <init> (Lio/modelcontextprotocol/kotlin/sdk/LoggingMessageNotification$Params;)V
public final fun component1 ()Lio/modelcontextprotocol/kotlin/sdk/LoggingMessageNotification$Params;
public final fun copy (Lio/modelcontextprotocol/kotlin/sdk/LoggingMessageNotification$Params;)Lio/modelcontextprotocol/kotlin/sdk/LoggingMessageNotification;
public static synthetic fun copy$default (Lio/modelcontextprotocol/kotlin/sdk/LoggingMessageNotification;Lio/modelcontextprotocol/kotlin/sdk/LoggingMessageNotification$Params;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/LoggingMessageNotification;
public fun equals (Ljava/lang/Object;)Z
public final fun getData ()Lkotlinx/serialization/json/JsonObject;
public final fun getLevel ()Lio/modelcontextprotocol/kotlin/sdk/LoggingLevel;
public final fun getLogger ()Ljava/lang/String;
public fun getMethod ()Lio/modelcontextprotocol/kotlin/sdk/Method;
public fun get_meta ()Lkotlinx/serialization/json/JsonObject;
public fun getParams ()Lio/modelcontextprotocol/kotlin/sdk/LoggingMessageNotification$Params;
public synthetic fun getParams ()Lio/modelcontextprotocol/kotlin/sdk/NotificationParams;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}
@ -1421,6 +1482,40 @@ public final class io/modelcontextprotocol/kotlin/sdk/LoggingMessageNotification
public final fun serializer ()Lkotlinx/serialization/KSerializer;
}
public final class io/modelcontextprotocol/kotlin/sdk/LoggingMessageNotification$Params : io/modelcontextprotocol/kotlin/sdk/NotificationParams {
public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/LoggingMessageNotification$Params$Companion;
public fun <init> (Lio/modelcontextprotocol/kotlin/sdk/LoggingLevel;Ljava/lang/String;Lkotlinx/serialization/json/JsonElement;Lkotlinx/serialization/json/JsonObject;)V
public synthetic fun <init> (Lio/modelcontextprotocol/kotlin/sdk/LoggingLevel;Ljava/lang/String;Lkotlinx/serialization/json/JsonElement;Lkotlinx/serialization/json/JsonObject;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun component1 ()Lio/modelcontextprotocol/kotlin/sdk/LoggingLevel;
public final fun component2 ()Ljava/lang/String;
public final fun component3 ()Lkotlinx/serialization/json/JsonElement;
public final fun component4 ()Lkotlinx/serialization/json/JsonObject;
public final fun copy (Lio/modelcontextprotocol/kotlin/sdk/LoggingLevel;Ljava/lang/String;Lkotlinx/serialization/json/JsonElement;Lkotlinx/serialization/json/JsonObject;)Lio/modelcontextprotocol/kotlin/sdk/LoggingMessageNotification$Params;
public static synthetic fun copy$default (Lio/modelcontextprotocol/kotlin/sdk/LoggingMessageNotification$Params;Lio/modelcontextprotocol/kotlin/sdk/LoggingLevel;Ljava/lang/String;Lkotlinx/serialization/json/JsonElement;Lkotlinx/serialization/json/JsonObject;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/LoggingMessageNotification$Params;
public fun equals (Ljava/lang/Object;)Z
public final fun getData ()Lkotlinx/serialization/json/JsonElement;
public final fun getLevel ()Lio/modelcontextprotocol/kotlin/sdk/LoggingLevel;
public final fun getLogger ()Ljava/lang/String;
public fun get_meta ()Lkotlinx/serialization/json/JsonObject;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}
public final synthetic class io/modelcontextprotocol/kotlin/sdk/LoggingMessageNotification$Params$$serializer : kotlinx/serialization/internal/GeneratedSerializer {
public static final field INSTANCE Lio/modelcontextprotocol/kotlin/sdk/LoggingMessageNotification$Params$$serializer;
public final fun childSerializers ()[Lkotlinx/serialization/KSerializer;
public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lio/modelcontextprotocol/kotlin/sdk/LoggingMessageNotification$Params;
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/LoggingMessageNotification$Params;)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/LoggingMessageNotification$Params$Companion {
public final fun serializer ()Lkotlinx/serialization/KSerializer;
}
public final class io/modelcontextprotocol/kotlin/sdk/LoggingMessageNotification$SetLevelRequest : io/modelcontextprotocol/kotlin/sdk/ClientRequest, io/modelcontextprotocol/kotlin/sdk/WithMeta {
public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/LoggingMessageNotification$SetLevelRequest$Companion;
public fun <init> (Lio/modelcontextprotocol/kotlin/sdk/LoggingLevel;Lkotlinx/serialization/json/JsonObject;)V
@ -1587,12 +1682,21 @@ public final class io/modelcontextprotocol/kotlin/sdk/ModelPreferences$Companion
public abstract interface class io/modelcontextprotocol/kotlin/sdk/Notification {
public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/Notification$Companion;
public abstract fun getMethod ()Lio/modelcontextprotocol/kotlin/sdk/Method;
public abstract fun getParams ()Lio/modelcontextprotocol/kotlin/sdk/NotificationParams;
}
public final class io/modelcontextprotocol/kotlin/sdk/Notification$Companion {
public final fun serializer ()Lkotlinx/serialization/KSerializer;
}
public abstract interface class io/modelcontextprotocol/kotlin/sdk/NotificationParams : io/modelcontextprotocol/kotlin/sdk/WithMeta {
public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/NotificationParams$Companion;
}
public final class io/modelcontextprotocol/kotlin/sdk/NotificationParams$Companion {
public final fun serializer ()Lkotlinx/serialization/KSerializer;
}
public abstract interface class io/modelcontextprotocol/kotlin/sdk/PaginatedRequest : io/modelcontextprotocol/kotlin/sdk/Request, io/modelcontextprotocol/kotlin/sdk/WithMeta {
public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/PaginatedRequest$Companion;
public abstract fun getCursor ()Ljava/lang/String;
@ -1635,10 +1739,10 @@ public final class io/modelcontextprotocol/kotlin/sdk/PingRequest$Companion {
public class io/modelcontextprotocol/kotlin/sdk/Progress : io/modelcontextprotocol/kotlin/sdk/ProgressBase {
public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/Progress$Companion;
public synthetic fun <init> (IILjava/lang/Double;Ljava/lang/String;Lkotlinx/serialization/internal/SerializationConstructorMarker;)V
public fun <init> (ILjava/lang/Double;Ljava/lang/String;)V
public fun <init> (DLjava/lang/Double;Ljava/lang/String;)V
public synthetic fun <init> (IDLjava/lang/Double;Ljava/lang/String;Lkotlinx/serialization/internal/SerializationConstructorMarker;)V
public fun getMessage ()Ljava/lang/String;
public fun getProgress ()I
public fun getProgress ()D
public fun getTotal ()Ljava/lang/Double;
public static final synthetic fun write$Self (Lio/modelcontextprotocol/kotlin/sdk/Progress;Lkotlinx/serialization/encoding/CompositeEncoder;Lkotlinx/serialization/descriptors/SerialDescriptor;)V
}
@ -1661,7 +1765,7 @@ public final class io/modelcontextprotocol/kotlin/sdk/Progress$Companion {
public abstract interface class io/modelcontextprotocol/kotlin/sdk/ProgressBase {
public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/ProgressBase$Companion;
public abstract fun getMessage ()Ljava/lang/String;
public abstract fun getProgress ()I
public abstract fun getProgress ()D
public abstract fun getTotal ()Ljava/lang/Double;
}
@ -1669,24 +1773,16 @@ public final class io/modelcontextprotocol/kotlin/sdk/ProgressBase$Companion {
public final fun serializer ()Lkotlinx/serialization/KSerializer;
}
public final class io/modelcontextprotocol/kotlin/sdk/ProgressNotification : io/modelcontextprotocol/kotlin/sdk/ClientNotification, io/modelcontextprotocol/kotlin/sdk/ProgressBase, io/modelcontextprotocol/kotlin/sdk/ServerNotification {
public final class io/modelcontextprotocol/kotlin/sdk/ProgressNotification : io/modelcontextprotocol/kotlin/sdk/ClientNotification, io/modelcontextprotocol/kotlin/sdk/ServerNotification {
public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/ProgressNotification$Companion;
public fun <init> (ILio/modelcontextprotocol/kotlin/sdk/RequestId;Lkotlinx/serialization/json/JsonObject;Ljava/lang/Double;Ljava/lang/String;)V
public synthetic fun <init> (ILio/modelcontextprotocol/kotlin/sdk/RequestId;Lkotlinx/serialization/json/JsonObject;Ljava/lang/Double;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun component1 ()I
public final fun component2 ()Lio/modelcontextprotocol/kotlin/sdk/RequestId;
public final fun component3 ()Lkotlinx/serialization/json/JsonObject;
public final fun component4 ()Ljava/lang/Double;
public final fun component5 ()Ljava/lang/String;
public final fun copy (ILio/modelcontextprotocol/kotlin/sdk/RequestId;Lkotlinx/serialization/json/JsonObject;Ljava/lang/Double;Ljava/lang/String;)Lio/modelcontextprotocol/kotlin/sdk/ProgressNotification;
public static synthetic fun copy$default (Lio/modelcontextprotocol/kotlin/sdk/ProgressNotification;ILio/modelcontextprotocol/kotlin/sdk/RequestId;Lkotlinx/serialization/json/JsonObject;Ljava/lang/Double;Ljava/lang/String;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/ProgressNotification;
public fun <init> (Lio/modelcontextprotocol/kotlin/sdk/ProgressNotification$Params;)V
public final fun component1 ()Lio/modelcontextprotocol/kotlin/sdk/ProgressNotification$Params;
public final fun copy (Lio/modelcontextprotocol/kotlin/sdk/ProgressNotification$Params;)Lio/modelcontextprotocol/kotlin/sdk/ProgressNotification;
public static synthetic fun copy$default (Lio/modelcontextprotocol/kotlin/sdk/ProgressNotification;Lio/modelcontextprotocol/kotlin/sdk/ProgressNotification$Params;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/ProgressNotification;
public fun equals (Ljava/lang/Object;)Z
public fun getMessage ()Ljava/lang/String;
public fun getMethod ()Lio/modelcontextprotocol/kotlin/sdk/Method;
public fun getProgress ()I
public final fun getProgressToken ()Lio/modelcontextprotocol/kotlin/sdk/RequestId;
public fun getTotal ()Ljava/lang/Double;
public final fun get_meta ()Lkotlinx/serialization/json/JsonObject;
public synthetic fun getParams ()Lio/modelcontextprotocol/kotlin/sdk/NotificationParams;
public fun getParams ()Lio/modelcontextprotocol/kotlin/sdk/ProgressNotification$Params;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}
@ -1706,6 +1802,42 @@ public final class io/modelcontextprotocol/kotlin/sdk/ProgressNotification$Compa
public final fun serializer ()Lkotlinx/serialization/KSerializer;
}
public final class io/modelcontextprotocol/kotlin/sdk/ProgressNotification$Params : io/modelcontextprotocol/kotlin/sdk/NotificationParams, io/modelcontextprotocol/kotlin/sdk/ProgressBase {
public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/ProgressNotification$Params$Companion;
public fun <init> (DLio/modelcontextprotocol/kotlin/sdk/RequestId;Ljava/lang/Double;Ljava/lang/String;Lkotlinx/serialization/json/JsonObject;)V
public synthetic fun <init> (DLio/modelcontextprotocol/kotlin/sdk/RequestId;Ljava/lang/Double;Ljava/lang/String;Lkotlinx/serialization/json/JsonObject;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun component1 ()D
public final fun component2 ()Lio/modelcontextprotocol/kotlin/sdk/RequestId;
public final fun component3 ()Ljava/lang/Double;
public final fun component4 ()Ljava/lang/String;
public final fun component5 ()Lkotlinx/serialization/json/JsonObject;
public final fun copy (DLio/modelcontextprotocol/kotlin/sdk/RequestId;Ljava/lang/Double;Ljava/lang/String;Lkotlinx/serialization/json/JsonObject;)Lio/modelcontextprotocol/kotlin/sdk/ProgressNotification$Params;
public static synthetic fun copy$default (Lio/modelcontextprotocol/kotlin/sdk/ProgressNotification$Params;DLio/modelcontextprotocol/kotlin/sdk/RequestId;Ljava/lang/Double;Ljava/lang/String;Lkotlinx/serialization/json/JsonObject;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/ProgressNotification$Params;
public fun equals (Ljava/lang/Object;)Z
public fun getMessage ()Ljava/lang/String;
public fun getProgress ()D
public final fun getProgressToken ()Lio/modelcontextprotocol/kotlin/sdk/RequestId;
public fun getTotal ()Ljava/lang/Double;
public fun get_meta ()Lkotlinx/serialization/json/JsonObject;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}
public final synthetic class io/modelcontextprotocol/kotlin/sdk/ProgressNotification$Params$$serializer : kotlinx/serialization/internal/GeneratedSerializer {
public static final field INSTANCE Lio/modelcontextprotocol/kotlin/sdk/ProgressNotification$Params$$serializer;
public final fun childSerializers ()[Lkotlinx/serialization/KSerializer;
public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lio/modelcontextprotocol/kotlin/sdk/ProgressNotification$Params;
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/ProgressNotification$Params;)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/ProgressNotification$Params$Companion {
public final fun serializer ()Lkotlinx/serialization/KSerializer;
}
public final class io/modelcontextprotocol/kotlin/sdk/Prompt {
public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/Prompt$Companion;
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/util/List;)V
@ -1763,7 +1895,17 @@ public final class io/modelcontextprotocol/kotlin/sdk/PromptArgument$Companion {
public final class io/modelcontextprotocol/kotlin/sdk/PromptListChangedNotification : io/modelcontextprotocol/kotlin/sdk/ServerNotification {
public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/PromptListChangedNotification$Companion;
public fun <init> ()V
public fun <init> (Lio/modelcontextprotocol/kotlin/sdk/PromptListChangedNotification$Params;)V
public synthetic fun <init> (Lio/modelcontextprotocol/kotlin/sdk/PromptListChangedNotification$Params;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun component1 ()Lio/modelcontextprotocol/kotlin/sdk/PromptListChangedNotification$Params;
public final fun copy (Lio/modelcontextprotocol/kotlin/sdk/PromptListChangedNotification$Params;)Lio/modelcontextprotocol/kotlin/sdk/PromptListChangedNotification;
public static synthetic fun copy$default (Lio/modelcontextprotocol/kotlin/sdk/PromptListChangedNotification;Lio/modelcontextprotocol/kotlin/sdk/PromptListChangedNotification$Params;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/PromptListChangedNotification;
public fun equals (Ljava/lang/Object;)Z
public fun getMethod ()Lio/modelcontextprotocol/kotlin/sdk/Method;
public synthetic fun getParams ()Lio/modelcontextprotocol/kotlin/sdk/NotificationParams;
public fun getParams ()Lio/modelcontextprotocol/kotlin/sdk/PromptListChangedNotification$Params;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}
public final synthetic class io/modelcontextprotocol/kotlin/sdk/PromptListChangedNotification$$serializer : kotlinx/serialization/internal/GeneratedSerializer {
@ -1781,6 +1923,35 @@ public final class io/modelcontextprotocol/kotlin/sdk/PromptListChangedNotificat
public final fun serializer ()Lkotlinx/serialization/KSerializer;
}
public final class io/modelcontextprotocol/kotlin/sdk/PromptListChangedNotification$Params : io/modelcontextprotocol/kotlin/sdk/NotificationParams {
public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/PromptListChangedNotification$Params$Companion;
public fun <init> ()V
public fun <init> (Lkotlinx/serialization/json/JsonObject;)V
public synthetic fun <init> (Lkotlinx/serialization/json/JsonObject;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun component1 ()Lkotlinx/serialization/json/JsonObject;
public final fun copy (Lkotlinx/serialization/json/JsonObject;)Lio/modelcontextprotocol/kotlin/sdk/PromptListChangedNotification$Params;
public static synthetic fun copy$default (Lio/modelcontextprotocol/kotlin/sdk/PromptListChangedNotification$Params;Lkotlinx/serialization/json/JsonObject;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/PromptListChangedNotification$Params;
public fun equals (Ljava/lang/Object;)Z
public fun get_meta ()Lkotlinx/serialization/json/JsonObject;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}
public final synthetic class io/modelcontextprotocol/kotlin/sdk/PromptListChangedNotification$Params$$serializer : kotlinx/serialization/internal/GeneratedSerializer {
public static final field INSTANCE Lio/modelcontextprotocol/kotlin/sdk/PromptListChangedNotification$Params$$serializer;
public final fun childSerializers ()[Lkotlinx/serialization/KSerializer;
public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lio/modelcontextprotocol/kotlin/sdk/PromptListChangedNotification$Params;
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/PromptListChangedNotification$Params;)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/PromptListChangedNotification$Params$Companion {
public final fun serializer ()Lkotlinx/serialization/KSerializer;
}
public final class io/modelcontextprotocol/kotlin/sdk/PromptMessage {
public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/PromptMessage$Companion;
public fun <init> (Lio/modelcontextprotocol/kotlin/sdk/Role;Lio/modelcontextprotocol/kotlin/sdk/PromptMessageContent;)V
@ -2053,7 +2224,17 @@ public final class io/modelcontextprotocol/kotlin/sdk/ResourceContents$Companion
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 <init> ()V
public fun <init> (Lio/modelcontextprotocol/kotlin/sdk/ResourceListChangedNotification$Params;)V
public synthetic fun <init> (Lio/modelcontextprotocol/kotlin/sdk/ResourceListChangedNotification$Params;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun component1 ()Lio/modelcontextprotocol/kotlin/sdk/ResourceListChangedNotification$Params;
public final fun copy (Lio/modelcontextprotocol/kotlin/sdk/ResourceListChangedNotification$Params;)Lio/modelcontextprotocol/kotlin/sdk/ResourceListChangedNotification;
public static synthetic fun copy$default (Lio/modelcontextprotocol/kotlin/sdk/ResourceListChangedNotification;Lio/modelcontextprotocol/kotlin/sdk/ResourceListChangedNotification$Params;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/ResourceListChangedNotification;
public fun equals (Ljava/lang/Object;)Z
public fun getMethod ()Lio/modelcontextprotocol/kotlin/sdk/Method;
public synthetic fun getParams ()Lio/modelcontextprotocol/kotlin/sdk/NotificationParams;
public fun getParams ()Lio/modelcontextprotocol/kotlin/sdk/ResourceListChangedNotification$Params;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}
public final synthetic class io/modelcontextprotocol/kotlin/sdk/ResourceListChangedNotification$$serializer : kotlinx/serialization/internal/GeneratedSerializer {
@ -2071,6 +2252,35 @@ public final class io/modelcontextprotocol/kotlin/sdk/ResourceListChangedNotific
public final fun serializer ()Lkotlinx/serialization/KSerializer;
}
public final class io/modelcontextprotocol/kotlin/sdk/ResourceListChangedNotification$Params : io/modelcontextprotocol/kotlin/sdk/NotificationParams {
public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/ResourceListChangedNotification$Params$Companion;
public fun <init> ()V
public fun <init> (Lkotlinx/serialization/json/JsonObject;)V
public synthetic fun <init> (Lkotlinx/serialization/json/JsonObject;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun component1 ()Lkotlinx/serialization/json/JsonObject;
public final fun copy (Lkotlinx/serialization/json/JsonObject;)Lio/modelcontextprotocol/kotlin/sdk/ResourceListChangedNotification$Params;
public static synthetic fun copy$default (Lio/modelcontextprotocol/kotlin/sdk/ResourceListChangedNotification$Params;Lkotlinx/serialization/json/JsonObject;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/ResourceListChangedNotification$Params;
public fun equals (Ljava/lang/Object;)Z
public fun get_meta ()Lkotlinx/serialization/json/JsonObject;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}
public final synthetic class io/modelcontextprotocol/kotlin/sdk/ResourceListChangedNotification$Params$$serializer : kotlinx/serialization/internal/GeneratedSerializer {
public static final field INSTANCE Lio/modelcontextprotocol/kotlin/sdk/ResourceListChangedNotification$Params$$serializer;
public final fun childSerializers ()[Lkotlinx/serialization/KSerializer;
public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lio/modelcontextprotocol/kotlin/sdk/ResourceListChangedNotification$Params;
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/ResourceListChangedNotification$Params;)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/ResourceListChangedNotification$Params$Companion {
public final fun serializer ()Lkotlinx/serialization/KSerializer;
}
public final class io/modelcontextprotocol/kotlin/sdk/ResourceReference : io/modelcontextprotocol/kotlin/sdk/Reference {
public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/ResourceReference$Companion;
public static final field TYPE Ljava/lang/String;
@ -2133,18 +2343,16 @@ public final class io/modelcontextprotocol/kotlin/sdk/ResourceTemplate$Companion
public final fun serializer ()Lkotlinx/serialization/KSerializer;
}
public final class io/modelcontextprotocol/kotlin/sdk/ResourceUpdatedNotification : io/modelcontextprotocol/kotlin/sdk/ServerNotification, io/modelcontextprotocol/kotlin/sdk/WithMeta {
public final class io/modelcontextprotocol/kotlin/sdk/ResourceUpdatedNotification : io/modelcontextprotocol/kotlin/sdk/ServerNotification {
public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/ResourceUpdatedNotification$Companion;
public fun <init> (Ljava/lang/String;Lkotlinx/serialization/json/JsonObject;)V
public synthetic fun <init> (Ljava/lang/String;Lkotlinx/serialization/json/JsonObject;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun component1 ()Ljava/lang/String;
public final fun component2 ()Lkotlinx/serialization/json/JsonObject;
public final fun copy (Ljava/lang/String;Lkotlinx/serialization/json/JsonObject;)Lio/modelcontextprotocol/kotlin/sdk/ResourceUpdatedNotification;
public static synthetic fun copy$default (Lio/modelcontextprotocol/kotlin/sdk/ResourceUpdatedNotification;Ljava/lang/String;Lkotlinx/serialization/json/JsonObject;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/ResourceUpdatedNotification;
public fun <init> (Lio/modelcontextprotocol/kotlin/sdk/ResourceUpdatedNotification$Params;)V
public final fun component1 ()Lio/modelcontextprotocol/kotlin/sdk/ResourceUpdatedNotification$Params;
public final fun copy (Lio/modelcontextprotocol/kotlin/sdk/ResourceUpdatedNotification$Params;)Lio/modelcontextprotocol/kotlin/sdk/ResourceUpdatedNotification;
public static synthetic fun copy$default (Lio/modelcontextprotocol/kotlin/sdk/ResourceUpdatedNotification;Lio/modelcontextprotocol/kotlin/sdk/ResourceUpdatedNotification$Params;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/ResourceUpdatedNotification;
public fun equals (Ljava/lang/Object;)Z
public fun getMethod ()Lio/modelcontextprotocol/kotlin/sdk/Method;
public final fun getUri ()Ljava/lang/String;
public fun get_meta ()Lkotlinx/serialization/json/JsonObject;
public synthetic fun getParams ()Lio/modelcontextprotocol/kotlin/sdk/NotificationParams;
public fun getParams ()Lio/modelcontextprotocol/kotlin/sdk/ResourceUpdatedNotification$Params;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}
@ -2164,6 +2372,36 @@ public final class io/modelcontextprotocol/kotlin/sdk/ResourceUpdatedNotificatio
public final fun serializer ()Lkotlinx/serialization/KSerializer;
}
public final class io/modelcontextprotocol/kotlin/sdk/ResourceUpdatedNotification$Params : io/modelcontextprotocol/kotlin/sdk/NotificationParams {
public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/ResourceUpdatedNotification$Params$Companion;
public fun <init> (Ljava/lang/String;Lkotlinx/serialization/json/JsonObject;)V
public synthetic fun <init> (Ljava/lang/String;Lkotlinx/serialization/json/JsonObject;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun component1 ()Ljava/lang/String;
public final fun component2 ()Lkotlinx/serialization/json/JsonObject;
public final fun copy (Ljava/lang/String;Lkotlinx/serialization/json/JsonObject;)Lio/modelcontextprotocol/kotlin/sdk/ResourceUpdatedNotification$Params;
public static synthetic fun copy$default (Lio/modelcontextprotocol/kotlin/sdk/ResourceUpdatedNotification$Params;Ljava/lang/String;Lkotlinx/serialization/json/JsonObject;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/ResourceUpdatedNotification$Params;
public fun equals (Ljava/lang/Object;)Z
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;
}
public final synthetic class io/modelcontextprotocol/kotlin/sdk/ResourceUpdatedNotification$Params$$serializer : kotlinx/serialization/internal/GeneratedSerializer {
public static final field INSTANCE Lio/modelcontextprotocol/kotlin/sdk/ResourceUpdatedNotification$Params$$serializer;
public final fun childSerializers ()[Lkotlinx/serialization/KSerializer;
public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lio/modelcontextprotocol/kotlin/sdk/ResourceUpdatedNotification$Params;
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/ResourceUpdatedNotification$Params;)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/ResourceUpdatedNotification$Params$Companion {
public final fun serializer ()Lkotlinx/serialization/KSerializer;
}
public final class io/modelcontextprotocol/kotlin/sdk/Role : java/lang/Enum {
public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/Role$Companion;
public static final field assistant Lio/modelcontextprotocol/kotlin/sdk/Role;
@ -2209,7 +2447,17 @@ public final class io/modelcontextprotocol/kotlin/sdk/Root$Companion {
public final class io/modelcontextprotocol/kotlin/sdk/RootsListChangedNotification : io/modelcontextprotocol/kotlin/sdk/ClientNotification {
public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/RootsListChangedNotification$Companion;
public fun <init> ()V
public fun <init> (Lio/modelcontextprotocol/kotlin/sdk/RootsListChangedNotification$Params;)V
public synthetic fun <init> (Lio/modelcontextprotocol/kotlin/sdk/RootsListChangedNotification$Params;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun component1 ()Lio/modelcontextprotocol/kotlin/sdk/RootsListChangedNotification$Params;
public final fun copy (Lio/modelcontextprotocol/kotlin/sdk/RootsListChangedNotification$Params;)Lio/modelcontextprotocol/kotlin/sdk/RootsListChangedNotification;
public static synthetic fun copy$default (Lio/modelcontextprotocol/kotlin/sdk/RootsListChangedNotification;Lio/modelcontextprotocol/kotlin/sdk/RootsListChangedNotification$Params;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/RootsListChangedNotification;
public fun equals (Ljava/lang/Object;)Z
public fun getMethod ()Lio/modelcontextprotocol/kotlin/sdk/Method;
public synthetic fun getParams ()Lio/modelcontextprotocol/kotlin/sdk/NotificationParams;
public fun getParams ()Lio/modelcontextprotocol/kotlin/sdk/RootsListChangedNotification$Params;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}
public final synthetic class io/modelcontextprotocol/kotlin/sdk/RootsListChangedNotification$$serializer : kotlinx/serialization/internal/GeneratedSerializer {
@ -2227,6 +2475,35 @@ public final class io/modelcontextprotocol/kotlin/sdk/RootsListChangedNotificati
public final fun serializer ()Lkotlinx/serialization/KSerializer;
}
public final class io/modelcontextprotocol/kotlin/sdk/RootsListChangedNotification$Params : io/modelcontextprotocol/kotlin/sdk/NotificationParams {
public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/RootsListChangedNotification$Params$Companion;
public fun <init> ()V
public fun <init> (Lkotlinx/serialization/json/JsonObject;)V
public synthetic fun <init> (Lkotlinx/serialization/json/JsonObject;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun component1 ()Lkotlinx/serialization/json/JsonObject;
public final fun copy (Lkotlinx/serialization/json/JsonObject;)Lio/modelcontextprotocol/kotlin/sdk/RootsListChangedNotification$Params;
public static synthetic fun copy$default (Lio/modelcontextprotocol/kotlin/sdk/RootsListChangedNotification$Params;Lkotlinx/serialization/json/JsonObject;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/RootsListChangedNotification$Params;
public fun equals (Ljava/lang/Object;)Z
public fun get_meta ()Lkotlinx/serialization/json/JsonObject;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}
public final synthetic class io/modelcontextprotocol/kotlin/sdk/RootsListChangedNotification$Params$$serializer : kotlinx/serialization/internal/GeneratedSerializer {
public static final field INSTANCE Lio/modelcontextprotocol/kotlin/sdk/RootsListChangedNotification$Params$$serializer;
public final fun childSerializers ()[Lkotlinx/serialization/KSerializer;
public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lio/modelcontextprotocol/kotlin/sdk/RootsListChangedNotification$Params;
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/RootsListChangedNotification$Params;)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/RootsListChangedNotification$Params$Companion {
public final fun serializer ()Lkotlinx/serialization/KSerializer;
}
public final class io/modelcontextprotocol/kotlin/sdk/SamplingMessage {
public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/SamplingMessage$Companion;
public fun <init> (Lio/modelcontextprotocol/kotlin/sdk/Role;Lio/modelcontextprotocol/kotlin/sdk/PromptMessageContentMultimodal;)V
@ -2696,7 +2973,17 @@ public final class io/modelcontextprotocol/kotlin/sdk/ToolAnnotations$Companion
public final class io/modelcontextprotocol/kotlin/sdk/ToolListChangedNotification : io/modelcontextprotocol/kotlin/sdk/ServerNotification {
public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/ToolListChangedNotification$Companion;
public fun <init> ()V
public fun <init> (Lio/modelcontextprotocol/kotlin/sdk/ToolListChangedNotification$Params;)V
public synthetic fun <init> (Lio/modelcontextprotocol/kotlin/sdk/ToolListChangedNotification$Params;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun component1 ()Lio/modelcontextprotocol/kotlin/sdk/ToolListChangedNotification$Params;
public final fun copy (Lio/modelcontextprotocol/kotlin/sdk/ToolListChangedNotification$Params;)Lio/modelcontextprotocol/kotlin/sdk/ToolListChangedNotification;
public static synthetic fun copy$default (Lio/modelcontextprotocol/kotlin/sdk/ToolListChangedNotification;Lio/modelcontextprotocol/kotlin/sdk/ToolListChangedNotification$Params;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/ToolListChangedNotification;
public fun equals (Ljava/lang/Object;)Z
public fun getMethod ()Lio/modelcontextprotocol/kotlin/sdk/Method;
public synthetic fun getParams ()Lio/modelcontextprotocol/kotlin/sdk/NotificationParams;
public fun getParams ()Lio/modelcontextprotocol/kotlin/sdk/ToolListChangedNotification$Params;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}
public final synthetic class io/modelcontextprotocol/kotlin/sdk/ToolListChangedNotification$$serializer : kotlinx/serialization/internal/GeneratedSerializer {
@ -2714,6 +3001,35 @@ public final class io/modelcontextprotocol/kotlin/sdk/ToolListChangedNotificatio
public final fun serializer ()Lkotlinx/serialization/KSerializer;
}
public final class io/modelcontextprotocol/kotlin/sdk/ToolListChangedNotification$Params : io/modelcontextprotocol/kotlin/sdk/NotificationParams {
public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/ToolListChangedNotification$Params$Companion;
public fun <init> ()V
public fun <init> (Lkotlinx/serialization/json/JsonObject;)V
public synthetic fun <init> (Lkotlinx/serialization/json/JsonObject;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun component1 ()Lkotlinx/serialization/json/JsonObject;
public final fun copy (Lkotlinx/serialization/json/JsonObject;)Lio/modelcontextprotocol/kotlin/sdk/ToolListChangedNotification$Params;
public static synthetic fun copy$default (Lio/modelcontextprotocol/kotlin/sdk/ToolListChangedNotification$Params;Lkotlinx/serialization/json/JsonObject;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/ToolListChangedNotification$Params;
public fun equals (Ljava/lang/Object;)Z
public fun get_meta ()Lkotlinx/serialization/json/JsonObject;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}
public final synthetic class io/modelcontextprotocol/kotlin/sdk/ToolListChangedNotification$Params$$serializer : kotlinx/serialization/internal/GeneratedSerializer {
public static final field INSTANCE Lio/modelcontextprotocol/kotlin/sdk/ToolListChangedNotification$Params$$serializer;
public final fun childSerializers ()[Lkotlinx/serialization/KSerializer;
public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lio/modelcontextprotocol/kotlin/sdk/ToolListChangedNotification$Params;
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/ToolListChangedNotification$Params;)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/ToolListChangedNotification$Params$Companion {
public final fun serializer ()Lkotlinx/serialization/KSerializer;
}
public final class io/modelcontextprotocol/kotlin/sdk/TypesKt {
public static final field JSONRPC_VERSION Ljava/lang/String;
public static final field LATEST_PROTOCOL_VERSION Ljava/lang/String;
@ -2756,12 +3072,15 @@ public final class io/modelcontextprotocol/kotlin/sdk/UnknownContent$Companion {
public final class io/modelcontextprotocol/kotlin/sdk/UnknownMethodRequestOrNotification : io/modelcontextprotocol/kotlin/sdk/ClientNotification, io/modelcontextprotocol/kotlin/sdk/ClientRequest, io/modelcontextprotocol/kotlin/sdk/ServerNotification, io/modelcontextprotocol/kotlin/sdk/ServerRequest {
public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/UnknownMethodRequestOrNotification$Companion;
public fun <init> (Lio/modelcontextprotocol/kotlin/sdk/Method;)V
public fun <init> (Lio/modelcontextprotocol/kotlin/sdk/Method;Lio/modelcontextprotocol/kotlin/sdk/NotificationParams;)V
public synthetic fun <init> (Lio/modelcontextprotocol/kotlin/sdk/Method;Lio/modelcontextprotocol/kotlin/sdk/NotificationParams;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun component1 ()Lio/modelcontextprotocol/kotlin/sdk/Method;
public final fun copy (Lio/modelcontextprotocol/kotlin/sdk/Method;)Lio/modelcontextprotocol/kotlin/sdk/UnknownMethodRequestOrNotification;
public static synthetic fun copy$default (Lio/modelcontextprotocol/kotlin/sdk/UnknownMethodRequestOrNotification;Lio/modelcontextprotocol/kotlin/sdk/Method;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/UnknownMethodRequestOrNotification;
public final fun component2 ()Lio/modelcontextprotocol/kotlin/sdk/NotificationParams;
public final fun copy (Lio/modelcontextprotocol/kotlin/sdk/Method;Lio/modelcontextprotocol/kotlin/sdk/NotificationParams;)Lio/modelcontextprotocol/kotlin/sdk/UnknownMethodRequestOrNotification;
public static synthetic fun copy$default (Lio/modelcontextprotocol/kotlin/sdk/UnknownMethodRequestOrNotification;Lio/modelcontextprotocol/kotlin/sdk/Method;Lio/modelcontextprotocol/kotlin/sdk/NotificationParams;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/UnknownMethodRequestOrNotification;
public fun equals (Ljava/lang/Object;)Z
public fun getMethod ()Lio/modelcontextprotocol/kotlin/sdk/Method;
public fun getParams ()Lio/modelcontextprotocol/kotlin/sdk/NotificationParams;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}

View file

@ -558,7 +558,7 @@ public open class Server(
* @param params The logging message notification parameters.
*/
public suspend fun sendLoggingMessage(params: LoggingMessageNotification) {
logger.trace { "Sending logging message: ${params.data}" }
logger.trace { "Sending logging message: ${params.params.data}" }
notification(params)
}
@ -568,7 +568,7 @@ public open class Server(
* @param params Details of the updated resource.
*/
public suspend fun sendResourceUpdated(params: ResourceUpdatedNotification) {
logger.debug { "Sending resource updated notification for: ${params.uri}" }
logger.debug { "Sending resource updated notification for: ${params.params.uri}" }
notification(params)
}

View file

@ -292,11 +292,11 @@ public abstract class Protocol(
}
private fun onProgress(notification: ProgressNotification) {
LOGGER.trace { "Received progress notification: token=${notification.progressToken}, progress=${notification.progress}/${notification.total}" }
val progress = notification.progress
val total = notification.total
val message = notification.message
val progressToken = notification.progressToken
LOGGER.trace { "Received progress notification: token=${notification.params.progressToken}, progress=${notification.params.progress}/${notification.params.total}" }
val progress = notification.params.progress
val total = notification.params.total
val message = notification.params.message
val progressToken = notification.params.progressToken
val handler = _progressHandlers.value[progressToken]
if (handler == null) {
@ -424,7 +424,12 @@ public abstract class Protocol(
_responseHandlers.update { current -> current.remove(messageId) }
_progressHandlers.update { current -> current.remove(messageId) }
val notification = CancelledNotification(requestId = messageId, reason = reason.message ?: "Unknown")
val notification = CancelledNotification(
params = CancelledNotification.Params(
requestId = messageId,
reason = reason.message ?: "Unknown"
)
)
val serialized = JSONRPCNotification(
notification.method.value,

View file

@ -9,6 +9,7 @@ import kotlinx.serialization.Serializable
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.JsonPrimitive
import kotlinx.serialization.json.buildJsonObject
import kotlinx.serialization.json.decodeFromJsonElement
import kotlinx.serialization.json.encodeToJsonElement
import kotlinx.serialization.json.jsonObject
@ -125,10 +126,11 @@ public sealed interface Request {
* @return The JSON-RPC request representation.
*/
internal fun Request.toJSON(): JSONRPCRequest {
val encoded = JsonObject(McpJson.encodeToJsonElement(this).jsonObject.minus("method"))
val fullJson = McpJson.encodeToJsonElement(this).jsonObject
val params = JsonObject(fullJson.filterKeys { it != "method" })
return JSONRPCRequest(
method = method.value,
params = encoded,
params = params,
jsonrpc = JSONRPC_VERSION,
)
}
@ -139,8 +141,7 @@ internal fun Request.toJSON(): JSONRPCRequest {
* @return The decoded [Request] or null
*/
internal fun JSONRPCRequest.fromJSON(): Request {
val requestData = JsonObject(params.jsonObject.plus("method" to JsonPrimitive(method)))
val requestData = JsonObject(params.jsonObject + ("method" to JsonPrimitive(method)))
val deserializer = selectRequestDeserializer(method)
return McpJson.decodeFromJsonElement(deserializer, requestData)
}
@ -159,6 +160,7 @@ public open class CustomRequest(override val method: Method) : Request
@Serializable(with = NotificationPolymorphicSerializer::class)
public sealed interface Notification {
public val method: Method
public val params: NotificationParams?
}
/**
@ -167,10 +169,9 @@ public sealed interface Notification {
* @return The JSON-RPC notification representation.
*/
internal fun Notification.toJSON(): JSONRPCNotification {
val encoded = JsonObject(McpJson.encodeToJsonElement<Notification>(this).jsonObject.minus("method"))
return JSONRPCNotification(
method.value,
params = encoded
method = method.value,
params = McpJson.encodeToJsonElement(params),
)
}
@ -180,7 +181,10 @@ internal fun Notification.toJSON(): JSONRPCNotification {
* @return The decoded [Notification].
*/
internal fun JSONRPCNotification.fromJSON(): Notification {
val data = JsonObject(params.jsonObject.plus("method" to JsonPrimitive(method)))
val data = buildJsonObject {
put("method", JsonPrimitive(method))
put("params", params)
}
return McpJson.decodeFromJsonElement<Notification>(data)
}
@ -295,6 +299,12 @@ public data class JSONRPCError(
val data: JsonObject = EmptyJsonObject,
) : JSONRPCMessage
/**
* Base interface for notification parameters with optional metadata.
*/
@Serializable
public sealed interface NotificationParams : WithMeta
/* Cancellation */
/**
* This notification can be sent by either side to indicate that it is cancelling a previously issued request.
@ -307,19 +317,24 @@ public data class JSONRPCError(
*/
@Serializable
public data class CancelledNotification(
/**
* The ID of the request to cancel.
*
* It MUST correspond to the ID of a request previously issued in the same direction.
*/
val requestId: RequestId,
/**
* An optional string describing the reason for the cancellation. This MAY be logged or presented to the user.
*/
val reason: String?,
override val _meta: JsonObject = EmptyJsonObject,
) : ClientNotification, ServerNotification, WithMeta {
override val params: Params,
) : ClientNotification, ServerNotification {
override val method: Method = Method.Defined.NotificationsCancelled
@Serializable
public data class Params(
/**
* The ID of the request to cancel.
*
* It MUST correspond to the ID of a request previously issued in the same direction.
*/
val requestId: RequestId,
/**
* An optional string describing the reason for the cancellation. This MAY be logged or presented to the user.
*/
val reason: String? = null,
override val _meta: JsonObject = EmptyJsonObject,
) : NotificationParams
}
/* Initialization */
@ -408,7 +423,7 @@ public sealed interface ServerResult : RequestResult
*/
@Serializable
public data class UnknownMethodRequestOrNotification(
override val method: Method,
override val method: Method, override val params: NotificationParams? = null,
) : ClientNotification, ClientRequest, ServerNotification, ServerRequest
/**
@ -506,8 +521,15 @@ public data class InitializeResult(
* This notification is sent from the client to the server after initialization has finished.
*/
@Serializable
public class InitializedNotification : ClientNotification {
public data class InitializedNotification(
override val params: Params = Params(),
) : ClientNotification {
override val method: Method = Method.Defined.NotificationsInitialized
@Serializable
public data class Params(
override val _meta: JsonObject = EmptyJsonObject,
) : NotificationParams
}
/* Ping */
@ -528,7 +550,7 @@ public sealed interface ProgressBase {
/**
* The progress thus far. This should increase every time progress is made, even if the total is unknown.
*/
public val progress: Int
public val progress: Double
/**
* Total number of items to a process (or total progress required), if known.
@ -553,7 +575,7 @@ public open class Progress(
/**
* The progress thus far. This should increase every time progress is made, even if the total is unknown.
*/
override val progress: Int,
override val progress: Double,
/**
* Total number of items to a process (or total progress required), if known.
@ -571,18 +593,32 @@ public open class Progress(
*/
@Serializable
public data class ProgressNotification(
override val progress: Int,
/**
* The progress token,
* which was given in the initial request,
* used to associate this notification with the request that is proceeding.
*/
public val progressToken: ProgressToken,
@Suppress("PropertyName") val _meta: JsonObject = EmptyJsonObject,
override val total: Double?,
override val message: String?,
) : ClientNotification, ServerNotification, ProgressBase {
override val params: Params,
) : ClientNotification, ServerNotification {
override val method: Method = Method.Defined.NotificationsProgress
@Serializable
public data class Params(
/**
* The progress thus far. This should increase every time progress is made, even if the total is unknown.
*/
override val progress: Double,
/**
* The progress token,
* which was given in the initial request,
* used to associate this notification with the request that is proceeding.
*/
val progressToken: ProgressToken,
/**
* Total number of items to process (or total progress required), if known.
*/
override val total: Double? = null,
/**
* An optional message describing the current progress.
*/
override val message: String? = null,
override val _meta: JsonObject = EmptyJsonObject,
) : NotificationParams, ProgressBase
}
/* Pagination */
@ -784,8 +820,15 @@ public class ReadResourceResult(
* Servers may issue this without any previous subscription from the client.
*/
@Serializable
public class ResourceListChangedNotification : ServerNotification {
public data class ResourceListChangedNotification(
override val params: Params = Params(),
) : ServerNotification {
override val method: Method = Method.Defined.NotificationsResourcesListChanged
@Serializable
public data class Params(
override val _meta: JsonObject = EmptyJsonObject,
) : NotificationParams
}
/**
@ -821,13 +864,18 @@ public data class UnsubscribeRequest(
*/
@Serializable
public data class ResourceUpdatedNotification(
/**
* The URI of the resource that has been updated. This might be a sub-resource of the one that the client actually subscribed to.
*/
val uri: String,
override val _meta: JsonObject = EmptyJsonObject,
) : ServerNotification, WithMeta {
override val params: Params,
) : ServerNotification {
override val method: Method = Method.Defined.NotificationsResourcesUpdated
@Serializable
public data class Params(
/**
* The URI of the resource that has been updated. This might be a sub-resource of the one that the client actually subscribed to.
*/
val uri: String,
override val _meta: JsonObject = EmptyJsonObject,
) : NotificationParams
}
/* Prompts */
@ -1044,8 +1092,15 @@ public class GetPromptResult(
* Servers may issue this without any previous subscription from the client.
*/
@Serializable
public class PromptListChangedNotification : ServerNotification {
public data class PromptListChangedNotification(
override val params: Params = Params(),
) : ServerNotification {
override val method: Method = Method.Defined.NotificationsPromptsListChanged
@Serializable
public data class Params(
override val _meta: JsonObject = EmptyJsonObject,
) : NotificationParams
}
/* Tools */
@ -1223,8 +1278,15 @@ public data class CallToolRequest(
* Servers may issue this without any previous subscription from the client.
*/
@Serializable
public class ToolListChangedNotification : ServerNotification {
public data class ToolListChangedNotification(
override val params: Params = Params(),
) : ServerNotification {
override val method: Method = Method.Defined.NotificationsToolsListChanged
@Serializable
public data class Params(
override val _meta: JsonObject = EmptyJsonObject,
) : NotificationParams
}
/* Logging */
@ -1252,22 +1314,27 @@ public enum class LoggingLevel {
*/
@Serializable
public data class LoggingMessageNotification(
/**
* The severity of this log message.
*/
val level: LoggingLevel,
override val params: Params,
) : ServerNotification {
override val method: Method = Method.Defined.NotificationsMessage
/**
* An optional name of the logger issuing this message.
*/
val logger: String? = null,
@Serializable
public data class Params(
/**
* The severity of this log message.
*/
val level: LoggingLevel,
/**
* An optional name of the logger issuing this message.
*/
val logger: String? = null,
/**
* The data to be logged, such as a string message or an object. Any JSON serializable type is allowed here.
*/
val data: JsonElement,
override val _meta: JsonObject = EmptyJsonObject,
) : NotificationParams
/**
* The data to be logged, such as a string message or an object. Any JSON serializable type is allowed here.
*/
val data: JsonObject = EmptyJsonObject,
override val _meta: JsonObject = EmptyJsonObject,
) : ServerNotification, WithMeta {
/**
* A request from the client to the server to enable or adjust logging.
*/
@ -1281,8 +1348,6 @@ public data class LoggingMessageNotification(
) : ClientRequest, WithMeta {
override val method: Method = Method.Defined.LoggingSetLevel
}
override val method: Method = Method.Defined.NotificationsMessage
}
/* Sampling */
@ -1578,8 +1643,15 @@ public class ListRootsResult(
* A notification from the client to the server, informing it that the list of roots has changed.
*/
@Serializable
public class RootsListChangedNotification : ClientNotification {
public data class RootsListChangedNotification(
override val params: Params = Params(),
) : ClientNotification {
override val method: Method = Method.Defined.NotificationsRootsListChanged
@Serializable
public data class Params(
override val _meta: JsonObject = EmptyJsonObject,
) : NotificationParams
}
/**

View file

@ -0,0 +1,33 @@
package io.modelcontextprotocol.kotlin.sdk
import io.kotest.assertions.json.shouldEqualJson
import io.modelcontextprotocol.kotlin.sdk.shared.McpJson
import kotlin.test.Test
import kotlin.test.assertEquals
class AudioContentSerializationTest {
private val audioContentJson = """
{
"data": "base64-encoded-audio-data",
"mimeType": "audio/wav",
"type": "audio"
}
""".trimIndent()
private val audioContent = AudioContent(
data = "base64-encoded-audio-data",
mimeType = "audio/wav"
)
@Test
fun `should serialize audio content`() {
McpJson.encodeToString(audioContent) shouldEqualJson audioContentJson
}
@Test
fun `should deserialize audio content`() {
val content = McpJson.decodeFromString<AudioContent>(audioContentJson)
assertEquals(expected = audioContent, actual = content)
}
}

View file

@ -0,0 +1,64 @@
package io.modelcontextprotocol.kotlin.sdk
import kotlinx.serialization.json.JsonPrimitive
import kotlinx.serialization.json.buildJsonObject
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertTrue
class CallToolResultUtilsTest {
@Test
fun testOkWithOnlyText() {
val content = "TextMessage"
val result = CallToolResult.Companion.ok(content)
assertEquals(1, result.content.size)
assertEquals(content, (result.content[0] as TextContent).text)
assertFalse(result.isError == true)
assertEquals(EmptyJsonObject, result._meta)
}
@Test
fun testOkWithMeta() {
val content = "TextMessageWithMeta"
val meta = buildJsonObject {
put("key1", JsonPrimitive("value1"))
put("key2", JsonPrimitive(42))
}
val result = CallToolResult.Companion.ok(content, meta)
assertEquals(1, result.content.size)
assertEquals(content, (result.content[0] as TextContent).text)
assertFalse(result.isError == true)
assertEquals(meta, result._meta)
}
@Test
fun testErrorWithOnlyText() {
val content = "ErrorMessage"
val result = CallToolResult.Companion.error(content)
assertEquals(1, result.content.size)
assertEquals(content, (result.content[0] as TextContent).text)
assertTrue(result.isError == true)
assertEquals(EmptyJsonObject, result._meta)
}
@Test
fun testErrorWithMeta() {
val content = "ErrorMessageWithMeta"
val meta = buildJsonObject {
put("errorCode", JsonPrimitive(404))
put("errorDetail", JsonPrimitive("资源未找到"))
}
val result = CallToolResult.Companion.error(content, meta)
assertEquals(1, result.content.size)
assertEquals(content, (result.content[0] as TextContent).text)
assertTrue(result.isError == true)
assertEquals(meta, result._meta)
}
}

View file

@ -0,0 +1,378 @@
package io.modelcontextprotocol.kotlin.sdk
import io.modelcontextprotocol.kotlin.sdk.shared.McpJson
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertIs
import kotlin.test.assertNotEquals
import kotlin.test.assertTrue
class TypesTest {
@Test
fun `should have correct latest protocol version`() {
assertNotEquals("", LATEST_PROTOCOL_VERSION)
assertEquals("2024-11-05", LATEST_PROTOCOL_VERSION)
}
@Test
fun `should have correct supported protocol versions`() {
assertIs<Array<String>>(SUPPORTED_PROTOCOL_VERSIONS)
assertTrue(SUPPORTED_PROTOCOL_VERSIONS.contains(LATEST_PROTOCOL_VERSION))
assertTrue(SUPPORTED_PROTOCOL_VERSIONS.contains("2024-10-07"))
assertEquals(2, SUPPORTED_PROTOCOL_VERSIONS.size)
}
@Test
fun `should validate JSONRPC version constant`() {
assertEquals("2.0", JSONRPC_VERSION)
}
// Reference Tests
@Test
fun `should validate ResourceReference`() {
val resourceRef = ResourceReference(uri = "file:///path/to/file.txt")
assertEquals("ref/resource", resourceRef.type)
assertEquals("file:///path/to/file.txt", resourceRef.uri)
}
@Test
fun `should serialize and deserialize ResourceReference correctly`() {
val resourceRef = ResourceReference(uri = "https://example.com/resource")
val json = McpJson.encodeToString<Reference>(resourceRef)
val decoded = McpJson.decodeFromString<Reference>(json)
assertIs<ResourceReference>(decoded)
assertEquals("ref/resource", decoded.type)
assertEquals("https://example.com/resource", decoded.uri)
}
@Test
fun `should validate PromptReference`() {
val promptRef = PromptReference(name = "greeting")
assertEquals("ref/prompt", promptRef.type)
assertEquals("greeting", promptRef.name)
}
@Test
fun `should serialize and deserialize PromptReference correctly`() {
val promptRef = PromptReference(name = "test-prompt")
val json = McpJson.encodeToString<Reference>(promptRef)
val decoded = McpJson.decodeFromString<Reference>(json)
assertIs<PromptReference>(decoded)
assertEquals("ref/prompt", decoded.type)
assertEquals("test-prompt", decoded.name)
}
@Test
fun `should handle UnknownReference for invalid type`() {
val invalidJson = """{"type": "invalid_type"}"""
val decoded = McpJson.decodeFromString<Reference>(invalidJson)
assertIs<UnknownReference>(decoded)
assertEquals("invalid_type", decoded.type)
}
// PromptMessageContent Tests
@Test
fun `should validate text content`() {
val textContent = TextContent(text = "Hello, world!")
assertEquals("text", textContent.type)
assertEquals("Hello, world!", textContent.text)
}
@Test
fun `should serialize and deserialize text content correctly`() {
val textContent = TextContent(text = "Test message")
val json = McpJson.encodeToString<PromptMessageContent>(textContent)
val decoded = McpJson.decodeFromString<PromptMessageContent>(json)
assertIs<TextContent>(decoded)
assertEquals("text", decoded.type)
assertEquals("Test message", decoded.text)
}
@Test
fun `should validate image content`() {
val imageContent = ImageContent(
data = "aGVsbG8=", // base64 encoded "hello"
mimeType = "image/png"
)
assertEquals("image", imageContent.type)
assertEquals("aGVsbG8=", imageContent.data)
assertEquals("image/png", imageContent.mimeType)
}
@Test
fun `should serialize and deserialize image content correctly`() {
val imageContent = ImageContent(
data = "dGVzdA==", // base64 encoded "test"
mimeType = "image/jpeg"
)
val json = McpJson.encodeToString<PromptMessageContent>(imageContent)
val decoded = McpJson.decodeFromString<PromptMessageContent>(json)
assertIs<ImageContent>(decoded)
assertEquals("image", decoded.type)
assertEquals("dGVzdA==", decoded.data)
assertEquals("image/jpeg", decoded.mimeType)
}
@Test
fun `should validate audio content`() {
val audioContent = AudioContent(
data = "aGVsbG8=", // base64 encoded "hello"
mimeType = "audio/mp3"
)
assertEquals("audio", audioContent.type)
assertEquals("aGVsbG8=", audioContent.data)
assertEquals("audio/mp3", audioContent.mimeType)
}
@Test
fun `should serialize and deserialize audio content correctly`() {
val audioContent = AudioContent(
data = "YXVkaW8=", // base64 encoded "audio"
mimeType = "audio/wav"
)
val json = McpJson.encodeToString<PromptMessageContent>(audioContent)
val decoded = McpJson.decodeFromString<PromptMessageContent>(json)
assertIs<AudioContent>(decoded)
assertEquals("audio", decoded.type)
assertEquals("YXVkaW8=", decoded.data)
assertEquals("audio/wav", decoded.mimeType)
}
@Test
fun `should validate embedded resource content`() {
val resource = TextResourceContents(
text = "File contents",
uri = "file:///path/to/file.txt",
mimeType = "text/plain"
)
val embeddedResource = EmbeddedResource(resource = resource)
assertEquals("resource", embeddedResource.type)
assertEquals(resource, embeddedResource.resource)
}
@Test
fun `should serialize and deserialize embedded resource content correctly`() {
val resource = BlobResourceContents(
blob = "YmluYXJ5ZGF0YQ==",
uri = "file:///path/to/binary.dat",
mimeType = "application/octet-stream"
)
val embeddedResource = EmbeddedResource(resource = resource)
val json = McpJson.encodeToString<PromptMessageContent>(embeddedResource)
val decoded = McpJson.decodeFromString<PromptMessageContent>(json)
assertIs<EmbeddedResource>(decoded)
assertEquals("resource", decoded.type)
assertIs<BlobResourceContents>(decoded.resource)
val decodedBlob = decoded.resource
assertEquals("YmluYXJ5ZGF0YQ==", decodedBlob.blob)
assertEquals("file:///path/to/binary.dat", decodedBlob.uri)
assertEquals("application/octet-stream", decodedBlob.mimeType)
}
@Test
fun `should handle unknown content type`() {
val unknownJson = """{"type": "unknown_type"}"""
val decoded = McpJson.decodeFromString<PromptMessageContent>(unknownJson)
assertIs<UnknownContent>(decoded)
assertEquals("unknown_type", decoded.type)
}
// PromptMessage Tests
@Test
fun `should validate prompt message with text content`() {
val textContent = TextContent(text = "Hello, assistant!")
val promptMessage = PromptMessage(
role = Role.user,
content = textContent
)
assertEquals(Role.user, promptMessage.role)
assertEquals(textContent, promptMessage.content)
assertEquals("text", promptMessage.content.type)
}
@Test
fun `should validate prompt message with embedded resource`() {
val resource = TextResourceContents(
text = "Primary application entry point",
uri = "file:///project/src/main.rs",
mimeType = "text/x-rust"
)
val embeddedResource = EmbeddedResource(resource = resource)
val promptMessage = PromptMessage(
role = Role.assistant,
content = embeddedResource
)
assertEquals(Role.assistant, promptMessage.role)
assertEquals("resource", promptMessage.content.type)
val content = promptMessage.content as EmbeddedResource
val textResource = content.resource as TextResourceContents
assertEquals("Primary application entry point", textResource.text)
assertEquals("file:///project/src/main.rs", textResource.uri)
assertEquals("text/x-rust", textResource.mimeType)
}
@Test
fun `should serialize and deserialize prompt message correctly`() {
val imageContent = ImageContent(
data = "aW1hZ2VkYXRh", // base64 encoded "imagedata"
mimeType = "image/png"
)
val promptMessage = PromptMessage(
role = Role.assistant,
content = imageContent
)
val json = McpJson.encodeToString(promptMessage)
val decoded = McpJson.decodeFromString<PromptMessage>(json)
assertEquals(Role.assistant, decoded.role)
assertIs<ImageContent>(decoded.content)
val decodedContent = decoded.content
assertEquals("aW1hZ2VkYXRh", decodedContent.data)
assertEquals("image/png", decodedContent.mimeType)
}
// CallToolResult Tests
@Test
fun `should validate tool result with multiple content types`() {
val toolResult = CallToolResult(
content = listOf(
TextContent(text = "Found the following files:"),
EmbeddedResource(
resource = TextResourceContents(
text = "fn main() {}",
uri = "file:///project/src/main.rs",
mimeType = "text/x-rust"
)
),
EmbeddedResource(
resource = TextResourceContents(
text = "pub mod lib;",
uri = "file:///project/src/lib.rs",
mimeType = "text/x-rust"
)
)
)
)
assertEquals(3, toolResult.content.size)
assertEquals("text", toolResult.content[0].type)
assertEquals("resource", toolResult.content[1].type)
assertEquals("resource", toolResult.content[2].type)
assertEquals(false, toolResult.isError)
}
@Test
fun `should validate empty content array with default`() {
val toolResult = CallToolResult(content = emptyList())
assertEquals(0, toolResult.content.size)
assertEquals(false, toolResult.isError)
}
@Test
fun `should serialize and deserialize CallToolResult correctly`() {
val toolResult = CallToolResult(
content = listOf(
TextContent(text = "Operation completed"),
ImageContent(data = "aW1hZ2U=", mimeType = "image/png")
),
isError = false
)
val json = McpJson.encodeToString(toolResult)
val decoded = McpJson.decodeFromString<CallToolResult>(json)
assertEquals(2, decoded.content.size)
assertIs<TextContent>(decoded.content[0])
assertIs<ImageContent>(decoded.content[1])
assertEquals(false, decoded.isError)
}
// CompleteRequest Tests
@Test
fun `should validate CompleteRequest with prompt reference`() {
val request = CompleteRequest(
ref = PromptReference(name = "greeting"),
argument = CompleteRequest.Argument(name = "name", value = "A")
)
assertEquals("completion/complete", request.method.value)
assertIs<PromptReference>(request.ref)
val promptRef = request.ref
assertEquals("greeting", promptRef.name)
assertEquals("name", request.argument.name)
assertEquals("A", request.argument.value)
}
@Test
fun `should validate CompleteRequest with resource reference`() {
val request = CompleteRequest(
ref = ResourceReference(uri = "github://repos/{owner}/{repo}"),
argument = CompleteRequest.Argument(name = "repo", value = "t")
)
assertEquals("completion/complete", request.method.value)
assertIs<ResourceReference>(request.ref)
val resourceRef = request.ref
assertEquals("github://repos/{owner}/{repo}", resourceRef.uri)
assertEquals("repo", request.argument.name)
assertEquals("t", request.argument.value)
}
@Test
fun `should serialize and deserialize CompleteRequest correctly`() {
val request = CompleteRequest(
ref = PromptReference(name = "test"),
argument = CompleteRequest.Argument(name = "arg", value = "")
)
val json = McpJson.encodeToString(request)
val decoded = McpJson.decodeFromString<CompleteRequest>(json)
assertEquals("completion/complete", decoded.method.value)
assertIs<PromptReference>(decoded.ref)
val promptRef = decoded.ref
assertEquals("test", promptRef.name)
assertEquals("arg", decoded.argument.name)
assertEquals("", decoded.argument.value)
}
@Test
fun `should validate CompleteRequest with complex URIs`() {
val request = CompleteRequest(
ref = ResourceReference(uri = "api://v1/{tenant}/{resource}/{id}"),
argument = CompleteRequest.Argument(name = "id", value = "123")
)
val resourceRef = request.ref as ResourceReference
assertEquals("api://v1/{tenant}/{resource}/{id}", resourceRef.uri)
assertEquals("id", request.argument.name)
assertEquals("123", request.argument.value)
}
}

View file

@ -0,0 +1,252 @@
package io.modelcontextprotocol.kotlin.sdk
import io.modelcontextprotocol.kotlin.sdk.shared.McpJson
import kotlinx.serialization.json.buildJsonObject
import kotlinx.serialization.json.put
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertIs
class TypesUtilTest {
// ErrorCode Serializer Tests
@Test
fun `should serialize and deserialize ErrorCode Defined correctly`() {
val errorCode: ErrorCode = ErrorCode.Defined.InvalidRequest
val json = McpJson.encodeToString(errorCode)
val decoded = McpJson.decodeFromString<ErrorCode>(json)
assertEquals(ErrorCode.Defined.InvalidRequest, decoded)
assertEquals(-32600, decoded.code)
}
@Test
fun `should serialize and deserialize ErrorCode Unknown correctly`() {
val errorCode: ErrorCode = ErrorCode.Unknown(1001)
val json = McpJson.encodeToString(errorCode)
val decoded = McpJson.decodeFromString<ErrorCode>(json)
assertIs<ErrorCode.Unknown>(decoded)
assertEquals(1001, decoded.code)
}
// Method Serializer Tests
@Test
fun `should serialize and deserialize Method Defined correctly`() {
val method: Method = Method.Defined.Initialize
val json = McpJson.encodeToString(method)
val decoded = McpJson.decodeFromString<Method>(json)
assertEquals(Method.Defined.Initialize, decoded)
assertEquals("initialize", decoded.value)
}
@Test
fun `should serialize and deserialize Method Custom correctly`() {
val method: Method = Method.Custom("custom/method")
val json = McpJson.encodeToString(method)
val decoded = McpJson.decodeFromString<Method>(json)
assertIs<Method.Custom>(decoded)
assertEquals("custom/method", decoded.value)
}
// StopReason Serializer Tests
@Test
fun `should serialize and deserialize StopReason EndTurn correctly`() {
val stopReason: StopReason = StopReason.EndTurn
val json = McpJson.encodeToString(stopReason)
val decoded = McpJson.decodeFromString<StopReason>(json)
assertEquals(StopReason.EndTurn, decoded)
assertEquals("endTurn", decoded.value)
}
@Test
fun `should serialize and deserialize StopReason Other correctly`() {
val stopReason: StopReason = StopReason.Other("custom_reason")
val json = McpJson.encodeToString(stopReason)
val decoded = McpJson.decodeFromString<StopReason>(json)
assertIs<StopReason.Other>(decoded)
assertEquals("custom_reason", decoded.value)
}
// Reference Polymorphic Serializer Tests
@Test
fun `should deserialize ResourceReference polymorphically`() {
val json = """{"type": "ref/resource", "uri": "file:///test.txt"}"""
val decoded = McpJson.decodeFromString<Reference>(json)
assertIs<ResourceReference>(decoded)
assertEquals("ref/resource", decoded.type)
assertEquals("file:///test.txt", decoded.uri)
}
@Test
fun `should deserialize PromptReference polymorphically`() {
val json = """{"type": "ref/prompt", "name": "test-prompt"}"""
val decoded = McpJson.decodeFromString<Reference>(json)
assertIs<PromptReference>(decoded)
assertEquals("ref/prompt", decoded.type)
assertEquals("test-prompt", decoded.name)
}
@Test
fun `should deserialize UnknownReference for invalid type`() {
val json = """{"type": "unknown_ref", "data": "test"}"""
val decoded = McpJson.decodeFromString<Reference>(json)
assertIs<UnknownReference>(decoded)
assertEquals("unknown_ref", decoded.type)
}
// PromptMessageContent Polymorphic Serializer Tests
@Test
fun `should deserialize TextContent polymorphically`() {
val json = """{"type": "text", "text": "Hello world"}"""
val decoded = McpJson.decodeFromString<PromptMessageContent>(json)
assertIs<TextContent>(decoded)
assertEquals("text", decoded.type)
assertEquals("Hello world", decoded.text)
}
@Test
fun `should deserialize ImageContent polymorphically`() {
val json = """{"type": "image", "data": "aW1hZ2U=", "mimeType": "image/png"}"""
val decoded = McpJson.decodeFromString<PromptMessageContent>(json)
assertIs<ImageContent>(decoded)
assertEquals("image", decoded.type)
assertEquals("aW1hZ2U=", decoded.data)
assertEquals("image/png", decoded.mimeType)
}
@Test
fun `should deserialize AudioContent polymorphically`() {
val json = """{"type": "audio", "data": "YXVkaW8=", "mimeType": "audio/mp3"}"""
val decoded = McpJson.decodeFromString<PromptMessageContent>(json)
assertIs<AudioContent>(decoded)
assertEquals("audio", decoded.type)
assertEquals("YXVkaW8=", decoded.data)
assertEquals("audio/mp3", decoded.mimeType)
}
@Test
fun `should deserialize EmbeddedResource polymorphically`() {
val json =
"""{"type": "resource", "resource": {"uri": "file:///test.txt", "mimeType": "text/plain", "text": "content"}}"""
val decoded = McpJson.decodeFromString<PromptMessageContent>(json)
assertIs<EmbeddedResource>(decoded)
assertEquals("resource", decoded.type)
assertIs<TextResourceContents>(decoded.resource)
val textResource = decoded.resource
assertEquals("file:///test.txt", textResource.uri)
assertEquals("content", textResource.text)
}
// ResourceContents Polymorphic Serializer Tests
@Test
fun `should deserialize TextResourceContents polymorphically`() {
val json = """{"uri": "file:///test.txt", "mimeType": "text/plain", "text": "file content"}"""
val decoded = McpJson.decodeFromString<ResourceContents>(json)
assertIs<TextResourceContents>(decoded)
assertEquals("file:///test.txt", decoded.uri)
assertEquals("file content", decoded.text)
assertEquals("text/plain", decoded.mimeType)
}
@Test
fun `should deserialize BlobResourceContents polymorphically`() {
val json = """{"uri": "file:///binary.dat", "mimeType": "application/octet-stream", "blob": "YmluYXJ5"}"""
val decoded = McpJson.decodeFromString<ResourceContents>(json)
assertIs<BlobResourceContents>(decoded)
assertEquals("file:///binary.dat", decoded.uri)
assertEquals("YmluYXJ5", decoded.blob)
assertEquals("application/octet-stream", decoded.mimeType)
}
@Test
fun `should deserialize UnknownResourceContents for missing fields`() {
val json = """{"uri": "file:///unknown.dat", "mimeType": "unknown/type"}"""
val decoded = McpJson.decodeFromString<ResourceContents>(json)
assertIs<UnknownResourceContents>(decoded)
assertEquals("file:///unknown.dat", decoded.uri)
assertEquals("unknown/type", decoded.mimeType)
}
// RequestId Serializer Tests
@Test
fun `should serialize and deserialize RequestId StringId correctly`() {
val requestId: RequestId = RequestId.StringId("test-id")
val json = McpJson.encodeToString(requestId)
val decoded = McpJson.decodeFromString<RequestId>(json)
assertIs<RequestId.StringId>(decoded)
assertEquals("test-id", decoded.value)
}
@Test
fun `should serialize and deserialize RequestId NumberId correctly`() {
val requestId: RequestId = RequestId.NumberId(42L)
val json = McpJson.encodeToString(requestId)
val decoded = McpJson.decodeFromString<RequestId>(json)
assertIs<RequestId.NumberId>(decoded)
assertEquals(42L, decoded.value)
}
// Utility Functions Tests
@Test
fun `should create CallToolResult ok correctly`() {
val result = CallToolResult.ok("Success message")
assertEquals(listOf(TextContent("Success message")), result.content)
assertEquals(false, result.isError)
assertEquals(EmptyJsonObject, result._meta)
}
@Test
fun `should create CallToolResult error correctly`() {
val result = CallToolResult.error("Error message")
assertEquals(listOf(TextContent("Error message")), result.content)
assertEquals(true, result.isError)
assertEquals(EmptyJsonObject, result._meta)
}
@Test
fun `should create CallToolResult with custom meta`() {
val meta = buildJsonObject { put("custom", "value") }
val result = CallToolResult.ok("Success", meta)
assertEquals(listOf(TextContent("Success")), result.content)
assertEquals(false, result.isError)
assertEquals(meta, result._meta)
}
}

View file

@ -0,0 +1,43 @@
package io.modelcontextprotocol.kotlin.sdk.client
import io.modelcontextprotocol.kotlin.sdk.Implementation
import io.modelcontextprotocol.kotlin.sdk.ListToolsResult
import kotlinx.coroutines.test.runTest
import kotlinx.io.asSink
import kotlinx.io.asSource
import kotlinx.io.buffered
import org.junit.jupiter.api.Disabled
import org.junit.jupiter.api.Test
import java.net.Socket
class ClientIntegrationTest {
fun createTransport(): StdioClientTransport {
val socket = Socket("localhost", 3000)
return StdioClientTransport(
socket.inputStream.asSource().buffered(),
socket.outputStream.asSink().buffered()
)
}
@Disabled("This test requires a running server")
@Test
fun testRequestTools() = runTest {
val client = Client(
Implementation("test", "1.0"),
)
val transport = createTransport()
try {
client.connect(transport)
val response: ListToolsResult? = client.listTools()
println(response?.tools)
} finally {
transport.close()
}
}
}

View file

@ -0,0 +1,908 @@
package io.modelcontextprotocol.kotlin.sdk.client
import io.mockk.coEvery
import io.mockk.spyk
import io.modelcontextprotocol.kotlin.sdk.ClientCapabilities
import io.modelcontextprotocol.kotlin.sdk.CreateElicitationRequest
import io.modelcontextprotocol.kotlin.sdk.CreateElicitationResult
import io.modelcontextprotocol.kotlin.sdk.CreateMessageRequest
import io.modelcontextprotocol.kotlin.sdk.CreateMessageResult
import io.modelcontextprotocol.kotlin.sdk.EmptyJsonObject
import io.modelcontextprotocol.kotlin.sdk.Implementation
import io.modelcontextprotocol.kotlin.sdk.InMemoryTransport
import io.modelcontextprotocol.kotlin.sdk.InitializeRequest
import io.modelcontextprotocol.kotlin.sdk.InitializeResult
import io.modelcontextprotocol.kotlin.sdk.JSONRPCMessage
import io.modelcontextprotocol.kotlin.sdk.JSONRPCRequest
import io.modelcontextprotocol.kotlin.sdk.JSONRPCResponse
import io.modelcontextprotocol.kotlin.sdk.LATEST_PROTOCOL_VERSION
import io.modelcontextprotocol.kotlin.sdk.ListResourcesRequest
import io.modelcontextprotocol.kotlin.sdk.ListResourcesResult
import io.modelcontextprotocol.kotlin.sdk.ListRootsRequest
import io.modelcontextprotocol.kotlin.sdk.ListToolsRequest
import io.modelcontextprotocol.kotlin.sdk.ListToolsResult
import io.modelcontextprotocol.kotlin.sdk.LoggingLevel
import io.modelcontextprotocol.kotlin.sdk.LoggingMessageNotification
import io.modelcontextprotocol.kotlin.sdk.Method
import io.modelcontextprotocol.kotlin.sdk.Role
import io.modelcontextprotocol.kotlin.sdk.Root
import io.modelcontextprotocol.kotlin.sdk.RootsListChangedNotification
import io.modelcontextprotocol.kotlin.sdk.SUPPORTED_PROTOCOL_VERSIONS
import io.modelcontextprotocol.kotlin.sdk.ServerCapabilities
import io.modelcontextprotocol.kotlin.sdk.TextContent
import io.modelcontextprotocol.kotlin.sdk.Tool
import io.modelcontextprotocol.kotlin.sdk.server.Server
import io.modelcontextprotocol.kotlin.sdk.server.ServerOptions
import io.modelcontextprotocol.kotlin.sdk.shared.AbstractTransport
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.TimeoutCancellationException
import kotlinx.coroutines.cancel
import kotlinx.coroutines.delay
import kotlinx.coroutines.joinAll
import kotlinx.coroutines.launch
import kotlinx.coroutines.test.runTest
import kotlinx.coroutines.withTimeout
import kotlinx.serialization.json.buildJsonObject
import kotlinx.serialization.json.put
import kotlinx.serialization.json.putJsonObject
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertInstanceOf
import org.junit.jupiter.api.assertThrows
import kotlin.coroutines.cancellation.CancellationException
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
import kotlin.test.assertTrue
import kotlin.test.fail
class ClientTest {
@Test
fun `should initialize with matching protocol version`() = runTest {
var initialised = false
val clientTransport = object : AbstractTransport() {
override suspend fun start() {}
override suspend fun send(message: JSONRPCMessage) {
if (message !is JSONRPCRequest) return
initialised = true
val result = InitializeResult(
protocolVersion = LATEST_PROTOCOL_VERSION,
capabilities = ServerCapabilities(),
serverInfo = Implementation(
name = "test",
version = "1.0"
)
)
val response = JSONRPCResponse(
id = message.id,
result = result
)
_onMessage.invoke(response)
}
override suspend fun close() {
}
}
val client = Client(
clientInfo = Implementation(
name = "test client",
version = "1.0"
),
options = ClientOptions(
capabilities = ClientCapabilities(
sampling = EmptyJsonObject
)
)
)
client.connect(clientTransport)
assertTrue(initialised)
}
@Test
fun `should initialize with supported older protocol version`() = runTest {
val OLD_VERSION = SUPPORTED_PROTOCOL_VERSIONS[1]
val clientTransport = object : AbstractTransport() {
override suspend fun start() {}
override suspend fun send(message: JSONRPCMessage) {
if (message !is JSONRPCRequest) return
check(message.method == Method.Defined.Initialize.value)
val result = InitializeResult(
protocolVersion = OLD_VERSION,
capabilities = ServerCapabilities(),
serverInfo = Implementation(
name = "test",
version = "1.0"
)
)
val response = JSONRPCResponse(
id = message.id,
result = result
)
_onMessage.invoke(response)
}
override suspend fun close() {
}
}
val client = Client(
clientInfo = Implementation(
name = "test client",
version = "1.0"
),
options = ClientOptions(
capabilities = ClientCapabilities(
sampling = EmptyJsonObject
)
)
)
client.connect(clientTransport)
assertEquals(
Implementation("test", "1.0"),
client.serverVersion
)
}
@Test
fun `should reject unsupported protocol version`() = runTest {
var closed = false
val clientTransport = object : AbstractTransport() {
override suspend fun start() {}
override suspend fun send(message: JSONRPCMessage) {
if (message !is JSONRPCRequest) return
check(message.method == Method.Defined.Initialize.value)
val result = InitializeResult(
protocolVersion = "invalid-version",
capabilities = ServerCapabilities(),
serverInfo = Implementation(
name = "test",
version = "1.0"
)
)
val response = JSONRPCResponse(
id = message.id,
result = result
)
_onMessage.invoke(response)
}
override suspend fun close() {
closed = true
}
}
val client = Client(
clientInfo = Implementation(
name = "test client",
version = "1.0"
),
options = ClientOptions()
)
assertFailsWith<IllegalStateException>("Server's protocol version is not supported: invalid-version") {
client.connect(clientTransport)
}
assertTrue(closed)
}
@Test
fun `should reject due to non cancellation exception`() = runTest {
var closed = false
val clientTransport = object : AbstractTransport() {
override suspend fun start() {}
override suspend fun send(message: JSONRPCMessage) {
if (message !is JSONRPCRequest) return
check(message.method == Method.Defined.Initialize.value)
val result = InitializeResult(
protocolVersion = LATEST_PROTOCOL_VERSION,
capabilities = ServerCapabilities(),
serverInfo = Implementation(
name = "test",
version = "1.0"
)
)
val response = JSONRPCResponse(
id = message.id,
result = result
)
_onMessage.invoke(response)
}
override suspend fun close() {
closed = true
}
}
val mockClient = spyk(
Client(
clientInfo = Implementation(
name = "test client",
version = "1.0"
),
options = ClientOptions()
)
)
coEvery{
mockClient.request<InitializeResult>(any())
} throws IllegalStateException("Test error")
val exception = assertFailsWith<IllegalStateException> {
mockClient.connect(clientTransport)
}
assertEquals("Error connecting to transport: Test error", exception.message)
assertTrue(closed)
}
@Test
fun `should respect server capabilities`() = runTest {
val serverOptions = ServerOptions(
capabilities = ServerCapabilities(
resources = ServerCapabilities.Resources(null, null),
tools = ServerCapabilities.Tools(null)
)
)
val server = Server(
Implementation(name = "test server", version = "1.0"),
serverOptions
)
server.setRequestHandler<InitializeRequest>(Method.Defined.Initialize) { request, _ ->
InitializeResult(
protocolVersion = LATEST_PROTOCOL_VERSION,
capabilities = ServerCapabilities(
resources = ServerCapabilities.Resources(null, null),
tools = ServerCapabilities.Tools(null)
),
serverInfo = Implementation(name = "test", version = "1.0")
)
}
server.setRequestHandler<ListResourcesRequest>(Method.Defined.ResourcesList) { request, _ ->
ListResourcesResult(resources = emptyList(), nextCursor = null)
}
server.setRequestHandler<ListToolsRequest>(Method.Defined.ToolsList) { request, _ ->
ListToolsResult(tools = emptyList(), nextCursor = null)
}
val (clientTransport, serverTransport) = InMemoryTransport.createLinkedPair()
val client = Client(
clientInfo = Implementation(name = "test client", version = "1.0"),
options = ClientOptions(
capabilities = ClientCapabilities(sampling = EmptyJsonObject),
)
)
listOf(
launch {
client.connect(clientTransport)
},
launch {
server.connect(serverTransport)
}
).joinAll()
// Server supports resources and tools, but not prompts
val caps = client.serverCapabilities
assertEquals(ServerCapabilities.Resources(null, null), caps?.resources)
assertEquals(ServerCapabilities.Tools(null), caps?.tools)
assertTrue(caps?.prompts == null) // or check that prompts are absent
// These should not throw
client.listResources()
client.listTools()
// This should fail because prompts are not supported
val ex = assertFailsWith<IllegalStateException> {
client.listPrompts()
}
assertTrue(ex.message?.contains("Server does not support prompts") == true)
}
@Test
fun `should respect client notification capabilities`() = runTest {
val server = Server(
Implementation(name = "test server", version = "1.0"),
ServerOptions(capabilities = ServerCapabilities())
)
val client = Client(
clientInfo = Implementation(name = "test client", version = "1.0"),
options = ClientOptions(
capabilities = ClientCapabilities(
roots = ClientCapabilities.Roots(listChanged = true)
)
)
)
val (clientTransport, serverTransport) = InMemoryTransport.createLinkedPair()
listOf(
launch {
client.connect(clientTransport)
println("Client connected")
},
launch {
server.connect(serverTransport)
println("Server connected")
}
).joinAll()
// This should not throw because the client supports roots.listChanged
client.sendRootsListChanged()
// Create a new client without the roots.listChanged capability
val clientWithoutCapability = Client(
clientInfo = Implementation(name = "test client without capability", version = "1.0"),
options = ClientOptions(
capabilities = ClientCapabilities(),
// enforceStrictCapabilities = true // TODO()
)
)
clientWithoutCapability.connect(clientTransport)
// Using the same transport pair might not be realistic - in a real scenario you'd create another pair.
// Adjust if necessary.
// This should fail
val ex = assertFailsWith<IllegalStateException> {
clientWithoutCapability.sendRootsListChanged()
}
assertTrue(ex.message?.startsWith("Client does not support") == true)
}
@Test
fun `should respect server notification capabilities`() = runTest {
val server = Server(
Implementation(name = "test server", version = "1.0"),
ServerOptions(
capabilities = ServerCapabilities(
logging = EmptyJsonObject,
resources = ServerCapabilities.Resources(listChanged = true, subscribe = null)
)
)
)
val client = Client(
clientInfo = Implementation(name = "test client", version = "1.0"),
options = ClientOptions(
capabilities = ClientCapabilities()
)
)
val (clientTransport, serverTransport) = InMemoryTransport.createLinkedPair()
listOf(
launch {
client.connect(clientTransport)
println("Client connected")
},
launch {
server.connect(serverTransport)
println("Server connected")
}
).joinAll()
// These should not throw
val jsonObject = buildJsonObject {
put("name", "John")
put("age", 30)
put("isStudent", false)
}
server.sendLoggingMessage(
LoggingMessageNotification(
params = LoggingMessageNotification.Params(
level = LoggingLevel.info,
data = jsonObject
)
)
)
server.sendResourceListChanged()
// This should fail because the server doesn't have the tools capability
val ex = assertFailsWith<IllegalStateException> {
server.sendToolListChanged()
}
assertTrue(ex.message?.contains("Server does not support notifying of tool list changes") == true)
}
@Test
fun `should handle client cancelling a request`() = runTest {
val server = Server(
Implementation(name = "test server", version = "1.0"),
ServerOptions(
capabilities = ServerCapabilities(resources = ServerCapabilities.Resources(listChanged = null, subscribe = null))
)
)
val def = CompletableDeferred<Unit>()
val defTimeOut = CompletableDeferred<Unit>()
server.setRequestHandler<ListResourcesRequest>(Method.Defined.ResourcesList) { _, extra ->
// Simulate delay
def.complete(Unit)
try {
delay(1000)
} catch (e: CancellationException) {
defTimeOut.complete(Unit)
throw e
}
fail("Shouldn't have been called")
ListResourcesResult(resources = emptyList())
}
val (clientTransport, serverTransport) = InMemoryTransport.createLinkedPair()
val client = Client(
clientInfo = Implementation(name = "test client", version = "1.0"),
options = ClientOptions(capabilities = ClientCapabilities())
)
listOf(
launch {
client.connect(clientTransport)
println("Client connected")
},
launch {
server.connect(serverTransport)
println("Server connected")
}
).joinAll()
val defCancel = CompletableDeferred<Unit>()
val job = launch {
try {
client.listResources()
} catch (e: CancellationException) {
defCancel.complete(Unit)
assertEquals("Cancelled by test", e.message)
}
}
def.await()
runCatching { job.cancel("Cancelled by test") }
defCancel.await()
defTimeOut.await()
}
@Test
fun `should handle request timeout`() = runTest {
val server = Server(
Implementation(name = "test server", version = "1.0"),
ServerOptions(
capabilities = ServerCapabilities(resources = ServerCapabilities.Resources(listChanged = null, subscribe = null))
)
)
server.setRequestHandler<ListResourcesRequest>(Method.Defined.ResourcesList) { _, extra ->
// Simulate a delayed response
// Wait ~100ms unless canceled
try {
withTimeout(100L) {
// Just delay here, if timeout is 0 on the client side, this won't return in time
delay(100)
}
} catch (_: Exception) {
// If aborted, just rethrow or return early
}
ListResourcesResult(resources = emptyList())
}
val (clientTransport, serverTransport) = InMemoryTransport.createLinkedPair()
val client = Client(
clientInfo = Implementation(name = "test client", version = "1.0"),
options = ClientOptions(capabilities = ClientCapabilities())
)
listOf(
launch {
client.connect(clientTransport)
println("Client connected")
},
launch {
server.connect(serverTransport)
println("Server connected")
}
).joinAll()
// Request with 1 msec timeout should fail immediately
val ex = assertFailsWith<Exception> {
withTimeout(1) {
client.listResources()
}
}
assertTrue(ex is TimeoutCancellationException)
}
@Test
fun `should only allow setRequestHandler for declared capabilities`() = runTest {
val client = Client(
clientInfo = Implementation(
name = "test client",
version = "1.0"
),
options = ClientOptions(
capabilities = ClientCapabilities(
sampling = EmptyJsonObject
)
)
)
client.setRequestHandler<CreateMessageRequest>(Method.Defined.SamplingCreateMessage) { request, _ ->
CreateMessageResult(
model = "test-model",
role = Role.assistant,
content = TextContent(
text = "Test response"
)
)
}
assertFailsWith<IllegalStateException>("Client does not support roots capability (required for RootsList)") {
client.setRequestHandler<ListRootsRequest>(Method.Defined.RootsList) { _, _ -> null }
}
}
@Test
fun `JSONRPCRequest with ToolsList method and default params returns list of tools`() = runTest {
val serverOptions = ServerOptions(
capabilities = ServerCapabilities(
tools = ServerCapabilities.Tools(null)
)
)
val server = Server(
Implementation(name = "test server", version = "1.0"),
serverOptions
)
server.setRequestHandler<InitializeRequest>(Method.Defined.Initialize) { request, _ ->
InitializeResult(
protocolVersion = LATEST_PROTOCOL_VERSION,
capabilities = ServerCapabilities(
resources = ServerCapabilities.Resources(null, null),
tools = ServerCapabilities.Tools(null)
),
serverInfo = Implementation(name = "test", version = "1.0")
)
}
val serverListToolsResult = ListToolsResult(
tools = listOf(
Tool(
name = "testTool",
title = "testTool title",
description = "testTool description",
annotations = null,
inputSchema = Tool.Input(),
outputSchema = null
)
), nextCursor = null
)
server.setRequestHandler<ListToolsRequest>(Method.Defined.ToolsList) { request, _ ->
serverListToolsResult
}
val (clientTransport, serverTransport) = InMemoryTransport.createLinkedPair()
val client = Client(
clientInfo = Implementation(name = "test client", version = "1.0"),
options = ClientOptions(
capabilities = ClientCapabilities(sampling = EmptyJsonObject),
)
)
var receivedMessage: JSONRPCMessage? = null
clientTransport.onMessage { msg ->
receivedMessage = msg
}
listOf(
launch {
client.connect(clientTransport)
},
launch {
server.connect(serverTransport)
}
).joinAll()
val serverCapabilities = client.serverCapabilities
assertEquals(ServerCapabilities.Tools(null), serverCapabilities?.tools)
val request = JSONRPCRequest(
method = Method.Defined.ToolsList.value
)
clientTransport.send(request)
assertInstanceOf<JSONRPCResponse>(receivedMessage)
val receivedAsResponse = receivedMessage as JSONRPCResponse
assertEquals(request.id, receivedAsResponse.id)
assertEquals(request.jsonrpc, receivedAsResponse.jsonrpc)
assertEquals(serverListToolsResult, receivedAsResponse.result)
assertEquals(null, receivedAsResponse.error)
}
@Test
fun `listRoots returns list of roots`() = runTest {
val client = Client(
Implementation(name = "test client", version = "1.0"),
ClientOptions(
capabilities = ClientCapabilities(
roots = ClientCapabilities.Roots(null)
)
)
)
val clientRoots = listOf(
Root(uri = "file:///test-root", name = "testRoot")
)
client.addRoots(clientRoots)
val (clientTransport, serverTransport) = InMemoryTransport.createLinkedPair()
val server = Server(
serverInfo = Implementation(name = "test server", version = "1.0"),
options = ServerOptions(
capabilities = ServerCapabilities()
)
)
listOf(
launch { client.connect(clientTransport) },
launch { server.connect(serverTransport) }
).joinAll()
val clientCapabilities = server.clientCapabilities
assertEquals(ClientCapabilities.Roots(null), clientCapabilities?.roots)
val listRootsResult = server.listRoots()
assertEquals(listRootsResult.roots, clientRoots)
}
@Test
fun `addRoot should throw when roots capability is not supported`() = runTest {
val client = Client(
Implementation(name = "test client", version = "1.0"),
ClientOptions(
capabilities = ClientCapabilities()
)
)
// Verify that adding a root throws an exception
val exception = assertThrows<IllegalStateException> {
client.addRoot(uri = "file:///test-root1", name = "testRoot1")
}
assertEquals("Client does not support roots capability.", exception.message)
}
@Test
fun `removeRoot should throw when roots capability is not supported`() = runTest {
val client = Client(
Implementation(name = "test client", version = "1.0"),
ClientOptions(
capabilities = ClientCapabilities()
)
)
// Verify that removing a root throws an exception
val exception = assertThrows<IllegalStateException> {
client.removeRoot(uri = "file:///test-root1")
}
assertEquals("Client does not support roots capability.", exception.message)
}
@Test
fun `removeRoot should remove a root`() = runTest {
val client = Client(
Implementation(name = "test client", version = "1.0"),
ClientOptions(
capabilities = ClientCapabilities(
roots = ClientCapabilities.Roots(null)
)
)
)
// Add some roots
client.addRoots(
listOf(
Root(uri = "file:///test-root1", name = "testRoot1"),
Root(uri = "file:///test-root2", name = "testRoot2"),
)
)
// Remove a root
val result = client.removeRoot("file:///test-root1")
// Verify the root was removed
assertTrue(result, "Root should be removed successfully")
}
@Test
fun `removeRoots should remove multiple roots`() = runTest {
val client = Client(
Implementation(name = "test client", version = "1.0"),
ClientOptions(
capabilities = ClientCapabilities(
roots = ClientCapabilities.Roots(null)
)
)
)
// Add some roots
client.addRoots(
listOf(
Root(uri = "file:///test-root1", name = "testRoot1"),
Root(uri = "file:///test-root2", name = "testRoot2"),
)
)
// Remove multiple roots
val result = client.removeRoots(
listOf("file:///test-root1", "file:///test-root2")
)
// Verify the root was removed
assertEquals(2, result, "Both roots should be removed")
}
@Test
fun `sendRootsListChanged should notify server`() = runTest {
val client = Client(
Implementation(name = "test client", version = "1.0"),
ClientOptions(
capabilities = ClientCapabilities(
roots = ClientCapabilities.Roots(listChanged = true)
)
)
)
val (clientTransport, serverTransport) = InMemoryTransport.createLinkedPair()
val server = Server(
serverInfo = Implementation(name = "test server", version = "1.0"),
options = ServerOptions(
capabilities = ServerCapabilities()
)
)
// Track notifications
var rootListChangedNotificationReceived = false
server.setNotificationHandler<RootsListChangedNotification>(Method.Defined.NotificationsRootsListChanged) {
rootListChangedNotificationReceived = true
CompletableDeferred(Unit)
}
listOf(
launch { client.connect(clientTransport) },
launch { server.connect(serverTransport) }
).joinAll()
client.sendRootsListChanged()
assertTrue(
rootListChangedNotificationReceived,
"Notification should be sent when sendRootsListChanged is called"
)
}
@Test
fun `should reject server elicitation when elicitation capability is not supported`() = runTest {
val client = Client(
Implementation(name = "test client", version = "1.0"),
ClientOptions(
capabilities = ClientCapabilities()
)
)
val (clientTransport, serverTransport) = InMemoryTransport.createLinkedPair()
val server = Server(
serverInfo = Implementation(name = "test server", version = "1.0"),
options = ServerOptions(
capabilities = ServerCapabilities()
)
)
listOf(
launch { client.connect(clientTransport) },
launch { server.connect(serverTransport) }
).joinAll()
// Verify that creating an elicitation throws an exception
val exception = assertThrows<IllegalStateException> {
server.createElicitation(
message = "Please provide your GitHub username",
requestedSchema = CreateElicitationRequest.RequestedSchema(
properties = buildJsonObject {
putJsonObject("name") {
put("type", "string")
}
},
required = listOf("name")
)
)
}
assertEquals(
"Client does not support elicitation (required for elicitation/create)",
exception.message
)
}
@Test
fun `should handle server elicitation`() = runTest {
val client = Client(
Implementation(name = "test client", version = "1.0"),
ClientOptions(
capabilities = ClientCapabilities(
elicitation = EmptyJsonObject,
)
)
)
val elicitationMessage = "Please provide your GitHub username"
val requestedSchema = CreateElicitationRequest.RequestedSchema(
properties = buildJsonObject {
putJsonObject("name") {
put("type", "string")
}
},
required = listOf("name")
)
val elicitationResultAction = CreateElicitationResult.Action.accept
val elicitationResultContent = buildJsonObject {
put("name", "octocat")
}
client.setElicitationHandler { request ->
assertEquals(elicitationMessage, request.message)
assertEquals(requestedSchema, request.requestedSchema)
CreateElicitationResult(
action = elicitationResultAction,
content = elicitationResultContent
)
}
val (clientTransport, serverTransport) = InMemoryTransport.createLinkedPair()
val server = Server(
serverInfo = Implementation(name = "test server", version = "1.0"),
options = ServerOptions(
capabilities = ServerCapabilities()
)
)
listOf(
launch { client.connect(clientTransport) },
launch { server.connect(serverTransport) }
).joinAll()
val result = server.createElicitation(
message = elicitationMessage,
requestedSchema = requestedSchema
)
assertEquals(elicitationResultAction, result.action)
assertEquals(elicitationResultContent, result.content)
}
}

View file

@ -0,0 +1,47 @@
package io.modelcontextprotocol.kotlin.sdk.client
import kotlinx.coroutines.test.runTest
import kotlinx.io.asSink
import kotlinx.io.asSource
import kotlinx.io.buffered
import org.junit.jupiter.api.Test
class StdioClientTransportTest : BaseTransportTest() {
@Test
fun `should start then close cleanly`() = runTest {
// Run process "/usr/bin/tee"
val processBuilder = ProcessBuilder("/usr/bin/tee")
val process = processBuilder.start()
val input = process.inputStream.asSource().buffered()
val output = process.outputStream.asSink().buffered()
val client = StdioClientTransport(
input = input,
output = output
)
testClientOpenClose(client)
process.destroy()
}
@Test
fun `should read messages`() = runTest {
val processBuilder = ProcessBuilder("/usr/bin/tee")
val process = processBuilder.start()
val input = process.inputStream.asSource().buffered()
val output = process.outputStream.asSink().buffered()
val client = StdioClientTransport(
input = input,
output = output
)
testClientRead(client)
process.waitFor()
process.destroy()
}
}

View file

@ -0,0 +1,368 @@
package io.modelcontextprotocol.kotlin.sdk.client
import io.ktor.client.HttpClient
import io.ktor.client.engine.mock.MockEngine
import io.ktor.client.engine.mock.MockRequestHandler
import io.ktor.client.engine.mock.respond
import io.ktor.client.plugins.sse.SSE
import io.ktor.http.ContentType
import io.ktor.http.HttpHeaders
import io.ktor.http.HttpMethod
import io.ktor.http.HttpStatusCode
import io.ktor.http.content.TextContent
import io.ktor.http.headersOf
import io.ktor.utils.io.ByteReadChannel
import io.modelcontextprotocol.kotlin.sdk.JSONRPCMessage
import io.modelcontextprotocol.kotlin.sdk.JSONRPCNotification
import io.modelcontextprotocol.kotlin.sdk.JSONRPCRequest
import io.modelcontextprotocol.kotlin.sdk.RequestId
import io.modelcontextprotocol.kotlin.sdk.shared.McpJson
import kotlinx.coroutines.delay
import kotlinx.coroutines.test.runTest
import kotlinx.serialization.json.JsonObject
import kotlinx.serialization.json.JsonPrimitive
import kotlinx.serialization.json.buildJsonObject
import org.junit.jupiter.api.assertDoesNotThrow
import kotlin.test.Ignore
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertNull
import kotlin.test.assertTrue
import kotlin.time.Duration.Companion.seconds
class StreamableHttpClientTransportTest {
private fun createTransport(handler: MockRequestHandler): StreamableHttpClientTransport {
val mockEngine = MockEngine(handler)
val httpClient = HttpClient(mockEngine) {
install(SSE) {
reconnectionTime = 1.seconds
}
}
return StreamableHttpClientTransport(httpClient, url = "http://localhost:8080/mcp")
}
@Test
fun testSendJsonRpcMessage() = runTest {
val message = JSONRPCRequest(
id = RequestId.StringId("test-id"),
method = "test",
params = buildJsonObject { }
)
val transport = createTransport { request ->
assertEquals(HttpMethod.Post, request.method)
assertEquals("http://localhost:8080/mcp", request.url.toString())
assertEquals(ContentType.Application.Json, request.body.contentType)
val body = (request.body as TextContent).text
val decodedMessage = McpJson.decodeFromString<JSONRPCMessage>(body)
assertEquals(message, decodedMessage)
respond(
content = "",
status = HttpStatusCode.Accepted
)
}
transport.start()
transport.send(message)
transport.close()
}
@Test
fun testStoreSessionId() = runTest {
val initMessage = JSONRPCRequest(
id = RequestId.StringId("test-id"),
method = "initialize",
params = buildJsonObject {
put("clientInfo", buildJsonObject {
put("name", JsonPrimitive("test-client"))
put("version", JsonPrimitive("1.0"))
})
put("protocolVersion", JsonPrimitive("2025-06-18"))
}
)
val transport = createTransport { request ->
when (val msg = McpJson.decodeFromString<JSONRPCMessage>((request.body as TextContent).text)) {
is JSONRPCRequest if msg.method == "initialize" -> respond(
content = "", status = HttpStatusCode.OK,
headers = headersOf("mcp-session-id", "test-session-id")
)
is JSONRPCNotification if msg.method == "test" -> {
assertEquals("test-session-id", request.headers["mcp-session-id"])
respond(
content = "",
status = HttpStatusCode.Accepted
)
}
else -> error("Unexpected message: $msg")
}
}
transport.start()
transport.send(initMessage)
assertEquals("test-session-id", transport.sessionId)
transport.send(JSONRPCNotification(method = "test"))
transport.close()
}
@Test
fun testTerminateSession() = runTest {
// transport.sessionId = "test-session-id"
val transport = createTransport { request ->
assertEquals(HttpMethod.Delete, request.method)
assertEquals("test-session-id", request.headers["mcp-session-id"])
respond(
content = "",
status = HttpStatusCode.OK
)
}
transport.start()
transport.terminateSession()
assertNull(transport.sessionId)
transport.close()
}
@Test
fun testTerminateSessionHandle405() = runTest {
// transport.sessionId = "test-session-id"
val transport = createTransport { request ->
assertEquals(HttpMethod.Delete, request.method)
respond(
content = "",
status = HttpStatusCode.MethodNotAllowed
)
}
transport.start()
// Should not throw for 405
assertDoesNotThrow {
transport.terminateSession()
}
// Session ID should still be cleared
assertNull(transport.sessionId)
transport.close()
}
@Test
fun testProtocolVersionHeader() = runTest {
val transport = createTransport { request ->
assertEquals("2025-06-18", request.headers["mcp-protocol-version"])
respond(
content = "",
status = HttpStatusCode.Accepted
)
}
transport.protocolVersion = "2025-06-18"
transport.start()
transport.send(JSONRPCNotification(method = "test"))
transport.close()
}
@Ignore("Engine doesn't support SSECapability: https://youtrack.jetbrains.com/issue/KTOR-8177/MockEngine-Add-SSE-support")
@Test
fun testNotificationSchemaE2E() = runTest {
val receivedMessages = mutableListOf<JSONRPCMessage>()
var sseStarted = false
val transport = createTransport { request ->
when (request.method) {
HttpMethod.Post if request.body.toString().contains("notifications/initialized") -> {
respond(
content = "",
status = HttpStatusCode.Accepted,
headers = headersOf("mcp-session-id", "notification-test-session")
)
}
// Handle SSE connection
HttpMethod.Get -> {
sseStarted = true
val sseContent = buildString {
// Server sends various notifications
appendLine("event: message")
appendLine("id: 1")
appendLine("""data: {"jsonrpc":"2.0","method":"notifications/progress","params":{"progressToken":"upload-123","progress":50,"total":100}}""")
appendLine()
appendLine("event: message")
appendLine("id: 2")
appendLine("""data: {"jsonrpc":"2.0","method":"notifications/resources/list_changed"}""")
appendLine()
appendLine("event: message")
appendLine("id: 3")
appendLine("""data: {"jsonrpc":"2.0","method":"notifications/tools/list_changed"}""")
appendLine()
}
respond(
content = ByteReadChannel(sseContent),
status = HttpStatusCode.OK,
headers = headersOf(
HttpHeaders.ContentType, ContentType.Text.EventStream.toString()
)
)
}
// Handle regular notifications
HttpMethod.Post -> {
respond(
content = "",
status = HttpStatusCode.Accepted
)
}
else -> respond("", HttpStatusCode.OK)
}
}
transport.onMessage { message ->
receivedMessages.add(message)
}
transport.start()
// Test 1: Send initialized notification to trigger SSE
val initializedNotification = JSONRPCNotification(
method = "notifications/initialized",
params = buildJsonObject {
put("protocolVersion", JsonPrimitive("1.0"))
put("capabilities", buildJsonObject {
put("tools", JsonPrimitive(true))
put("resources", JsonPrimitive(true))
})
}
)
transport.send(initializedNotification)
// Verify SSE was triggered
assertTrue(sseStarted, "SSE should start after initialized notification")
// Test 2: Verify received notifications
assertEquals(3, receivedMessages.size)
assertTrue(receivedMessages.all { it is JSONRPCNotification })
val notifications = receivedMessages.filterIsInstance<JSONRPCNotification>()
// Verify progress notification
val progressNotif = notifications[0]
assertEquals("notifications/progress", progressNotif.method)
val progressParams = progressNotif.params as JsonObject
assertEquals("upload-123", (progressParams["progressToken"] as JsonPrimitive).content)
assertEquals(50, (progressParams["progress"] as JsonPrimitive).content.toInt())
// Verify list changed notifications
assertEquals("notifications/resources/list_changed", notifications[1].method)
assertEquals("notifications/tools/list_changed", notifications[2].method)
// Test 3: Send various client notifications
val clientNotifications = listOf(
JSONRPCNotification(
method = "notifications/progress",
params = buildJsonObject {
put("progressToken", JsonPrimitive("download-456"))
put("progress", JsonPrimitive(75))
}
),
JSONRPCNotification(
method = "notifications/cancelled",
params = buildJsonObject {
put("requestId", JsonPrimitive("req-789"))
put("reason", JsonPrimitive("user_cancelled"))
}
),
JSONRPCNotification(
method = "notifications/message",
params = buildJsonObject {
put("level", JsonPrimitive("info"))
put("message", JsonPrimitive("Operation completed"))
put("data", buildJsonObject {
put("duration", JsonPrimitive(1234))
})
}
)
)
// Send all client notifications
clientNotifications.forEach { notification ->
transport.send(notification)
}
// Verify session ID is maintained
assertEquals("notification-test-session", transport.sessionId)
transport.close()
}
@Ignore("Engine doesn't support SSECapability: https://youtrack.jetbrains.com/issue/KTOR-8177/MockEngine-Add-SSE-support")
@Test
fun testNotificationWithResumptionToken() = runTest {
var resumptionTokenReceived: String? = null
var lastEventIdSent: String? = null
val transport = createTransport { request ->
// Capture Last-Event-ID header
lastEventIdSent = request.headers["Last-Event-ID"]
when (request.method) {
HttpMethod.Get -> {
val sseContent = buildString {
appendLine("event: message")
appendLine("id: resume-100")
appendLine("""data: {"jsonrpc":"2.0","method":"notifications/resumed","params":{"fromToken":"${lastEventIdSent}"}}""")
appendLine()
}
respond(
content = ByteReadChannel(sseContent),
status = HttpStatusCode.OK,
headers = headersOf(
HttpHeaders.ContentType, ContentType.Text.EventStream.toString()
)
)
}
else -> respond("", HttpStatusCode.Accepted)
}
}
transport.start()
// Send notification with resumption token
transport.send(
message = JSONRPCNotification(
method = "notifications/test",
params = buildJsonObject {
put("data", JsonPrimitive("test-data"))
}
),
resumptionToken = "previous-token-99",
onResumptionToken = { token ->
resumptionTokenReceived = token
}
)
// Wait for response
delay(1.seconds)
// Verify resumption token was sent in header
assertEquals("previous-token-99", lastEventIdSent)
// Verify new resumption token was received
assertEquals("resume-100", resumptionTokenReceived)
transport.close()
}
}

View file

@ -0,0 +1,469 @@
package io.modelcontextprotocol.kotlin.sdk.server
import io.modelcontextprotocol.kotlin.sdk.CallToolResult
import io.modelcontextprotocol.kotlin.sdk.GetPromptResult
import io.modelcontextprotocol.kotlin.sdk.Implementation
import io.modelcontextprotocol.kotlin.sdk.InMemoryTransport
import io.modelcontextprotocol.kotlin.sdk.Method
import io.modelcontextprotocol.kotlin.sdk.Prompt
import io.modelcontextprotocol.kotlin.sdk.PromptListChangedNotification
import io.modelcontextprotocol.kotlin.sdk.ReadResourceResult
import io.modelcontextprotocol.kotlin.sdk.ResourceListChangedNotification
import io.modelcontextprotocol.kotlin.sdk.ServerCapabilities
import io.modelcontextprotocol.kotlin.sdk.TextContent
import io.modelcontextprotocol.kotlin.sdk.TextResourceContents
import io.modelcontextprotocol.kotlin.sdk.Tool
import io.modelcontextprotocol.kotlin.sdk.ToolListChangedNotification
import io.modelcontextprotocol.kotlin.sdk.client.Client
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.launch
import kotlinx.coroutines.test.runTest
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertTrue
class ServerTest {
@Test
fun `removeTool should remove a tool`() = runTest {
// Create server with tools capability
val serverOptions = ServerOptions(
capabilities = ServerCapabilities(
tools = ServerCapabilities.Tools(null)
)
)
val server = Server(
Implementation(name = "test server", version = "1.0"),
serverOptions
)
// Add a tool
server.addTool("test-tool", "Test Tool", Tool.Input()) { request ->
CallToolResult(listOf(TextContent("Test result")))
}
// Setup client
val (clientTransport, serverTransport) = InMemoryTransport.createLinkedPair()
val client = Client(
clientInfo = Implementation(name = "test client", version = "1.0"),
)
// Connect client and server
launch { client.connect(clientTransport) }
launch { server.connect(serverTransport) }
// Remove the tool
val result = server.removeTool("test-tool")
// Verify the tool was removed
assertTrue(result, "Tool should be removed successfully")
}
@Test
fun `removeTool should return false when tool does not exist`() = runTest {
// Create server with tools capability
val serverOptions = ServerOptions(
capabilities = ServerCapabilities(
tools = ServerCapabilities.Tools(null)
)
)
val server = Server(
Implementation(name = "test server", version = "1.0"),
serverOptions
)
// Setup client
val (clientTransport, serverTransport) = InMemoryTransport.createLinkedPair()
val client = Client(
clientInfo = Implementation(name = "test client", version = "1.0"),
)
// Track notifications
var toolListChangedNotificationReceived = false
client.setNotificationHandler<ToolListChangedNotification>(Method.Defined.NotificationsToolsListChanged) {
toolListChangedNotificationReceived = true
CompletableDeferred(Unit)
}
// Connect client and server
launch { client.connect(clientTransport) }
launch { server.connect(serverTransport) }
// Try to remove a non-existent tool
val result = server.removeTool("non-existent-tool")
// Verify the result
assertFalse(result, "Removing non-existent tool should return false")
assertFalse(toolListChangedNotificationReceived, "No notification should be sent when tool doesn't exist")
}
@Test
fun `removeTool should throw when tools capability is not supported`() = runTest {
// Create server without tools capability
val serverOptions = ServerOptions(
capabilities = ServerCapabilities()
)
val server = Server(
Implementation(name = "test server", version = "1.0"),
serverOptions
)
// Verify that removing a tool throws an exception
val exception = assertThrows<IllegalStateException> {
server.removeTool("test-tool")
}
assertEquals("Server does not support tools capability.", exception.message)
}
@Test
fun `removeTools should remove multiple tools`() = runTest {
// Create server with tools capability
val serverOptions = ServerOptions(
capabilities = ServerCapabilities(
tools = ServerCapabilities.Tools(null)
)
)
val server = Server(
Implementation(name = "test server", version = "1.0"),
serverOptions
)
// Add tools
server.addTool("test-tool-1", "Test Tool 1") { request ->
CallToolResult(listOf(TextContent("Test result 1")))
}
server.addTool("test-tool-2", "Test Tool 2") { request ->
CallToolResult(listOf(TextContent("Test result 2")))
}
// Setup client
val (clientTransport, serverTransport) = InMemoryTransport.createLinkedPair()
val client = Client(
clientInfo = Implementation(name = "test client", version = "1.0"),
)
// Connect client and server
launch { client.connect(clientTransport) }
launch { server.connect(serverTransport) }
// Remove the tools
val result = server.removeTools(listOf("test-tool-1", "test-tool-2"))
// Verify the tools were removed
assertEquals(2, result, "Both tools should be removed")
}
@Test
fun `removePrompt should remove a prompt`() = runTest {
// Create server with prompts capability
val serverOptions = ServerOptions(
capabilities = ServerCapabilities(
prompts = ServerCapabilities.Prompts(listChanged = false)
)
)
val server = Server(
Implementation(name = "test server", version = "1.0"),
serverOptions
)
// Add a prompt
val testPrompt = Prompt("test-prompt", "Test Prompt", null)
server.addPrompt(testPrompt) { request ->
GetPromptResult(
description = "Test prompt description",
messages = listOf()
)
}
// Setup client
val (clientTransport, serverTransport) = InMemoryTransport.createLinkedPair()
val client = Client(
clientInfo = Implementation(name = "test client", version = "1.0"),
)
// Connect client and server
launch { client.connect(clientTransport) }
launch { server.connect(serverTransport) }
// Remove the prompt
val result = server.removePrompt(testPrompt.name)
// Verify the prompt was removed
assertTrue(result, "Prompt should be removed successfully")
}
@Test
fun `removePrompts should remove multiple prompts and send notification`() = runTest {
// Create server with prompts capability
val serverOptions = ServerOptions(
capabilities = ServerCapabilities(
prompts = ServerCapabilities.Prompts(listChanged = false)
)
)
val server = Server(
Implementation(name = "test server", version = "1.0"),
serverOptions
)
// Add prompts
val testPrompt1 = Prompt("test-prompt-1", "Test Prompt 1", null)
val testPrompt2 = Prompt("test-prompt-2", "Test Prompt 2", null)
server.addPrompt(testPrompt1) { request ->
GetPromptResult(
description = "Test prompt description 1",
messages = listOf()
)
}
server.addPrompt(testPrompt2) { request ->
GetPromptResult(
description = "Test prompt description 2",
messages = listOf()
)
}
// Setup client
val (clientTransport, serverTransport) = InMemoryTransport.createLinkedPair()
val client = Client(
clientInfo = Implementation(name = "test client", version = "1.0"),
)
// Connect client and server
launch { client.connect(clientTransport) }
launch { server.connect(serverTransport) }
// 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")
}
@Test
fun `removeResource should remove a resource and send notification`() = runTest {
// Create server with resources capability
val serverOptions = ServerOptions(
capabilities = ServerCapabilities(
resources = ServerCapabilities.Resources(null, null)
)
)
val server = Server(
Implementation(name = "test server", version = "1.0"),
serverOptions
)
// Add a resource
val testResourceUri = "test://resource"
server.addResource(
uri = testResourceUri,
name = "Test Resource",
description = "A test resource",
mimeType = "text/plain"
) { request ->
ReadResourceResult(
contents = listOf(
TextResourceContents(
text = "Test resource content",
uri = testResourceUri,
mimeType = "text/plain",
),
)
)
}
// Setup client
val (clientTransport, serverTransport) = InMemoryTransport.createLinkedPair()
val client = Client(
clientInfo = Implementation(name = "test client", version = "1.0"),
)
// Connect client and server
launch { client.connect(clientTransport) }
launch { server.connect(serverTransport) }
// Remove the resource
val result = server.removeResource(testResourceUri)
// Verify the resource was removed
assertTrue(result, "Resource should be removed successfully")
}
@Test
fun `removeResources should remove multiple resources and send notification`() = runTest {
// Create server with resources capability
val serverOptions = ServerOptions(
capabilities = ServerCapabilities(
resources = ServerCapabilities.Resources(null, null)
)
)
val server = Server(
Implementation(name = "test server", version = "1.0"),
serverOptions
)
// Add resources
val testResourceUri1 = "test://resource1"
val testResourceUri2 = "test://resource2"
server.addResource(
uri = testResourceUri1,
name = "Test Resource 1",
description = "A test resource 1",
mimeType = "text/plain"
) { request ->
ReadResourceResult(
contents = listOf(
TextResourceContents(
text = "Test resource content 1",
uri = testResourceUri1,
mimeType = "text/plain",
),
)
)
}
server.addResource(
uri = testResourceUri2,
name = "Test Resource 2",
description = "A test resource 2",
mimeType = "text/plain"
) { request ->
ReadResourceResult(
contents = listOf(
TextResourceContents(
text = "Test resource content 2",
uri = testResourceUri2,
mimeType = "text/plain",
),
)
)
}
// Setup client
val (clientTransport, serverTransport) = InMemoryTransport.createLinkedPair()
val client = Client(
clientInfo = Implementation(name = "test client", version = "1.0"),
)
// Connect client and server
launch { client.connect(clientTransport) }
launch { server.connect(serverTransport) }
// Remove the resources
val result = server.removeResources(listOf(testResourceUri1, testResourceUri2))
// Verify the resources were removed
assertEquals(2, result, "Both resources should be removed")
}
@Test
fun `removePrompt should return false when prompt does not exist`() = runTest {
// Create server with prompts capability
val serverOptions = ServerOptions(
capabilities = ServerCapabilities(
prompts = ServerCapabilities.Prompts(listChanged = false)
)
)
val server = Server(
Implementation(name = "test server", version = "1.0"),
serverOptions
)
// Setup client
val (clientTransport, serverTransport) = InMemoryTransport.createLinkedPair()
val client = Client(
clientInfo = Implementation(name = "test client", version = "1.0"),
)
// Track notifications
var promptListChangedNotificationReceived = false
client.setNotificationHandler<PromptListChangedNotification>(Method.Defined.NotificationsPromptsListChanged) {
promptListChangedNotificationReceived = true
CompletableDeferred(Unit)
}
// Connect client and server
launch { client.connect(clientTransport) }
launch { server.connect(serverTransport) }
// Try to remove a non-existent prompt
val result = server.removePrompt("non-existent-prompt")
// Verify the result
assertFalse(result, "Removing non-existent prompt should return false")
assertFalse(promptListChangedNotificationReceived, "No notification should be sent when prompt doesn't exist")
}
@Test
fun `removePrompt should throw when prompts capability is not supported`() = runTest {
// Create server without prompts capability
val serverOptions = ServerOptions(
capabilities = ServerCapabilities()
)
val server = Server(
Implementation(name = "test server", version = "1.0"),
serverOptions
)
// Verify that removing a prompt throws an exception
val exception = assertThrows<IllegalStateException> {
server.removePrompt("test-prompt")
}
assertEquals("Server does not support prompts capability.", exception.message)
}
@Test
fun `removeResource should return false when resource does not exist`() = runTest {
// Create server with resources capability
val serverOptions = ServerOptions(
capabilities = ServerCapabilities(
resources = ServerCapabilities.Resources(null, null)
)
)
val server = Server(
Implementation(name = "test server", version = "1.0"),
serverOptions
)
// Setup client
val (clientTransport, serverTransport) = InMemoryTransport.createLinkedPair()
val client = Client(
clientInfo = Implementation(name = "test client", version = "1.0"),
)
// Track notifications
var resourceListChangedNotificationReceived = false
client.setNotificationHandler<ResourceListChangedNotification>(Method.Defined.NotificationsResourcesListChanged) {
resourceListChangedNotificationReceived = true
CompletableDeferred(Unit)
}
// Connect client and server
launch { client.connect(clientTransport) }
launch { server.connect(serverTransport) }
// Try to remove a non-existent resource
val result = server.removeResource("non-existent-resource")
// Verify the result
assertFalse(result, "Removing non-existent resource should return false")
assertFalse(resourceListChangedNotificationReceived, "No notification should be sent when resource doesn't exist")
}
@Test
fun `removeResource should throw when resources capability is not supported`() = runTest {
// Create server without resources capability
val serverOptions = ServerOptions(
capabilities = ServerCapabilities()
)
val server = Server(
Implementation(name = "test server", version = "1.0"),
serverOptions
)
// Verify that removing a resource throws an exception
val exception = assertThrows<IllegalStateException> {
server.removeResource("test://resource")
}
assertEquals("Server does not support resources capability.", exception.message)
}
}

View file

@ -0,0 +1,147 @@
package io.modelcontextprotocol.kotlin.sdk.server
import io.modelcontextprotocol.kotlin.sdk.InitializedNotification
import io.modelcontextprotocol.kotlin.sdk.JSONRPCMessage
import io.modelcontextprotocol.kotlin.sdk.PingRequest
import io.modelcontextprotocol.kotlin.sdk.shared.ReadBuffer
import io.modelcontextprotocol.kotlin.sdk.shared.serializeMessage
import io.modelcontextprotocol.kotlin.sdk.toJSON
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.runBlocking
import kotlinx.io.Sink
import kotlinx.io.Source
import kotlinx.io.asSink
import kotlinx.io.asSource
import kotlinx.io.buffered
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertFalse
import org.junit.jupiter.api.Assertions.assertTrue
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import java.io.ByteArrayOutputStream
import java.io.PipedInputStream
import java.io.PipedOutputStream
class StdioServerTransportTest {
private lateinit var input: PipedInputStream
private lateinit var inputWriter: PipedOutputStream
private lateinit var outputBuffer: ReadBuffer
private lateinit var output: ByteArrayOutputStream
// We'll store the wrapped streams that meet the constructor requirements
private lateinit var bufferedInput: Source
private lateinit var printOutput: Sink
@BeforeEach
fun setUp() {
// Simulate an input stream that we can push data into using inputWriter.
input = PipedInputStream()
inputWriter = PipedOutputStream(input)
outputBuffer = ReadBuffer()
// A custom ByteArrayOutputStream that appends all written data into outputBuffer
output = object : ByteArrayOutputStream() {
override fun write(b: ByteArray, off: Int, len: Int) {
super.write(b, off, len)
outputBuffer.append(b.copyOfRange(off, off + len))
}
}
bufferedInput = input.asSource().buffered()
printOutput = output.asSink().buffered()
}
@Test
fun `should start then close cleanly`() {
runBlocking {
val server = StdioServerTransport(bufferedInput, printOutput)
server.onError { error ->
throw error
}
var didClose = false
server.onClose {
didClose = true
}
server.start()
assertFalse(didClose, "Should not have closed yet")
server.close()
assertTrue(didClose, "Should have closed after calling close()")
}
}
@Test
fun `should not read until started`() {
runBlocking {
val server = StdioServerTransport(bufferedInput, printOutput)
server.onError { error ->
throw error
}
var didRead = false
val readMessage = CompletableDeferred<JSONRPCMessage>()
server.onMessage { message ->
didRead = true
readMessage.complete(message)
}
val message = PingRequest().toJSON()
// Push a message before the server started
val serialized = serializeMessage(message)
inputWriter.write(serialized)
inputWriter.flush()
assertFalse(didRead, "Should not have read message before start")
server.start()
val received = readMessage.await()
assertEquals(message, received)
}
}
@Test
fun `should read multiple messages`() {
runBlocking {
val server = StdioServerTransport(bufferedInput, printOutput)
server.onError { error ->
throw error
}
val messages = listOf(
PingRequest().toJSON(),
InitializedNotification().toJSON(),
)
val readMessages = mutableListOf<JSONRPCMessage>()
val finished = CompletableDeferred<Unit>()
server.onMessage { message ->
readMessages.add(message)
if (message == messages[1]) {
finished.complete(Unit)
}
}
// Push both messages before starting the server
for (m in messages) {
inputWriter.write(serializeMessage(m))
}
inputWriter.flush()
server.start()
finished.await()
assertEquals(messages, readMessages)
}
}
}
fun PipedOutputStream.write(s: String) {
write(s.toByteArray())
}