Skip to main content

rmcp/handler/
server.rs

1// Sampling/Roots/Logging are SEP-2577-deprecated; internal references are expected.
2#![expect(deprecated)]
3use std::sync::Arc;
4
5use crate::{
6    error::ErrorData as McpError,
7    model::{TaskSupport, *},
8    service::{
9        MaybeSendFuture, NotificationContext, RequestContext, RoleServer, Service, ServiceRole,
10    },
11};
12
13pub mod common;
14pub mod prompt;
15mod resource;
16pub mod router;
17pub mod tool;
18pub mod tool_name_validation;
19pub mod wrapper;
20
21impl<H: ServerHandler> Service<RoleServer> for H {
22    async fn handle_request(
23        &self,
24        request: <RoleServer as ServiceRole>::PeerReq,
25        context: RequestContext<RoleServer>,
26    ) -> Result<<RoleServer as ServiceRole>::Resp, McpError> {
27        // `context` is moved into the dispatch below, so read the negotiated version first.
28        let protocol_version = context.protocol_version();
29        let result = match request {
30            ClientRequest::InitializeRequest(request) => self
31                .initialize(request.params, context)
32                .await
33                .map(ServerResult::InitializeResult),
34            ClientRequest::PingRequest(_request) => {
35                self.ping(context).await.map(ServerResult::empty)
36            }
37            ClientRequest::CompleteRequest(request) => self
38                .complete(request.params, context)
39                .await
40                .map(ServerResult::CompleteResult),
41            ClientRequest::SetLevelRequest(request) => self
42                .set_level(request.params, context)
43                .await
44                .map(ServerResult::empty),
45            ClientRequest::GetPromptRequest(request) => self
46                .get_prompt(request.params, context)
47                .await
48                .map(ServerResult::GetPromptResult),
49            ClientRequest::ListPromptsRequest(request) => self
50                .list_prompts(request.params, context)
51                .await
52                .map(ServerResult::ListPromptsResult),
53            ClientRequest::ListResourcesRequest(request) => self
54                .list_resources(request.params, context)
55                .await
56                .map(ServerResult::ListResourcesResult),
57            ClientRequest::ListResourceTemplatesRequest(request) => self
58                .list_resource_templates(request.params, context)
59                .await
60                .map(ServerResult::ListResourceTemplatesResult),
61            ClientRequest::ReadResourceRequest(request) => self
62                .read_resource(request.params, context)
63                .await
64                .map(ServerResult::ReadResourceResult),
65            ClientRequest::SubscribeRequest(request) => self
66                .subscribe(request.params, context)
67                .await
68                .map(ServerResult::empty),
69            ClientRequest::UnsubscribeRequest(request) => self
70                .unsubscribe(request.params, context)
71                .await
72                .map(ServerResult::empty),
73            ClientRequest::CallToolRequest(request) => {
74                let is_task = request.params.task.is_some();
75
76                // Validate task support mode per MCP specification
77                if let Some(tool) = self.get_tool(&request.params.name) {
78                    match (tool.task_support(), is_task) {
79                        // If taskSupport is "required", clients MUST invoke the tool as a task.
80                        // Servers MUST return a -32601 (Method not found) error if they don't.
81                        (TaskSupport::Required, false) => {
82                            return Err(McpError::new(
83                                ErrorCode::METHOD_NOT_FOUND,
84                                "Tool requires task-based invocation",
85                                None,
86                            ));
87                        }
88                        // If taskSupport is "forbidden" (default), clients MUST NOT invoke as a task.
89                        (TaskSupport::Forbidden, true) => {
90                            return Err(McpError::invalid_params(
91                                "Tool does not support task-based invocation",
92                                None,
93                            ));
94                        }
95                        _ => {}
96                    }
97                }
98
99                if is_task {
100                    tracing::info!("Enqueueing task for tool call: {}", request.params.name);
101                    self.enqueue_task(request.params, context.clone())
102                        .await
103                        .map(ServerResult::CreateTaskResult)
104                } else {
105                    self.call_tool(request.params, context)
106                        .await
107                        .map(ServerResult::CallToolResult)
108                }
109            }
110            ClientRequest::ListToolsRequest(request) => self
111                .list_tools(request.params, context)
112                .await
113                .map(ServerResult::ListToolsResult),
114            ClientRequest::CustomRequest(request) => self
115                .on_custom_request(request, context)
116                .await
117                .map(ServerResult::CustomResult),
118            ClientRequest::ListTasksRequest(request) => self
119                .list_tasks(request.params, context)
120                .await
121                .map(ServerResult::ListTasksResult),
122            ClientRequest::GetTaskRequest(request) => self
123                .get_task_info(request.params, context)
124                .await
125                .map(ServerResult::GetTaskResult),
126            ClientRequest::GetTaskPayloadRequest(request) => self
127                .get_task_result(request.params, context)
128                .await
129                .map(ServerResult::GetTaskPayloadResult),
130            ClientRequest::CancelTaskRequest(request) => self
131                .cancel_task(request.params, context)
132                .await
133                .map(ServerResult::CancelTaskResult),
134        };
135        // SEP-2164: peers negotiating 2026-07-28+ get the standard INVALID_PARAMS code for
136        // resource-not-found; older peers keep RESOURCE_NOT_FOUND. ISO `YYYY-MM-DD` versions
137        // compare lexically the same as chronologically.
138        let use_invalid_params =
139            protocol_version.is_some_and(|v| v.as_str() >= ProtocolVersion::V_2026_07_28.as_str());
140        result.map_err(|mut error| {
141            if use_invalid_params && error.code == ErrorCode::RESOURCE_NOT_FOUND {
142                error.code = ErrorCode::INVALID_PARAMS;
143            }
144            error
145        })
146    }
147
148    async fn handle_notification(
149        &self,
150        notification: <RoleServer as ServiceRole>::PeerNot,
151        context: NotificationContext<RoleServer>,
152    ) -> Result<(), McpError> {
153        match notification {
154            ClientNotification::CancelledNotification(notification) => {
155                self.on_cancelled(notification.params, context).await
156            }
157            ClientNotification::ProgressNotification(notification) => {
158                self.on_progress(notification.params, context).await
159            }
160            ClientNotification::InitializedNotification(_notification) => {
161                self.on_initialized(context).await
162            }
163            ClientNotification::RootsListChangedNotification(_notification) => {
164                self.on_roots_list_changed(context).await
165            }
166            ClientNotification::TaskStatusNotification(notification) => {
167                self.on_task_status(notification.params, context).await
168            }
169            ClientNotification::CustomNotification(notification) => {
170                self.on_custom_notification(notification, context).await
171            }
172        };
173        Ok(())
174    }
175
176    fn get_info(&self) -> <RoleServer as ServiceRole>::Info {
177        self.get_info()
178    }
179}
180
181macro_rules! server_handler_methods {
182    () => {
183        fn enqueue_task(
184            &self,
185            _request: CallToolRequestParams,
186            _context: RequestContext<RoleServer>,
187        ) -> impl Future<Output = Result<CreateTaskResult, McpError>> + MaybeSendFuture + '_ {
188            std::future::ready(Err(McpError::internal_error(
189                "Task processing not implemented".to_string(),
190                None,
191            )))
192        }
193        fn ping(
194            &self,
195            context: RequestContext<RoleServer>,
196        ) -> impl Future<Output = Result<(), McpError>> + MaybeSendFuture + '_ {
197            std::future::ready(Ok(()))
198        }
199        // handle requests
200        fn initialize(
201            &self,
202            request: InitializeRequestParams,
203            context: RequestContext<RoleServer>,
204        ) -> impl Future<Output = Result<InitializeResult, McpError>> + MaybeSendFuture + '_ {
205            context.peer.set_peer_info(request);
206            std::future::ready(Ok(self.get_info()))
207        }
208        fn complete(
209            &self,
210            request: CompleteRequestParams,
211            context: RequestContext<RoleServer>,
212        ) -> impl Future<Output = Result<CompleteResult, McpError>> + MaybeSendFuture + '_ {
213            std::future::ready(Ok(CompleteResult::default()))
214        }
215        fn set_level(
216            &self,
217            request: SetLevelRequestParams,
218            context: RequestContext<RoleServer>,
219        ) -> impl Future<Output = Result<(), McpError>> + MaybeSendFuture + '_ {
220            std::future::ready(Err(McpError::method_not_found::<SetLevelRequestMethod>()))
221        }
222        fn get_prompt(
223            &self,
224            request: GetPromptRequestParams,
225            context: RequestContext<RoleServer>,
226        ) -> impl Future<Output = Result<GetPromptResult, McpError>> + MaybeSendFuture + '_ {
227            std::future::ready(Err(McpError::method_not_found::<GetPromptRequestMethod>()))
228        }
229        fn list_prompts(
230            &self,
231            request: Option<PaginatedRequestParams>,
232            context: RequestContext<RoleServer>,
233        ) -> impl Future<Output = Result<ListPromptsResult, McpError>> + MaybeSendFuture + '_ {
234            std::future::ready(Ok(ListPromptsResult::default()))
235        }
236        fn list_resources(
237            &self,
238            request: Option<PaginatedRequestParams>,
239            context: RequestContext<RoleServer>,
240        ) -> impl Future<Output = Result<ListResourcesResult, McpError>> + MaybeSendFuture + '_ {
241            std::future::ready(Ok(ListResourcesResult::default()))
242        }
243        fn list_resource_templates(
244            &self,
245            request: Option<PaginatedRequestParams>,
246            context: RequestContext<RoleServer>,
247        ) -> impl Future<Output = Result<ListResourceTemplatesResult, McpError>>
248               + MaybeSendFuture
249               + '_ {
250            std::future::ready(Ok(ListResourceTemplatesResult::default()))
251        }
252        fn read_resource(
253            &self,
254            request: ReadResourceRequestParams,
255            context: RequestContext<RoleServer>,
256        ) -> impl Future<Output = Result<ReadResourceResult, McpError>> + MaybeSendFuture + '_ {
257            std::future::ready(Err(
258                McpError::method_not_found::<ReadResourceRequestMethod>(),
259            ))
260        }
261        fn subscribe(
262            &self,
263            request: SubscribeRequestParams,
264            context: RequestContext<RoleServer>,
265        ) -> impl Future<Output = Result<(), McpError>> + MaybeSendFuture + '_ {
266            std::future::ready(Err(McpError::method_not_found::<SubscribeRequestMethod>()))
267        }
268        fn unsubscribe(
269            &self,
270            request: UnsubscribeRequestParams,
271            context: RequestContext<RoleServer>,
272        ) -> impl Future<Output = Result<(), McpError>> + MaybeSendFuture + '_ {
273            std::future::ready(Err(
274                McpError::method_not_found::<UnsubscribeRequestMethod>(),
275            ))
276        }
277        /// Handle a `tools/call` request from a client.
278        ///
279        /// # Choosing a return value
280        ///
281        /// MCP distinguishes two failure modes; the API forces you to pick
282        /// the right one explicitly because they reach the caller's UI very
283        /// differently:
284        ///
285        /// - `Ok(`[`CallToolResult::error`]`(...))` — the tool ran (or tried
286        ///   to) and produced a failure the caller should see. The
287        ///   `content` you supply is rendered in the caller's MCP client,
288        ///   so the user gets your message. **This is the right return
289        ///   value for almost every "the tool didn't work" path** — empty
290        ///   results, validation failures the user can fix, downstream
291        ///   service unavailability, etc.
292        ///
293        /// - `Err(`[`McpError`]`)` — a JSON-RPC protocol error. Use this
294        ///   only when the request itself is unroutable: unknown tool
295        ///   ([`ErrorCode::METHOD_NOT_FOUND`]), malformed request shape that
296        ///   cannot be treated as a valid `tools/call`, or a server-internal
297        ///   failure that means the server cannot serve any request right now
298        ///   ([`ErrorCode::INTERNAL_ERROR`], `-32603`). MCP clients
299        ///   typically render protocol errors opaquely; **the caller will
300        ///   not see your message** — they see something like "Tool result
301        ///   missing due to internal error". If you want the caller to read
302        ///   your error, use `Ok(CallToolResult::error(...))`.
303        ///
304        /// See [`CallToolResult::error`] for a worked example.
305        fn call_tool(
306            &self,
307            request: CallToolRequestParams,
308            context: RequestContext<RoleServer>,
309        ) -> impl Future<Output = Result<CallToolResult, McpError>> + MaybeSendFuture + '_ {
310            std::future::ready(Err(McpError::method_not_found::<CallToolRequestMethod>()))
311        }
312        fn list_tools(
313            &self,
314            request: Option<PaginatedRequestParams>,
315            context: RequestContext<RoleServer>,
316        ) -> impl Future<Output = Result<ListToolsResult, McpError>> + MaybeSendFuture + '_ {
317            std::future::ready(Ok(ListToolsResult::default()))
318        }
319        /// Get a tool definition by name.
320        ///
321        /// The default implementation returns `None`, which bypasses validation.
322        /// When using `#[tool_handler]`, this method is automatically implemented.
323        fn get_tool(&self, _name: &str) -> Option<Tool> {
324            None
325        }
326        fn on_custom_request(
327            &self,
328            request: CustomRequest,
329            context: RequestContext<RoleServer>,
330        ) -> impl Future<Output = Result<CustomResult, McpError>> + MaybeSendFuture + '_ {
331            let CustomRequest { method, .. } = request;
332            let _ = context;
333            std::future::ready(Err(McpError::new(
334                ErrorCode::METHOD_NOT_FOUND,
335                method,
336                None,
337            )))
338        }
339
340        fn on_cancelled(
341            &self,
342            notification: CancelledNotificationParam,
343            context: NotificationContext<RoleServer>,
344        ) -> impl Future<Output = ()> + MaybeSendFuture + '_ {
345            std::future::ready(())
346        }
347        fn on_progress(
348            &self,
349            notification: ProgressNotificationParam,
350            context: NotificationContext<RoleServer>,
351        ) -> impl Future<Output = ()> + MaybeSendFuture + '_ {
352            std::future::ready(())
353        }
354        fn on_initialized(
355            &self,
356            context: NotificationContext<RoleServer>,
357        ) -> impl Future<Output = ()> + MaybeSendFuture + '_ {
358            tracing::info!("client initialized");
359            std::future::ready(())
360        }
361        fn on_roots_list_changed(
362            &self,
363            context: NotificationContext<RoleServer>,
364        ) -> impl Future<Output = ()> + MaybeSendFuture + '_ {
365            std::future::ready(())
366        }
367        fn on_task_status(
368            &self,
369            params: TaskStatusNotificationParam,
370            context: NotificationContext<RoleServer>,
371        ) -> impl Future<Output = ()> + MaybeSendFuture + '_ {
372            std::future::ready(())
373        }
374        fn on_custom_notification(
375            &self,
376            notification: CustomNotification,
377            context: NotificationContext<RoleServer>,
378        ) -> impl Future<Output = ()> + MaybeSendFuture + '_ {
379            let _ = (notification, context);
380            std::future::ready(())
381        }
382
383        fn get_info(&self) -> ServerInfo {
384            ServerInfo::default()
385        }
386
387        fn list_tasks(
388            &self,
389            request: Option<PaginatedRequestParams>,
390            context: RequestContext<RoleServer>,
391        ) -> impl Future<Output = Result<ListTasksResult, McpError>> + MaybeSendFuture + '_ {
392            std::future::ready(Err(McpError::method_not_found::<ListTasksMethod>()))
393        }
394
395        fn get_task_info(
396            &self,
397            request: GetTaskParams,
398            context: RequestContext<RoleServer>,
399        ) -> impl Future<Output = Result<GetTaskResult, McpError>> + MaybeSendFuture + '_ {
400            let _ = (request, context);
401            std::future::ready(Err(McpError::method_not_found::<GetTaskMethod>()))
402        }
403
404        fn get_task_result(
405            &self,
406            request: GetTaskPayloadParams,
407            context: RequestContext<RoleServer>,
408        ) -> impl Future<Output = Result<GetTaskPayloadResult, McpError>> + MaybeSendFuture + '_ {
409            let _ = (request, context);
410            std::future::ready(Err(McpError::method_not_found::<GetTaskPayloadMethod>()))
411        }
412
413        fn cancel_task(
414            &self,
415            request: CancelTaskParams,
416            context: RequestContext<RoleServer>,
417        ) -> impl Future<Output = Result<CancelTaskResult, McpError>> + MaybeSendFuture + '_ {
418            let _ = (request, context);
419            std::future::ready(Err(McpError::method_not_found::<CancelTaskMethod>()))
420        }
421    };
422}
423
424#[allow(unused_variables)]
425#[cfg(not(feature = "local"))]
426pub trait ServerHandler: Sized + Send + Sync + 'static {
427    server_handler_methods!();
428}
429
430#[allow(unused_variables)]
431#[cfg(feature = "local")]
432pub trait ServerHandler: Sized + 'static {
433    server_handler_methods!();
434}
435
436macro_rules! impl_server_handler_for_wrapper {
437    ($wrapper:ident) => {
438        impl<T: ServerHandler> ServerHandler for $wrapper<T> {
439            fn enqueue_task(
440                &self,
441                request: CallToolRequestParams,
442                context: RequestContext<RoleServer>,
443            ) -> impl Future<Output = Result<CreateTaskResult, McpError>> + MaybeSendFuture + '_ {
444                (**self).enqueue_task(request, context)
445            }
446
447            fn ping(
448                &self,
449                context: RequestContext<RoleServer>,
450            ) -> impl Future<Output = Result<(), McpError>> + MaybeSendFuture + '_ {
451                (**self).ping(context)
452            }
453
454            fn initialize(
455                &self,
456                request: InitializeRequestParams,
457                context: RequestContext<RoleServer>,
458            ) -> impl Future<Output = Result<InitializeResult, McpError>> + MaybeSendFuture + '_ {
459                (**self).initialize(request, context)
460            }
461
462            fn complete(
463                &self,
464                request: CompleteRequestParams,
465                context: RequestContext<RoleServer>,
466            ) -> impl Future<Output = Result<CompleteResult, McpError>> + MaybeSendFuture + '_ {
467                (**self).complete(request, context)
468            }
469
470            fn set_level(
471                &self,
472                request: SetLevelRequestParams,
473                context: RequestContext<RoleServer>,
474            ) -> impl Future<Output = Result<(), McpError>> + MaybeSendFuture + '_ {
475                (**self).set_level(request, context)
476            }
477
478            fn get_prompt(
479                &self,
480                request: GetPromptRequestParams,
481                context: RequestContext<RoleServer>,
482            ) -> impl Future<Output = Result<GetPromptResult, McpError>> + MaybeSendFuture + '_ {
483                (**self).get_prompt(request, context)
484            }
485
486            fn list_prompts(
487                &self,
488                request: Option<PaginatedRequestParams>,
489                context: RequestContext<RoleServer>,
490            ) -> impl Future<Output = Result<ListPromptsResult, McpError>> + MaybeSendFuture + '_ {
491                (**self).list_prompts(request, context)
492            }
493
494            fn list_resources(
495                &self,
496                request: Option<PaginatedRequestParams>,
497                context: RequestContext<RoleServer>,
498            ) -> impl Future<Output = Result<ListResourcesResult, McpError>> + MaybeSendFuture + '_ {
499                (**self).list_resources(request, context)
500            }
501
502            fn list_resource_templates(
503                &self,
504                request: Option<PaginatedRequestParams>,
505                context: RequestContext<RoleServer>,
506            ) -> impl Future<Output = Result<ListResourceTemplatesResult, McpError>> + MaybeSendFuture + '_
507            {
508                (**self).list_resource_templates(request, context)
509            }
510
511            fn read_resource(
512                &self,
513                request: ReadResourceRequestParams,
514                context: RequestContext<RoleServer>,
515            ) -> impl Future<Output = Result<ReadResourceResult, McpError>> + MaybeSendFuture + '_ {
516                (**self).read_resource(request, context)
517            }
518
519            fn subscribe(
520                &self,
521                request: SubscribeRequestParams,
522                context: RequestContext<RoleServer>,
523            ) -> impl Future<Output = Result<(), McpError>> + MaybeSendFuture + '_ {
524                (**self).subscribe(request, context)
525            }
526
527            fn unsubscribe(
528                &self,
529                request: UnsubscribeRequestParams,
530                context: RequestContext<RoleServer>,
531            ) -> impl Future<Output = Result<(), McpError>> + MaybeSendFuture + '_ {
532                (**self).unsubscribe(request, context)
533            }
534
535            fn call_tool(
536                &self,
537                request: CallToolRequestParams,
538                context: RequestContext<RoleServer>,
539            ) -> impl Future<Output = Result<CallToolResult, McpError>> + MaybeSendFuture + '_ {
540                (**self).call_tool(request, context)
541            }
542
543            fn list_tools(
544                &self,
545                request: Option<PaginatedRequestParams>,
546                context: RequestContext<RoleServer>,
547            ) -> impl Future<Output = Result<ListToolsResult, McpError>> + MaybeSendFuture + '_ {
548                (**self).list_tools(request, context)
549            }
550
551            fn get_tool(&self, name: &str) -> Option<Tool> {
552                (**self).get_tool(name)
553            }
554
555            fn on_custom_request(
556                &self,
557                request: CustomRequest,
558                context: RequestContext<RoleServer>,
559            ) -> impl Future<Output = Result<CustomResult, McpError>> + MaybeSendFuture + '_ {
560                (**self).on_custom_request(request, context)
561            }
562
563            fn on_cancelled(
564                &self,
565                notification: CancelledNotificationParam,
566                context: NotificationContext<RoleServer>,
567            ) -> impl Future<Output = ()> + MaybeSendFuture + '_ {
568                (**self).on_cancelled(notification, context)
569            }
570
571            fn on_progress(
572                &self,
573                notification: ProgressNotificationParam,
574                context: NotificationContext<RoleServer>,
575            ) -> impl Future<Output = ()> + MaybeSendFuture + '_ {
576                (**self).on_progress(notification, context)
577            }
578
579            fn on_initialized(
580                &self,
581                context: NotificationContext<RoleServer>,
582            ) -> impl Future<Output = ()> + MaybeSendFuture + '_ {
583                (**self).on_initialized(context)
584            }
585
586            fn on_roots_list_changed(
587                &self,
588                context: NotificationContext<RoleServer>,
589            ) -> impl Future<Output = ()> + MaybeSendFuture + '_ {
590                (**self).on_roots_list_changed(context)
591            }
592
593            fn on_task_status(
594                &self,
595                params: TaskStatusNotificationParam,
596                context: NotificationContext<RoleServer>,
597            ) -> impl Future<Output = ()> + MaybeSendFuture + '_ {
598                (**self).on_task_status(params, context)
599            }
600
601            fn on_custom_notification(
602                &self,
603                notification: CustomNotification,
604                context: NotificationContext<RoleServer>,
605            ) -> impl Future<Output = ()> + MaybeSendFuture + '_ {
606                (**self).on_custom_notification(notification, context)
607            }
608
609            fn get_info(&self) -> ServerInfo {
610                (**self).get_info()
611            }
612
613            fn list_tasks(
614                &self,
615                request: Option<PaginatedRequestParams>,
616                context: RequestContext<RoleServer>,
617            ) -> impl Future<Output = Result<ListTasksResult, McpError>> + MaybeSendFuture + '_ {
618                (**self).list_tasks(request, context)
619            }
620
621            fn get_task_info(
622                &self,
623                request: GetTaskParams,
624                context: RequestContext<RoleServer>,
625            ) -> impl Future<Output = Result<GetTaskResult, McpError>> + MaybeSendFuture + '_ {
626                (**self).get_task_info(request, context)
627            }
628
629            fn get_task_result(
630                &self,
631                request: GetTaskPayloadParams,
632                context: RequestContext<RoleServer>,
633            ) -> impl Future<Output = Result<GetTaskPayloadResult, McpError>> + MaybeSendFuture + '_ {
634                (**self).get_task_result(request, context)
635            }
636
637            fn cancel_task(
638                &self,
639                request: CancelTaskParams,
640                context: RequestContext<RoleServer>,
641            ) -> impl Future<Output = Result<CancelTaskResult, McpError>> + MaybeSendFuture + '_ {
642                (**self).cancel_task(request, context)
643            }
644        }
645    };
646}
647
648impl_server_handler_for_wrapper!(Box);
649impl_server_handler_for_wrapper!(Arc);