Add URL elicitation support (SEP-1036) (#993)

Add URL-type elicitation schema support allowing servers to request URL
input from users during tool execution. This enables out-of-band
interactions like payment processing or API key entry.

Breaking changes:

- `ElicitRequest` changed from a `record` to an `interface`.
- The original `ElicitRequest` record was renamed to `ElicitFormRequest`.
- `McpClient` builder `elicitation()` methods now accept `ElicitFormRequest` instead of `ElicitRequest`.

New APIs:

- `ElicitUrlRequest` record for URL-mode elicitation.
- `urlElicitation()` builder methods in `McpClient`.
- `elicitationCompleteConsumer()` and `elicitationCompleteConsumers()` builder methods in `McpClient`.
- `sendElicitationComplete()` methods in `McpAsyncServer` and `McpSyncServer`.
- `McpError.URL_ELICITATION_REQUIRED` and `McpSchema.ErrorCodes.URL_ELICITATION_REQUIRED`.
- `ElicitationCompleteNotification` record and `METHOD_NOTIFICATION_ELICITATION_COMPLETE` constant.

Co-authored-by: Daniel Garnier-Moiroux <git@garnier.wf>
Co-authored-by: Sainath Reddy Bobbala <bsnr@amazon.com>
This commit is contained in:
Daniel Garnier-Moiroux 2026-06-03 16:24:29 +02:00 • committed by GitHub
commit dbb9bdad51
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 1413 additions and 196 deletions

View file

@ -270,20 +270,28 @@ This capability allows:
Elicitation enables servers to request additional information or user input through the client. This is useful when a server needs clarification or confirmation during an operation:
```java
// Configure elicitation handler
Function<ElicitRequest, ElicitResult> elicitationHandler = request -> {
// Configure form elicitation handler
Function<ElicitFormRequest, ElicitResult> formElicitationHandler = request -> {
// Present the request to the user and collect their response
// The request contains a message and a schema describing the expected input
Map<String, Object> userResponse = collectUserInput(request.message(), request.requestedSchema());
return new ElicitResult(ElicitResult.Action.ACCEPT, userResponse);
};
// Configure URL elicitation handler
Function<ElicitUrlRequest, ElicitResult> urlElicitationHandler = request -> {
// Prompt the user to visit the URL
// e.g. openBrowser(request.url());
return new ElicitResult(ElicitResult.Action.ACCEPT, Map.of());
};
// Create client with elicitation support
var client = McpClient.sync(transport)
.capabilities(ClientCapabilities.builder()
.elicitation()
.elicitation(true, true) // enables both form and URL elicitation
.build())
.elicitation(elicitationHandler)
.elicitation(formElicitationHandler)
.urlElicitation(urlElicitationHandler)
.build();
```
@ -293,6 +301,28 @@ The `ElicitResult` supports three actions:
- `DECLINE` - The user declined to provide the information
- `CANCEL` - The operation was cancelled
#### URL Elicitation Required Handling
When a server requires out-of-band URL elicitation but the client has not negotiated support for it (or the server strictly requires out-of-band handling), the server may return a `URL_ELICITATION_REQUIRED` error during tool execution or prompt retrieval.
```java
try {
mcpClient.callTool(new McpSchema.CallToolRequest("tool1", Map.of()));
} catch (McpError e) {
if (e.getJsonRpcError().code() == McpSchema.ErrorCodes.URL_ELICITATION_REQUIRED) {
// Extract elicitation requests from the error data
Map<String, Object> data = (Map<String, Object>) e.getJsonRpcError().data();
TypeRef<List<McpSchema.ElicitUrlRequest>> typeRef = new TypeRef<>() {};
var requests = McpJsonDefaults.getMapper()
.convertValue(data.get("elicitations"), typeRef);
for (var req : requests) {
// handle elicitation requests
}
}
}
```
### Logging Support
The client can register a logging consumer to receive log messages from the server and set the minimum logging level to filter messages:

View file

@ -715,9 +715,7 @@ var tool = SyncToolSpecification.builder()
}
// Request user confirmation
ElicitRequest elicitRequest = ElicitRequest.builder()
.message("Do you want to proceed with this action?")
.requestedSchema(Map.of(
ElicitRequest elicitRequest = ElicitFormRequest.builder("Do you want to proceed with this action?", Map.of(
"type", "object",
"properties", Map.of("confirmed", Map.of("type", "boolean"))
))
@ -739,6 +737,40 @@ var tool = SyncToolSpecification.builder()
.build();
```
To request out-of-band URL elicitation, such as a user authorizing an OAuth flow:
```java
var urlTool = SyncToolSpecification.builder()
.tool(Tool.builder()
.name("oauth-auth")
.description("Authenticates via OAuth")
.inputSchema(schema)
.build())
.callHandler((exchange, request) -> {
// Request URL elicitation from client
if (
exchange.getClientCapabilities().elicitation() != null
&& exchange.getClientCapabilities().elicitation().url() != null
) {
ElicitRequest urlRequest = McpSchema.ElicitUrlRequest
.builder("Please authenticate", "https://example.com/oauth", "oauth-123").build();
ElicitResult result = exchange.elicit(urlRequest);
// handle result.action == CANCELLED or DENIED
if (result.action() != ElicitResult.Action.ACCEPT) {
return CallToolResult.builder()
.content(List.of(new McpSchema.TextContent("Authentication failed or cancelled")))
.build();
}
}
// wait for user to visit the URL
return CallToolResult.builder()
.content(List.of(new McpSchema.TextContent("Authentication successful")))
.build();
})
.build();
```
### Logging Support
The server provides structured logging capabilities that allow sending log messages to clients with different severity levels.