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
2 changed files with 566 additions and 36 deletions
|
|
@ -1,5 +1 @@
|
|||
server:
|
||||
# TODO: Server-to-client requests (elicitation/create) are not implemented.
|
||||
- tools-call-elicitation
|
||||
- elicitation-sep1034-defaults
|
||||
- elicitation-sep1330-enums
|
||||
server: []
|
||||
|
|
|
|||
|
|
@ -178,7 +178,6 @@ module Conformance
|
|||
end
|
||||
end
|
||||
|
||||
# TODO: Implement when `Transport` supports server-to-client requests.
|
||||
class TestElicitation < MCP::Tool
|
||||
tool_name "test_elicitation"
|
||||
description "A tool that requests user input from the client"
|
||||
|
|
@ -188,41 +187,96 @@ module Conformance
|
|||
)
|
||||
|
||||
class << self
|
||||
def call(message:)
|
||||
MCP::Tool::Response.new(
|
||||
[MCP::Content::Text.new("Elicitation not supported in this SDK version").to_h],
|
||||
error: true,
|
||||
def call(server_context:, message:)
|
||||
result = server_context.create_form_elicitation(
|
||||
message: message,
|
||||
requested_schema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
username: { type: "string", description: "User's response" },
|
||||
email: { type: "string", description: "User's email address" },
|
||||
},
|
||||
required: ["username", "email"],
|
||||
},
|
||||
)
|
||||
MCP::Tool::Response.new([MCP::Content::Text.new("User response: #{result}").to_h])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# TODO: Implement when `Transport` supports server-to-client requests.
|
||||
class TestElicitationSep1034Defaults < MCP::Tool
|
||||
tool_name "test_elicitation_sep1034_defaults"
|
||||
description "A tool that tests elicitation with default values"
|
||||
|
||||
class << self
|
||||
def call(**_args)
|
||||
MCP::Tool::Response.new(
|
||||
[MCP::Content::Text.new("Elicitation not supported in this SDK version").to_h],
|
||||
error: true,
|
||||
def call(server_context:, **_args)
|
||||
result = server_context.create_form_elicitation(
|
||||
message: "Please provide your information (with defaults)",
|
||||
requested_schema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
name: { type: "string", default: "John Doe" },
|
||||
age: { type: "integer", default: 30 },
|
||||
score: { type: "number", default: 95.5 },
|
||||
status: { type: "string", enum: ["active", "inactive", "pending"], default: "active" },
|
||||
verified: { type: "boolean", default: true },
|
||||
},
|
||||
},
|
||||
)
|
||||
MCP::Tool::Response.new([MCP::Content::Text.new("Elicitation result: #{result}").to_h])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# TODO: Implement when `Transport` supports server-to-client requests.
|
||||
class TestElicitationSep1330Enums < MCP::Tool
|
||||
tool_name "test_elicitation_sep1330_enums"
|
||||
description "A tool that tests elicitation with enum schemas"
|
||||
|
||||
class << self
|
||||
def call(**_args)
|
||||
MCP::Tool::Response.new(
|
||||
[MCP::Content::Text.new("Elicitation not supported in this SDK version").to_h],
|
||||
error: true,
|
||||
def call(server_context:, **_args)
|
||||
result = server_context.create_form_elicitation(
|
||||
message: "Please select options",
|
||||
requested_schema: {
|
||||
type: "object",
|
||||
properties: {
|
||||
untitledSingle: {
|
||||
type: "string",
|
||||
enum: ["option1", "option2", "option3"],
|
||||
},
|
||||
titledSingle: {
|
||||
type: "string",
|
||||
oneOf: [
|
||||
{ const: "value1", title: "First Option" },
|
||||
{ const: "value2", title: "Second Option" },
|
||||
{ const: "value3", title: "Third Option" },
|
||||
],
|
||||
},
|
||||
legacyEnum: {
|
||||
type: "string",
|
||||
enum: ["opt1", "opt2", "opt3"],
|
||||
enumNames: ["Option One", "Option Two", "Option Three"],
|
||||
},
|
||||
untitledMulti: {
|
||||
type: "array",
|
||||
items: {
|
||||
type: "string",
|
||||
enum: ["option1", "option2", "option3"],
|
||||
},
|
||||
},
|
||||
titledMulti: {
|
||||
type: "array",
|
||||
items: {
|
||||
anyOf: [
|
||||
{ const: "value1", title: "First Choice" },
|
||||
{ const: "value2", title: "Second Choice" },
|
||||
{ const: "value3", title: "Third Choice" },
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
)
|
||||
MCP::Tool::Response.new([MCP::Content::Text.new("Elicitation result: #{result}").to_h])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
Loading…
Reference in a new issue