feat: add URL mode elicitation and typed schema definitions per MCP (#660)
Add URL mode elicitation support and typed `PrimitiveSchemaDefinition` hierarchy to align with MCP specification 2025-11-25 closes #540 ## How Has This Been Tested? unit/integration/conformance ## Breaking Changes - `ElicitRequestParams` changed from `data class` to `sealed interface`. A deprecated factory function preserves source compatibility for `ElicitRequestParams(message, requestedSchema)`. - `RequestedSchema.properties` changed from `JsonObject` to `Map<String, PrimitiveSchemaDefinition>`. - `ElicitRequest.requestedSchema` is deprecated, use `(params as ElicitRequestFormParams).requestedSchema`. ## Types of changes - [ ] Bug fix (non-breaking change which fixes an issue) - [x] New feature (non-breaking change which adds functionality) - [x] 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:
parent
ad072325c9
commit
fdecf6bdf5
3 changed files with 1838 additions and 384 deletions
|
|
@ -5,7 +5,9 @@ import io.modelcontextprotocol.kotlin.sdk.shared.RequestOptions
|
|||
import io.modelcontextprotocol.kotlin.sdk.types.CreateMessageRequest
|
||||
import io.modelcontextprotocol.kotlin.sdk.types.CreateMessageResult
|
||||
import io.modelcontextprotocol.kotlin.sdk.types.ElicitRequest
|
||||
import io.modelcontextprotocol.kotlin.sdk.types.ElicitRequestFormParams
|
||||
import io.modelcontextprotocol.kotlin.sdk.types.ElicitRequestParams
|
||||
import io.modelcontextprotocol.kotlin.sdk.types.ElicitRequestURLParams
|
||||
import io.modelcontextprotocol.kotlin.sdk.types.ElicitResult
|
||||
import io.modelcontextprotocol.kotlin.sdk.types.EmptyResult
|
||||
import io.modelcontextprotocol.kotlin.sdk.types.ListRootsRequest
|
||||
|
|
@ -104,6 +106,24 @@ public interface ClientConnection {
|
|||
options: RequestOptions? = null,
|
||||
): ElicitResult
|
||||
|
||||
/**
|
||||
* Sends a URL mode elicitation request to the client, directing the user
|
||||
* to an external URL for out-of-band interactions.
|
||||
*
|
||||
* @param message The message explaining why the interaction is needed.
|
||||
* @param elicitationId A unique identifier for the elicitation.
|
||||
* @param url The URL that the user should navigate to.
|
||||
* @param options Optional request options.
|
||||
* @return The result of the elicitation request.
|
||||
* @throws IllegalStateException If the server or client does not support elicitation.
|
||||
*/
|
||||
public suspend fun createElicitation(
|
||||
message: String,
|
||||
elicitationId: String,
|
||||
url: String,
|
||||
options: RequestOptions? = null,
|
||||
): ElicitResult
|
||||
|
||||
/**
|
||||
* Sends a message to the client requesting elicitation.
|
||||
* This typically results in a form being displayed to the user.
|
||||
|
|
@ -189,10 +209,16 @@ internal class ClientConnectionImpl(private val session: ServerSession) : Client
|
|||
}
|
||||
|
||||
override suspend fun createElicitation(request: ElicitRequest, options: RequestOptions?): ElicitResult {
|
||||
with(request.params) {
|
||||
logger.debug {
|
||||
"Creating elicitation with message length=${message.length}, " +
|
||||
"schema properties count=${requestedSchema.properties.size}"
|
||||
val params = request.params
|
||||
logger.debug {
|
||||
when (params) {
|
||||
is ElicitRequestFormParams ->
|
||||
"Creating form elicitation with message length=${params.message.length}, " +
|
||||
"schema properties count=${params.requestedSchema.properties.size}"
|
||||
|
||||
is ElicitRequestURLParams ->
|
||||
"Creating URL elicitation with message length=${params.message.length}, " +
|
||||
"elicitationId=${params.elicitationId}"
|
||||
}
|
||||
}
|
||||
logger.trace { "ElicitRequest: $request" }
|
||||
|
|
@ -204,7 +230,19 @@ internal class ClientConnectionImpl(private val session: ServerSession) : Client
|
|||
requestedSchema: ElicitRequestParams.RequestedSchema,
|
||||
options: RequestOptions?,
|
||||
): ElicitResult = createElicitation(
|
||||
request = ElicitRequest(params = ElicitRequestParams(message, requestedSchema)),
|
||||
request = ElicitRequest(params = ElicitRequestFormParams(message = message, requestedSchema = requestedSchema)),
|
||||
options = options,
|
||||
)
|
||||
|
||||
override suspend fun createElicitation(
|
||||
message: String,
|
||||
elicitationId: String,
|
||||
url: String,
|
||||
options: RequestOptions?,
|
||||
): ElicitResult = createElicitation(
|
||||
request = ElicitRequest(
|
||||
params = ElicitRequestURLParams(message = message, elicitationId = elicitationId, url = url),
|
||||
),
|
||||
options = options,
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -780,6 +780,30 @@ public open class Server(
|
|||
options = options,
|
||||
)
|
||||
|
||||
/**
|
||||
* Triggers URL mode [ClientConnection.createElicitation] request for session by provided [sessionId].
|
||||
*
|
||||
* @param sessionId The session ID to create elicitation for.
|
||||
* @param message The message explaining why the interaction is needed.
|
||||
* @param elicitationId A unique identifier for the elicitation.
|
||||
* @param url The URL that the user should navigate to.
|
||||
* @param options Optional request options.
|
||||
* @return The created elicitation result.
|
||||
* @throws IllegalStateException If the server does not support elicitation or if the request fails.
|
||||
*/
|
||||
public suspend fun createElicitation(
|
||||
sessionId: String,
|
||||
message: String,
|
||||
elicitationId: String,
|
||||
url: String,
|
||||
options: RequestOptions? = null,
|
||||
): ElicitResult = clientConnection(sessionId).createElicitation(
|
||||
message = message,
|
||||
elicitationId = elicitationId,
|
||||
url = url,
|
||||
options = options,
|
||||
)
|
||||
|
||||
/**
|
||||
* Triggers [ClientConnection.sendLoggingMessage] for session by provided [sessionId].
|
||||
*
|
||||
|
|
|
|||
|
|
@ -382,6 +382,24 @@ public open class ServerSession(
|
|||
options: RequestOptions? = null,
|
||||
): ElicitResult = clientConnection.createElicitation(message, requestedSchema, options)
|
||||
|
||||
/**
|
||||
* Sends a URL mode elicitation request to the client, directing the user
|
||||
* to an external URL for out-of-band interactions.
|
||||
*
|
||||
* @param message The message explaining why the interaction is needed.
|
||||
* @param elicitationId A unique identifier for the elicitation.
|
||||
* @param url The URL that the user should navigate to.
|
||||
* @param options Optional request options.
|
||||
* @return The result of the elicitation request.
|
||||
* @throws IllegalStateException If the server or client does not support elicitation.
|
||||
*/
|
||||
public suspend fun createElicitation(
|
||||
message: String,
|
||||
elicitationId: String,
|
||||
url: String,
|
||||
options: RequestOptions? = null,
|
||||
): ElicitResult = clientConnection.createElicitation(message, elicitationId, url, options)
|
||||
|
||||
/**
|
||||
* Sends a logging message notification to the client.
|
||||
* Messages are filtered based on the current logging level set by the client.
|
||||
|
|
|
|||
Loading…
Reference in a new issue