Support elicitation/create per MCP specification
## Motivation and Context The MCP specification (2025-11-25) defines elicitation as a server-to-client request mechanism that allows servers to request user input during tool execution: https://modelcontextprotocol.io/specification/2025-11-25/client/elicitation This aligns with the Python SDK, which also exposes separate methods for form and URL mode elicitation. Three methods are added following the Python SDK's API design: - `create_form_elicitation`: collects structured data via JSON Schema - `create_url_elicitation`: directs users to external URLs for sensitive interactions (OAuth, credentials, etc.) - `notify_elicitation_complete`: notifies the client that a URL mode interaction has completed `URLElicitationRequiredError` (error code `-32042`) is defined as a subclass of `RequestHandlerError` with its own `error_code` and `error_data` attributes. `JsonRpcHandler#handle_request_error` is extended to check for these attributes so that MCP-specific error codes can be propagated without embedding MCP knowledge in the generic JSON-RPC layer. ## How Has This Been Tested? - Unit tests for `ServerSession` form/URL mode, capability checks, nil capabilities, notification, and `URLElicitationRequiredError` - `ServerContext` delegation tests for all three elicitation methods - `JsonRpcHandler` tests for custom error code and fallback behavior - `Methods` capability test for `NOTIFICATIONS_ELICITATION_COMPLETE` - Conformance tests pass (40/40), including `tools-call-elicitation`, `elicitation-sep1034-defaults`, and `elicitation-sep1330-enums` - All tests pass (`rake test`), RuboCop is clean - Verified form mode elicitation end-to-end with a sample MCP server running in Claude Code ## Breaking Changes None. This is a new feature addition.
This commit is contained in:
parent
88248fc4c7
commit
985fd03ecb
1 changed files with 566 additions and 36 deletions
104
README.md
104
README.md
|
|
@ -53,6 +53,7 @@ It implements the Model Context Protocol specification, handling model context r
|
|||
- `resources/templates/list` - Lists all registered resource templates and their schemas
|
||||
- `completion/complete` - Returns autocompletion suggestions for prompt arguments and resource URIs
|
||||
- `sampling/createMessage` - Requests LLM completion from the client (server-to-client)
|
||||
- `elicitation/create` - Requests user input from the client (server-to-client)
|
||||
|
||||
### Usage
|
||||
|
||||
|
|
@ -1085,6 +1086,108 @@ The SDK automatically enforces the 100-item limit per the MCP specification.
|
|||
The server validates that the referenced prompt, resource, or resource template is registered before calling the handler.
|
||||
Requests for unknown references return an error.
|
||||
|
||||
### Elicitation
|
||||
|
||||
The MCP Ruby SDK supports [elicitation](https://modelcontextprotocol.io/specification/2025-11-25/client/elicitation),
|
||||
which allows servers to request additional information from users through the client during tool execution.
|
||||
|
||||
Elicitation is a **server-to-client request**. The server sends a request and blocks until the user responds via the client.
|
||||
|
||||
#### Capabilities
|
||||
|
||||
Clients must declare the `elicitation` capability during initialization. The server checks this before sending any elicitation request
|
||||
and raises a `RuntimeError` if the client does not support it.
|
||||
|
||||
For URL mode support, the client must also declare `elicitation.url` capability.
|
||||
|
||||
#### Using Elicitation in Tools
|
||||
|
||||
Tools that accept a `server_context:` parameter can call `create_form_elicitation` on it:
|
||||
|
||||
```ruby
|
||||
server.define_tool(name: "collect_info", description: "Collect user info") do |server_context:|
|
||||
result = server_context.create_form_elicitation(
|
||||
message: "Please provide your name",
|
||||
requested_schema: {
|
||||
type: "object",
|
||||
properties: { name: { type: "string" } },
|
||||
required: ["name"],
|
||||
},
|
||||
)
|
||||
|
||||
MCP::Tool::Response.new([{ type: "text", text: "Hello, #{result[:content][:name]}" }])
|
||||
end
|
||||
```
|
||||
|
||||
#### Form Mode
|
||||
|
||||
Form mode collects structured data from the user directly through the MCP client:
|
||||
|
||||
```ruby
|
||||
server.define_tool(name: "collect_contact", description: "Collect contact info") do |server_context:|
|
||||
result = server_context.create_form_elicitation(
|
||||
message: "Please provide your contact information",
|
||||
requested_schema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
name: { type: "string", description: "Your full name" },
|
||||
email: { type: "string", format: "email", description: "Your email address" },
|
||||
},
|
||||
required: ["name", "email"],
|
||||
},
|
||||
)
|
||||
|
||||
text = case result[:action]
|
||||
when "accept"
|
||||
"Hello, #{result[:content][:name]} (#{result[:content][:email]})"
|
||||
when "decline"
|
||||
"User declined"
|
||||
when "cancel"
|
||||
"User cancelled"
|
||||
end
|
||||
|
||||
MCP::Tool::Response.new([{ type: "text", text: text }])
|
||||
end
|
||||
```
|
||||
|
||||
#### URL Mode
|
||||
|
||||
URL mode directs the user to an external URL for out-of-band interactions such as OAuth flows:
|
||||
|
||||
```ruby
|
||||
server.define_tool(name: "authorize_github", description: "Authorize GitHub") do |server_context:|
|
||||
elicitation_id = SecureRandom.uuid
|
||||
|
||||
result = server_context.create_url_elicitation(
|
||||
message: "Please authorize access to your GitHub account",
|
||||
url: "https://example.com/oauth/authorize?elicitation_id=#{elicitation_id}",
|
||||
elicitation_id: elicitation_id,
|
||||
)
|
||||
|
||||
server_context.notify_elicitation_complete(elicitation_id: elicitation_id)
|
||||
|
||||
MCP::Tool::Response.new([{ type: "text", text: "Authorization complete" }])
|
||||
end
|
||||
```
|
||||
|
||||
#### URLElicitationRequiredError
|
||||
|
||||
When a tool cannot proceed until an out-of-band elicitation is completed, raise `MCP::Server::URLElicitationRequiredError`.
|
||||
This returns a JSON-RPC error with code `-32042` to the client:
|
||||
|
||||
```ruby
|
||||
server.define_tool(name: "access_github", description: "Access GitHub") do |server_context:|
|
||||
raise MCP::Server::URLElicitationRequiredError.new([
|
||||
{
|
||||
mode: "url",
|
||||
elicitationId: SecureRandom.uuid,
|
||||
url: "https://example.com/oauth/authorize",
|
||||
message: "GitHub authorization is required.",
|
||||
},
|
||||
])
|
||||
end
|
||||
```
|
||||
|
||||
### Logging
|
||||
|
||||
The MCP Ruby SDK supports structured logging through the `notify_log_message` method, following the [MCP Logging specification](https://modelcontextprotocol.io/specification/latest/server/utilities/logging).
|
||||
|
|
@ -1250,7 +1353,6 @@ end
|
|||
### Unsupported Features (to be implemented in future versions)
|
||||
|
||||
- Resource subscriptions
|
||||
- Elicitation
|
||||
|
||||
## Building an MCP Client
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue