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:
parent
c49a994463
commit
dbb9bdad51
2 changed files with 1413 additions and 196 deletions
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
Loading…
Reference in a new issue