Skip to main content

rmcp/handler/
client.rs

1// Sampling/Roots/Logging are SEP-2577-deprecated; internal references are expected.
2#![expect(deprecated)]
3pub mod progress;
4use std::sync::Arc;
5
6use crate::{
7    error::ErrorData as McpError,
8    model::*,
9    service::{
10        MaybeSendFuture, NotificationContext, RequestContext, RoleClient, Service, ServiceRole,
11    },
12};
13
14impl<H: ClientHandler> Service<RoleClient> for H {
15    async fn handle_request(
16        &self,
17        request: <RoleClient as ServiceRole>::PeerReq,
18        context: RequestContext<RoleClient>,
19    ) -> Result<<RoleClient as ServiceRole>::Resp, McpError> {
20        match request {
21            ServerRequest::PingRequest(_) => self.ping(context).await.map(ClientResult::empty),
22            ServerRequest::CreateMessageRequest(request) => self
23                .create_message(request.params, context)
24                .await
25                .map(Box::new)
26                .map(ClientResult::CreateMessageResult),
27            ServerRequest::ListRootsRequest(_) => self
28                .list_roots(context)
29                .await
30                .map(ClientResult::ListRootsResult),
31            ServerRequest::ElicitRequest(request) => self
32                .create_elicitation(request.params, context)
33                .await
34                .map(ClientResult::ElicitResult),
35            ServerRequest::CustomRequest(request) => self
36                .on_custom_request(request, context)
37                .await
38                .map(ClientResult::CustomResult),
39        }
40    }
41
42    async fn handle_notification(
43        &self,
44        notification: <RoleClient as ServiceRole>::PeerNot,
45        context: NotificationContext<RoleClient>,
46    ) -> Result<(), McpError> {
47        match notification {
48            ServerNotification::CancelledNotification(notification) => {
49                self.on_cancelled(notification.params, context).await
50            }
51            ServerNotification::ProgressNotification(notification) => {
52                self.on_progress(notification.params, context).await
53            }
54            ServerNotification::LoggingMessageNotification(notification) => {
55                self.on_logging_message(notification.params, context).await
56            }
57            ServerNotification::ResourceUpdatedNotification(notification) => {
58                self.on_resource_updated(notification.params, context).await
59            }
60            ServerNotification::ResourceListChangedNotification(_notification_no_param) => {
61                self.on_resource_list_changed(context).await
62            }
63            ServerNotification::ToolListChangedNotification(_notification_no_param) => {
64                self.on_tool_list_changed(context).await
65            }
66            ServerNotification::PromptListChangedNotification(_notification_no_param) => {
67                self.on_prompt_list_changed(context).await
68            }
69            ServerNotification::SubscriptionsAcknowledgedNotification(notification) => {
70                self.on_subscriptions_acknowledged(notification.params, context)
71                    .await
72            }
73            ServerNotification::TaskStatusNotification(notification) => {
74                self.on_task_status(notification.params, context).await
75            }
76            ServerNotification::CustomNotification(notification) => {
77                self.on_custom_notification(notification, context).await
78            }
79        };
80        Ok(())
81    }
82
83    fn get_info(&self) -> <RoleClient as ServiceRole>::Info {
84        self.get_info()
85    }
86}
87
88macro_rules! client_handler_methods {
89    () => {
90        fn ping(
91            &self,
92            context: RequestContext<RoleClient>,
93        ) -> impl Future<Output = Result<(), McpError>> + MaybeSendFuture + '_ {
94            std::future::ready(Ok(()))
95        }
96
97        fn create_message(
98            &self,
99            params: CreateMessageRequestParams,
100            context: RequestContext<RoleClient>,
101        ) -> impl Future<Output = Result<CreateMessageResult, McpError>> + MaybeSendFuture + '_ {
102            std::future::ready(Err(
103                McpError::method_not_found::<CreateMessageRequestMethod>(),
104            ))
105        }
106
107        fn list_roots(
108            &self,
109            context: RequestContext<RoleClient>,
110        ) -> impl Future<Output = Result<ListRootsResult, McpError>> + MaybeSendFuture + '_ {
111            std::future::ready(Ok(ListRootsResult::default()))
112        }
113
114        /// Handle an elicitation request from a server asking for user input.
115        ///
116        /// This method is called when a server needs interactive input from the user
117        /// during tool execution. Implementations should present the message to the user,
118        /// collect their input according to the requested schema, and return the result.
119        ///
120        /// # Arguments
121        /// * `request` - The elicitation request with message and schema
122        /// * `context` - The request context
123        ///
124        /// # Returns
125        /// The user's response including action (accept/decline/cancel) and optional data
126        ///
127        /// # Default Behavior
128        /// The default implementation automatically declines all elicitation requests.
129        /// Real clients should override this to provide user interaction.
130        ///
131        /// # Example
132        /// ```rust,no_run
133        /// use rmcp::{
134        ///     ClientHandler,
135        ///     model::{
136        ///         ElicitRequestParams, ElicitResult, ElicitationAction, ElicitationSchema,
137        ///         ErrorData as McpError,
138        ///     },
139        ///     service::{RequestContext, RoleClient},
140        /// };
141        ///
142        /// # struct MyClient;
143        /// #
144        /// # async fn get_user_input(
145        /// #     _message: String,
146        /// #     _schema: ElicitationSchema,
147        /// # ) -> Result<serde_json::Value, McpError> {
148        /// #     std::future::pending().await
149        /// # }
150        /// #
151        /// # async fn open_url_in_browser(_url: String) -> Result<(), McpError> {
152        /// #     Ok(())
153        /// # }
154        /// #
155        /// impl ClientHandler for MyClient {
156        ///     async fn create_elicitation(
157        ///         &self,
158        ///         request: ElicitRequestParams,
159        ///         _context: RequestContext<RoleClient>,
160        ///     ) -> Result<ElicitResult, McpError> {
161        ///         match request {
162        ///             ElicitRequestParams::FormElicitationParams {
163        ///                 message,
164        ///                 requested_schema,
165        ///                 ..
166        ///             } => {
167        ///                 let input = get_user_input(message, requested_schema).await?;
168        ///                 Ok(ElicitResult::new(ElicitationAction::Accept).with_content(input))
169        ///             }
170        ///             ElicitRequestParams::UrlElicitationParams { url, .. } => {
171        ///                 open_url_in_browser(url).await?;
172        ///                 Ok(ElicitResult::new(ElicitationAction::Accept))
173        ///             }
174        ///             _ => Ok(ElicitResult::new(ElicitationAction::Decline)),
175        ///         }
176        ///     }
177        /// }
178        /// ```
179        fn create_elicitation(
180            &self,
181            request: ElicitRequestParams,
182            context: RequestContext<RoleClient>,
183        ) -> impl Future<Output = Result<ElicitResult, McpError>> + MaybeSendFuture + '_ {
184            // Default implementation declines all requests - real clients should override this
185            let _ = (request, context);
186            std::future::ready(Ok(ElicitResult {
187                action: ElicitationAction::Decline,
188                content: None,
189                meta: None,
190            }))
191        }
192
193        fn on_custom_request(
194            &self,
195            request: CustomRequest,
196            context: RequestContext<RoleClient>,
197        ) -> impl Future<Output = Result<CustomResult, McpError>> + MaybeSendFuture + '_ {
198            let CustomRequest { method, .. } = request;
199            let _ = context;
200            std::future::ready(Err(McpError::new(
201                ErrorCode::METHOD_NOT_FOUND,
202                method,
203                None,
204            )))
205        }
206
207        fn on_cancelled(
208            &self,
209            params: CancelledNotificationParam,
210            context: NotificationContext<RoleClient>,
211        ) -> impl Future<Output = ()> + MaybeSendFuture + '_ {
212            std::future::ready(())
213        }
214        fn on_progress(
215            &self,
216            params: ProgressNotificationParam,
217            context: NotificationContext<RoleClient>,
218        ) -> impl Future<Output = ()> + MaybeSendFuture + '_ {
219            std::future::ready(())
220        }
221        fn on_logging_message(
222            &self,
223            params: LoggingMessageNotificationParam,
224            context: NotificationContext<RoleClient>,
225        ) -> impl Future<Output = ()> + MaybeSendFuture + '_ {
226            std::future::ready(())
227        }
228        fn on_resource_updated(
229            &self,
230            params: ResourceUpdatedNotificationParam,
231            context: NotificationContext<RoleClient>,
232        ) -> impl Future<Output = ()> + MaybeSendFuture + '_ {
233            std::future::ready(())
234        }
235        fn on_resource_list_changed(
236            &self,
237            context: NotificationContext<RoleClient>,
238        ) -> impl Future<Output = ()> + MaybeSendFuture + '_ {
239            std::future::ready(())
240        }
241        fn on_tool_list_changed(
242            &self,
243            context: NotificationContext<RoleClient>,
244        ) -> impl Future<Output = ()> + MaybeSendFuture + '_ {
245            std::future::ready(())
246        }
247        fn on_prompt_list_changed(
248            &self,
249            context: NotificationContext<RoleClient>,
250        ) -> impl Future<Output = ()> + MaybeSendFuture + '_ {
251            std::future::ready(())
252        }
253        fn on_subscriptions_acknowledged(
254            &self,
255            params: SubscriptionsAcknowledgedNotificationParams,
256            context: NotificationContext<RoleClient>,
257        ) -> impl Future<Output = ()> + MaybeSendFuture + '_ {
258            std::future::ready(())
259        }
260
261        fn on_task_status(
262            &self,
263            params: TaskStatusNotificationParams,
264            context: NotificationContext<RoleClient>,
265        ) -> impl Future<Output = ()> + MaybeSendFuture + '_ {
266            std::future::ready(())
267        }
268        fn on_custom_notification(
269            &self,
270            notification: CustomNotification,
271            context: NotificationContext<RoleClient>,
272        ) -> impl Future<Output = ()> + MaybeSendFuture + '_ {
273            let _ = (notification, context);
274            std::future::ready(())
275        }
276
277        fn get_info(&self) -> ClientInfo {
278            ClientInfo::default()
279        }
280    };
281}
282
283#[allow(unused_variables)]
284#[cfg(not(feature = "local"))]
285pub trait ClientHandler: Sized + Send + Sync + 'static {
286    client_handler_methods!();
287}
288
289#[allow(unused_variables)]
290#[cfg(feature = "local")]
291pub trait ClientHandler: Sized + 'static {
292    client_handler_methods!();
293}
294
295/// Do nothing, with default client info.
296impl ClientHandler for () {}
297
298/// Do nothing, with a specific client info.
299impl ClientHandler for ClientInfo {
300    fn get_info(&self) -> ClientInfo {
301        self.clone()
302    }
303}
304
305macro_rules! impl_client_handler_for_wrapper {
306    ($wrapper:ident) => {
307        impl<T: ClientHandler> ClientHandler for $wrapper<T> {
308            fn ping(
309                &self,
310                context: RequestContext<RoleClient>,
311            ) -> impl Future<Output = Result<(), McpError>> + MaybeSendFuture + '_ {
312                (**self).ping(context)
313            }
314
315            fn create_message(
316                &self,
317                params: CreateMessageRequestParams,
318                context: RequestContext<RoleClient>,
319            ) -> impl Future<Output = Result<CreateMessageResult, McpError>> + MaybeSendFuture + '_
320            {
321                (**self).create_message(params, context)
322            }
323
324            fn list_roots(
325                &self,
326                context: RequestContext<RoleClient>,
327            ) -> impl Future<Output = Result<ListRootsResult, McpError>> + MaybeSendFuture + '_
328            {
329                (**self).list_roots(context)
330            }
331
332            fn create_elicitation(
333                &self,
334                request: ElicitRequestParams,
335                context: RequestContext<RoleClient>,
336            ) -> impl Future<Output = Result<ElicitResult, McpError>> + MaybeSendFuture + '_ {
337                (**self).create_elicitation(request, context)
338            }
339
340            fn on_custom_request(
341                &self,
342                request: CustomRequest,
343                context: RequestContext<RoleClient>,
344            ) -> impl Future<Output = Result<CustomResult, McpError>> + MaybeSendFuture + '_ {
345                (**self).on_custom_request(request, context)
346            }
347
348            fn on_cancelled(
349                &self,
350                params: CancelledNotificationParam,
351                context: NotificationContext<RoleClient>,
352            ) -> impl Future<Output = ()> + MaybeSendFuture + '_ {
353                (**self).on_cancelled(params, context)
354            }
355
356            fn on_progress(
357                &self,
358                params: ProgressNotificationParam,
359                context: NotificationContext<RoleClient>,
360            ) -> impl Future<Output = ()> + MaybeSendFuture + '_ {
361                (**self).on_progress(params, context)
362            }
363
364            fn on_logging_message(
365                &self,
366                params: LoggingMessageNotificationParam,
367                context: NotificationContext<RoleClient>,
368            ) -> impl Future<Output = ()> + MaybeSendFuture + '_ {
369                (**self).on_logging_message(params, context)
370            }
371
372            fn on_resource_updated(
373                &self,
374                params: ResourceUpdatedNotificationParam,
375                context: NotificationContext<RoleClient>,
376            ) -> impl Future<Output = ()> + MaybeSendFuture + '_ {
377                (**self).on_resource_updated(params, context)
378            }
379
380            fn on_resource_list_changed(
381                &self,
382                context: NotificationContext<RoleClient>,
383            ) -> impl Future<Output = ()> + MaybeSendFuture + '_ {
384                (**self).on_resource_list_changed(context)
385            }
386
387            fn on_tool_list_changed(
388                &self,
389                context: NotificationContext<RoleClient>,
390            ) -> impl Future<Output = ()> + MaybeSendFuture + '_ {
391                (**self).on_tool_list_changed(context)
392            }
393
394            fn on_prompt_list_changed(
395                &self,
396                context: NotificationContext<RoleClient>,
397            ) -> impl Future<Output = ()> + MaybeSendFuture + '_ {
398                (**self).on_prompt_list_changed(context)
399            }
400
401            fn on_subscriptions_acknowledged(
402                &self,
403                params: SubscriptionsAcknowledgedNotificationParams,
404                context: NotificationContext<RoleClient>,
405            ) -> impl Future<Output = ()> + MaybeSendFuture + '_ {
406                (**self).on_subscriptions_acknowledged(params, context)
407            }
408
409            fn on_task_status(
410                &self,
411                params: TaskStatusNotificationParams,
412                context: NotificationContext<RoleClient>,
413            ) -> impl Future<Output = ()> + MaybeSendFuture + '_ {
414                (**self).on_task_status(params, context)
415            }
416
417            fn on_custom_notification(
418                &self,
419                notification: CustomNotification,
420                context: NotificationContext<RoleClient>,
421            ) -> impl Future<Output = ()> + MaybeSendFuture + '_ {
422                (**self).on_custom_notification(notification, context)
423            }
424
425            fn get_info(&self) -> ClientInfo {
426                (**self).get_info()
427            }
428        }
429    };
430}
431
432impl_client_handler_for_wrapper!(Box);
433impl_client_handler_for_wrapper!(Arc);