turbomcp_protocol/handlers.rs
1//! Handler traits for extensible MCP protocol support
2//!
3//! This module provides trait definitions for handling various MCP protocol
4//! features including elicitation, completion, resource templates, and ping.
5//!
6//! ## Handler Types
7//!
8//! ### [`ElicitationHandler`]
9//! Handle server-initiated user input requests. Useful for asking users for
10//! additional information during tool execution.
11//!
12//! ### [`CompletionProvider`]
13//! Provide argument completion suggestions for tools and commands. Implements
14//! autocomplete functionality in MCP clients.
15//!
16//! ### [`ResourceTemplateHandler`]
17//! Manage dynamic resource templates with parameter substitution. Enables
18//! pattern-based resource access (e.g., `file:///{path}`).
19//!
20//! ### [`PingHandler`]
21//! Handle bidirectional ping/pong for connection health monitoring.
22//!
23//! ## Example: Implementing an Elicitation Handler
24//!
25//! ```rust
26//! use turbomcp_protocol::{ElicitationHandler, ElicitationContext, ElicitationResponse};
27//! use turbomcp_protocol::Result;
28//! use std::collections::HashMap;
29//! use std::future::Future;
30//! use std::pin::Pin;
31//!
32//! struct MyElicitationHandler;
33//!
34//! impl ElicitationHandler for MyElicitationHandler {
35//! fn handle_elicitation(
36//! &self,
37//! context: &ElicitationContext
38//! ) -> Pin<Box<dyn Future<Output = Result<ElicitationResponse>> + Send + '_>> {
39//! let can_handle = self.can_handle(context);
40//! Box::pin(async move {
41//! // Check if we can handle this elicitation type
42//! if !can_handle {
43//! return Ok(ElicitationResponse {
44//! accepted: false,
45//! content: None,
46//! decline_reason: Some("Unsupported elicitation type".to_string()),
47//! });
48//! }
49//!
50//! // Process the elicitation (e.g., prompt user)
51//! let mut response_data = HashMap::new();
52//! response_data.insert(
53//! "user_input".to_string(),
54//! serde_json::json!("User provided value")
55//! );
56//!
57//! Ok(ElicitationResponse {
58//! accepted: true,
59//! content: Some(response_data),
60//! decline_reason: None,
61//! })
62//! })
63//! }
64//!
65//! fn can_handle(&self, context: &ElicitationContext) -> bool {
66//! // Check if elicitation has required input
67//! context.required && !context.message.is_empty()
68//! }
69//!
70//! fn priority(&self) -> i32 {
71//! 100 // Higher priority than default (0)
72//! }
73//! }
74//! ```
75//!
76//! ## Example: Implementing a Completion Provider
77//!
78//! ```rust
79//! use turbomcp_protocol::{CompletionProvider, CompletionContext, CompletionItem};
80//! use turbomcp_protocol::Result;
81//! use std::future::Future;
82//! use std::pin::Pin;
83//!
84//! struct FilePathCompletionProvider;
85//!
86//! impl CompletionProvider for FilePathCompletionProvider {
87//! fn provide_completions(
88//! &self,
89//! context: &CompletionContext
90//! ) -> Pin<Box<dyn Future<Output = Result<Vec<CompletionItem>>> + Send + '_>> {
91//! Box::pin(async move {
92//! // Provide file path completions
93//! let completions = vec![
94//! CompletionItem {
95//! value: "/home/user/documents".to_string(),
96//! label: Some("Documents".to_string()),
97//! documentation: Some("User documents folder".to_string()),
98//! sort_priority: Some(1),
99//! insert_text: None,
100//! metadata: Default::default(),
101//! },
102//! CompletionItem {
103//! value: "/home/user/downloads".to_string(),
104//! label: Some("Downloads".to_string()),
105//! documentation: Some("Downloads folder".to_string()),
106//! sort_priority: Some(2),
107//! insert_text: None,
108//! metadata: Default::default(),
109//! },
110//! ];
111//!
112//! Ok(completions)
113//! })
114//! }
115//!
116//! fn can_provide(&self, context: &CompletionContext) -> bool {
117//! // Only provide completions for "path" arguments
118//! context.argument_name.as_deref() == Some("path")
119//! }
120//! }
121//! ```
122
123use serde_json::Value;
124use std::collections::HashMap;
125
126use std::future::Future;
127use std::pin::Pin;
128
129use crate::Result;
130use crate::context::{CompletionContext, ElicitationContext, ServerInitiatedContext};
131
132/// Handler for server-initiated elicitation requests
133pub trait ElicitationHandler: Send + Sync {
134 /// Handle an elicitation request from the server
135 fn handle_elicitation(
136 &self,
137 context: &ElicitationContext,
138 ) -> Pin<Box<dyn Future<Output = Result<ElicitationResponse>> + Send + '_>>;
139
140 /// Check if this handler can process the given elicitation
141 fn can_handle(&self, context: &ElicitationContext) -> bool;
142
143 /// Get handler priority (higher = higher priority)
144 fn priority(&self) -> i32 {
145 0
146 }
147}
148
149/// Response to an elicitation request
150#[derive(Debug, Clone)]
151pub struct ElicitationResponse {
152 /// Whether the elicitation was accepted
153 pub accepted: bool,
154 /// The response content if accepted
155 pub content: Option<HashMap<String, Value>>,
156 /// Optional reason for declining
157 pub decline_reason: Option<String>,
158}
159
160/// Provider for argument completion
161pub trait CompletionProvider: Send + Sync {
162 /// Provide completions for the given context
163 fn provide_completions(
164 &self,
165 context: &CompletionContext,
166 ) -> Pin<Box<dyn Future<Output = Result<Vec<CompletionItem>>> + Send + '_>>;
167
168 /// Check if this provider can handle the completion request
169 fn can_provide(&self, context: &CompletionContext) -> bool;
170
171 /// Get provider priority
172 fn priority(&self) -> i32 {
173 0
174 }
175}
176
177/// A single completion item
178#[derive(Debug, Clone)]
179pub struct CompletionItem {
180 /// The completion value
181 pub value: String,
182 /// Human-readable label
183 pub label: Option<String>,
184 /// Additional documentation
185 pub documentation: Option<String>,
186 /// Sort priority (lower = higher priority)
187 pub sort_priority: Option<i32>,
188 /// Text to insert
189 pub insert_text: Option<String>,
190 /// Item metadata
191 pub metadata: HashMap<String, Value>,
192}
193
194/// Handler for resource templates
195pub trait ResourceTemplateHandler: Send + Sync {
196 /// List available resource templates
197 fn list_templates(
198 &self,
199 ) -> Pin<Box<dyn Future<Output = Result<Vec<ResourceTemplate>>> + Send + '_>>;
200
201 /// Get a specific resource template
202 fn get_template(
203 &self,
204 name: &str,
205 ) -> Pin<Box<dyn Future<Output = Result<Option<ResourceTemplate>>> + Send + '_>>;
206
207 /// Resolve template parameters
208 fn resolve_template(
209 &self,
210 template: &ResourceTemplate,
211 params: HashMap<String, Value>,
212 ) -> Pin<Box<dyn Future<Output = Result<ResolvedResource>> + Send + '_>>;
213}
214
215/// Resource template definition
216#[derive(Debug, Clone)]
217pub struct ResourceTemplate {
218 /// Template name
219 pub name: String,
220 /// Template description
221 pub description: Option<String>,
222 /// URI template pattern
223 pub uri_template: String,
224 /// Template parameters
225 pub parameters: Vec<TemplateParam>,
226 /// Template metadata
227 pub metadata: HashMap<String, Value>,
228}
229
230/// Template parameter definition
231#[derive(Debug, Clone)]
232pub struct TemplateParam {
233 /// Parameter name
234 pub name: String,
235 /// Parameter description
236 pub description: Option<String>,
237 /// Whether the parameter is required
238 pub required: bool,
239 /// Parameter type
240 pub param_type: String,
241 /// Default value
242 pub default_value: Option<Value>,
243}
244
245/// Resolved resource from template
246#[derive(Debug, Clone)]
247pub struct ResolvedResource {
248 /// Resolved URI
249 pub uri: String,
250 /// Resource name
251 pub name: String,
252 /// Resource description
253 pub description: Option<String>,
254 /// Resource content
255 pub content: Option<Value>,
256 /// Resource metadata
257 pub metadata: HashMap<String, Value>,
258}
259
260/// Handler for bidirectional ping requests
261pub trait PingHandler: Send + Sync {
262 /// Handle a ping request
263 fn handle_ping(
264 &self,
265 context: &ServerInitiatedContext,
266 ) -> Pin<Box<dyn Future<Output = Result<PingResponse>> + Send + '_>>;
267
268 /// Send a ping to the remote party
269 fn send_ping(
270 &self,
271 target: &str,
272 ) -> Pin<Box<dyn Future<Output = Result<PingResponse>> + Send + '_>>;
273}
274
275/// Response to a ping request
276#[derive(Debug, Clone)]
277pub struct PingResponse {
278 /// Whether the ping was successful
279 pub success: bool,
280 /// Round-trip time in milliseconds
281 pub rtt_ms: Option<u64>,
282 /// Additional metadata
283 pub metadata: HashMap<String, Value>,
284}
285
286/// Capabilities for server-initiated features
287#[derive(Debug, Clone, Default)]
288pub struct ServerInitiatedCapabilities {
289 /// Supports sampling/message creation
290 pub sampling: bool,
291 /// Supports roots listing
292 pub roots: bool,
293 /// Supports elicitation
294 pub elicitation: bool,
295 /// Maximum concurrent requests
296 pub max_concurrent_requests: usize,
297 /// Supported experimental features
298 pub experimental: HashMap<String, bool>,
299}
300
301/// Handler capability tracking
302#[derive(Debug, Clone, Default)]
303pub struct HandlerCapabilities {
304 /// Supports elicitation
305 pub elicitation: bool,
306 /// Supports completion
307 pub completion: bool,
308 /// Supports resource templates
309 pub templates: bool,
310 /// Supports bidirectional ping
311 pub ping: bool,
312 /// Server-initiated capabilities
313 pub server_initiated: ServerInitiatedCapabilities,
314}
315
316impl HandlerCapabilities {
317 /// Create new handler capabilities
318 pub fn new() -> Self {
319 Self::default()
320 }
321
322 /// Enable elicitation support
323 pub fn with_elicitation(mut self) -> Self {
324 self.elicitation = true;
325 self
326 }
327
328 /// Enable completion support
329 pub fn with_completion(mut self) -> Self {
330 self.completion = true;
331 self
332 }
333
334 /// Enable template support
335 pub fn with_templates(mut self) -> Self {
336 self.templates = true;
337 self
338 }
339
340 /// Enable ping support
341 pub fn with_ping(mut self) -> Self {
342 self.ping = true;
343 self
344 }
345
346 /// Set server-initiated capabilities
347 pub fn with_server_initiated(mut self, capabilities: ServerInitiatedCapabilities) -> Self {
348 self.server_initiated = capabilities;
349 self
350 }
351}
352
353/// Handler for JSON-RPC requests - Core abstraction for MCP protocol implementation
354///
355/// This trait provides a transport-agnostic interface for handling MCP JSON-RPC requests.
356/// Implementations of this trait can work seamlessly across all transport layers
357/// (HTTP, STDIO, WebSocket, etc.) without transport-specific code.
358///
359/// # Architecture
360///
361/// The `JsonRpcHandler` trait serves as the bridge between:
362/// - **Protocol Logic**: Tools, resources, prompts dispatch (typically macro-generated)
363/// - **Transport Layer**: HTTP, STDIO, WebSocket protocol details
364///
365/// This separation enables:
366/// - Clean, testable handler implementations
367/// - Transport-agnostic server code
368/// - Full current MCP compliance in the transport layer
369/// - Compile-time dispatch optimizations in handlers
370///
371/// # Example: Macro-Generated Implementation
372///
373/// ```rust,ignore
374/// use turbomcp_protocol::JsonRpcHandler;
375/// use serde_json::Value;
376///
377/// #[derive(Clone)]
378/// struct WeatherServer;
379///
380/// impl JsonRpcHandler for WeatherServer {
381/// async fn handle_request(&self, req: Value) -> Value {
382/// // Parse method and dispatch
383/// let method = req["method"].as_str().unwrap_or("");
384/// match method {
385/// "initialize" => { /* ... */ },
386/// "tools/call" => { /* dispatch to tools */ },
387/// "resources/read" => { /* dispatch to resources */ },
388/// _ => serde_json::json!({"error": "method not found"}),
389/// }
390/// }
391///
392/// fn server_info(&self) -> ServerInfo {
393/// ServerInfo {
394/// name: "Weather Server".to_string(),
395/// version: "1.0.0".to_string(),
396/// }
397/// }
398/// }
399/// ```
400///
401/// # Usage with Transports
402///
403/// ```rust,ignore
404/// // HTTP Transport
405/// use turbomcp_transport::streamable_http::StreamableHttpTransport;
406///
407/// let handler = Arc::new(WeatherServer);
408/// let transport = StreamableHttpTransport::new(config, handler);
409/// transport.run().await?;
410///
411/// // STDIO Transport
412/// use turbomcp_transport::stdio::StdioTransport;
413///
414/// let handler = Arc::new(WeatherServer);
415/// let transport = StdioTransport::new(handler);
416/// transport.run().await?;
417/// ```
418pub trait JsonRpcHandler: Send + Sync + 'static {
419 /// Handle a JSON-RPC request and return a response
420 ///
421 /// This method receives a JSON-RPC request as a `serde_json::Value` and must return
422 /// a valid JSON-RPC response. The implementation should:
423 /// - Route the request based on the `method` field
424 /// - Validate parameters
425 /// - Execute the appropriate handler logic
426 /// - Return a success response with results or an error response
427 ///
428 /// # Arguments
429 ///
430 /// * `request` - The JSON-RPC request as a JSON value
431 ///
432 /// # Returns
433 ///
434 /// A JSON-RPC response as a JSON value containing either:
435 /// - `result`: For successful operations
436 /// - `error`: For failed operations with error details
437 ///
438 /// # Note
439 ///
440 /// The request and response are `serde_json::Value` to avoid tight coupling with
441 /// protocol types. Transport layers handle conversion to/from typed structs.
442 fn handle_request(
443 &self,
444 request: serde_json::Value,
445 ) -> Pin<Box<dyn Future<Output = serde_json::Value> + Send + '_>>;
446
447 /// Get server metadata
448 ///
449 /// Returns information about the server including name and version.
450 /// This is used during the MCP initialization handshake.
451 ///
452 /// # Returns
453 ///
454 /// Server information including name and version
455 fn server_info(&self) -> ServerInfo {
456 ServerInfo {
457 name: "TurboMCP Server".to_string(),
458 version: env!("CARGO_PKG_VERSION").to_string(),
459 }
460 }
461
462 /// Get server capabilities
463 ///
464 /// Returns the capabilities supported by this server.
465 /// Override this to advertise custom capabilities to clients.
466 ///
467 /// # Returns
468 ///
469 /// JSON value describing server capabilities
470 fn capabilities(&self) -> serde_json::Value {
471 serde_json::json!({})
472 }
473}
474
475/// Server metadata information
476#[derive(Debug, Clone)]
477pub struct ServerInfo {
478 /// Server name
479 pub name: String,
480 /// Server version
481 pub version: String,
482}