rmcp_server_builder/
server.rs

1//! The composed Server type and its ServerHandler implementation.
2
3use rmcp::{
4    handler::server::ServerHandler,
5    model::{
6        CallToolRequestParam, CallToolResult, CancelledNotificationParam, CompleteRequestParam,
7        CompleteResult, ErrorCode, ErrorData, GetPromptRequestParam, GetPromptResult,
8        InitializeRequestParam, InitializeResult, JsonObject, ListPromptsResult,
9        ListResourceTemplatesResult, ListResourcesResult, ListToolsResult, PaginatedRequestParam,
10        ProgressNotificationParam, PromptsCapability, ReadResourceRequestParam, ReadResourceResult,
11        ResourcesCapability, ServerCapabilities, ServerInfo, SetLevelRequestParam,
12        SubscribeRequestParam, ToolsCapability, UnsubscribeRequestParam,
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 mut info = self.info.get_info();
89        info.capabilities = self.combined_capabilities();
90        if self.instructions.is_some() {
91            info.instructions = self.instructions.clone();
92        }
93        info
94    }
95
96    async fn initialize(
97        &self,
98        _request: InitializeRequestParam,
99        _context: RequestContext<RoleServer>,
100    ) -> Result<InitializeResult, ErrorData> {
101        let base = self.info.get_info();
102        Ok(InitializeResult {
103            protocol_version: base.protocol_version,
104            capabilities: self.combined_capabilities(),
105            server_info: base.server_info,
106            instructions: self.instructions.clone().or(base.instructions),
107        })
108    }
109
110    async fn ping(&self, _context: RequestContext<RoleServer>) -> Result<(), ErrorData> {
111        Ok(())
112    }
113
114    async fn list_tools(
115        &self,
116        request: Option<PaginatedRequestParam>,
117        context: RequestContext<RoleServer>,
118    ) -> Result<ListToolsResult, ErrorData> {
119        match &self.tools {
120            Some(provider) => provider.list_tools(request, context).await,
121            None => Err(ErrorData::new(
122                ErrorCode::METHOD_NOT_FOUND,
123                "tools not supported",
124                None,
125            )),
126        }
127    }
128
129    async fn call_tool(
130        &self,
131        request: CallToolRequestParam,
132        context: RequestContext<RoleServer>,
133    ) -> Result<CallToolResult, ErrorData> {
134        match &self.tools {
135            Some(provider) => provider.call_tool(request, context).await,
136            None => Err(ErrorData::new(
137                ErrorCode::METHOD_NOT_FOUND,
138                "tools not supported",
139                None,
140            )),
141        }
142    }
143
144    async fn list_prompts(
145        &self,
146        request: Option<PaginatedRequestParam>,
147        context: RequestContext<RoleServer>,
148    ) -> Result<ListPromptsResult, ErrorData> {
149        match &self.prompts {
150            Some(provider) => provider.list_prompts(request, context).await,
151            None => Err(ErrorData::new(
152                ErrorCode::METHOD_NOT_FOUND,
153                "prompts not supported",
154                None,
155            )),
156        }
157    }
158
159    async fn get_prompt(
160        &self,
161        request: GetPromptRequestParam,
162        context: RequestContext<RoleServer>,
163    ) -> Result<GetPromptResult, ErrorData> {
164        match &self.prompts {
165            Some(provider) => provider.get_prompt(request, context).await,
166            None => Err(ErrorData::new(
167                ErrorCode::METHOD_NOT_FOUND,
168                "prompts not supported",
169                None,
170            )),
171        }
172    }
173
174    async fn list_resources(
175        &self,
176        request: Option<PaginatedRequestParam>,
177        context: RequestContext<RoleServer>,
178    ) -> Result<ListResourcesResult, ErrorData> {
179        match &self.resources {
180            Some(provider) => provider.list_resources(request, context).await,
181            None => Err(ErrorData::new(
182                ErrorCode::METHOD_NOT_FOUND,
183                "resources not supported",
184                None,
185            )),
186        }
187    }
188
189    async fn list_resource_templates(
190        &self,
191        request: Option<PaginatedRequestParam>,
192        context: RequestContext<RoleServer>,
193    ) -> Result<ListResourceTemplatesResult, ErrorData> {
194        match &self.resources {
195            Some(provider) => provider.list_resource_templates(request, context).await,
196            None => Err(ErrorData::new(
197                ErrorCode::METHOD_NOT_FOUND,
198                "resources not supported",
199                None,
200            )),
201        }
202    }
203
204    async fn read_resource(
205        &self,
206        request: ReadResourceRequestParam,
207        context: RequestContext<RoleServer>,
208    ) -> Result<ReadResourceResult, ErrorData> {
209        match &self.resources {
210            Some(provider) => provider.read_resource(request, context).await,
211            None => Err(ErrorData::new(
212                ErrorCode::METHOD_NOT_FOUND,
213                "resources not supported",
214                None,
215            )),
216        }
217    }
218
219    async fn subscribe(
220        &self,
221        request: SubscribeRequestParam,
222        context: RequestContext<RoleServer>,
223    ) -> Result<(), ErrorData> {
224        match &self.resources {
225            Some(provider) => provider.subscribe(request, context).await,
226            None => Err(ErrorData::new(
227                ErrorCode::METHOD_NOT_FOUND,
228                "resources not supported",
229                None,
230            )),
231        }
232    }
233
234    async fn unsubscribe(
235        &self,
236        request: UnsubscribeRequestParam,
237        context: RequestContext<RoleServer>,
238    ) -> Result<(), ErrorData> {
239        match &self.resources {
240            Some(provider) => provider.unsubscribe(request, context).await,
241            None => Err(ErrorData::new(
242                ErrorCode::METHOD_NOT_FOUND,
243                "resources not supported",
244                None,
245            )),
246        }
247    }
248
249    async fn complete(
250        &self,
251        request: CompleteRequestParam,
252        context: RequestContext<RoleServer>,
253    ) -> Result<CompleteResult, ErrorData> {
254        match &self.completion {
255            Some(provider) => provider.complete(request, context).await,
256            None => Err(ErrorData::new(
257                ErrorCode::METHOD_NOT_FOUND,
258                "completion not supported",
259                None,
260            )),
261        }
262    }
263
264    async fn set_level(
265        &self,
266        request: SetLevelRequestParam,
267        context: RequestContext<RoleServer>,
268    ) -> Result<(), ErrorData> {
269        match &self.logging {
270            Some(provider) => provider.set_level(request, context).await,
271            None => Err(ErrorData::new(
272                ErrorCode::METHOD_NOT_FOUND,
273                "logging not supported",
274                None,
275            )),
276        }
277    }
278
279    async fn on_cancelled(
280        &self,
281        _notification: CancelledNotificationParam,
282        _context: NotificationContext<RoleServer>,
283    ) {
284    }
285
286    async fn on_progress(
287        &self,
288        _notification: ProgressNotificationParam,
289        _context: NotificationContext<RoleServer>,
290    ) {
291    }
292
293    async fn on_initialized(&self, _context: NotificationContext<RoleServer>) {}
294
295    async fn on_roots_list_changed(&self, _context: NotificationContext<RoleServer>) {}
296}
297
298// =============================================================================
299// Provider implementations for Unset marker
300// =============================================================================
301
302impl ToolsProvider for Unset {
303    async fn list_tools(
304        &self,
305        _request: Option<PaginatedRequestParam>,
306        _context: RequestContext<RoleServer>,
307    ) -> Result<ListToolsResult, ErrorData> {
308        Err(ErrorData::new(
309            ErrorCode::METHOD_NOT_FOUND,
310            "tools not supported",
311            None,
312        ))
313    }
314
315    async fn call_tool(
316        &self,
317        _request: CallToolRequestParam,
318        _context: RequestContext<RoleServer>,
319    ) -> Result<CallToolResult, ErrorData> {
320        Err(ErrorData::new(
321            ErrorCode::METHOD_NOT_FOUND,
322            "tools not supported",
323            None,
324        ))
325    }
326}
327
328impl PromptsProvider for Unset {
329    async fn list_prompts(
330        &self,
331        _request: Option<PaginatedRequestParam>,
332        _context: RequestContext<RoleServer>,
333    ) -> Result<ListPromptsResult, ErrorData> {
334        Err(ErrorData::new(
335            ErrorCode::METHOD_NOT_FOUND,
336            "prompts not supported",
337            None,
338        ))
339    }
340
341    async fn get_prompt(
342        &self,
343        _request: GetPromptRequestParam,
344        _context: RequestContext<RoleServer>,
345    ) -> Result<GetPromptResult, ErrorData> {
346        Err(ErrorData::new(
347            ErrorCode::METHOD_NOT_FOUND,
348            "prompts not supported",
349            None,
350        ))
351    }
352}
353
354impl ResourcesProvider for Unset {
355    async fn list_resources(
356        &self,
357        _request: Option<PaginatedRequestParam>,
358        _context: RequestContext<RoleServer>,
359    ) -> Result<ListResourcesResult, ErrorData> {
360        Err(ErrorData::new(
361            ErrorCode::METHOD_NOT_FOUND,
362            "resources not supported",
363            None,
364        ))
365    }
366
367    async fn list_resource_templates(
368        &self,
369        _request: Option<PaginatedRequestParam>,
370        _context: RequestContext<RoleServer>,
371    ) -> Result<ListResourceTemplatesResult, ErrorData> {
372        Err(ErrorData::new(
373            ErrorCode::METHOD_NOT_FOUND,
374            "resources not supported",
375            None,
376        ))
377    }
378
379    async fn read_resource(
380        &self,
381        _request: ReadResourceRequestParam,
382        _context: RequestContext<RoleServer>,
383    ) -> Result<ReadResourceResult, ErrorData> {
384        Err(ErrorData::new(
385            ErrorCode::METHOD_NOT_FOUND,
386            "resources not supported",
387            None,
388        ))
389    }
390
391    async fn subscribe(
392        &self,
393        _request: SubscribeRequestParam,
394        _context: RequestContext<RoleServer>,
395    ) -> Result<(), ErrorData> {
396        Err(ErrorData::new(
397            ErrorCode::METHOD_NOT_FOUND,
398            "resources not supported",
399            None,
400        ))
401    }
402
403    async fn unsubscribe(
404        &self,
405        _request: UnsubscribeRequestParam,
406        _context: RequestContext<RoleServer>,
407    ) -> Result<(), ErrorData> {
408        Err(ErrorData::new(
409            ErrorCode::METHOD_NOT_FOUND,
410            "resources not supported",
411            None,
412        ))
413    }
414}
415
416impl CompletionProvider for Unset {
417    async fn complete(
418        &self,
419        _request: CompleteRequestParam,
420        _context: RequestContext<RoleServer>,
421    ) -> Result<CompleteResult, ErrorData> {
422        Err(ErrorData::new(
423            ErrorCode::METHOD_NOT_FOUND,
424            "completion not supported",
425            None,
426        ))
427    }
428}
429
430impl LoggingProvider for Unset {
431    async fn set_level(
432        &self,
433        _request: SetLevelRequestParam,
434        _context: RequestContext<RoleServer>,
435    ) -> Result<(), ErrorData> {
436        Err(ErrorData::new(
437            ErrorCode::METHOD_NOT_FOUND,
438            "logging not supported",
439            None,
440        ))
441    }
442}