Skip to content

Commit e5e5ec2

Browse files
authored
feat: add notification for elicitation complete (#667)
closes #546 ## How Has This Been Tested? unit ## Breaking Changes none ## Types of changes - [ ] Bug fix (non-breaking change which fixes an issue) - [x] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to change) - [ ] Documentation update ## Checklist - [x] I have read the [MCP Documentation](https://modelcontextprotocol.io) - [x] My code follows the repository's style guidelines - [x] New and existing tests pass locally - [ ] I have added appropriate error handling - [ ] I have added or updated documentation as needed
1 parent 3617ce4 commit e5e5ec2

10 files changed

Lines changed: 186 additions & 0 deletions

File tree

integration-test/src/jvmTest/kotlin/io/modelcontextprotocol/kotlin/sdk/server/ClientConnectionTest.kt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import io.kotest.matchers.string.shouldNotBeBlank
77
import io.modelcontextprotocol.kotlin.sdk.types.CallToolRequest
88
import io.modelcontextprotocol.kotlin.sdk.types.CallToolRequestParams
99
import io.modelcontextprotocol.kotlin.sdk.types.ClientCapabilities
10+
import io.modelcontextprotocol.kotlin.sdk.types.ElicitationCompleteNotification
11+
import io.modelcontextprotocol.kotlin.sdk.types.ElicitationCompleteNotificationParams
1012
import io.modelcontextprotocol.kotlin.sdk.types.GetPromptRequest
1113
import io.modelcontextprotocol.kotlin.sdk.types.GetPromptRequestParams
1214
import io.modelcontextprotocol.kotlin.sdk.types.ListRootsRequest
@@ -77,6 +79,8 @@ class ClientConnectionTest : AbstractServerFeaturesTest() {
7779
val loggingMessage = onClientNotification<LoggingMessageNotification>(Method.Defined.NotificationsMessage)
7880
val resourceUpdated =
7981
onClientNotification<ResourceUpdatedNotification>(Method.Defined.NotificationsResourcesUpdated)
82+
val elicitationComplete =
83+
onClientNotification<ElicitationCompleteNotification>(Method.Defined.NotificationsElicitationComplete)
8084

8185
init {
8286
onClientRequest<ListRootsRequest, ListRootsResult>(Method.Defined.RootsList) {
@@ -109,6 +113,12 @@ class ClientConnectionTest : AbstractServerFeaturesTest() {
109113
withClue("sendResourceUpdated() delivered wrong URI") {
110114
resourceUri shouldBe "test://res"
111115
}
116+
withClue("sendElicitationComplete() did not deliver a notification to the client") {
117+
elicitationComplete.isCompleted shouldBe true
118+
}
119+
withClue("sendElicitationComplete() delivered wrong elicitationId") {
120+
elicitationComplete.await().params.elicitationId shouldBe "elicit-123"
121+
}
112122
capturedSessionId.shouldNotBeBlank()
113123
}
114124
}
@@ -128,6 +138,9 @@ class ClientConnectionTest : AbstractServerFeaturesTest() {
128138
),
129139
)
130140
sendResourceUpdated(ResourceUpdatedNotification(ResourceUpdatedNotificationParams("test://res")))
141+
sendElicitationComplete(
142+
ElicitationCompleteNotification(ElicitationCompleteNotificationParams(elicitationId = "elicit-123")),
143+
)
131144
cap.sessionId.complete(sessionId)
132145
}
133146

kotlin-sdk-core/api/kotlin-sdk-core.api

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1417,6 +1417,65 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/ElicitResult$Compani
14171417
public final fun serializer ()Lkotlinx/serialization/KSerializer;
14181418
}
14191419

