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    CancellationHandler, ElicitationHandler, LogHandler, ProgressHandler, PromptListChangedHandler,
8    ResourceListChangedHandler, ResourceUpdateHandler, RootsHandler, ToolListChangedHandler,
9};
10use std::sync::Arc;
11
12impl<T: turbomcp_transport::Transport + 'static> super::super::core::Client<T> {
13    /// Register a roots handler for responding to server filesystem root requests
14    ///
15    /// Roots handlers respond to `roots/list` requests from servers (SERVER->CLIENT).
16    /// Per MCP 2025-06-18 specification, servers ask clients what filesystem roots
17    /// they have access to. This is commonly used when servers need to understand
18    /// their operating boundaries, such as which repositories or project directories
19    /// they can access.
20    ///
21    /// # Arguments
22    ///
23    /// * `handler` - The roots handler implementation
24    ///
25    /// # Examples
26    ///
27    /// ```rust,no_run
28    /// use turbomcp_client::Client;
29    /// use turbomcp_client::handlers::{RootsHandler, HandlerResult};
30    /// use turbomcp_protocol::types::Root;
31    /// use turbomcp_transport::stdio::StdioTransport;
32    /// use async_trait::async_trait;
33    /// use std::sync::Arc;
34    ///
35    /// #[derive(Debug)]
36    /// struct MyRootsHandler {
37    ///     project_dir: String,
38    /// }
39    ///
40    /// #[async_trait]
41    /// impl RootsHandler for MyRootsHandler {
42    ///     async fn handle_roots_request(&self) -> HandlerResult<Vec<Root>> {
43    ///         Ok(vec![Root {
44    ///             uri: format!("file://{}", self.project_dir).into(),
45    ///             name: Some("My Project".to_string()),
46    ///         }])
47    ///     }
48    /// }
49    ///
50    /// let mut client = Client::new(StdioTransport::new());
51    /// client.set_roots_handler(Arc::new(MyRootsHandler {
52    ///     project_dir: "/home/user/projects/myproject".to_string(),
53    /// }));
54    /// ```
55    pub fn set_roots_handler(&self, handler: Arc<dyn RootsHandler>) {
56        self.inner
57            .handlers
58            .lock()
59            .expect("handlers mutex poisoned")
60            .set_roots_handler(handler);
61    }
62
63    /// Register an elicitation handler for processing user input requests
64    ///
65    /// Elicitation handlers are called when the server needs user input during
66    /// operations. The handler should present the request to the user and
67    /// collect their response according to the provided schema.
68    ///
69    /// # Arguments
70    ///
71    /// * `handler` - The elicitation handler implementation
72    ///
73    /// # Examples
74    ///
75    /// ```rust,no_run
76    /// use turbomcp_client::Client;
77    /// use turbomcp_client::handlers::{ElicitationHandler, ElicitationRequest, ElicitationResponse, ElicitationAction, HandlerResult};
78    /// use turbomcp_transport::stdio::StdioTransport;
79    /// use async_trait::async_trait;
80    /// use std::sync::Arc;
81    /// use serde_json::json;
82    ///
83    /// #[derive(Debug)]
84    /// struct MyElicitationHandler;
85    ///
86    /// #[async_trait]
87    /// impl ElicitationHandler for MyElicitationHandler {
88    ///     async fn handle_elicitation(
89    ///         &self,
90    ///         request: ElicitationRequest,
91    ///     ) -> HandlerResult<ElicitationResponse> {
92    ///         let mut content = std::collections::HashMap::new();
93    ///         content.insert("user_input".to_string(), json!("example"));
94    ///         Ok(ElicitationResponse::accept(content))
95    ///     }
96    /// }
97    ///
98    /// let mut client = Client::new(StdioTransport::new());
99    /// client.set_elicitation_handler(Arc::new(MyElicitationHandler));
100    /// ```
101    pub fn set_elicitation_handler(&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.set_progress_handler(Arc::new(MyProgressHandler));
141    /// ```
142    pub fn set_progress_handler(&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, LoggingNotification, 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: LoggingNotification) -> HandlerResult<()> {
175    ///         println!("Server log: {}", log.data);
176    ///         Ok(())
177    ///     }
178    /// }
179    ///
180    /// let mut client = Client::new(StdioTransport::new());
181    /// client.set_log_handler(Arc::new(MyLogHandler));
182    /// ```
183    pub fn set_log_handler(&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, ResourceUpdatedNotification, 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: ResourceUpdatedNotification,
218    ///     ) -> HandlerResult<()> {
219    ///         println!("Resource updated: {}", notification.uri);
220    ///         Ok(())
221    ///     }
222    /// }
223    ///
224    /// let mut client = Client::new(StdioTransport::new());
225    /// client.set_resource_update_handler(Arc::new(MyResourceUpdateHandler));
226    /// ```
227    pub fn set_resource_update_handler(&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    /// Register a cancellation handler for processing cancellation notifications
236    ///
237    /// Per MCP 2025-06-18 specification, cancellation notifications can be sent
238    /// by the server to indicate that a previously-issued request is being cancelled.
239    ///
240    /// # Arguments
241    ///
242    /// * `handler` - The cancellation handler implementation
243    pub fn set_cancellation_handler(&self, handler: Arc<dyn CancellationHandler>) {
244        self.inner
245            .handlers
246            .lock()
247            .expect("handlers mutex poisoned")
248            .set_cancellation_handler(handler);
249    }
250
251    /// Register a resource list changed handler
252    ///
253    /// This handler is called when the server's available resource list changes.
254    ///
255    /// # Arguments
256    ///
257    /// * `handler` - The resource list changed handler implementation
258    pub fn set_resource_list_changed_handler(&self, handler: Arc<dyn ResourceListChangedHandler>) {
259        self.inner
260            .handlers
261            .lock()
262            .expect("handlers mutex poisoned")
263            .set_resource_list_changed_handler(handler);
264    }
265
266    /// Register a prompt list changed handler
267    ///
268    /// This handler is called when the server's available prompt list changes.
269    ///
270    /// # Arguments
271    ///
272    /// * `handler` - The prompt list changed handler implementation
273    pub fn set_prompt_list_changed_handler(&self, handler: Arc<dyn PromptListChangedHandler>) {
274        self.inner
275            .handlers
276            .lock()
277            .expect("handlers mutex poisoned")
278            .set_prompt_list_changed_handler(handler);
279    }
280
281    /// Register a tool list changed handler
282    ///
283    /// This handler is called when the server's available tool list changes.
284    ///
285    /// # Arguments
286    ///
287    /// * `handler` - The tool list changed handler implementation
288    pub fn set_tool_list_changed_handler(&self, handler: Arc<dyn ToolListChangedHandler>) {
289        self.inner
290            .handlers
291            .lock()
292            .expect("handlers mutex poisoned")
293            .set_tool_list_changed_handler(handler);
294    }
295
296    /// Check if a roots handler is registered
297    pub fn has_roots_handler(&self) -> bool {
298        self.inner
299            .handlers
300            .lock()
301            .expect("handlers mutex poisoned")
302            .has_roots_handler()
303    }
304
305    /// Check if an elicitation handler is registered
306    pub fn has_elicitation_handler(&self) -> bool {
307        self.inner
308            .handlers
309            .lock()
310            .expect("handlers mutex poisoned")
311            .has_elicitation_handler()
312    }
313
314    /// Check if a progress handler is registered
315    pub fn has_progress_handler(&self) -> bool {
316        self.inner
317            .handlers
318            .lock()
319            .expect("handlers mutex poisoned")
320            .has_progress_handler()
321    }
322
323    /// Check if a log handler is registered
324    pub fn has_log_handler(&self) -> bool {
325        self.inner
326            .handlers
327            .lock()
328            .expect("handlers mutex poisoned")
329            .has_log_handler()
330    }
331
332    /// Check if a resource update handler is registered
333    pub fn has_resource_update_handler(&self) -> bool {
334        self.inner
335            .handlers
336            .lock()
337            .expect("handlers mutex poisoned")
338            .has_resource_update_handler()
339    }
340}