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
|
|
@ -83,6 +83,62 @@ module MCP
|
|||
assert_equal "Fallback", result[:content][:text]
|
||||
end
|
||||
|
||||
test "ServerContext#create_form_elicitation delegates to notification_target" do
|
||||
notification_target = mock
|
||||
notification_target.expects(:create_form_elicitation).with(
|
||||
message: "Please provide your name",
|
||||
requested_schema: { type: "object", properties: { name: { type: "string" } } },
|
||||
related_request_id: nil,
|
||||
).returns(action: "accept", content: { name: "test_user" })
|
||||
|
||||
context = mock
|
||||
progress = Progress.new(notification_target: notification_target, progress_token: nil)
|
||||
|
||||
server_context = ServerContext.new(context, progress: progress, notification_target: notification_target)
|
||||
|
||||
result = server_context.create_form_elicitation(
|
||||
message: "Please provide your name",
|
||||
requested_schema: { type: "object", properties: { name: { type: "string" } } },
|
||||
)
|
||||
|
||||
assert_equal "accept", result[:action]
|
||||
end
|
||||
|
||||
test "ServerContext#create_url_elicitation delegates to notification_target" do
|
||||
notification_target = mock
|
||||
notification_target.expects(:create_url_elicitation).with(
|
||||
message: "Please authorize",
|
||||
url: "https://example.com/oauth",
|
||||
elicitation_id: "abc-123",
|
||||
related_request_id: nil,
|
||||
).returns(action: "accept")
|
||||
|
||||
context = mock
|
||||
progress = Progress.new(notification_target: notification_target, progress_token: nil)
|
||||
|
||||
server_context = ServerContext.new(context, progress: progress, notification_target: notification_target)
|
||||
|
||||
result = server_context.create_url_elicitation(
|
||||
message: "Please authorize",
|
||||
url: "https://example.com/oauth",
|
||||
elicitation_id: "abc-123",
|
||||
)
|
||||
|
||||
assert_equal "accept", result[:action]
|
||||
end
|
||||
|
||||
test "ServerContext#notify_elicitation_complete delegates to notification_target" do
|
||||
notification_target = mock
|
||||
notification_target.expects(:notify_elicitation_complete).with(elicitation_id: "abc-123")
|
||||
|
||||
context = mock
|
||||
progress = Progress.new(notification_target: notification_target, progress_token: nil)
|
||||
|
||||
server_context = ServerContext.new(context, progress: progress, notification_target: notification_target)
|
||||
|
||||
server_context.notify_elicitation_complete(elicitation_id: "abc-123")
|
||||
end
|
||||
|
||||
test "ServerContext delegates to custom object context" do
|
||||
context = Object.new
|
||||
def context.custom_method
|
||||
|
|
|
|||
Loading…
Reference in a new issue