1420+
public final class io/modelcontextprotocol/kotlin/sdk/types/ElicitationCompleteNotification : io/modelcontextprotocol/kotlin/sdk/types/ServerNotification {
1421+
public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/types/ElicitationCompleteNotification$Companion;
1422+
public fun <init> (Lio/modelcontextprotocol/kotlin/sdk/types/ElicitationCompleteNotificationParams;)V
1423+
public final fun component1 ()Lio/modelcontextprotocol/kotlin/sdk/types/ElicitationCompleteNotificationParams;
1424+
public final fun copy (Lio/modelcontextprotocol/kotlin/sdk/types/ElicitationCompleteNotificationParams;)Lio/modelcontextprotocol/kotlin/sdk/types/ElicitationCompleteNotification;
1425+
public static synthetic fun copy$default (Lio/modelcontextprotocol/kotlin/sdk/types/ElicitationCompleteNotification;Lio/modelcontextprotocol/kotlin/sdk/types/ElicitationCompleteNotificationParams;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/types/ElicitationCompleteNotification;
1426+
public fun equals (Ljava/lang/Object;)Z
1427+
public fun getMethod ()Lio/modelcontextprotocol/kotlin/sdk/types/Method;
1428+
public fun getParams ()Lio/modelcontextprotocol/kotlin/sdk/types/ElicitationCompleteNotificationParams;
1429+
public synthetic fun getParams ()Lio/modelcontextprotocol/kotlin/sdk/types/NotificationParams;
1430+
public fun hashCode ()I
1431+
public fun toString ()Ljava/lang/String;
1432+
}
1433+
1434+
public final synthetic class io/modelcontextprotocol/kotlin/sdk/types/ElicitationCompleteNotification$$serializer : kotlinx/serialization/internal/GeneratedSerializer {
1435+
public static final field INSTANCE Lio/modelcontextprotocol/kotlin/sdk/types/ElicitationCompleteNotification$$serializer;
1436+
public final fun childSerializers ()[Lkotlinx/serialization/KSerializer;
1437+
public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lio/modelcontextprotocol/kotlin/sdk/types/ElicitationCompleteNotification;
1438+
public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;
1439+
public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
1440+
public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lio/modelcontextprotocol/kotlin/sdk/types/ElicitationCompleteNotification;)V
1441+
public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
1442+
public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer;
1443+
}
1444+
1445+
public final class io/modelcontextprotocol/kotlin/sdk/types/ElicitationCompleteNotification$Companion {
1446+
public final fun serializer ()Lkotlinx/serialization/KSerializer;
1447+
}
1448+
1449+
public final class io/modelcontextprotocol/kotlin/sdk/types/ElicitationCompleteNotificationParams : io/modelcontextprotocol/kotlin/sdk/types/NotificationParams {
1450+
public static final field Companion Lio/modelcontextprotocol/kotlin/sdk/types/ElicitationCompleteNotificationParams$Companion;
1451+
public fun <init> (Ljava/lang/String;Lkotlinx/serialization/json/JsonObject;)V
1452+
public synthetic fun <init> (Ljava/lang/String;Lkotlinx/serialization/json/JsonObject;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
1453+
public final fun component1 ()Ljava/lang/String;
1454+
public final fun component2 ()Lkotlinx/serialization/json/JsonObject;
1455+
public final fun copy (Ljava/lang/String;Lkotlinx/serialization/json/JsonObject;)Lio/modelcontextprotocol/kotlin/sdk/types/ElicitationCompleteNotificationParams;
1456+
public static synthetic fun copy$default (Lio/modelcontextprotocol/kotlin/sdk/types/ElicitationCompleteNotificationParams;Ljava/lang/String;Lkotlinx/serialization/json/JsonObject;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/types/ElicitationCompleteNotificationParams;
1457+
public fun equals (Ljava/lang/Object;)Z
1458+
public final fun getElicitationId ()Ljava/lang/String;
1459+
public fun getMeta ()Lkotlinx/serialization/json/JsonObject;
1460+
public fun hashCode ()I
1461+
public fun toString ()Ljava/lang/String;
1462+
}
1463+
1464+
public final synthetic class io/modelcontextprotocol/kotlin/sdk/types/ElicitationCompleteNotificationParams$$serializer : kotlinx/serialization/internal/GeneratedSerializer {
1465+
public static final field INSTANCE Lio/modelcontextprotocol/kotlin/sdk/types/ElicitationCompleteNotificationParams$$serializer;
1466+
public final fun childSerializers ()[Lkotlinx/serialization/KSerializer;
1467+
public final fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lio/modelcontextprotocol/kotlin/sdk/types/ElicitationCompleteNotificationParams;
1468+
public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;
1469+
public final fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
1470+
public final fun serialize (Lkotlinx/serialization/encoding/Encoder;Lio/modelcontextprotocol/kotlin/sdk/types/ElicitationCompleteNotificationParams;)V
1471+
public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
1472+
public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer;
1473+
}
1474+
1475+
public final class io/modelcontextprotocol/kotlin/sdk/types/ElicitationCompleteNotificationParams$Companion {
1476+
public final fun serializer ()Lkotlinx/serialization/KSerializer;
1477+
}
1478+
14201479
public final class io/modelcontextprotocol/kotlin/sdk/types/ElicitationKt {
14211480
public static final fun ElicitRequestParams-EDEVuBg (Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/types/ElicitRequestParams$RequestedSchema;Lkotlinx/serialization/json/JsonObject;)Lio/modelcontextprotocol/kotlin/sdk/types/ElicitRequestFormParams;
14221481
public static synthetic fun ElicitRequestParams-EDEVuBg$default (Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/types/ElicitRequestParams$RequestedSchema;Lkotlinx/serialization/json/JsonObject;ILjava/lang/Object;)Lio/modelcontextprotocol/kotlin/sdk/types/ElicitRequestFormParams;
@@ -2939,6 +2998,7 @@ public final class io/modelcontextprotocol/kotlin/sdk/types/Method$Defined : jav
29392998
public static final field Initialize Lio/modelcontextprotocol/kotlin/sdk/types/Method$Defined;
29402999
public static final field LoggingSetLevel Lio/modelcontextprotocol/kotlin/sdk/types/Method$Defined;
29413000
public static final field NotificationsCancelled Lio/modelcontextprotocol/kotlin/sdk/types/Method$Defined;
3001+
public static final field NotificationsElicitationComplete Lio/modelcontextprotocol/kotlin/sdk/types/Method$Defined;
29423002
public static final field NotificationsInitialized Lio/modelcontextprotocol/kotlin/sdk/types/Method$Defined;
29433003
public static final field NotificationsMessage Lio/modelcontextprotocol/kotlin/sdk/types/Method$Defined;
29443004
public static final field NotificationsProgress Lio/modelcontextprotocol/kotlin/sdk/types/Method$Defined;

kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/types/methods.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ public sealed interface Method {
3232
NotificationsToolsListChanged("notifications/tools/list_changed"),
3333
NotificationsRootsListChanged("notifications/roots/list_changed"),
3434
NotificationsPromptsListChanged("notifications/prompts/list_changed"),
35+
NotificationsElicitationComplete("notifications/elicitation/complete"),
3536
NotificationsTasksStatus("notifications/tasks/status"),
3637
ToolsList("tools/list"),
3738
ToolsCall("tools/call"),

kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/types/notification.kt

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,36 @@ public data class RootsListChangedNotification(override val params: BaseNotifica
330330
override val method: Method = Method.Defined.NotificationsRootsListChanged
331331
}
332332

333+
// ============================================================================
334+
// Elicitation Complete Notification
335+
// ============================================================================
336+
337+
/**
338+
* An optional notification from the server to the client,
339+
* informing it of a completion of an out-of-band elicitation request.
340+
*
341+
* @property params Parameters identifying which elicitation completed.
342+
*/
343+
@Serializable
344+
public data class ElicitationCompleteNotification(override val params: ElicitationCompleteNotificationParams) :
345+
ServerNotification {
346+
@EncodeDefault
347+
override val method: Method = Method.Defined.NotificationsElicitationComplete
348+
}
349+
350+
/**
351+
* Parameters for a notifications/elicitation/complete notification.
352+
*
353+
* @property elicitationId The ID of the elicitation that completed.
354+
* @property meta Optional metadata for this notification.
355+
*/
356+
@Serializable
357+
public data class ElicitationCompleteNotificationParams(
358+
val elicitationId: String,
359+
@SerialName("_meta")
360+
override val meta: JsonObject? = null,
361+
) : NotificationParams
362+
333363
// ============================================================================
334364
// Tools List Changed Notification
335365
// ============================================================================

kotlin-sdk-core/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/types/serializers.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,7 @@ private val serverNotificationDeserializers: Map<String, DeserializationStrategy
241241
Method.Defined.NotificationsResourcesListChanged.value to ResourceListChangedNotification.serializer(),
242242
Method.Defined.NotificationsToolsListChanged.value to ToolListChangedNotification.serializer(),
243243
Method.Defined.NotificationsPromptsListChanged.value to PromptListChangedNotification.serializer(),
244+
Method.Defined.NotificationsElicitationComplete.value to ElicitationCompleteNotification.serializer(),
244245
Method.Defined.NotificationsTasksStatus.value to TaskStatusNotification.serializer(),
245246
)
246247
}

kotlin-sdk-core/src/commonTest/kotlin/io/modelcontextprotocol/kotlin/sdk/types/NotificationTest.kt

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,51 @@ class NotificationTest {
311311
)
312312
}
313313

314+
@Test
315+
fun `should serialize ElicitationCompleteNotification with meta`() {
316+
val notification = ElicitationCompleteNotification(
317+
ElicitationCompleteNotificationParams(
318+
elicitationId = "elicit-42",
319+
meta = buildJsonObject { put("source", "oauth-flow") },
320+
),
321+
)
322+
323+
verifySerialization(
324+
notification,
325+
McpJson,
326+
"""
327+
{
328+
"method": "notifications/elicitation/complete",
329+
"params": {
330+
"elicitationId": "elicit-42",
331+
"_meta": {
332+
"source": "oauth-flow"
333+
}
334+
}
335+
}
336+
""".trimIndent(),
337+
)
338+
}
339+
340+
@Test
341+
fun `should deserialize ElicitationCompleteNotification`() {
342+
val json = """
343+
{
344+
"method": "notifications/elicitation/complete",
345+
"params": {
346+
"elicitationId": "elicit-99"
347+
}
348+
}
349+
""".trimIndent()
350+
351+
val notification = verifyDeserialization<ElicitationCompleteNotification>(McpJson, json)
352+
val params = notification.params
353+
354+
assertEquals(Method.Defined.NotificationsElicitationComplete, notification.method)
355+
assertEquals("elicit-99", params.elicitationId)
356+
assertNull(params.meta)
357+
}
358+
314359
@Test
315360
fun `should serialize TaskStatusNotification with all fields`() {
316361
val notification = TaskStatusNotification(

kotlin-sdk-server/api/kotlin-sdk-server.api

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ public abstract interface class io/modelcontextprotocol/kotlin/sdk/server/Client
1414
public static synthetic fun notification$default (Lio/modelcontextprotocol/kotlin/sdk/server/ClientConnection;Lio/modelcontextprotocol/kotlin/sdk/types/ServerNotification;Lio/modelcontextprotocol/kotlin/sdk/types/RequestId;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object;
1515
public abstract fun ping (Lio/modelcontextprotocol/kotlin/sdk/types/PingRequest;Lio/modelcontextprotocol/kotlin/sdk/shared/RequestOptions;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
1616
public static synthetic fun ping$default (Lio/modelcontextprotocol/kotlin/sdk/server/ClientConnection;Lio/modelcontextprotocol/kotlin/sdk/types/PingRequest;Lio/modelcontextprotocol/kotlin/sdk/shared/RequestOptions;Lkotlin/coroutines/Continuation;ILjava/lang/Object;)Ljava/lang/Object;
17+
public abstract fun sendElicitationComplete (Lio/modelcontextprotocol/kotlin/sdk/types/ElicitationCompleteNotification;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
1718
public abstract fun sendLoggingMessage (Lio/modelcontextprotocol/kotlin/sdk/types/LoggingMessageNotification;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
1819
public abstract fun sendPromptListChanged (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
1920
public abstract fun sendResourceListChanged (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
@@ -139,6 +140,7 @@ public class io/modelcontextprotocol/kotlin/sdk/server/Server {
139140
public final fun removeResources (Ljava/util/List;)I
140141
public final fun removeTool (Ljava/lang/String;)Z
141142
public final fun removeTools (Ljava/util/List;)I
143+
public final fun sendElicitationComplete (Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/types/ElicitationCompleteNotification;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
142144
public final fun sendLoggingMessage (Ljava/lang/String;Lio/modelcontextprotocol/kotlin/sdk/types/LoggingMessageNotification;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
143145
public final fun sendPromptListChanged (Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
144146
public final fun sendResourceListChanged (Ljava/lang/String;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
@@ -182,6 +184,7 @@ public class io/modelcontextprotocol/kotlin/sdk/server/ServerSession : io/modelc
182184
public final fun onClose (Lkotlin/jvm/functions/Function0;)V
183185
public final fun onInitialized (Lkotlin/jvm/functions/Function0;)V
184186
public final fun ping (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
187+
public final fun sendElicitationComplete (Lio/modelcontextprotocol/kotlin/sdk/types/ElicitationCompleteNotification;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
185188
public final fun sendLoggingMessage (Lio/modelcontextprotocol/kotlin/sdk/types/LoggingMessageNotification;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
186189
public final fun sendPromptListChanged (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
187190
public final fun sendResourceListChanged (Lkotlin/coroutines/Continuation;)Ljava/lang/Object;

kotlin-sdk-server/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/server/ClientConnection.kt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import io.modelcontextprotocol.kotlin.sdk.types.ElicitRequestFormParams
99
import io.modelcontextprotocol.kotlin.sdk.types.ElicitRequestParams
1010
import io.modelcontextprotocol.kotlin.sdk.types.ElicitRequestURLParams
1111
import io.modelcontextprotocol.kotlin.sdk.types.ElicitResult
12+
import io.modelcontextprotocol.kotlin.sdk.types.ElicitationCompleteNotification
1213
import io.modelcontextprotocol.kotlin.sdk.types.EmptyResult
1314
import io.modelcontextprotocol.kotlin.sdk.types.ListRootsRequest
1415
import io.modelcontextprotocol.kotlin.sdk.types.ListRootsResult
@@ -165,6 +166,13 @@ public interface ClientConnection {
165166
* Sends a notification to the client indicating that the list of prompts has changed.
166167
*/
167168
public suspend fun sendPromptListChanged()
169+
170+
/**
171+
* Sends a notification to the client indicating that an out-of-band elicitation has completed.
172+
*
173+
* @param notification Details of the completed elicitation.
174+
*/
175+
public suspend fun sendElicitationComplete(notification: ElicitationCompleteNotification)
168176
}
169177

170178
@Suppress("TooManyFunctions")
@@ -270,6 +278,11 @@ internal class ClientConnectionImpl(private val session: ServerSession) : Client
270278
notification(PromptListChangedNotification())
271279
}
272280

281+
override suspend fun sendElicitationComplete(notification: ElicitationCompleteNotification) {
282+
logger.debug { "Sending elicitation complete notification for: ${notification.params.elicitationId}" }
283+
notification(notification)
284+
}
285+
273286
/**
274287
* Determines whether a message with the specified logging level is accepted
275288
* based on the current logging level of the session.

kotlin-sdk-server/src/commonMain/kotlin/io/modelcontextprotocol/kotlin/sdk/server/Server.kt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import io.modelcontextprotocol.kotlin.sdk.types.CreateMessageRequest
1010
import io.modelcontextprotocol.kotlin.sdk.types.CreateMessageResult
1111
import io.modelcontextprotocol.kotlin.sdk.types.ElicitRequestParams
1212
import io.modelcontextprotocol.kotlin.sdk.types.ElicitResult
13+
import io.modelcontextprotocol.kotlin.sdk.types.ElicitationCompleteNotification
1314
import io.modelcontextprotocol.kotlin.sdk.types.EmptyJsonObject
1415
import io.modelcontextprotocol.kotlin.sdk.types.EmptyResult
1516
import io.modelcontextprotocol.kotlin.sdk.types.GetPromptRequest
@@ -850,6 +851,16 @@ public open class Server(
850851
public suspend fun sendPromptListChanged(sessionId: String) {
851852
clientConnection(sessionId).sendPromptListChanged()
852853
}
854+
855+
/**
856+
* Triggers [ClientConnection.sendElicitationComplete] for session by provided [sessionId].
857+
*
858+
* @param sessionId The session ID to send the elicitation complete notification to.
859+
* @param notification Details of the completed elicitation.
860+
*/
861+
public suspend fun sendElicitationComplete(sessionId: String, notification: ElicitationCompleteNotification) {
862+
clientConnection(sessionId).sendElicitationComplete(notification)
863+
}
853864
// End the ServerSession / ClientConnection redirection section
854865

855866
// Start the notification handling section

0 commit comments

Comments
 (0)