turbomcp_client/
handlers.rs

1//! Handler traits for bidirectional communication in MCP client
2//!
3//! This module provides handler traits and registration mechanisms for processing
4//! server-initiated requests. The MCP protocol is bidirectional, meaning servers
5//! can also send requests to clients for various purposes like elicitation,
6//! progress reporting, logging, and resource updates.
7//!
8//! ## Handler Types
9//!
10//! - **ElicitationHandler**: Handle user input requests from servers
11//! - **ProgressHandler**: Process progress notifications from long-running operations
12//! - **LogHandler**: Route server log messages to client logging systems
13//! - **ResourceUpdateHandler**: Handle notifications when resources change
14//!
15//! ## Usage
16//!
17//! ```rust,no_run
18//! use turbomcp_client::handlers::{ElicitationHandler, ElicitationRequest, ElicitationResponse, ElicitationAction, HandlerError};
19//! use async_trait::async_trait;
20//!
21//! // Implement elicitation handler
22//! #[derive(Debug)]
23//! struct MyElicitationHandler;
24//!
25//! #[async_trait]
26//! impl ElicitationHandler for MyElicitationHandler {
27//!     async fn handle_elicitation(
28//!         &self,
29//!         request: ElicitationRequest,
30//!     ) -> Result<ElicitationResponse, HandlerError> {
31//!         // Display the prompt to the user
32//!         eprintln!("\n{}", request.prompt);
33//!         eprintln!("---");
34//!
35//!         // For each field in the schema, collect user input
36//!         let mut content = std::collections::HashMap::new();
37//!
38//!         if let Some(properties) = request.schema.get("properties") {
39//!             if let Some(props) = properties.as_object() {
40//!                 for (field_name, field_schema) in props {
41//!                     let field_type = field_schema.get("type")
42//!                         .and_then(|v| v.as_str())
43//!                         .unwrap_or("string");
44//!
45//!                     eprint!("{} ({}): ", field_name, field_type);
46//!
47//!                     let mut input = String::new();
48//!                     std::io::stdin().read_line(&mut input)
49//!                         .map_err(|e| HandlerError::Generic {
50//!                             message: e.to_string()
51//!                         })?;
52//!
53//!                     let input = input.trim();
54//!
55//!                     // Parse input based on field type
56//!                     let value: serde_json::Value = match field_type {
57//!                         "boolean" => serde_json::json!(input == "true" || input == "yes" || input == "1"),
58//!                         "number" | "integer" => input.parse::<f64>()
59//!                             .map(|n| serde_json::json!(n))
60//!                             .unwrap_or_else(|_| serde_json::json!(input)),
61//!                         _ => serde_json::json!(input),
62//!                     };
63//!
64//!                     content.insert(field_name.clone(), value);
65//!                 }
66//!             }
67//!         }
68//!
69//!         Ok(ElicitationResponse {
70//!             action: ElicitationAction::Accept,
71//!             content: Some(serde_json::to_value(content).unwrap()),
72//!         })
73//!     }
74//! }
75//! ```
76
77use async_trait::async_trait;
78use serde::{Deserialize, Serialize};
79use std::collections::HashMap;
80use std::sync::Arc;
81use thiserror::Error;
82use tracing::{debug, error, info, warn};
83use turbomcp_protocol::types::{
84    LogLevel, ProgressNotification as ProtocolProgressNotification, ResourceContents,
85};
86
87// ============================================================================
88// ERROR TYPES FOR HANDLER OPERATIONS
89// ============================================================================
90
91/// Errors that can occur during handler operations
92#[derive(Error, Debug)]
93#[non_exhaustive]
94pub enum HandlerError {
95    /// Handler operation failed due to user cancellation
96    #[error("User cancelled the operation")]
97    UserCancelled,
98
99    /// Handler operation timed out
100    #[error("Handler operation timed out after {timeout_seconds} seconds")]
101    Timeout { timeout_seconds: u64 },
102
103    /// Input validation failed
104    #[error("Invalid input: {details}")]
105    InvalidInput { details: String },
106
107    /// Handler configuration error
108    #[error("Handler configuration error: {message}")]
109    Configuration { message: String },
110
111    /// Generic handler error
112    #[error("Handler error: {message}")]
113    Generic { message: String },
114
115    /// External system error (e.g., UI framework, database)
116    #[error("External system error: {source}")]
117    External {
118        #[from]
119        source: Box<dyn std::error::Error + Send + Sync>,
120    },
121}
122
123pub type HandlerResult<T> = Result<T, HandlerError>;
124
125// ============================================================================
126// ELICITATION HANDLER TRAIT
127// ============================================================================
128
129/// Request structure for elicitation operations
130#[derive(Debug, Serialize, Deserialize, Clone)]
131pub struct ElicitationRequest {
132    /// Unique identifier for this elicitation request
133    pub id: String,
134
135    /// Human-readable prompt for the user
136    pub prompt: String,
137
138    /// JSON schema defining the expected response structure
139    pub schema: serde_json::Value,
140
141    /// Optional timeout in seconds
142    pub timeout: Option<u64>,
143
144    /// Additional metadata for the request
145    pub metadata: HashMap<String, serde_json::Value>,
146}
147
148/// Elicitation response action indicating user's choice
149#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
150#[serde(rename_all = "lowercase")]
151pub enum ElicitationAction {
152    /// User explicitly approved and submitted with data
153    Accept,
154    /// User explicitly declined the request
155    Decline,
156    /// User dismissed without making an explicit choice
157    Cancel,
158}
159
160/// Response structure for elicitation operations (MCP-compliant)
161#[derive(Debug, Serialize, Deserialize, Clone)]
162pub struct ElicitationResponse {
163    /// User's action choice (accept, decline, or cancel)
164    pub action: ElicitationAction,
165
166    /// User's response data (must conform to the request schema)
167    /// Only present for "accept" actions
168    #[serde(skip_serializing_if = "Option::is_none")]
169    pub content: Option<serde_json::Value>,
170}
171
172/// Handler for server-initiated elicitation requests
173///
174/// Elicitation is a mechanism where servers can request user input during
175/// operations. For example, a server might need user preferences, authentication
176/// credentials, or configuration choices to complete a task.
177///
178/// Implementations should:
179/// - Present the schema/prompt to the user in an appropriate UI
180/// - Validate user input against the provided schema
181/// - Handle user cancellation gracefully
182/// - Respect timeout constraints
183///
184/// # Examples
185///
186/// ```rust,no_run
187/// use turbomcp_client::handlers::{ElicitationAction, ElicitationHandler, ElicitationRequest, ElicitationResponse, HandlerResult};
188/// use async_trait::async_trait;
189/// use serde_json::json;
190///
191/// #[derive(Debug)]
192/// struct CLIElicitationHandler;
193///
194/// #[async_trait]
195/// impl ElicitationHandler for CLIElicitationHandler {
196///     async fn handle_elicitation(
197///         &self,
198///         request: ElicitationRequest,
199///     ) -> HandlerResult<ElicitationResponse> {
200///         println!("Server request: {}", request.prompt);
201///         
202///         // In a real implementation, you would:
203///         // 1. Parse the schema to understand what input is needed
204///         // 2. Present an appropriate UI (CLI prompts, GUI forms, etc.)
205///         // 3. Validate the user's input against the schema
206///         // 4. Return the structured response
207///         
208///         Ok(ElicitationResponse {
209///             action: ElicitationAction::Accept,
210///             content: Some(json!({ "user_choice": "example_value" })),
211///         })
212///     }
213/// }
214/// ```
215#[async_trait]
216pub trait ElicitationHandler: Send + Sync + std::fmt::Debug {
217    /// Handle an elicitation request from the server
218    ///
219    /// This method is called when a server needs user input. The implementation
220    /// should present the request to the user and collect their response.
221    ///
222    /// # Arguments
223    ///
224    /// * `request` - The elicitation request containing prompt, schema, and metadata
225    ///
226    /// # Returns
227    ///
228    /// Returns the user's response or an error if the operation failed.
229    async fn handle_elicitation(
230        &self,
231        request: ElicitationRequest,
232    ) -> HandlerResult<ElicitationResponse>;
233}
234
235// ============================================================================
236// PROGRESS HANDLER TRAIT
237// ============================================================================
238
239/// Progress notification from server operations
240#[derive(Debug, Serialize, Deserialize, Clone)]
241pub struct ProgressNotification {
242    /// Unique identifier for the operation being tracked
243    pub operation_id: String,
244
245    /// Current progress information
246    pub progress: ProtocolProgressNotification,
247
248    /// Human-readable status message
249    pub message: Option<String>,
250
251    /// Whether the operation has completed
252    pub completed: bool,
253
254    /// Optional error information if the operation failed
255    pub error: Option<String>,
256}
257
258/// Handler for server progress notifications
259///
260/// Progress handlers receive notifications about long-running server operations.
261/// This allows clients to display progress bars, status updates, or other
262/// feedback to users during operations that take significant time.
263///
264/// # Examples
265///
266/// ```rust,no_run
267/// use turbomcp_client::handlers::{ProgressHandler, ProgressNotification, HandlerResult};
268/// use async_trait::async_trait;
269///
270/// #[derive(Debug)]
271/// struct ProgressBarHandler;
272///
273/// #[async_trait]
274/// impl ProgressHandler for ProgressBarHandler {
275///     async fn handle_progress(&self, notification: ProgressNotification) -> HandlerResult<()> {
276///         let progress_val = notification.progress.progress;
277///         if let Some(total) = notification.progress.total {
278///             let percentage = (progress_val / total) * 100.0;
279///             println!("Progress: {:.1}% - {}", percentage,
280///                 notification.message.unwrap_or_default());
281///         } else {
282///             println!("Progress: {} - {}", progress_val,
283///                 notification.message.unwrap_or_default());
284///         }
285///
286///         if notification.completed {
287///             if let Some(error) = notification.error {
288///                 println!("Operation failed: {}", error);
289///             } else {
290///                 println!("Operation completed successfully!");
291///             }
292///         }
293///
294///         Ok(())
295///     }
296/// }
297/// ```
298#[async_trait]
299pub trait ProgressHandler: Send + Sync + std::fmt::Debug {
300    /// Handle a progress notification from the server
301    ///
302    /// This method is called when the server sends progress updates for
303    /// long-running operations.
304    ///
305    /// # Arguments
306    ///
307    /// * `notification` - Progress information including current status and completion state
308    ///
309    /// # Returns
310    ///
311    /// Returns `Ok(())` if the notification was processed successfully.
312    async fn handle_progress(&self, notification: ProgressNotification) -> HandlerResult<()>;
313}
314
315// ============================================================================
316// LOG HANDLER TRAIT
317// ============================================================================
318
319/// Log message from the server
320#[derive(Debug, Serialize, Deserialize, Clone)]
321pub struct LogMessage {
322    /// Log level (Error, Warning, Info, Debug)
323    pub level: LogLevel,
324
325    /// The log message content
326    pub message: String,
327
328    /// Optional logger name/category
329    pub logger: Option<String>,
330
331    /// Timestamp when the log was created (ISO 8601 format)
332    pub timestamp: String,
333
334    /// Additional structured data
335    pub data: Option<serde_json::Value>,
336}
337
338/// Handler for server log messages
339///
340/// Log handlers receive log messages from the server and can route them to
341/// the client's logging system. This is useful for debugging, monitoring,
342/// and maintaining a unified log across client and server.
343///
344/// # Examples
345///
346/// ```rust,no_run
347/// use turbomcp_client::handlers::{LogHandler, LogMessage, HandlerResult};
348/// use turbomcp_protocol::types::LogLevel;
349/// use async_trait::async_trait;
350///
351/// #[derive(Debug)]
352/// struct TraceLogHandler;
353///
354/// #[async_trait]
355/// impl LogHandler for TraceLogHandler {
356///     async fn handle_log(&self, log: LogMessage) -> HandlerResult<()> {
357///         match log.level {
358///             LogLevel::Error => tracing::error!("Server: {}", log.message),
359///             LogLevel::Warning => tracing::warn!("Server: {}", log.message),
360///             LogLevel::Info => tracing::info!("Server: {}", log.message),
361///             LogLevel::Debug => tracing::debug!("Server: {}", log.message),
362///             LogLevel::Notice => tracing::info!("Server: {}", log.message),
363///             LogLevel::Critical => tracing::error!("Server CRITICAL: {}", log.message),
364///             LogLevel::Alert => tracing::error!("Server ALERT: {}", log.message),
365///             LogLevel::Emergency => tracing::error!("Server EMERGENCY: {}", log.message),
366///         }
367///         Ok(())
368///     }
369/// }
370/// ```
371#[async_trait]
372pub trait LogHandler: Send + Sync + std::fmt::Debug {
373    /// Handle a log message from the server
374    ///
375    /// This method is called when the server sends log messages to the client.
376    /// Implementations can route these to the client's logging system.
377    ///
378    /// # Arguments
379    ///
380    /// * `log` - The log message with level, content, and metadata
381    ///
382    /// # Returns
383    ///
384    /// Returns `Ok(())` if the log message was processed successfully.
385    async fn handle_log(&self, log: LogMessage) -> HandlerResult<()>;
386}
387
388// ============================================================================
389// RESOURCE UPDATE HANDLER TRAIT
390// ============================================================================
391
392/// Resource update notification
393#[derive(Debug, Serialize, Deserialize, Clone)]
394pub struct ResourceUpdateNotification {
395    /// URI of the resource that changed
396    pub uri: String,
397
398    /// Type of change (created, modified, deleted)
399    pub change_type: ResourceChangeType,
400
401    /// Updated resource content (for create/modify operations)
402    pub content: Option<ResourceContents>,
403
404    /// Timestamp of the change
405    pub timestamp: String,
406
407    /// Additional metadata about the change
408    pub metadata: HashMap<String, serde_json::Value>,
409}
410
411/// Types of resource changes
412#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
413#[serde(rename_all = "lowercase")]
414pub enum ResourceChangeType {
415    /// Resource was created
416    Created,
417    /// Resource was modified
418    Modified,
419    /// Resource was deleted
420    Deleted,
421}
422
423/// Handler for resource update notifications
424///
425/// Resource update handlers receive notifications when resources that the
426/// client has subscribed to are modified. This enables reactive updates
427/// to cached data or UI refreshes when server-side resources change.
428///
429/// # Examples
430///
431/// ```rust,no_run
432/// use turbomcp_client::handlers::{ResourceUpdateHandler, ResourceUpdateNotification, HandlerResult};
433/// use async_trait::async_trait;
434///
435/// #[derive(Debug)]
436/// struct CacheInvalidationHandler;
437///
438/// #[async_trait]
439/// impl ResourceUpdateHandler for CacheInvalidationHandler {
440///     async fn handle_resource_update(
441///         &self,
442///         notification: ResourceUpdateNotification,
443///     ) -> HandlerResult<()> {
444///         println!("Resource {} was {:?}",
445///             notification.uri,
446///             notification.change_type);
447///         
448///         // In a real implementation, you might:
449///         // - Invalidate cached data for this resource
450///         // - Refresh UI components that display this resource
451///         // - Log the change for audit purposes
452///         // - Trigger dependent computations
453///         
454///         Ok(())
455///     }
456/// }
457/// ```
458#[async_trait]
459pub trait ResourceUpdateHandler: Send + Sync + std::fmt::Debug {
460    /// Handle a resource update notification
461    ///
462    /// This method is called when a subscribed resource changes on the server.
463    ///
464    /// # Arguments
465    ///
466    /// * `notification` - Information about the resource change
467    ///
468    /// # Returns
469    ///
470    /// Returns `Ok(())` if the notification was processed successfully.
471    async fn handle_resource_update(
472        &self,
473        notification: ResourceUpdateNotification,
474    ) -> HandlerResult<()>;
475}
476
477// ============================================================================
478// ROOTS HANDLER TRAIT
479// ============================================================================
480
481/// Roots handler for responding to server requests for filesystem roots
482///
483/// Per MCP 2025-06-18 specification, `roots/list` is a SERVER->CLIENT request.
484/// Servers ask clients what filesystem roots (directories/files) they have access to.
485/// This is commonly used when servers need to understand their operating boundaries,
486/// such as which repositories or project directories they can access.
487///
488/// # Examples
489///
490/// ```rust,no_run
491/// use turbomcp_client::handlers::{RootsHandler, HandlerResult};
492/// use turbomcp_protocol::types::Root;
493/// use async_trait::async_trait;
494///
495/// #[derive(Debug)]
496/// struct MyRootsHandler {
497///     project_dirs: Vec<String>,
498/// }
499///
500/// #[async_trait]
501/// impl RootsHandler for MyRootsHandler {
502///     async fn handle_roots_request(&self) -> HandlerResult<Vec<Root>> {
503///         Ok(self.project_dirs
504///             .iter()
505///             .map(|dir| Root {
506///                 uri: format!("file://{}", dir).into(),
507///                 name: Some(dir.split('/').last().unwrap_or("").to_string()),
508///             })
509///             .collect())
510///     }
511/// }
512/// ```
513#[async_trait]
514pub trait RootsHandler: Send + Sync + std::fmt::Debug {
515    /// Handle a roots/list request from the server
516    ///
517    /// This method is called when the server wants to know which filesystem roots
518    /// the client has available. The implementation should return a list of Root
519    /// objects representing directories or files the server can operate on.
520    ///
521    /// # Returns
522    ///
523    /// Returns a vector of Root objects, each with a URI (must start with file://)
524    /// and optional human-readable name.
525    ///
526    /// # Note
527    ///
528    /// Per MCP specification, URIs must start with `file://` for now. This restriction
529    /// may be relaxed in future protocol versions.
530    async fn handle_roots_request(&self) -> HandlerResult<Vec<turbomcp_protocol::types::Root>>;
531}
532
533// ============================================================================
534// HANDLER REGISTRY FOR CLIENT
535// ============================================================================
536
537/// Registry for managing client-side handlers
538///
539/// This registry holds all the handler implementations and provides methods
540/// for registering and invoking them. It's used internally by the Client
541/// to dispatch server-initiated requests to the appropriate handlers.
542#[derive(Debug, Default)]
543pub struct HandlerRegistry {
544    /// Roots handler for filesystem root requests
545    pub roots: Option<Arc<dyn RootsHandler>>,
546
547    /// Elicitation handler for user input requests
548    pub elicitation: Option<Arc<dyn ElicitationHandler>>,
549
550    /// Progress handler for operation updates
551    pub progress: Option<Arc<dyn ProgressHandler>>,
552
553    /// Log handler for server log messages
554    pub log: Option<Arc<dyn LogHandler>>,
555
556    /// Resource update handler for resource change notifications
557    pub resource_update: Option<Arc<dyn ResourceUpdateHandler>>,
558}
559
560impl HandlerRegistry {
561    /// Create a new empty handler registry
562    pub fn new() -> Self {
563        Self::default()
564    }
565
566    /// Register a roots handler
567    pub fn set_roots_handler(&mut self, handler: Arc<dyn RootsHandler>) {
568        debug!("Registering roots handler");
569        self.roots = Some(handler);
570    }
571
572    /// Register an elicitation handler
573    pub fn set_elicitation_handler(&mut self, handler: Arc<dyn ElicitationHandler>) {
574        debug!("Registering elicitation handler");
575        self.elicitation = Some(handler);
576    }
577
578    /// Register a progress handler
579    pub fn set_progress_handler(&mut self, handler: Arc<dyn ProgressHandler>) {
580        debug!("Registering progress handler");
581        self.progress = Some(handler);
582    }
583
584    /// Register a log handler
585    pub fn set_log_handler(&mut self, handler: Arc<dyn LogHandler>) {
586        debug!("Registering log handler");
587        self.log = Some(handler);
588    }
589
590    /// Register a resource update handler
591    pub fn set_resource_update_handler(&mut self, handler: Arc<dyn ResourceUpdateHandler>) {
592        debug!("Registering resource update handler");
593        self.resource_update = Some(handler);
594    }
595
596    /// Check if a roots handler is registered
597    pub fn has_roots_handler(&self) -> bool {
598        self.roots.is_some()
599    }
600
601    /// Check if an elicitation handler is registered
602    pub fn has_elicitation_handler(&self) -> bool {
603        self.elicitation.is_some()
604    }
605
606    /// Check if a progress handler is registered
607    pub fn has_progress_handler(&self) -> bool {
608        self.progress.is_some()
609    }
610
611    /// Check if a log handler is registered
612    pub fn has_log_handler(&self) -> bool {
613        self.log.is_some()
614    }
615
616    /// Check if a resource update handler is registered
617    pub fn has_resource_update_handler(&self) -> bool {
618        self.resource_update.is_some()
619    }
620
621    /// Handle a roots/list request from the server
622    pub async fn handle_roots_request(&self) -> HandlerResult<Vec<turbomcp_protocol::types::Root>> {
623        match &self.roots {
624            Some(handler) => {
625                info!("Processing roots/list request from server");
626                handler.handle_roots_request().await
627            }
628            None => {
629                warn!("No roots handler registered, returning empty roots list");
630                // Return empty list per MCP spec - client has no roots available
631                Ok(Vec::new())
632            }
633        }
634    }
635
636    /// Handle an elicitation request
637    pub async fn handle_elicitation(
638        &self,
639        request: ElicitationRequest,
640    ) -> HandlerResult<ElicitationResponse> {
641        match &self.elicitation {
642            Some(handler) => {
643                info!("Processing elicitation request: {}", request.id);
644                handler.handle_elicitation(request).await
645            }
646            None => {
647                warn!("No elicitation handler registered, declining request");
648                Err(HandlerError::Configuration {
649                    message: "No elicitation handler registered".to_string(),
650                })
651            }
652        }
653    }
654
655    /// Handle a progress notification
656    pub async fn handle_progress(&self, notification: ProgressNotification) -> HandlerResult<()> {
657        match &self.progress {
658            Some(handler) => {
659                debug!(
660                    "Processing progress notification: {}",
661                    notification.operation_id
662                );
663                handler.handle_progress(notification).await
664            }
665            None => {
666                debug!("No progress handler registered, ignoring notification");
667                Ok(())
668            }
669        }
670    }
671
672    /// Handle a log message
673    pub async fn handle_log(&self, log: LogMessage) -> HandlerResult<()> {
674        match &self.log {
675            Some(handler) => handler.handle_log(log).await,
676            None => {
677                debug!("No log handler registered, ignoring log message");
678                Ok(())
679            }
680        }
681    }
682
683    /// Handle a resource update notification
684    pub async fn handle_resource_update(
685        &self,
686        notification: ResourceUpdateNotification,
687    ) -> HandlerResult<()> {
688        match &self.resource_update {
689            Some(handler) => {
690                debug!("Processing resource update: {}", notification.uri);
691                handler.handle_resource_update(notification).await
692            }
693            None => {
694                debug!("No resource update handler registered, ignoring notification");
695                Ok(())
696            }
697        }
698    }
699}
700
701// ============================================================================
702// DEFAULT HANDLER IMPLEMENTATIONS
703// ============================================================================
704
705/// Default elicitation handler that declines all requests
706#[derive(Debug)]
707pub struct DeclineElicitationHandler;
708
709#[async_trait]
710impl ElicitationHandler for DeclineElicitationHandler {
711    async fn handle_elicitation(
712        &self,
713        request: ElicitationRequest,
714    ) -> HandlerResult<ElicitationResponse> {
715        warn!("Declining elicitation request: {}", request.prompt);
716        Ok(ElicitationResponse {
717            action: ElicitationAction::Decline,
718            content: None,
719        })
720    }
721}
722
723/// Default progress handler that logs progress to tracing
724#[derive(Debug)]
725pub struct LoggingProgressHandler;
726
727#[async_trait]
728impl ProgressHandler for LoggingProgressHandler {
729    async fn handle_progress(&self, notification: ProgressNotification) -> HandlerResult<()> {
730        if notification.completed {
731            if let Some(error) = &notification.error {
732                error!("Operation {} failed: {}", notification.operation_id, error);
733            } else {
734                info!(
735                    "Operation {} completed successfully",
736                    notification.operation_id
737                );
738            }
739        } else if let Some(message) = &notification.message {
740            info!("Operation {}: {}", notification.operation_id, message);
741        }
742
743        Ok(())
744    }
745}
746
747/// Default log handler that routes server logs to tracing
748#[derive(Debug)]
749pub struct TracingLogHandler;
750
751#[async_trait]
752impl LogHandler for TracingLogHandler {
753    async fn handle_log(&self, log: LogMessage) -> HandlerResult<()> {
754        let logger_prefix = log.logger.as_deref().unwrap_or("server");
755
756        match log.level {
757            LogLevel::Error => error!("[{}] {}", logger_prefix, log.message),
758            LogLevel::Warning => warn!("[{}] {}", logger_prefix, log.message),
759            LogLevel::Info => info!("[{}] {}", logger_prefix, log.message),
760            LogLevel::Debug => debug!("[{}] {}", logger_prefix, log.message),
761            LogLevel::Notice => info!("[{}] [NOTICE] {}", logger_prefix, log.message),
762            LogLevel::Critical => error!("[{}] [CRITICAL] {}", logger_prefix, log.message),
763            LogLevel::Alert => error!("[{}] [ALERT] {}", logger_prefix, log.message),
764            LogLevel::Emergency => error!("[{}] [EMERGENCY] {}", logger_prefix, log.message),
765        }
766
767        Ok(())
768    }
769}
770
771/// Default resource update handler that logs changes
772#[derive(Debug)]
773pub struct LoggingResourceUpdateHandler;
774
775#[async_trait]
776impl ResourceUpdateHandler for LoggingResourceUpdateHandler {
777    async fn handle_resource_update(
778        &self,
779        notification: ResourceUpdateNotification,
780    ) -> HandlerResult<()> {
781        info!(
782            "Resource {} was {:?} at {}",
783            notification.uri, notification.change_type, notification.timestamp
784        );
785        Ok(())
786    }
787}
788
789#[cfg(test)]
790mod tests {
791    use super::*;
792    use serde_json::json;
793    use tokio;
794
795    // Test handler implementations
796    #[derive(Debug)]
797    struct TestElicitationHandler;
798
799    #[async_trait]
800    impl ElicitationHandler for TestElicitationHandler {
801        async fn handle_elicitation(
802            &self,
803            _request: ElicitationRequest,
804        ) -> HandlerResult<ElicitationResponse> {
805            Ok(ElicitationResponse {
806                action: ElicitationAction::Accept,
807                content: Some(json!({"test": "response"})),
808            })
809        }
810    }
811
812    #[derive(Debug)]
813    struct TestProgressHandler;
814
815    #[async_trait]
816    impl ProgressHandler for TestProgressHandler {
817        async fn handle_progress(&self, _notification: ProgressNotification) -> HandlerResult<()> {
818            Ok(())
819        }
820    }
821
822    #[tokio::test]
823    async fn test_handler_registry_creation() {
824        let registry = HandlerRegistry::new();
825        assert!(!registry.has_elicitation_handler());
826        assert!(!registry.has_progress_handler());
827        assert!(!registry.has_log_handler());
828        assert!(!registry.has_resource_update_handler());
829    }
830
831    #[tokio::test]
832    async fn test_elicitation_handler_registration() {
833        let mut registry = HandlerRegistry::new();
834        let handler = Arc::new(TestElicitationHandler);
835
836        registry.set_elicitation_handler(handler);
837        assert!(registry.has_elicitation_handler());
838    }
839
840    #[tokio::test]
841    async fn test_elicitation_request_handling() {
842        let mut registry = HandlerRegistry::new();
843        let handler = Arc::new(TestElicitationHandler);
844        registry.set_elicitation_handler(handler);
845
846        let request = ElicitationRequest {
847            id: "test-123".to_string(),
848            prompt: "Test prompt".to_string(),
849            schema: json!({"type": "object"}),
850            timeout: None,
851            metadata: HashMap::new(),
852        };
853
854        let response = registry.handle_elicitation(request).await.unwrap();
855        assert_eq!(response.action, ElicitationAction::Accept);
856        assert!(response.content.is_some());
857    }
858
859    #[tokio::test]
860    async fn test_progress_handler_registration() {
861        let mut registry = HandlerRegistry::new();
862        let handler = Arc::new(TestProgressHandler);
863
864        registry.set_progress_handler(handler);
865        assert!(registry.has_progress_handler());
866    }
867
868    #[tokio::test]
869    async fn test_default_handlers() {
870        let decline_handler = DeclineElicitationHandler;
871        let request = ElicitationRequest {
872            id: "test".to_string(),
873            prompt: "Test".to_string(),
874            schema: json!({}),
875            timeout: None,
876            metadata: HashMap::new(),
877        };
878
879        let response = decline_handler.handle_elicitation(request).await.unwrap();
880        assert_eq!(response.action, ElicitationAction::Decline);
881    }
882
883    #[tokio::test]
884    async fn test_handler_error_types() {
885        let error = HandlerError::UserCancelled;
886        assert!(error.to_string().contains("User cancelled"));
887
888        let timeout_error = HandlerError::Timeout {
889            timeout_seconds: 30,
890        };
891        assert!(timeout_error.to_string().contains("30 seconds"));
892    }
893}