|
| 1 | +--- |
| 2 | +layout: default |
| 3 | +title: Elicitation |
| 4 | +parent: Client Interactions |
| 5 | +nav_order: 9 |
| 6 | +description: "MCP elicitation - allow servers to request additional structured information from users" |
| 7 | +--- |
| 8 | + |
| 9 | +# Elicitation |
| 10 | +{: .no_toc } |
| 11 | + |
| 12 | +Elicitation allows MCP servers to request additional structured information from users during interactions. This enables dynamic workflows where servers can ask for clarification, gather additional context, or collect user preferences in real-time. |
| 13 | + |
| 14 | +## Table of contents |
| 15 | +{: .no_toc .text-delta } |
| 16 | + |
| 17 | +1. TOC |
| 18 | +{:toc} |
| 19 | + |
| 20 | +--- |
| 21 | + |
| 22 | +## Overview |
| 23 | + |
| 24 | +When elicitation is enabled, MCP servers can send "elicitation" requests to your client, which will: |
| 25 | + |
| 26 | +1. Present the server's request message to the user |
| 27 | +2. Collect structured user input based on a JSON schema |
| 28 | +3. Validate the response against the schema |
| 29 | +4. Return the structured data back to the server |
| 30 | + |
| 31 | +This is useful for servers that need user input or clarification during complex workflows. |
| 32 | + |
| 33 | +{: .new } |
| 34 | +Elicitation is a new feature in MCP Protocol 2025-06-18. |
| 35 | + |
| 36 | +**Note:** Elicitation is only available for clients that support the `2025-06-18` protocol version. |
| 37 | + |
| 38 | +Additional, in this current version the original request still needs to complete in the bounds of the initial request timeout. In some cases (like if you were to get real user input) that may take longer than what a normal request would take. A solution to this will come out in future versions. |
| 39 | + |
| 40 | +## Basic Elicitation Configuration |
| 41 | + |
| 42 | +### Global Configuration |
| 43 | + |
| 44 | +Configure elicitation handling globally for all clients: |
| 45 | + |
| 46 | +```ruby |
| 47 | +RubyLLM::MCP.configure do |config| |
| 48 | + config.elicitation = lambda do |elicitation| |
| 49 | + # Handle elicitation requests from MCP servers |
| 50 | + puts "Server requests: #{elicitation.message}" |
| 51 | + |
| 52 | + # Example: Always accept with a default response |
| 53 | + elicitation.structured_response = { "status": "accepted" } |
| 54 | + true |
| 55 | + end |
| 56 | +end |
| 57 | +``` |
| 58 | + |
| 59 | +### Per-Client Configuration |
| 60 | + |
| 61 | +Configure elicitation handling for specific clients: |
| 62 | + |
| 63 | +```ruby |
| 64 | +client = RubyLLM::MCP.client( |
| 65 | + name: "interactive-server", |
| 66 | + transport_type: :streamable, |
| 67 | + config: { url: "https://server.com/mcp" } |
| 68 | +) |
| 69 | + |
| 70 | +client.on_elicitation do |elicitation| |
| 71 | + # Handle server's elicitation request |
| 72 | + puts "Server message: #{elicitation.message}" |
| 73 | + |
| 74 | + # The requested schema defines the expected response format |
| 75 | + schema = elicitation.requested_schema |
| 76 | + |
| 77 | + # Provide structured response matching the schema |
| 78 | + response_data = collect_user_input(schema) |
| 79 | + elicitation.structured_response = response_data |
| 80 | + |
| 81 | + # Return true to accept, false to reject |
| 82 | + true |
| 83 | +end |
| 84 | +``` |
| 85 | + |
| 86 | +--- |
| 87 | + |
| 88 | +## Elicitation Object |
| 89 | + |
| 90 | +The elicitation object provides access to the server's request and allows you to set the response: |
| 91 | + |
| 92 | +### Properties |
| 93 | + |
| 94 | +| Property | Type | Description | |
| 95 | +|----------|------|-------------| |
| 96 | +| `message` | String | Human-readable message from the server | |
| 97 | +| `requested_schema` | Hash | JSON schema defining the expected response format | |
| 98 | +| `structured_response` | Hash | Your structured response (set this) | |
| 99 | + |
| 100 | +### Methods |
| 101 | + |
| 102 | +| Method | Returns | Description | |
| 103 | +|--------|---------|-------------| |
| 104 | +| `validate_response` | Boolean | Validates the structured response against the schema | |
| 105 | + |
| 106 | +### Example Usage |
| 107 | + |
| 108 | +```ruby |
| 109 | +client.on_elicitation do |elicitation| |
| 110 | + puts "Server says: #{elicitation.message}" |
| 111 | + |
| 112 | + # Examine the requested schema |
| 113 | + schema = elicitation.requested_schema |
| 114 | + puts "Expected format: #{schema}" |
| 115 | + |
| 116 | + # Create a response that matches the schema |
| 117 | + response = { |
| 118 | + "user_choice": "option_a", |
| 119 | + "confidence": 0.8, |
| 120 | + "reasoning": "This option seems most appropriate" |
| 121 | + } |
| 122 | + |
| 123 | + # Set the response |
| 124 | + elicitation.structured_response = response |
| 125 | + |
| 126 | + # Validation happens automatically, but you can check manually |
| 127 | + if elicitation.validate_response |
| 128 | + puts "Response is valid" |
| 129 | + true # Accept the elicitation |
| 130 | + else |
| 131 | + puts "Response is invalid" |
| 132 | + false # Reject the elicitation |
| 133 | + end |
| 134 | +end |
| 135 | +``` |
| 136 | + |
| 137 | +--- |
| 138 | + |
| 139 | +## Response Actions |
| 140 | + |
| 141 | +Your elicitation handler should return one of these values to indicate the action: |
| 142 | + |
| 143 | +### Accept (true) |
| 144 | + |
| 145 | +Accept the elicitation and provide the structured response: |
| 146 | + |
| 147 | +```ruby |
| 148 | +client.on_elicitation do |elicitation| |
| 149 | + elicitation.structured_response = { "decision": "approved" } |
| 150 | + true # Accept |
| 151 | +end |
| 152 | +``` |
| 153 | + |
| 154 | +### Reject (false) |
| 155 | + |
| 156 | +Reject the elicitation request: |
| 157 | + |
| 158 | +```ruby |
| 159 | +client.on_elicitation do |elicitation| |
| 160 | + false # Reject - don't want to provide this information |
| 161 | +end |
| 162 | +``` |
| 163 | + |
| 164 | +### Cancel (validation failure) |
| 165 | + |
| 166 | +If your response doesn't match the schema, the client automatically cancels: |
| 167 | + |
| 168 | +```ruby |
| 169 | +client.on_elicitation do |elicitation| |
| 170 | + # This will fail validation and trigger a cancel |
| 171 | + elicitation.structured_response = { "invalid": "format" } |
| 172 | + true # You accept, but validation fails |
| 173 | +end |
| 174 | +``` |
| 175 | + |
| 176 | +--- |
| 177 | + |
| 178 | +## Schema Examples |
| 179 | + |
| 180 | +### Simple User Preference |
| 181 | + |
| 182 | +```json |
| 183 | +{ |
| 184 | + "type": "object", |
| 185 | + "properties": { |
| 186 | + "preference": { |
| 187 | + "type": "string", |
| 188 | + "enum": ["option_a", "option_b", "option_c"] |
| 189 | + }, |
| 190 | + "confidence": { |
| 191 | + "type": "number", |
| 192 | + "minimum": 0, |
| 193 | + "maximum": 1 |
| 194 | + } |
| 195 | + }, |
| 196 | + "required": ["preference"] |
| 197 | +} |
| 198 | +``` |
| 199 | + |
| 200 | +Corresponding response: |
| 201 | + |
| 202 | +```ruby |
| 203 | +elicitation.structured_response = { |
| 204 | + "preference": "option_a", |
| 205 | + "confidence": 0.9 |
| 206 | +} |
| 207 | +``` |
| 208 | + |
| 209 | +### Complex Configuration |
| 210 | + |
| 211 | +```json |
| 212 | +{ |
| 213 | + "type": "object", |
| 214 | + "properties": { |
| 215 | + "settings": { |
| 216 | + "type": "object", |
| 217 | + "properties": { |
| 218 | + "theme": {"type": "string"}, |
| 219 | + "notifications": {"type": "boolean"}, |
| 220 | + "advanced": { |
| 221 | + "type": "array", |
| 222 | + "items": {"type": "string"} |
| 223 | + } |
| 224 | + } |
| 225 | + }, |
| 226 | + "user_info": { |
| 227 | + "type": "object", |
| 228 | + "properties": { |
| 229 | + "name": {"type": "string"}, |
| 230 | + "department": {"type": "string"} |
| 231 | + }, |
| 232 | + "required": ["name"] |
| 233 | + } |
| 234 | + }, |
| 235 | + "required": ["user_info"] |
| 236 | +} |
| 237 | +``` |
| 238 | + |
| 239 | +Corresponding response: |
| 240 | + |
| 241 | +```ruby |
| 242 | +elicitation.structured_response = { |
| 243 | + "settings": { |
| 244 | + "theme": "dark", |
| 245 | + "notifications": true, |
| 246 | + "advanced": ["experimental_features", "debug_mode"] |
| 247 | + }, |
| 248 | + "user_info": { |
| 249 | + "name": "Alice Smith", |
| 250 | + "department": "Engineering" |
| 251 | + } |
| 252 | +} |
| 253 | +``` |
| 254 | + |
| 255 | +--- |
| 256 | + |
| 257 | +## Error Handling |
| 258 | + |
| 259 | +### Schema Validation Errors |
| 260 | + |
| 261 | +```ruby |
| 262 | +client.on_elicitation do |elicitation| |
| 263 | + begin |
| 264 | + response = build_response(elicitation.requested_schema) |
| 265 | + elicitation.structured_response = response |
| 266 | + |
| 267 | + unless elicitation.validate_response |
| 268 | + puts "Response validation failed" |
| 269 | + return false |
| 270 | + end |
| 271 | + |
| 272 | + true |
| 273 | + rescue StandardError => e |
| 274 | + puts "Error processing elicitation: #{e.message}" |
| 275 | + false |
| 276 | + end |
| 277 | +end |
| 278 | +``` |
| 279 | + |
| 280 | +### Timeout Handling |
| 281 | + |
| 282 | +```ruby |
| 283 | +client.on_elicitation do |elicitation| |
| 284 | + Timeout.timeout(30) do # 30 second timeout |
| 285 | + response = collect_user_input(elicitation.requested_schema) |
| 286 | + elicitation.structured_response = response |
| 287 | + true |
| 288 | + end |
| 289 | +rescue Timeout::Error |
| 290 | + puts "Elicitation timed out" |
| 291 | + false |
| 292 | +end |
| 293 | +``` |
| 294 | + |
| 295 | +--- |
| 296 | + |
| 297 | +## Best Practices |
| 298 | + |
| 299 | +### Security Considerations |
| 300 | + |
| 301 | +- **Validate all user input** before setting structured responses |
| 302 | +- **Sanitize data** to prevent injection attacks |
| 303 | +- **Limit response size** to prevent memory issues |
| 304 | +- **Implement timeouts** for user input collection |
| 305 | + |
| 306 | +### User Experience |
| 307 | + |
| 308 | +- **Provide clear prompts** based on the server's message |
| 309 | +- **Show schema information** to help users understand what's expected |
| 310 | +- **Implement input validation** with helpful error messages |
| 311 | +- **Support cancellation** for long-running input collection |
| 312 | + |
| 313 | +### Error Recovery |
| 314 | + |
| 315 | +- **Handle schema validation failures** gracefully |
| 316 | +- **Provide fallback responses** for critical workflows |
| 317 | +- **Log elicitation requests** for debugging |
| 318 | +- **Implement retry logic** for temporary failures |
| 319 | + |
| 320 | +### Performance |
| 321 | + |
| 322 | +- **Cache frequent responses** for common schemas |
| 323 | +- **Implement async processing** for complex input collection |
| 324 | +- **Set reasonable timeouts** for user interactions |
| 325 | +- **Monitor elicitation frequency** to detect issues |
| 326 | + |
| 327 | +## Next Steps |
| 328 | + |
| 329 | +Once you understand client interactions, explore: |
| 330 | + |
| 331 | +- **[Server Interactions]({% link server/index.md %})** - Working with server capabilities |
| 332 | +- **[Configuration]({% link configuration.md %})** - Advanced client configuration options |
| 333 | +- **[Rails Integration]({% link guides/rails-integration.md %})** - Using MCP with Rails applications |
0 commit comments