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