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, 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 log handler for processing server log messages
110    ///
111    /// Log handlers receive log messages from the server and can route them
112    /// to the client's logging system. This is useful for debugging and
113    /// maintaining a unified log across client and server.
114    ///
115    /// # Arguments
116    ///
117    /// * `handler` - The log handler implementation
118    ///
119    /// # Examples
120    ///
121    /// ```rust,no_run
122    /// use turbomcp_client::Client;
123    /// use turbomcp_client::handlers::{LogHandler, LoggingNotification, HandlerResult};
124    /// use turbomcp_transport::stdio::StdioTransport;
125    /// use async_trait::async_trait;
126    /// use std::sync::Arc;
127    ///
128    /// #[derive(Debug)]
129    /// struct MyLogHandler;
130    ///
131    /// #[async_trait]
132    /// impl LogHandler for MyLogHandler {
133    ///     async fn handle_log(&self, log: LoggingNotification) -> HandlerResult<()> {
134    ///         println!("Server log: {}", log.data);
135    ///         Ok(())
136    ///     }
137    /// }
138    ///
139    /// let mut client = Client::new(StdioTransport::new());
140    /// client.set_log_handler(Arc::new(MyLogHandler));
141    /// ```
142    pub fn set_log_handler(&self, handler: Arc<dyn LogHandler>) {
143        self.inner
144            .handlers
145            .lock()
146            .expect("handlers mutex poisoned")
147            .set_log_handler(handler);
148    }
149
150    /// Register a resource update handler for processing resource change notifications
151    ///
152    /// Resource update handlers receive notifications when subscribed resources
153    /// change on the server. Supports reactive updates to cached data or
154    /// UI refreshes when server-side resources change.
155    ///
156    /// # Arguments
157    ///
158    /// * `handler` - The resource update handler implementation
159    ///
160    /// # Examples
161    ///
162    /// ```rust,no_run
163    /// use turbomcp_client::Client;
164    /// use turbomcp_client::handlers::{ResourceUpdateHandler, ResourceUpdatedNotification, HandlerResult};
165    /// use turbomcp_transport::stdio::StdioTransport;
166    /// use async_trait::async_trait;
167    /// use std::sync::Arc;
168    ///
169    /// #[derive(Debug)]
170    /// struct MyResourceUpdateHandler;
171    ///
172    /// #[async_trait]
173    /// impl ResourceUpdateHandler for MyResourceUpdateHandler {
174    ///     async fn handle_resource_update(
175    ///         &self,
176    ///         notification: ResourceUpdatedNotification,
177    ///     ) -> HandlerResult<()> {
178    ///         println!("Resource updated: {}", notification.uri);
179    ///         Ok(())
180    ///     }
181    /// }
182    ///
183    /// let mut client = Client::new(StdioTransport::new());
184    /// client.set_resource_update_handler(Arc::new(MyResourceUpdateHandler));
185    /// ```
186    pub fn set_resource_update_handler(&self, handler: Arc<dyn ResourceUpdateHandler>) {
187        self.inner
188            .handlers
189            .lock()
190            .expect("handlers mutex poisoned")
191            .set_resource_update_handler(handler);
192    }
193
194    /// Register a cancellation handler for processing cancellation notifications
195    ///
196    /// Per MCP 2025-06-18 specification, cancellation notifications can be sent
197    /// by the server to indicate that a previously-issued request is being cancelled.
198    ///
199    /// # Arguments
200    ///
201    /// * `handler` - The cancellation handler implementation
202    pub fn set_cancellation_handler(&self, handler: Arc<dyn CancellationHandler>) {
203        self.inner
204            .handlers
205            .lock()
206            .expect("handlers mutex poisoned")
207            .set_cancellation_handler(handler);
208    }
209
210    /// Register a resource list changed handler
211    ///
212    /// This handler is called when the server's available resource list changes.
213    ///
214    /// # Arguments
215    ///
216    /// * `handler` - The resource list changed handler implementation
217    pub fn set_resource_list_changed_handler(&self, handler: Arc<dyn ResourceListChangedHandler>) {
218        self.inner
219            .handlers
220            .lock()
221            .expect("handlers mutex poisoned")
222            .set_resource_list_changed_handler(handler);
223    }
224
225    /// Register a prompt list changed handler
226    ///
227    /// This handler is called when the server's available prompt list changes.
228    ///
229    /// # Arguments
230    ///
231    /// * `handler` - The prompt list changed handler implementation
232    pub fn set_prompt_list_changed_handler(&self, handler: Arc<dyn PromptListChangedHandler>) {
233        self.inner
234            .handlers
235            .lock()
236            .expect("handlers mutex poisoned")
237            .set_prompt_list_changed_handler(handler);
238    }
239
240    /// Register a tool list changed handler
241    ///
242    /// This handler is called when the server's available tool list changes.
243    ///
244    /// # Arguments
245    ///
246    /// * `handler` - The tool list changed handler implementation
247    pub fn set_tool_list_changed_handler(&self, handler: Arc<dyn ToolListChangedHandler>) {
248        self.inner
249            .handlers
250            .lock()
251            .expect("handlers mutex poisoned")
252            .set_tool_list_changed_handler(handler);
253    }
254
255    /// Check if a roots handler is registered
256    #[must_use]
257    pub fn has_roots_handler(&self) -> bool {
258        self.inner
259            .handlers
260            .lock()
261            .expect("handlers mutex poisoned")
262            .has_roots_handler()
263    }
264
265    /// Check if an elicitation handler is registered
266    #[must_use]
267    pub fn has_elicitation_handler(&self) -> bool {
268        self.inner
269            .handlers
270            .lock()
271            .expect("handlers mutex poisoned")
272            .has_elicitation_handler()
273    }
274
275    /// Check if a log handler is registered
276    #[must_use]
277    pub fn has_log_handler(&self) -> bool {
278        self.inner
279            .handlers
280            .lock()
281            .expect("handlers mutex poisoned")
282            .has_log_handler()
283    }
284
285    /// Check if a resource update handler is registered
286    #[must_use]
287    pub fn has_resource_update_handler(&self) -> bool {
288        self.inner
289            .handlers
290            .lock()
291            .expect("handlers mutex poisoned")
292            .has_resource_update_handler()
293    }
294}