Skip to main content

mcpkit_rs/handler/
client.rs

1pub mod progress;
2use std::sync::Arc;
3
4use crate::{
5    error::ErrorData as McpError,
6    model::*,
7    service::{NotificationContext, RequestContext, RoleClient, Service, ServiceRole},
8};
9
10impl<H: ClientHandler> Service<RoleClient> for H {
11    async fn handle_request(
12        &self,
13        request: <RoleClient as ServiceRole>::PeerReq,
14        context: RequestContext<RoleClient>,
15    ) -> Result<<RoleClient as ServiceRole>::Resp, McpError> {
16        match request {
17            ServerRequest::PingRequest(_) => self.ping(context).await.map(ClientResult::empty),
18            ServerRequest::CreateMessageRequest(request) => self
19                .create_message(request.params, context)
20                .await
21                .map(Box::new)
22                .map(ClientResult::CreateMessageResult),
23            ServerRequest::ListRootsRequest(_) => self
24                .list_roots(context)
25                .await
26                .map(ClientResult::ListRootsResult),
27            ServerRequest::CreateElicitationRequest(request) => self
28                .create_elicitation(request.params, context)
29                .await
30                .map(ClientResult::CreateElicitationResult),
31            ServerRequest::CustomRequest(request) => self
32                .on_custom_request(request, context)
33                .await
34                .map(ClientResult::CustomResult),
35        }
36    }
37
38    async fn handle_notification(
39        &self,
40        notification: <RoleClient as ServiceRole>::PeerNot,
41        context: NotificationContext<RoleClient>,
42    ) -> Result<(), McpError> {
43        match notification {
44            ServerNotification::CancelledNotification(notification) => {
45                self.on_cancelled(notification.params, context).await
46            }
47            ServerNotification::ProgressNotification(notification) => {
48                self.on_progress(notification.params, context).await
49            }
50            ServerNotification::LoggingMessageNotification(notification) => {
51                self.on_logging_message(notification.params, context).await
52            }
53            ServerNotification::ResourceUpdatedNotification(notification) => {
54                self.on_resource_updated(notification.params, context).await
55            }
56            ServerNotification::ResourceListChangedNotification(_notification_no_param) => {
57                self.on_resource_list_changed(context).await
58            }
59            ServerNotification::ToolListChangedNotification(_notification_no_param) => {
60                self.on_tool_list_changed(context).await
61            }
62            ServerNotification::PromptListChangedNotification(_notification_no_param) => {
63                self.on_prompt_list_changed(context).await
64            }
65            ServerNotification::CustomNotification(notification) => {
66                self.on_custom_notification(notification, context).await
67            }
68        };
69        Ok(())
70    }
71
72    fn get_info(&self) -> <RoleClient as ServiceRole>::Info {
73        self.get_info()
74    }
75}
76
77#[allow(unused_variables)]
78pub trait ClientHandler: Sized + Send + Sync + 'static {
79    fn ping(
80        &self,
81        context: RequestContext<RoleClient>,
82    ) -> impl Future<Output = Result<(), McpError>> + Send + '_ {
83        std::future::ready(Ok(()))
84    }
85
86    fn create_message(
87        &self,
88        params: CreateMessageRequestParams,
89        context: RequestContext<RoleClient>,
90    ) -> impl Future<Output = Result<CreateMessageResult, McpError>> + Send + '_ {
91        std::future::ready(Err(
92            McpError::method_not_found::<CreateMessageRequestMethod>(),
93        ))
94    }
95
96    fn list_roots(
97        &self,
98        context: RequestContext<RoleClient>,
99    ) -> impl Future<Output = Result<ListRootsResult, McpError>> + Send + '_ {
100        std::future::ready(Ok(ListRootsResult::default()))
101    }
102
103    /// Handle an elicitation request from a server asking for user input.
104    ///
105    /// This method is called when a server needs interactive input from the user
106    /// during tool execution. Implementations should present the message to the user,
107    /// collect their input according to the requested schema, and return the result.
108    ///
109    /// # Arguments
110    /// * `request` - The elicitation request with message and schema
111    /// * `context` - The request context
112    ///
113    /// # Returns
114    /// The user's response including action (accept/decline/cancel) and optional data
115    ///
116    /// # Default Behavior
117    /// The default implementation automatically declines all elicitation requests.
118    /// Real clients should override this to provide user interaction.
119    fn create_elicitation(
120        &self,
121        request: CreateElicitationRequestParams,
122        context: RequestContext<RoleClient>,
123    ) -> impl Future<Output = Result<CreateElicitationResult, McpError>> + Send + '_ {
124        // Default implementation declines all requests - real clients should override this
125        let _ = (request, context);
126        std::future::ready(Ok(CreateElicitationResult {
127            action: ElicitationAction::Decline,
128            content: None,
129        }))
130    }
131
132    fn on_custom_request(
133        &self,
134        request: CustomRequest,
135        context: RequestContext<RoleClient>,
136    ) -> impl Future<Output = Result<CustomResult, McpError>> + Send + '_ {
137        let CustomRequest { method, .. } = request;
138        let _ = context;
139        std::future::ready(Err(McpError::new(
140            ErrorCode::METHOD_NOT_FOUND,
141            method,
142            None,
143        )))
144    }
145
146    fn on_cancelled(
147        &self,
148        params: CancelledNotificationParam,
149        context: NotificationContext<RoleClient>,
150    ) -> impl Future<Output = ()> + Send + '_ {
151        std::future::ready(())
152    }
153    fn on_progress(
154        &self,
155        params: ProgressNotificationParam,
156        context: NotificationContext<RoleClient>,
157    ) -> impl Future<Output = ()> + Send + '_ {
158        std::future::ready(())
159    }
160    fn on_logging_message(
161        &self,
162        params: LoggingMessageNotificationParam,
163        context: NotificationContext<RoleClient>,
164    ) -> impl Future<Output = ()> + Send + '_ {
165        std::future::ready(())
166    }
167    fn on_resource_updated(
168        &self,
169        params: ResourceUpdatedNotificationParam,
170        context: NotificationContext<RoleClient>,
171    ) -> impl Future<Output = ()> + Send + '_ {
172        std::future::ready(())
173    }
174    fn on_resource_list_changed(
175        &self,
176        context: NotificationContext<RoleClient>,
177    ) -> impl Future<Output = ()> + Send + '_ {
178        std::future::ready(())
179    }
180    fn on_tool_list_changed(
181        &self,
182        context: NotificationContext<RoleClient>,
183    ) -> impl Future<Output = ()> + Send + '_ {
184        std::future::ready(())
185    }
186    fn on_prompt_list_changed(
187        &self,
188        context: NotificationContext<RoleClient>,
189    ) -> impl Future<Output = ()> + Send + '_ {
190        std::future::ready(())
191    }
192    fn on_custom_notification(
193        &self,
194        notification: CustomNotification,
195        context: NotificationContext<RoleClient>,
196    ) -> impl Future<Output = ()> + Send + '_ {
197        let _ = (notification, context);
198        std::future::ready(())
199    }
200
201    fn get_info(&self) -> ClientInfo {
202        ClientInfo::default()
203    }
204}
205
206/// Do nothing, with default client info.
207impl ClientHandler for () {}
208
209/// Do nothing, with a specific client info.
210impl ClientHandler for ClientInfo {
211    fn get_info(&self) -> ClientInfo {
212        self.clone()
213    }
214}
215
216macro_rules! impl_client_handler_for_wrapper {
217    ($wrapper:ident) => {
218        impl<T: ClientHandler> ClientHandler for $wrapper<T> {
219            fn ping(
220                &self,
221                context: RequestContext<RoleClient>,
222            ) -> impl Future<Output = Result<(), McpError>> + Send + '_ {
223                (**self).ping(context)
224            }
225
226            fn create_message(
227                &self,
228                params: CreateMessageRequestParams,
229                context: RequestContext<RoleClient>,
230            ) -> impl Future<Output = Result<CreateMessageResult, McpError>> + Send + '_ {
231                (**self).create_message(params, context)
232            }
233
234            fn list_roots(
235                &self,
236                context: RequestContext<RoleClient>,
237            ) -> impl Future<Output = Result<ListRootsResult, McpError>> + Send + '_ {
238                (**self).list_roots(context)
239            }
240
241            fn create_elicitation(
242                &self,
243                request: CreateElicitationRequestParams,
244                context: RequestContext<RoleClient>,
245            ) -> impl Future<Output = Result<CreateElicitationResult, McpError>> + Send + '_ {
246                (**self).create_elicitation(request, context)
247            }
248
249            fn on_custom_request(
250                &self,
251                request: CustomRequest,
252                context: RequestContext<RoleClient>,
253            ) -> impl Future<Output = Result<CustomResult, McpError>> + Send + '_ {
254                (**self).on_custom_request(request, context)
255            }
256
257            fn on_cancelled(
258                &self,
259                params: CancelledNotificationParam,
260                context: NotificationContext<RoleClient>,
261            ) -> impl Future<Output = ()> + Send + '_ {
262                (**self).on_cancelled(params, context)
263            }
264
265            fn on_progress(
266                &self,
267                params: ProgressNotificationParam,
268                context: NotificationContext<RoleClient>,
269            ) -> impl Future<Output = ()> + Send + '_ {
270                (**self).on_progress(params, context)
271            }
272
273            fn on_logging_message(
274                &self,
275                params: LoggingMessageNotificationParam,
276                context: NotificationContext<RoleClient>,
277            ) -> impl Future<Output = ()> + Send + '_ {
278                (**self).on_logging_message(params, context)
279            }
280
281            fn on_resource_updated(
282                &self,
283                params: ResourceUpdatedNotificationParam,
284                context: NotificationContext<RoleClient>,
285            ) -> impl Future<Output = ()> + Send + '_ {
286                (**self).on_resource_updated(params, context)
287            }
288
289            fn on_resource_list_changed(
290                &self,
291                context: NotificationContext<RoleClient>,
292            ) -> impl Future<Output = ()> + Send + '_ {
293                (**self).on_resource_list_changed(context)
294            }
295
296            fn on_tool_list_changed(
297                &self,
298                context: NotificationContext<RoleClient>,
299            ) -> impl Future<Output = ()> + Send + '_ {
300                (**self).on_tool_list_changed(context)
301            }
302
303            fn on_prompt_list_changed(
304                &self,
305                context: NotificationContext<RoleClient>,
306            ) -> impl Future<Output = ()> + Send + '_ {
307                (**self).on_prompt_list_changed(context)
308            }
309
310            fn on_custom_notification(
311                &self,
312                notification: CustomNotification,
313                context: NotificationContext<RoleClient>,
314            ) -> impl Future<Output = ()> + Send + '_ {
315                (**self).on_custom_notification(notification, context)
316            }
317
318            fn get_info(&self) -> ClientInfo {
319                (**self).get_info()
320            }
321        }
322    };
323}
324
325impl_client_handler_for_wrapper!(Box);
326impl_client_handler_for_wrapper!(Arc);