diff --git a/api/kotlin-sdk.api b/api/kotlin-sdk.api index ed82d3b6..6b711675 100644 --- a/api/kotlin-sdk.api +++ b/api/kotlin-sdk.api @@ -167,14 +167,16 @@ public final class io/modelcontextprotocol/kotlin/sdk/CancelledNotification$Comp public final class io/modelcontextprotocol/kotlin/sdk/ClientCapabilities { public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/ClientCapabilities$Companion; public fun ()V - public fun (Lkotlinx/serialization/json/JsonObject;Lkotlinx/serialization/json/JsonObject;Lio/modelcontextprotocol/kotlin/sdk/ClientCapabilities$Roots;)V - public synthetic fun (Lkotlinx/serialization/json/JsonObject;Lkotlinx/serialization/json/JsonObject;Lio/modelcontextprotocol/kotlin/sdk/ClientCapabilities$Roots;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (Lkotlinx/serialization/json/JsonObject;Lkotlinx/serialization/json/JsonObject;Lio/modelcontextprotocol/kotlin/sdk/ClientCapabilities$Roots;Lkotlinx/serialization/json/JsonObject;)V + public synthetic fun (Lkotlinx/serialization/json/JsonObject;Lkotlinx/serialization/json/JsonObject;Lio/modelcontextprotocol/kotlin/sdk/ClientCapabilities$Roots;Lkotlinx/serialization/json/JsonObject;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun component1 ()Lkotlinx/serialization/json/JsonObject; public final fun component2 ()Lkotlinx/serialization/json/JsonObject; public final fun component3 ()Lio/modelcontextprotocol/kotlin/sdk/ClientCapabilities$Roots; - public final fun copy (Lkotlinx/serialization/json/JsonObject;Lkotlinx/serialization/json/JsonObject;Lio/modelcontextprotocol/kotlin/sdk/ClientCapabilities$Roots;)Lio/modelcontextprotocol/kotlin/sdk/ClientCapabilities; - public static synthetic fun copy$default (Lio/modelcontextprotocol/kotlin/sdk/ClientCapabilities;Lkotlinx/serialization/json/JsonObject;Lkotlinx/serialization/json/JsonObject;Lio/modelcontextprotocol/kotlin/sdk/ClientCapabilities$Roots;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/ClientCapabilities; + public final fun component4 ()Lkotlinx/serialization/json/JsonObject; + public final fun copy (Lkotlinx/serialization/json/JsonObject;Lkotlinx/serialization/json/JsonObject;Lio/modelcontextprotocol/kotlin/sdk/ClientCapabilities$Roots;Lkotlinx/serialization/json/JsonObject;)Lio/modelcontextprotocol/kotlin/sdk/ClientCapabilities; + public static synthetic fun copy$default (Lio/modelcontextprotocol/kotlin/sdk/ClientCapabilities;Lkotlinx/serialization/json/JsonObject;Lkotlinx/serialization/json/JsonObject;Lio/modelcontextprotocol/kotlin/sdk/ClientCapabilities$Roots;Lkotlinx/serialization/json/JsonObject;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/ClientCapabilities; public fun equals (Ljava/lang/Object;)Z + public final fun getElicitation ()Lkotlinx/serialization/json/JsonObject; public final fun getExperimental ()Lkotlinx/serialization/json/JsonObject; public final fun getRoots ()Lio/modelcontextprotocol/kotlin/sdk/ClientCapabilities$Roots; public final fun getSampling ()Lkotlinx/serialization/json/JsonObject; @@ -383,6 +385,117 @@ public final class io/modelcontextprotocol/kotlin/sdk/CompleteResult$Completion$ public final fun serializer ()Lkotlinx/serialization/KSerializer; } +public final class io/modelcontextprotocol/kotlin/sdk/CreateElicitationRequest : io/modelcontextprotocol/kotlin/sdk/ServerRequest, io/modelcontextprotocol/kotlin/sdk/WithMeta { + public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/CreateElicitationRequest$Companion; + public fun (Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/CreateElicitationRequest$RequestedSchema;Lkotlinx/serialization/json/JsonObject;)V + public synthetic fun (Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/CreateElicitationRequest$RequestedSchema;Lkotlinx/serialization/json/JsonObject;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Ljava/lang/String; + public final fun component2 ()Lio/modelcontextprotocol/kotlin/sdk/CreateElicitationRequest$RequestedSchema; + public final fun component3 ()Lkotlinx/serialization/json/JsonObject; + public final fun copy (Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/CreateElicitationRequest$RequestedSchema;Lkotlinx/serialization/json/JsonObject;)Lio/modelcontextprotocol/kotlin/sdk/CreateElicitationRequest; + public static synthetic fun copy$default (Lio/modelcontextprotocol/kotlin/sdk/CreateElicitationRequest;Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/CreateElicitationRequest$RequestedSchema;Lkotlinx/serialization/json/JsonObject;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/CreateElicitationRequest; + public fun equals (Ljava/lang/Object;)Z + public final fun getMessage ()Ljava/lang/String; + public fun getMethod ()Lio/modelcontextprotocol/kotlin/sdk/Method; + public final fun getRequestedSchema ()Lio/modelcontextprotocol/kotlin/sdk/CreateElicitationRequest$RequestedSchema; + 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/CreateElicitationRequest$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lio/modelcontextprotocol/kotlin/sdk/CreateElicitationRequest$$serializer; + public final fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lio/modelcontextprotocol/kotlin/sdk/CreateElicitationRequest; + 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/CreateElicitationRequest;)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/CreateElicitationRequest$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + +public final class io/modelcontextprotocol/kotlin/sdk/CreateElicitationRequest$RequestedSchema { + public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/CreateElicitationRequest$RequestedSchema$Companion; + public fun ()V + public fun (Lkotlinx/serialization/json/JsonObject;Ljava/util/List;)V + public synthetic fun (Lkotlinx/serialization/json/JsonObject;Ljava/util/List;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Lkotlinx/serialization/json/JsonObject; + public final fun component2 ()Ljava/util/List; + public final fun copy (Lkotlinx/serialization/json/JsonObject;Ljava/util/List;)Lio/modelcontextprotocol/kotlin/sdk/CreateElicitationRequest$RequestedSchema; + public static synthetic fun copy$default (Lio/modelcontextprotocol/kotlin/sdk/CreateElicitationRequest$RequestedSchema;Lkotlinx/serialization/json/JsonObject;Ljava/util/List;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/CreateElicitationRequest$RequestedSchema; + public fun equals (Ljava/lang/Object;)Z + public final fun getProperties ()Lkotlinx/serialization/json/JsonObject; + public final fun getRequired ()Ljava/util/List; + public final fun getType ()Ljava/lang/String; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final synthetic class io/modelcontextprotocol/kotlin/sdk/CreateElicitationRequest$RequestedSchema$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lio/modelcontextprotocol/kotlin/sdk/CreateElicitationRequest$RequestedSchema$$serializer; + public final fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lio/modelcontextprotocol/kotlin/sdk/CreateElicitationRequest$RequestedSchema; + 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/CreateElicitationRequest$RequestedSchema;)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/CreateElicitationRequest$RequestedSchema$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + +public final class io/modelcontextprotocol/kotlin/sdk/CreateElicitationResult : io/modelcontextprotocol/kotlin/sdk/ClientResult { + public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/CreateElicitationResult$Companion; + public fun (Lio/modelcontextprotocol/kotlin/sdk/CreateElicitationResult$Action;Lkotlinx/serialization/json/JsonObject;Lkotlinx/serialization/json/JsonObject;)V + public synthetic fun (Lio/modelcontextprotocol/kotlin/sdk/CreateElicitationResult$Action;Lkotlinx/serialization/json/JsonObject;Lkotlinx/serialization/json/JsonObject;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Lio/modelcontextprotocol/kotlin/sdk/CreateElicitationResult$Action; + public final fun component2 ()Lkotlinx/serialization/json/JsonObject; + public final fun component3 ()Lkotlinx/serialization/json/JsonObject; + public final fun copy (Lio/modelcontextprotocol/kotlin/sdk/CreateElicitationResult$Action;Lkotlinx/serialization/json/JsonObject;Lkotlinx/serialization/json/JsonObject;)Lio/modelcontextprotocol/kotlin/sdk/CreateElicitationResult; + public static synthetic fun copy$default (Lio/modelcontextprotocol/kotlin/sdk/CreateElicitationResult;Lio/modelcontextprotocol/kotlin/sdk/CreateElicitationResult$Action;Lkotlinx/serialization/json/JsonObject;Lkotlinx/serialization/json/JsonObject;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/CreateElicitationResult; + public fun equals (Ljava/lang/Object;)Z + public final fun getAction ()Lio/modelcontextprotocol/kotlin/sdk/CreateElicitationResult$Action; + public final fun getContent ()Lkotlinx/serialization/json/JsonObject; + 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/CreateElicitationResult$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Lio/modelcontextprotocol/kotlin/sdk/CreateElicitationResult$$serializer; + public final fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lio/modelcontextprotocol/kotlin/sdk/CreateElicitationResult; + 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/CreateElicitationResult;)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/CreateElicitationResult$Action : java/lang/Enum { + public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/CreateElicitationResult$Action$Companion; + public static final field accept Lio/modelcontextprotocol/kotlin/sdk/CreateElicitationResult$Action; + public static final field cancel Lio/modelcontextprotocol/kotlin/sdk/CreateElicitationResult$Action; + public static final field decline Lio/modelcontextprotocol/kotlin/sdk/CreateElicitationResult$Action; + public static fun getEntries ()Lkotlin/enums/EnumEntries; + public static fun valueOf (Ljava/lang/String;)Lio/modelcontextprotocol/kotlin/sdk/CreateElicitationResult$Action; + public static fun values ()[Lio/modelcontextprotocol/kotlin/sdk/CreateElicitationResult$Action; +} + +public final class io/modelcontextprotocol/kotlin/sdk/CreateElicitationResult$Action$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + +public final class io/modelcontextprotocol/kotlin/sdk/CreateElicitationResult$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + public final class io/modelcontextprotocol/kotlin/sdk/CreateMessageRequest : io/modelcontextprotocol/kotlin/sdk/ServerRequest, io/modelcontextprotocol/kotlin/sdk/WithMeta { public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/CreateMessageRequest$Companion; public fun (Ljava/util/List;Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/CreateMessageRequest$IncludeContext;Ljava/lang/Double;ILjava/util/List;Lkotlinx/serialization/json/JsonObject;Lio/modelcontextprotocol/kotlin/sdk/ModelPreferences;Lkotlinx/serialization/json/JsonObject;)V @@ -1381,6 +1494,7 @@ public final class io/modelcontextprotocol/kotlin/sdk/Method$Custom$Companion { public final class io/modelcontextprotocol/kotlin/sdk/Method$Defined : java/lang/Enum, io/modelcontextprotocol/kotlin/sdk/Method { public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/Method$Defined$Companion; public static final field CompletionComplete Lio/modelcontextprotocol/kotlin/sdk/Method$Defined; + public static final field ElicitationCreate Lio/modelcontextprotocol/kotlin/sdk/Method$Defined; public static final field Initialize Lio/modelcontextprotocol/kotlin/sdk/Method$Defined; public static final field LoggingSetLevel Lio/modelcontextprotocol/kotlin/sdk/Method$Defined; public static final field NotificationsCancelled Lio/modelcontextprotocol/kotlin/sdk/Method$Defined; @@ -2758,6 +2872,7 @@ public class io/modelcontextprotocol/kotlin/sdk/client/Client : io/modelcontextp public final fun removeRoot (Ljava/lang/String;)Z public final fun removeRoots (Ljava/util/List;)I public final fun sendRootsListChanged (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public final fun setElicitationHandler (Lkotlin/jvm/functions/Function1;)V public final fun setLoggingLevel (Lio/modelcontextprotocol/kotlin/sdk/LoggingLevel;Lio/modelcontextprotocol/kotlin/sdk/shared/RequestOptions;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public static synthetic fun setLoggingLevel$default (Lio/modelcontextprotocol/kotlin/sdk/client/Client;Lio/modelcontextprotocol/kotlin/sdk/LoggingLevel;Lio/modelcontextprotocol/kotlin/sdk/shared/RequestOptions;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; public final fun subscribeResource (Lio/modelcontextprotocol/kotlin/sdk/SubscribeRequest;Lio/modelcontextprotocol/kotlin/sdk/shared/RequestOptions;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; @@ -2868,6 +2983,8 @@ public class io/modelcontextprotocol/kotlin/sdk/server/Server : io/modelcontextp protected fun assertCapabilityForMethod (Lio/modelcontextprotocol/kotlin/sdk/Method;)V protected fun assertNotificationCapability (Lio/modelcontextprotocol/kotlin/sdk/Method;)V public fun assertRequestHandlerCapability (Lio/modelcontextprotocol/kotlin/sdk/Method;)V + public final fun createElicitation (Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/CreateElicitationRequest$RequestedSchema;Lio/modelcontextprotocol/kotlin/sdk/shared/RequestOptions;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public static synthetic fun createElicitation$default (Lio/modelcontextprotocol/kotlin/sdk/server/Server;Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/CreateElicitationRequest$RequestedSchema;Lio/modelcontextprotocol/kotlin/sdk/shared/RequestOptions;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; public final fun createMessage (Lio/modelcontextprotocol/kotlin/sdk/CreateMessageRequest;Lio/modelcontextprotocol/kotlin/sdk/shared/RequestOptions;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public static synthetic fun createMessage$default (Lio/modelcontextprotocol/kotlin/sdk/server/Server;Lio/modelcontextprotocol/kotlin/sdk/CreateMessageRequest;Lio/modelcontextprotocol/kotlin/sdk/shared/RequestOptions;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object; public final fun getClientCapabilities ()Lio/modelcontextprotocol/kotlin/sdk/ClientCapabilities; diff --git a/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/client/Client.kt b/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/client/Client.kt index 880c8847..59abee5b 100644 --- a/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/client/Client.kt +++ b/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/client/Client.kt @@ -8,6 +8,8 @@ import io.modelcontextprotocol.kotlin.sdk.ClientCapabilities import io.modelcontextprotocol.kotlin.sdk.CompatibilityCallToolResult import io.modelcontextprotocol.kotlin.sdk.CompleteRequest import io.modelcontextprotocol.kotlin.sdk.CompleteResult +import io.modelcontextprotocol.kotlin.sdk.CreateElicitationRequest +import io.modelcontextprotocol.kotlin.sdk.CreateElicitationResult import io.modelcontextprotocol.kotlin.sdk.EmptyRequestResult import io.modelcontextprotocol.kotlin.sdk.GetPromptRequest import io.modelcontextprotocol.kotlin.sdk.GetPromptResult @@ -262,6 +264,14 @@ public open class Client( } } + Method.Defined.ElicitationCreate -> { + if (capabilities.elicitation == null) { + throw IllegalStateException( + "Client does not support elicitation capability (required for $method)" + ) + } + } + Method.Defined.Ping -> { // No capability required } @@ -571,6 +581,24 @@ public open class Client( notification(RootsListChangedNotification()) } + /** + * Sets the elicitation handler. + * + * @param handler The elicitation handler. + * @throws IllegalStateException if the client does not support elicitation. + */ + public fun setElicitationHandler(handler: (CreateElicitationRequest) -> CreateElicitationResult) { + if (capabilities.elicitation == null) { + logger.error { "Failed to set elicitation handler: Client does not support elicitation" } + throw IllegalStateException("Client does not support elicitation.") + } + logger.info { "Setting the elicitation handler" } + + setRequestHandler(Method.Defined.ElicitationCreate) { request, _ -> + handler(request) + } + } + // --- Internal Handlers --- private suspend fun handleListRoots(): ListRootsResult { diff --git a/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/server/Server.kt b/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/server/Server.kt index e98c4ced..6f3a7c78 100644 --- a/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/server/Server.kt +++ b/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/server/Server.kt @@ -4,6 +4,9 @@ import io.github.oshai.kotlinlogging.KotlinLogging import io.modelcontextprotocol.kotlin.sdk.CallToolRequest import io.modelcontextprotocol.kotlin.sdk.CallToolResult import io.modelcontextprotocol.kotlin.sdk.ClientCapabilities +import io.modelcontextprotocol.kotlin.sdk.CreateElicitationRequest +import io.modelcontextprotocol.kotlin.sdk.CreateElicitationRequest.RequestedSchema +import io.modelcontextprotocol.kotlin.sdk.CreateElicitationResult import io.modelcontextprotocol.kotlin.sdk.CreateMessageRequest import io.modelcontextprotocol.kotlin.sdk.CreateMessageResult import io.modelcontextprotocol.kotlin.sdk.EmptyJsonObject @@ -525,6 +528,15 @@ public open class Server( return request(ListRootsRequest(params), options) } + public suspend fun createElicitation( + message: String, + requestedSchema: RequestedSchema, + options: RequestOptions? = null + ): CreateElicitationResult { + logger.debug { "Creating elicitation with message: $message" } + return request(CreateElicitationRequest(message, requestedSchema), options) + } + /** * Sends a logging message notification to the client. * @@ -666,6 +678,12 @@ public open class Server( } } + "elicitation/create" -> { + if (clientCapabilities?.elicitation == null) { + throw IllegalStateException("Client does not support elicitation (required for ${method.value})") + } + } + "ping" -> { // No specific capability required } diff --git a/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/types.kt b/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/types.kt index 8972e16c..f84233e8 100644 --- a/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/types.kt +++ b/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/types.kt @@ -98,7 +98,8 @@ public sealed interface Method { LoggingSetLevel("logging/setLevel"), SamplingCreateMessage("sampling/createMessage"), CompletionComplete("completion/complete"), - RootsList("roots/list") + RootsList("roots/list"), + ElicitationCreate("elicitation/create"), } /** @@ -337,6 +338,10 @@ public data class ClientCapabilities( * Present if the client supports listing roots. */ val roots: Roots? = null, + /** + * Present if the client supports elicitation. + */ + val elicitation: JsonObject? = null, ) { @Serializable public data class Roots( @@ -1544,6 +1549,48 @@ public class RootsListChangedNotification : ClientNotification { override val method: Method = Method.Defined.NotificationsRootsListChanged } +/** + * Sent from the server to create an elicitation from the client. + */ +@Serializable +public data class CreateElicitationRequest( + public val message: String, + public val requestedSchema: RequestedSchema, + override val _meta: JsonObject = EmptyJsonObject, +) : ServerRequest, WithMeta { + override val method: Method = Method.Defined.ElicitationCreate + + @Serializable + public data class RequestedSchema( + val properties: JsonObject = EmptyJsonObject, + val required: List? = null, + ) { + @OptIn(ExperimentalSerializationApi::class) + @EncodeDefault + val type: String = "object" + } +} + +/** + * The client's response to an elicitation/create request from the server. + */ +@Serializable +public data class CreateElicitationResult( + public val action: Action, + public val content: JsonObject? = null, + override val _meta: JsonObject = EmptyJsonObject, +) : ClientResult { + + init { + require(action == Action.accept || content == null) { + "Content can only be provided for an 'accept' action" + } + } + + @Serializable + public enum class Action { accept, decline, cancel } +} + /** * Represents an error specific to the MCP protocol. * diff --git a/src/jvmTest/kotlin/client/ClientTest.kt b/src/jvmTest/kotlin/client/ClientTest.kt index f74cd0a5..20f89114 100644 --- a/src/jvmTest/kotlin/client/ClientTest.kt +++ b/src/jvmTest/kotlin/client/ClientTest.kt @@ -3,6 +3,8 @@ package 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 @@ -42,6 +44,7 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.test.runTest 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 @@ -796,4 +799,106 @@ class ClientTest { "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 { + 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) + } }