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}