Skip to main content

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 the current MCP 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 std::sync::Arc;
33    /// use std::future::Future;
34    /// use std::pin::Pin;
35    ///
36    /// #[derive(Debug)]
37    /// struct MyRootsHandler {
38    ///     project_dir: String,
39    /// }
40    ///
41    /// impl RootsHandler for MyRootsHandler {
42    ///     fn handle_roots_request(&self) -> Pin<Box<dyn Future<Output = HandlerResult<Vec<Root>>> + Send + '_>> {
43    ///         Box::pin(async move {
44    ///             Ok(vec![Root {
45    ///                 uri: format!("file://{}", self.project_dir).into(),
46    ///                 name: Some("My Project".to_string()),
47    ///             }])
48    ///         })
49    ///     }
50    /// }
51    ///
52    /// let mut client = Client::new(StdioTransport::new());
53    /// client.set_roots_handler(Arc::new(MyRootsHandler {
54    ///     project_dir: "/home/user/projects/myproject".to_string(),
55    /// }));
56    /// ```
57    pub fn set_roots_handler(&self, handler: Arc<dyn RootsHandler>) {
58        self.inner.handlers.lock().set_roots_handler(handler);
59    }
60
61    /// Register an elicitation handler for processing user input requests
62    ///
63    /// Elicitation handlers are called when the server needs user input during
64    /// operations. The handler should present the request to the user and
65    /// collect their response according to the provided schema.
66    ///
67    /// # Arguments
68    ///
69    /// * `handler` - The elicitation handler implementation
70    ///
71    /// # Examples
72    ///
73    /// ```rust,no_run
74    /// use turbomcp_client::Client;
75    /// use turbomcp_client::handlers::{ElicitationHandler, ElicitationRequest, ElicitationResponse, ElicitationAction, HandlerResult};
76    /// use turbomcp_transport::stdio::StdioTransport;
77    /// use std::sync::Arc;
78    /// use serde_json::json;
79    /// use std::future::Future;
80    /// use std::pin::Pin;
81    ///
82    /// #[derive(Debug)]
83    /// struct MyElicitationHandler;
84    ///
85    /// impl ElicitationHandler for MyElicitationHandler {
86    ///     fn handle_elicitation(
87    ///         &self,
88    ///         request: ElicitationRequest,
89    ///     ) -> Pin<Box<dyn Future<Output = HandlerResult<ElicitationResponse>> + Send + '_>> {
90    ///         Box::pin(async move {
91    ///             let mut content = std::collections::HashMap::new();
92    ///             content.insert("user_input".to_string(), json!("example"));
93    ///             Ok(ElicitationResponse::accept(content))
94    ///         })
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.handlers.lock().set_elicitation_handler(handler);
103    }
104
105    /// Register a log handler for processing server log messages
106    ///
107    /// Log handlers receive log messages from the server and can route them
108    /// to the client's logging system. This is useful for debugging and
109    /// maintaining a unified log across client and server.
110    ///
111    /// # Arguments
112    ///
113    /// * `handler` - The log handler implementation
114    ///
115    /// # Examples
116    ///
117    /// ```rust,no_run
118    /// use turbomcp_client::Client;
119    /// use turbomcp_client::handlers::{LogHandler, LoggingNotification, HandlerResult};
120    /// use turbomcp_transport::stdio::StdioTransport;
121    /// use std::sync::Arc;
122    /// use std::future::Future;
123    /// use std::pin::Pin;
124    ///
125    /// #[derive(Debug)]
126    /// struct MyLogHandler;
127    ///
128    /// impl LogHandler for MyLogHandler {
129    ///     fn handle_log(&self, log: LoggingNotification) -> Pin<Box<dyn Future<Output = HandlerResult<()>> + Send + '_>> {
130    ///         Box::pin(async move {
131    ///             println!("Server log: {}", log.data);
132    ///             Ok(())
133    ///         })
134    ///     }
135    /// }
136    ///
137    /// let mut client = Client::new(StdioTransport::new());
138    /// client.set_log_handler(Arc::new(MyLogHandler));
139    /// ```
140    pub fn set_log_handler(&self, handler: Arc<dyn LogHandler>) {
141        self.inner.handlers.lock().set_log_handler(handler);
142    }
143
144    /// Register a resource update handler for processing resource change notifications
145    ///
146    /// Resource update handlers receive notifications when subscribed resources
147    /// change on the server. Supports reactive updates to cached data or
148    /// UI refreshes when server-side resources change.
149    ///
150    /// # Arguments
151    ///
152    /// * `handler` - The resource update handler implementation
153    ///
154    /// # Examples
155    ///
156    /// ```rust,no_run
157    /// use turbomcp_client::Client;
158    /// use turbomcp_client::handlers::{ResourceUpdateHandler, ResourceUpdatedNotification, HandlerResult};
159    /// use turbomcp_transport::stdio::StdioTransport;
160    /// use std::sync::Arc;
161    /// use std::future::Future;
162    /// use std::pin::Pin;
163    ///
164    /// #[derive(Debug)]
165    /// struct MyResourceUpdateHandler;
166    ///
167    /// impl ResourceUpdateHandler for MyResourceUpdateHandler {
168    ///     fn handle_resource_update(
169    ///         &self,
170    ///         notification: ResourceUpdatedNotification,
171    ///     ) -> Pin<Box<dyn Future<Output = HandlerResult<()>> + Send + '_>> {
172    ///         Box::pin(async move {
173    ///             println!("Resource updated: {}", notification.uri);
174    ///             Ok(())
175    ///         })
176    ///     }
177    /// }
178    ///
179    /// let mut client = Client::new(StdioTransport::new());
180    /// client.set_resource_update_handler(Arc::new(MyResourceUpdateHandler));
181    /// ```
182    pub fn set_resource_update_handler(&self, handler: Arc<dyn ResourceUpdateHandler>) {
183        self.inner
184            .handlers
185            .lock()
186            .set_resource_update_handler(handler);
187    }
188
189    /// Register a cancellation handler for processing cancellation notifications
190    ///
191    /// Per the current MCP specification, cancellation notifications can be sent
192    /// by the server to indicate that a previously-issued request is being cancelled.
193    ///
194    /// # Arguments
195    ///
196    /// * `handler` - The cancellation handler implementation
197    pub fn set_cancellation_handler(&self, handler: Arc<dyn CancellationHandler>) {
198        self.inner.handlers.lock().set_cancellation_handler(handler);
199    }
200
201    /// Register a resource list changed handler
202    ///
203    /// This handler is called when the server's available resource list changes.
204    ///
205    /// # Arguments
206    ///
207    /// * `handler` - The resource list changed handler implementation
208    pub fn set_resource_list_changed_handler(&self, handler: Arc<dyn ResourceListChangedHandler>) {
209        self.inner
210            .handlers
211            .lock()
212            .set_resource_list_changed_handler(handler);
213    }
214
215    /// Register a prompt list changed handler
216    ///
217    /// This handler is called when the server's available prompt list changes.
218    ///
219    /// # Arguments
220    ///
221    /// * `handler` - The prompt list changed handler implementation
222    pub fn set_prompt_list_changed_handler(&self, handler: Arc<dyn PromptListChangedHandler>) {
223        self.inner
224            .handlers
225            .lock()
226            .set_prompt_list_changed_handler(handler);
227    }
228
229    /// Register a tool list changed handler
230    ///
231    /// This handler is called when the server's available tool list changes.
232    ///
233    /// # Arguments
234    ///
235    /// * `handler` - The tool list changed handler implementation
236    pub fn set_tool_list_changed_handler(&self, handler: Arc<dyn ToolListChangedHandler>) {
237        self.inner
238            .handlers
239            .lock()
240            .set_tool_list_changed_handler(handler);
241    }
242
243    /// Check if a roots handler is registered
244    #[must_use]
245    pub fn has_roots_handler(&self) -> bool {
246        self.inner.handlers.lock().has_roots_handler()
247    }
248
249    /// Check if an elicitation handler is registered
250    #[must_use]
251    pub fn has_elicitation_handler(&self) -> bool {
252        self.inner.handlers.lock().has_elicitation_handler()
253    }
254
255    /// Check if a log handler is registered
256    #[must_use]
257    pub fn has_log_handler(&self) -> bool {
258        self.inner.handlers.lock().has_log_handler()
259    }
260
261    /// Check if a resource update handler is registered
262    #[must_use]
263    pub fn has_resource_update_handler(&self) -> bool {
264        self.inner.handlers.lock().has_resource_update_handler()
265    }
266}