Skip to main content

rmcp_server_builder/
server.rs

1//! The composed Server type and its ServerHandler implementation.
2
3use rmcp::{
4    handler::server::ServerHandler,
5    model::{
6        CallToolRequestParams, CallToolResult, CancelledNotificationParam, CompleteRequestParams,
7        CompleteResult, ErrorCode, ErrorData, GetPromptRequestParams, GetPromptResult,
8        InitializeRequestParams, InitializeResult, JsonObject, ListPromptsResult,
9        ListResourceTemplatesResult, ListResourcesResult, ListToolsResult, PaginatedRequestParams,
10        ProgressNotificationParam, PromptsCapability, ReadResourceRequestParams,
11        ReadResourceResult, ResourcesCapability, ServerCapabilities, ServerInfo,
12        SetLevelRequestParams, SubscribeRequestParams, ToolsCapability, UnsubscribeRequestParams,
13    },
14    service::{NotificationContext, RequestContext, RoleServer},
15};
16
17use crate::providers::{
18    CompletionProvider, LoggingProvider, PromptsProvider, ResourcesProvider, ServerInfoProvider,
19    ToolsProvider,
20};
21
22/// Marker for an unset provider.
23#[derive(Clone, Copy, Debug, Default)]
24pub struct Unset;
25
26/// A composable MCP server that routes requests to individual capability providers.
27///
28/// Use [`ServerBuilder`](crate::ServerBuilder) to construct a server.
29///
30/// # Type Parameters
31///
32/// - `T`: Tools provider (or `Unset`)
33/// - `P`: Prompts provider (or `Unset`)
34/// - `R`: Resources provider (or `Unset`)
35/// - `C`: Completion provider (or `Unset`)
36/// - `L`: Logging provider (or `Unset`)
37/// - `I`: Server info provider (required)
38#[derive(Clone)]
39pub struct Server<T, P, R, C, L, I> {
40    pub(crate) tools: Option<T>,
41    pub(crate) prompts: Option<P>,
42    pub(crate) resources: Option<R>,
43    pub(crate) completion: Option<C>,
44    pub(crate) logging: Option<L>,
45    pub(crate) info: I,
46    pub(crate) instructions: Option<String>,
47}
48
49impl<T, P, R, C, L, I> Server<T, P, R, C, L, I>
50where
51    I: ServerInfoProvider,
52{
53    /// Get the combined capabilities based on which providers are set.
54    fn combined_capabilities(&self) -> ServerCapabilities {
55        let mut caps = self.info.capabilities();
56
57        if self.tools.is_some() {
58            caps.tools = Some(ToolsCapability::default());
59        }
60        if self.prompts.is_some() {
61            caps.prompts = Some(PromptsCapability::default());
62        }
63        if self.resources.is_some() {
64            caps.resources = Some(ResourcesCapability::default());
65        }
66        if self.logging.is_some() {
67            caps.logging = Some(JsonObject::default());
68        }
69
70        caps
71    }
72}
73
74// =============================================================================
75// ServerHandler implementation
76// =============================================================================
77
78impl<T, P, R, C, L, I> ServerHandler for Server<T, P, R, C, L, I>
79where
80    T: ToolsProvider,
81    P: PromptsProvider,
82    R: ResourcesProvider,
83    C: CompletionProvider,
84    L: LoggingProvider,
85    I: ServerInfoProvider,
86{
87    fn get_info(&self) -> ServerInfo {
88        let base = self.info.get_info();
89        let mut info = ServerInfo::new(self.combined_capabilities())
90            .with_protocol_version(base.protocol_version)
91            .with_server_info(base.server_info);
92        if let Some(instructions) = self.instructions.clone().or(base.instructions) {
93            info = info.with_instructions(instructions);
94        }
95        info
96    }
97
98    async fn initialize(
99        &self,
100        _request: InitializeRequestParams,
101        _context: RequestContext<RoleServer>,
102    ) -> Result<InitializeResult, ErrorData> {
103        let base = self.info.get_info();
104        let mut result = InitializeResult::new(self.combined_capabilities())
105            .with_protocol_version(base.protocol_version)
106            .with_server_info(base.server_info);
107        if let Some(instructions) = self.instructions.clone().or(base.instructions) {
108            result = result.with_instructions(instructions);
109        }
110        Ok(result)
111    }
112
113    async fn ping(&self, _context: RequestContext<RoleServer>) -> Result<(), ErrorData> {
114        Ok(())
115    }
116
117    async fn list_tools(
118        &self,
119        request: Option<PaginatedRequestParams>,
120        context: RequestContext<RoleServer>,
121    ) -> Result<ListToolsResult, ErrorData> {
122        match &self.tools {
123            Some(provider) => provider.list_tools(request, context).await,
124            None => Err(ErrorData::new(
125                ErrorCode::METHOD_NOT_FOUND,
126                "tools not supported",
127                None,
128            )),
129        }
130    }
131
132    async fn call_tool(
133        &self,
134        request: CallToolRequestParams,
135        context: RequestContext<RoleServer>,
136    ) -> Result<CallToolResult, ErrorData> {
137        match &self.tools {
138            Some(provider) => provider.call_tool(request, context).await,
139            None => Err(ErrorData::new(
140                ErrorCode::METHOD_NOT_FOUND,
141                "tools not supported",
142                None,
143            )),
144        }
145    }
146
147    async fn list_prompts(
148        &self,
149        request: Option<PaginatedRequestParams>,
150        context: RequestContext<RoleServer>,
151    ) -> Result<ListPromptsResult, ErrorData> {
152        match &self.prompts {
153            Some(provider) => provider.list_prompts(request, context).await,
154            None => Err(ErrorData::new(
155                ErrorCode::METHOD_NOT_FOUND,
156                "prompts not supported",
157                None,
158            )),
159        }
160    }
161
162    async fn get_prompt(
163        &self,
164        request: GetPromptRequestParams,
165        context: RequestContext<RoleServer>,
166    ) -> Result<GetPromptResult, ErrorData> {
167        match &self.prompts {
168            Some(provider) => provider.get_prompt(request, context).await,
169            None => Err(ErrorData::new(
170                ErrorCode::METHOD_NOT_FOUND,
171                "prompts not supported",
172                None,
173            )),
174        }
175    }
176
177    async fn list_resources(
178        &self,
179        request: Option<PaginatedRequestParams>,
180        context: RequestContext<RoleServer>,
181    ) -> Result<ListResourcesResult, ErrorData> {
182        match &self.resources {
183            Some(provider) => provider.list_resources(request, context).await,
184            None => Err(ErrorData::new(
185                ErrorCode::METHOD_NOT_FOUND,
186                "resources not supported",
187                None,
188            )),
189        }
190    }
191
192    async fn list_resource_templates(
193        &self,
194        request: Option<PaginatedRequestParams>,
195        context: RequestContext<RoleServer>,
196    ) -> Result<ListResourceTemplatesResult, ErrorData> {
197        match &self.resources {
198            Some(provider) => provider.list_resource_templates(request, context).await,
199            None => Err(ErrorData::new(
200                ErrorCode::METHOD_NOT_FOUND,
201                "resources not supported",
202                None,
203            )),
204        }
205    }
206
207    async fn read_resource(
208        &self,
209        request: ReadResourceRequestParams,
210        context: RequestContext<RoleServer>,
211    ) -> Result<ReadResourceResult, ErrorData> {
212        match &self.resources {
213            Some(provider) => provider.read_resource(request, context).await,
214            None => Err(ErrorData::new(
215                ErrorCode::METHOD_NOT_FOUND,
216                "resources not supported",
217                None,
218            )),
219        }
220    }
221
222    async fn subscribe(
223        &self,
224        request: SubscribeRequestParams,
225        context: RequestContext<RoleServer>,
226    ) -> Result<(), ErrorData> {
227        match &self.resources {
228            Some(provider) => provider.subscribe(request, context).await,
229            None => Err(ErrorData::new(
230                ErrorCode::METHOD_NOT_FOUND,
231                "resources not supported",
232                None,
233            )),
234        }
235    }
236
237    async fn unsubscribe(
238        &self,
239        request: UnsubscribeRequestParams,
240        context: RequestContext<RoleServer>,
241    ) -> Result<(), ErrorData> {
242        match &self.resources {
243            Some(provider) => provider.unsubscribe(request, context).await,
244            None => Err(ErrorData::new(
245                ErrorCode::METHOD_NOT_FOUND,
246                "resources not supported",
247                None,
248            )),
249        }
250    }
251
252    async fn complete(
253        &self,
254        request: CompleteRequestParams,
255        context: RequestContext<RoleServer>,
256    ) -> Result<CompleteResult, ErrorData> {
257        match &self.completion {
258            Some(provider) => provider.complete(request, context).await,
259            None => Err(ErrorData::new(
260                ErrorCode::METHOD_NOT_FOUND,
261                "completion not supported",
262                None,
263            )),
264        }
265    }
266
267    async fn set_level(
268        &self,
269        request: SetLevelRequestParams,
270        context: RequestContext<RoleServer>,
271    ) -> Result<(), ErrorData> {
272        match &self.logging {
273            Some(provider) => provider.set_level(request, context).await,
274            None => Err(ErrorData::new(
275                ErrorCode::METHOD_NOT_FOUND,
276                "logging not supported",
277                None,
278            )),
279        }
280    }
281
282    async fn on_cancelled(
283        &self,
284        _notification: CancelledNotificationParam,
285        _context: NotificationContext<RoleServer>,
286    ) {
287    }
288
289    async fn on_progress(
290        &self,
291        _notification: ProgressNotificationParam,
292        _context: NotificationContext<RoleServer>,
293    ) {
294    }
295
296    async fn on_initialized(&self, _context: NotificationContext<RoleServer>) {}
297
298    async fn on_roots_list_changed(&self, _context: NotificationContext<RoleServer>) {}
299}
300
301// =============================================================================
302// Provider implementations for Unset marker
303// =============================================================================
304
305impl ToolsProvider for Unset {
306    async fn list_tools(
307        &self,
308        _request: Option<PaginatedRequestParams>,
309        _context: RequestContext<RoleServer>,
310    ) -> Result<ListToolsResult, ErrorData> {
311        Err(ErrorData::new(
312            ErrorCode::METHOD_NOT_FOUND,
313            "tools not supported",
314            None,
315        ))
316    }
317
318    async fn call_tool(
319        &self,
320        _request: CallToolRequestParams,
321        _context: RequestContext<RoleServer>,
322    ) -> Result<CallToolResult, ErrorData> {
323        Err(ErrorData::new(
324            ErrorCode::METHOD_NOT_FOUND,
325            "tools not supported",
326            None,
327        ))
328    }
329}
330
331impl PromptsProvider for Unset {
332    async fn list_prompts(
333        &self,
334        _request: Option<PaginatedRequestParams>,
335        _context: RequestContext<RoleServer>,
336    ) -> Result<ListPromptsResult, ErrorData> {
337        Err(ErrorData::new(
338            ErrorCode::METHOD_NOT_FOUND,
339            "prompts not supported",
340            None,
341        ))
342    }
343
344    async fn get_prompt(
345        &self,
346        _request: GetPromptRequestParams,
347        _context: RequestContext<RoleServer>,
348    ) -> Result<GetPromptResult, ErrorData> {
349        Err(ErrorData::new(
350            ErrorCode::METHOD_NOT_FOUND,
351            "prompts not supported",
352            None,
353        ))
354    }
355}
356
357impl ResourcesProvider for Unset {
358    async fn list_resources(
359        &self,
360        _request: Option<PaginatedRequestParams>,
361        _context: RequestContext<RoleServer>,
362    ) -> Result<ListResourcesResult, ErrorData> {
363        Err(ErrorData::new(
364            ErrorCode::METHOD_NOT_FOUND,
365            "resources not supported",
366            None,
367        ))
368    }
369
370    async fn list_resource_templates(
371        &self,
372        _request: Option<PaginatedRequestParams>,
373        _context: RequestContext<RoleServer>,
374    ) -> Result<ListResourceTemplatesResult, ErrorData> {
375        Err(ErrorData::new(
376            ErrorCode::METHOD_NOT_FOUND,
377            "resources not supported",
378            None,
379        ))
380    }
381
382    async fn read_resource(
383        &self,
384        _request: ReadResourceRequestParams,
385        _context: RequestContext<RoleServer>,
386    ) -> Result<ReadResourceResult, ErrorData> {
387        Err(ErrorData::new(
388            ErrorCode::METHOD_NOT_FOUND,
389            "resources not supported",
390            None,
391        ))
392    }
393
394    async fn subscribe(
395        &self,
396        _request: SubscribeRequestParams,
397        _context: RequestContext<RoleServer>,
398    ) -> Result<(), ErrorData> {
399        Err(ErrorData::new(
400            ErrorCode::METHOD_NOT_FOUND,
401            "resources not supported",
402            None,
403        ))
404    }
405
406    async fn unsubscribe(
407        &self,
408        _request: UnsubscribeRequestParams,
409        _context: RequestContext<RoleServer>,
410    ) -> Result<(), ErrorData> {
411        Err(ErrorData::new(
412            ErrorCode::METHOD_NOT_FOUND,
413            "resources not supported",
414            None,
415        ))
416    }
417}
418
419impl CompletionProvider for Unset {
420    async fn complete(
421        &self,
422        _request: CompleteRequestParams,
423        _context: RequestContext<RoleServer>,
424    ) -> Result<CompleteResult, ErrorData> {
425        Err(ErrorData::new(
426            ErrorCode::METHOD_NOT_FOUND,
427            "completion not supported",
428            None,
429        ))
430    }
431}
432
433impl LoggingProvider for Unset {
434    async fn set_level(
435        &self,
436        _request: SetLevelRequestParams,
437        _context: RequestContext<RoleServer>,
438    ) -> Result<(), ErrorData> {
439        Err(ErrorData::new(
440            ErrorCode::METHOD_NOT_FOUND,
441            "logging not supported",
442            None,
443        ))
444    }
445}