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}