turbomcp_client/client/operations/
handlers.rs

1//! Handler registration operations for MCP client
2//!
3//! This module provides methods for registering and managing various event handlers
4//! that process server-initiated operations and notifications.
5
6use crate::handlers::{
7    ElicitationHandler, LogHandler, ProgressHandler, ResourceUpdateHandler, RootsHandler,
8};
9use std::sync::Arc;
10
11impl<T: turbomcp_transport::Transport> super::super::core::Client<T> {
12    /// Register a roots handler for responding to server filesystem root requests
13    ///
14    /// Roots handlers respond to `roots/list` requests from servers (SERVER->CLIENT).
15    /// Per MCP 2025-06-18 specification, servers ask clients what filesystem roots
16    /// they have access to. This is commonly used when servers need to understand
17    /// their operating boundaries, such as which repositories or project directories
18    /// they can access.
19    ///
20    /// # Arguments
21    ///
22    /// * `handler` - The roots handler implementation
23    ///
24    /// # Examples
25    ///
26    /// ```rust,no_run
27    /// use turbomcp_client::Client;
28    /// use turbomcp_client::handlers::{RootsHandler, HandlerResult};
29    /// use turbomcp_protocol::types::Root;
30    /// use turbomcp_transport::stdio::StdioTransport;
31    /// use async_trait::async_trait;
32    /// use std::sync::Arc;
33    ///
34    /// #[derive(Debug)]
35    /// struct MyRootsHandler {
36    ///     project_dir: String,
37    /// }
38    ///
39    /// #[async_trait]
40    /// impl RootsHandler for MyRootsHandler {
41    ///     async fn handle_roots_request(&self) -> HandlerResult<Vec<Root>> {
42    ///         Ok(vec![Root {
43    ///             uri: format!("file://{}", self.project_dir).into(),
44    ///             name: Some("My Project".to_string()),
45    ///         }])
46    ///     }
47    /// }
48    ///
49    /// let mut client = Client::new(StdioTransport::new());
50    /// client.on_roots(Arc::new(MyRootsHandler {
51    ///     project_dir: "/home/user/projects/myproject".to_string(),
52    /// }));
53    /// ```
54    pub fn on_roots(&self, handler: Arc<dyn RootsHandler>) {
55        self.inner
56            .handlers
57            .lock()
58            .expect("handlers mutex poisoned")
59            .set_roots_handler(handler);
60    }
61
62    /// Register an elicitation handler for processing user input requests
63    ///
64    /// Elicitation handlers are called when the server needs user input during
65    /// operations. The handler should present the request to the user and
66    /// collect their response according to the provided schema.
67    ///
68    /// # Arguments
69    ///
70    /// * `handler` - The elicitation handler implementation
71    ///
72    /// # Examples
73    ///
74    /// ```rust,no_run
75    /// use turbomcp_client::Client;
76    /// use turbomcp_client::handlers::{ElicitationHandler, ElicitationRequest, ElicitationResponse, ElicitationAction, HandlerResult};
77    /// use turbomcp_transport::stdio::StdioTransport;
78    /// use async_trait::async_trait;
79    /// use std::sync::Arc;
80    /// use serde_json::json;
81    ///
82    /// #[derive(Debug)]
83    /// struct MyElicitationHandler;
84    ///
85    /// #[async_trait]
86    /// impl ElicitationHandler for MyElicitationHandler {
87    ///     async fn handle_elicitation(
88    ///         &self,
89    ///         request: ElicitationRequest,
90    ///     ) -> HandlerResult<ElicitationResponse> {
91    ///         Ok(ElicitationResponse {
92    ///             action: ElicitationAction::Accept,
93    ///             content: Some(json!({"user_input": "example"})),
94    ///         })
95    ///     }
96    /// }
97    ///
98    /// let mut client = Client::new(StdioTransport::new());
99    /// client.on_elicitation(Arc::new(MyElicitationHandler));
100    /// ```
101    pub fn on_elicitation(&self, handler: Arc<dyn ElicitationHandler>) {
102        self.inner
103            .handlers
104            .lock()
105            .expect("handlers mutex poisoned")
106            .set_elicitation_handler(handler);
107    }
108
109    /// Register a progress handler for processing operation progress updates
110    ///
111    /// Progress handlers receive notifications about long-running server operations.
112    /// Display progress bars, status updates, or other
113    /// feedback to users.
114    ///
115    /// # Arguments
116    ///
117    /// * `handler` - The progress handler implementation
118    ///
119    /// # Examples
120    ///
121    /// ```rust,no_run
122    /// use turbomcp_client::Client;
123    /// use turbomcp_client::handlers::{ProgressHandler, ProgressNotification, HandlerResult};
124    /// use turbomcp_transport::stdio::StdioTransport;
125    /// use async_trait::async_trait;
126    /// use std::sync::Arc;
127    ///
128    /// #[derive(Debug)]
129    /// struct MyProgressHandler;
130    ///
131    /// #[async_trait]
132    /// impl ProgressHandler for MyProgressHandler {
133    ///     async fn handle_progress(&self, notification: ProgressNotification) -> HandlerResult<()> {
134    ///         println!("Progress: {:?}", notification);
135    ///         Ok(())
136    ///     }
137    /// }
138    ///
139    /// let mut client = Client::new(StdioTransport::new());
140    /// client.on_progress(Arc::new(MyProgressHandler));
141    /// ```
142    pub fn on_progress(&self, handler: Arc<dyn ProgressHandler>) {
143        self.inner
144            .handlers
145            .lock()
146            .expect("handlers mutex poisoned")
147            .set_progress_handler(handler);
148    }
149
150    /// Register a log handler for processing server log messages
151    ///
152    /// Log handlers receive log messages from the server and can route them
153    /// to the client's logging system. This is useful for debugging and
154    /// maintaining a unified log across client and server.
155    ///
156    /// # Arguments
157    ///
158    /// * `handler` - The log handler implementation
159    ///
160    /// # Examples
161    ///
162    /// ```rust,no_run
163    /// use turbomcp_client::Client;
164    /// use turbomcp_client::handlers::{LogHandler, LogMessage, HandlerResult};
165    /// use turbomcp_transport::stdio::StdioTransport;
166    /// use async_trait::async_trait;
167    /// use std::sync::Arc;
168    ///
169    /// #[derive(Debug)]
170    /// struct MyLogHandler;
171    ///
172    /// #[async_trait]
173    /// impl LogHandler for MyLogHandler {
174    ///     async fn handle_log(&self, log: LogMessage) -> HandlerResult<()> {
175    ///         println!("Server log: {}", log.message);
176    ///         Ok(())
177    ///     }
178    /// }
179    ///
180    /// let mut client = Client::new(StdioTransport::new());
181    /// client.on_log(Arc::new(MyLogHandler));
182    /// ```
183    pub fn on_log(&self, handler: Arc<dyn LogHandler>) {
184        self.inner
185            .handlers
186            .lock()
187            .expect("handlers mutex poisoned")
188            .set_log_handler(handler);
189    }
190
191    /// Register a resource update handler for processing resource change notifications
192    ///
193    /// Resource update handlers receive notifications when subscribed resources
194    /// change on the server. Supports reactive updates to cached data or
195    /// UI refreshes when server-side resources change.
196    ///
197    /// # Arguments
198    ///
199    /// * `handler` - The resource update handler implementation
200    ///
201    /// # Examples
202    ///
203    /// ```rust,no_run
204    /// use turbomcp_client::Client;
205    /// use turbomcp_client::handlers::{ResourceUpdateHandler, ResourceUpdateNotification, HandlerResult};
206    /// use turbomcp_transport::stdio::StdioTransport;
207    /// use async_trait::async_trait;
208    /// use std::sync::Arc;
209    ///
210    /// #[derive(Debug)]
211    /// struct MyResourceUpdateHandler;
212    ///
213    /// #[async_trait]
214    /// impl ResourceUpdateHandler for MyResourceUpdateHandler {
215    ///     async fn handle_resource_update(
216    ///         &self,
217    ///         notification: ResourceUpdateNotification,
218    ///     ) -> HandlerResult<()> {
219    ///         println!("Resource updated: {}", notification.uri);
220    ///         Ok(())
221    ///     }
222    /// }
223    ///
224    /// let mut client = Client::new(StdioTransport::new());
225    /// client.on_resource_update(Arc::new(MyResourceUpdateHandler));
226    /// ```
227    pub fn on_resource_update(&self, handler: Arc<dyn ResourceUpdateHandler>) {
228        self.inner
229            .handlers
230            .lock()
231            .expect("handlers mutex poisoned")
232            .set_resource_update_handler(handler);
233    }
234
235    /// Check if a roots handler is registered
236    pub fn has_roots_handler(&self) -> bool {
237        self.inner
238            .handlers
239            .lock()
240            .expect("handlers mutex poisoned")
241            .has_roots_handler()
242    }
243
244    /// Check if an elicitation handler is registered
245    pub fn has_elicitation_handler(&self) -> bool {
246        self.inner
247            .handlers
248            .lock()
249            .expect("handlers mutex poisoned")
250            .has_elicitation_handler()
251    }
252
253    /// Check if a progress handler is registered
254    pub fn has_progress_handler(&self) -> bool {
255        self.inner
256            .handlers
257            .lock()
258            .expect("handlers mutex poisoned")
259            .has_progress_handler()
260    }
261
262    /// Check if a log handler is registered
263    pub fn has_log_handler(&self) -> bool {
264        self.inner
265            .handlers
266            .lock()
267            .expect("handlers mutex poisoned")
268            .has_log_handler()
269    }
270
271    /// Check if a resource update handler is registered
272    pub fn has_resource_update_handler(&self) -> bool {
273        self.inner
274            .handlers
275            .lock()
276            .expect("handlers mutex poisoned")
277            .has_resource_update_handler()
278    }
279}