feat: sep-1036 add URL Mode Elicitation (#843)

closes #410 

## How Has This Been Tested?
integration and unit tests 

## 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
- [x] I have added appropriate error handling
- [x] I have added or updated documentation as needed
This commit is contained in:
Pavel Gorgulov 2026-06-23 22:14:59 +02:00 • committed by GitHub
commit c8a1103b16
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -19,6 +19,7 @@ import io.modelcontextprotocol.kotlin.sdk.types.DoubleSchema
import io.modelcontextprotocol.kotlin.sdk.types.ElicitRequest
import io.modelcontextprotocol.kotlin.sdk.types.ElicitRequestFormParams
import io.modelcontextprotocol.kotlin.sdk.types.ElicitResult
import io.modelcontextprotocol.kotlin.sdk.types.ElicitationCompleteNotification
import io.modelcontextprotocol.kotlin.sdk.types.EmptyResult
import io.modelcontextprotocol.kotlin.sdk.types.GetPromptRequest
import io.modelcontextprotocol.kotlin.sdk.types.GetPromptResult
@ -63,6 +64,7 @@ import io.modelcontextprotocol.kotlin.sdk.types.TitledSingleSelectEnumSchema
import io.modelcontextprotocol.kotlin.sdk.types.UnsubscribeRequest
import io.modelcontextprotocol.kotlin.sdk.types.UntitledMultiSelectEnumSchema
import io.modelcontextprotocol.kotlin.sdk.types.UntitledSingleSelectEnumSchema
import io.modelcontextprotocol.kotlin.sdk.types.supportsUrl
import io.modelcontextprotocol.kotlin.sdk.types.toJson
import kotlinx.atomicfu.atomic
import kotlinx.atomicfu.getAndUpdate
@ -70,6 +72,7 @@ import kotlinx.atomicfu.update
import kotlinx.collections.immutable.minus
import kotlinx.collections.immutable.persistentMapOf
import kotlinx.collections.immutable.toPersistentSet
import kotlinx.coroutines.CompletableDeferred
import kotlinx.serialization.SerializationException
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.JsonObject
@ -643,9 +646,18 @@ public open class Client(private val clientInfo: Implementation, options: Client
/**
* Sets the elicitation handler.
*
* When the handler returns [ElicitResult.Action.Accept], any properties missing from
* The handler receives both form-mode ([ElicitRequestFormParams]) and URL-mode
* ([io.modelcontextprotocol.kotlin.sdk.types.ElicitRequestURLParams]) requests;
* branch on `request.params` to tell them apart. For URL mode,
* the host application must obtain explicit user consent and display the target domain before
* navigating — the SDK never opens or validates the URL — and should return
* [ElicitResult.Action.Decline] or [ElicitResult.Action.Cancel] when it cannot or will not proceed.
* A URL-mode [ElicitResult.Action.Accept] only signals consent; the outcome arrives out-of-band via
* [setElicitationCompleteHandler].
*
* When a form-mode handler returns [ElicitResult.Action.Accept], any properties missing from
* [ElicitResult.content] are automatically populated with default values defined in the
* elicitation schema.
* elicitation schema. URL-mode responses carry no content.
*
* @param handler The elicitation handler.
* @throws IllegalStateException if the client does not support elicitation.
@ -663,6 +675,35 @@ public open class Client(private val clientInfo: Implementation, options: Client
}
}
/**
* Sets the handler invoked when the server reports that a URL-mode elicitation has completed.
*
* The handler is called for every `notifications/elicitation/complete` notification. Because the
* server only sends this for an out-of-band (URL-mode) interaction, the client must support url-mode
* elicitation. The client is responsible for correlating the notification's `elicitationId` with a
* pending elicitation, ignoring unknown or already-completed identifiers, and providing a manual way
* to continue if a notification never arrives.
*
* @param handler Invoked with each completion notification.
* @throws IllegalStateException if the client does not support url-mode elicitation.
*/
public fun setElicitationCompleteHandler(handler: (ElicitationCompleteNotification) -> Unit) {
check(capabilities.elicitation.supportsUrl) {
logger.error {
"Failed to set elicitation-complete handler: client does not support url-mode elicitation"
}
"Client does not support url-mode elicitation."
}
logger.info { "Setting the elicitation-complete handler" }
setNotificationHandler<ElicitationCompleteNotification>(
Method.Defined.NotificationsElicitationComplete,
) { notification ->
handler(notification)
CompletableDeferred(Unit)
}
}
// --- Internal Handlers ---
private fun applyElicitationDefaults(request: ElicitRequest, result: ElicitResult): ElicitResult {