Skip to main content

rust_mcp_sdk/mcp_traits/
mcp_server.rs

1use crate::auth::AuthInfo;
2use crate::error::SdkResult;
3use crate::schema::{
4    schema_utils::{
5        ClientMessage, McpMessage, MessageFromServer, NotificationFromServer, RequestFromServer,
6        ResultFromClient, ServerMessage,
7    },
8    CreateMessageRequestParams, CreateMessageResult, ElicitRequestParams, ElicitResult,
9    Implementation, InitializeRequestParams, InitializeResult, ListRootsResult,
10    LoggingMessageNotificationParams, NotificationParams, RequestId, RequestParams,
11    ResourceUpdatedNotificationParams, RpcError, ServerCapabilities,
12};
13use crate::task_store::{ClientTaskStore, CreateTaskOptions, ServerTaskStore};
14use async_trait::async_trait;
15use rust_mcp_schema::schema_utils::{
16    ClientTaskResult, CustomNotification, CustomRequest, ServerJsonrpcRequest,
17};
18use rust_mcp_schema::{
19    CancelTaskParams, CancelTaskResult, CancelledNotificationParams, CreateTaskResult,
20    ElicitCompleteParams, GenericResult, GetTaskParams, GetTaskPayloadParams, GetTaskResult,
21    ListTasksResult, PaginatedRequestParams, ProgressNotificationParams,
22    TaskStatusNotificationParams,
23};
24use rust_mcp_transport::SessionId;
25use std::{sync::Arc, time::Duration};
26use tokio::sync::RwLockReadGuard;
27
28#[async_trait]
29pub trait McpServer: Sync + Send {
30    async fn start(self: Arc<Self>) -> SdkResult<()>;
31    async fn set_client_details(&self, client_details: InitializeRequestParams) -> SdkResult<()>;
32    fn server_info(&self) -> &InitializeResult;
33    fn client_info(&self) -> Option<InitializeRequestParams>;
34
35    async fn auth_info(&self) -> RwLockReadGuard<'_, Option<AuthInfo>>;
36    async fn auth_info_cloned(&self) -> Option<AuthInfo>;
37    async fn update_auth_info(&self, auth_info: Option<AuthInfo>);
38
39    async fn wait_for_initialization(&self);
40
41    /// Returns the server-side task store, if available.
42    ///
43    /// This store tracks tasks initiated by the client that are being processed by the server.
44    fn task_store(&self) -> Option<Arc<ServerTaskStore>>;
45
46    /// Returns the client-side task store, if available.
47    ///
48    /// This store tracks tasks initiated by the server that are processed by the client.
49    /// It is responsible for polling task status until each task reaches a terminal state.
50    fn client_task_store(&self) -> Option<Arc<ClientTaskStore>>;
51
52    /// Checks if the client supports sampling.
53    ///
54    /// This function retrieves the client information and checks if the
55    /// client has sampling capabilities listed. If the client info has
56    /// not been retrieved yet, it returns `None`. Otherwise, it returns
57    /// `Some(true)` if sampling is supported, or `Some(false)` if not.
58    ///
59    /// # Returns
60    /// - `None` if client information is not yet available.
61    /// - `Some(true)` if sampling is supported by the client.
62    /// - `Some(false)` if sampling is not supported by the client.
63    fn client_supports_sampling(&self) -> Option<bool> {
64        self.client_info()
65            .map(|client_details| client_details.capabilities.sampling.is_some())
66    }
67
68    /// Checks if the client supports listing roots.
69    ///
70    /// This function retrieves the client information and checks if the
71    /// client has listing roots capabilities listed. If the client info has
72    /// not been retrieved yet, it returns `None`. Otherwise, it returns
73    /// `Some(true)` if listing roots is supported, or `Some(false)` if not.
74    ///
75    /// # Returns
76    /// - `None` if client information is not yet available.
77    /// - `Some(true)` if listing roots is supported by the client.
78    /// - `Some(false)` if listing roots is not supported by the client.
79    fn client_supports_root_list(&self) -> Option<bool> {
80        self.client_info()
81            .map(|client_details| client_details.capabilities.roots.is_some())
82    }
83
84    /// Checks if the client has experimental capabilities available.
85    ///
86    /// This function retrieves the client information and checks if the
87    /// client has experimental listed in its capabilities. If the client info
88    /// has not been retrieved yet, it returns `None`. Otherwise, it returns
89    /// `Some(true)` if experimental is available, or `Some(false)` if not.
90    ///
91    /// # Returns
92    /// - `None` if client information is not yet available.
93    /// - `Some(true)` if experimental capabilities are available on the client.
94    /// - `Some(false)` if no experimental capabilities are available on the client.
95    fn client_supports_experimental(&self) -> Option<bool> {
96        self.client_info()
97            .map(|client_details| client_details.capabilities.experimental.is_some())
98    }
99
100    /// Sends a message to the standard error output (stderr) asynchronously.
101    async fn stderr_message(&self, message: String) -> SdkResult<()>;
102
103    #[cfg(feature = "hyper-server")]
104    fn session_id(&self) -> Option<SessionId>;
105
106    async fn send(
107        &self,
108        message: MessageFromServer,
109        request_id: Option<RequestId>,
110        request_timeout: Option<Duration>,
111    ) -> SdkResult<Option<ClientMessage>>;
112
113    async fn send_batch(
114        &self,
115        messages: Vec<ServerMessage>,
116        request_timeout: Option<Duration>,
117    ) -> SdkResult<Option<Vec<ClientMessage>>>;
118
119    /// Checks whether the server has been initialized with client
120    fn is_initialized(&self) -> bool {
121        self.client_info().is_some()
122    }
123
124    /// Returns the client's name and version information once initialization is complete.
125    /// This method retrieves the client details, if available, after successful initialization.
126    fn client_version(&self) -> Option<Implementation> {
127        self.client_info()
128            .map(|client_details| client_details.client_info)
129    }
130
131    /// Returns the server's capabilities.
132    fn capabilities(&self) -> &ServerCapabilities {
133        &self.server_info().capabilities
134    }
135
136    /*******************
137          Requests
138    *******************/
139
140    /// Sends a request to the client and processes the response.
141    ///
142    /// This function sends a `RequestFromServer` message to the client, waits for the response,
143    /// and handles the result. If the response is empty or of an invalid type, an error is returned.
144    /// Otherwise, it returns the result from the client.
145    async fn request(
146        &self,
147        request: RequestFromServer,
148        timeout: Option<Duration>,
149    ) -> SdkResult<ResultFromClient> {
150        // keep a clone of the request for the task store
151        let request_clone = if request.is_task_augmented() {
152            Some(request.clone())
153        } else {
154            None
155        };
156        // Send the request and receive the response.
157        let response = self
158            .send(MessageFromServer::RequestFromServer(request), None, timeout)
159            .await?;
160
161        let client_message = response.ok_or_else(|| {
162            RpcError::internal_error()
163                .with_message("An empty response was received from the client.".to_string())
164        })?;
165
166        if client_message.is_error() {
167            return Err(client_message.as_error()?.error.into());
168        }
169
170        let client_response = client_message.as_response()?;
171
172        // track awaiting tasks in the client_task_store
173        // CreateTaskResult indicates that a task-augmented request was sent
174        // we keep request tasks in client_task_store and poll until task is in terminal status
175        if let ResultFromClient::CreateTaskResult(create_task_result) = &client_response.result {
176            if let Some(request_to_store) = request_clone {
177                if let Some(client_task_store) = self.client_task_store() {
178                    let session_id = {
179                        #[cfg(feature = "hyper-server")]
180                        {
181                            self.session_id()
182                        }
183                        #[cfg(not(feature = "hyper-server"))]
184                        None
185                    };
186                    client_task_store
187                        .create_task(
188                            CreateTaskOptions {
189                                ttl: create_task_result.task.ttl,
190                                poll_interval: create_task_result.task.poll_interval,
191                                meta: create_task_result.meta.clone(),
192                            },
193                            client_response.id.clone(),
194                            ServerJsonrpcRequest::new(client_response.id, request_to_store),
195                            session_id,
196                        )
197                        .await;
198                }
199            } else {
200                return Err(RpcError::internal_error()
201                    .with_message("No eligible request found for task storage.".to_string())
202                    .into());
203            }
204        }
205
206        return Ok(client_response.result);
207    }
208
209    /// Sends an elicitation request to the client to prompt user input and returns the received response.
210    ///
211    /// The requested_schema argument allows servers to define the structure of the expected response using a restricted subset of JSON Schema.
212    /// To simplify client user experience, elicitation schemas are limited to flat objects with primitive properties only
213    async fn request_elicitation(&self, params: ElicitRequestParams) -> SdkResult<ElicitResult> {
214        let response = self
215            .request(RequestFromServer::ElicitRequest(params), None)
216            .await?;
217        ElicitResult::try_from(response).map_err(|err| err.into())
218    }
219
220    async fn request_elicitation_task(
221        &self,
222        params: ElicitRequestParams,
223    ) -> SdkResult<CreateTaskResult> {
224        if !params.is_task_augmented() {
225            return Err(RpcError::invalid_params()
226                .with_message(
227                    "Invalid parameters: the request is not identified as task-augmented."
228                        .to_string(),
229                )
230                .into());
231        }
232        let response = self
233            .request(RequestFromServer::ElicitRequest(params), None)
234            .await?;
235
236        let response = CreateTaskResult::try_from(response)?;
237
238        Ok(response)
239    }
240
241    /// Request a list of root URIs from the client. Roots allow
242    /// servers to ask for specific directories or files to operate on. A common example
243    /// for roots is providing a set of repositories or directories a server should operate on.
244    /// This request is typically used when the server needs to understand the file system
245    /// structure or access specific locations that the client has permission to read from
246    async fn request_root_list(&self, params: Option<RequestParams>) -> SdkResult<ListRootsResult> {
247        let response = self
248            .request(RequestFromServer::ListRootsRequest(params), None)
249            .await?;
250        ListRootsResult::try_from(response).map_err(|err| err.into())
251    }
252
253    /// A ping request to check that the other party is still alive.
254    /// The receiver must promptly respond, or else may be disconnected.
255    ///
256    /// This function creates a `PingRequest` with no specific parameters, sends the request and awaits the response
257    /// Once the response is received, it attempts to convert it into the expected
258    /// result type.
259    ///
260    /// # Returns
261    /// A `SdkResult` containing the `rust_mcp_schema::Result` if the request is successful.
262    /// If the request or conversion fails, an error is returned.
263    async fn ping(
264        &self,
265        params: Option<RequestParams>,
266        timeout: Option<Duration>,
267    ) -> SdkResult<crate::schema::Result> {
268        let response = self
269            .request(RequestFromServer::PingRequest(params), timeout)
270            .await?;
271        Ok(response.try_into()?)
272    }
273
274    /// A request from the server to sample an LLM via the client.
275    /// The client has full discretion over which model to select.
276    /// The client should also inform the user before beginning sampling,
277    /// to allow them to inspect the request (human in the loop)
278    /// and decide whether to approve it.
279    async fn request_message_creation(
280        &self,
281        params: CreateMessageRequestParams,
282    ) -> SdkResult<CreateMessageResult> {
283        let response = self
284            .request(RequestFromServer::CreateMessageRequest(params), None)
285            .await?;
286        Ok(response.try_into()?)
287    }
288
289    ///Send a request to retrieve the state of a task.
290    async fn request_get_task(&self, params: GetTaskParams) -> SdkResult<GetTaskResult> {
291        let response = self
292            .request(RequestFromServer::GetTaskRequest(params), None)
293            .await?;
294        Ok(response.try_into()?)
295    }
296
297    ///Send a request to retrieve the result of a completed task.
298    async fn request_get_task_payload(
299        &self,
300        params: GetTaskPayloadParams,
301    ) -> SdkResult<ClientTaskResult> {
302        let response = self
303            .request(RequestFromServer::GetTaskPayloadRequest(params), None)
304            .await?;
305        Ok(response.try_into()?)
306    }
307
308    ///Send a request to cancel a task.
309    async fn request_task_cancellation(
310        &self,
311        params: CancelTaskParams,
312    ) -> SdkResult<CancelTaskResult> {
313        let response = self
314            .request(RequestFromServer::CancelTaskRequest(params), None)
315            .await?;
316        Ok(response.try_into()?)
317    }
318
319    ///A request to retrieve a list of tasks.
320    async fn request_task_list(
321        &self,
322        params: Option<PaginatedRequestParams>,
323    ) -> SdkResult<ListTasksResult> {
324        let response = self
325            .request(RequestFromServer::ListTasksRequest(params), None)
326            .await?;
327        Ok(response.try_into()?)
328    }
329
330    ///Send a custom request with a custom method name and params
331    async fn request_custom(&self, params: CustomRequest) -> SdkResult<GenericResult> {
332        let response = self
333            .request(RequestFromServer::CustomRequest(params), None)
334            .await?;
335        Ok(response.try_into()?)
336    }
337
338    /*******************
339        Notifications
340    *******************/
341
342    /// Sends a notification. This is a one-way message that is not expected
343    /// to return any response. The method asynchronously sends the notification using
344    /// the transport layer and does not wait for any acknowledgement or result.
345    async fn send_notification(&self, notification: NotificationFromServer) -> SdkResult<()> {
346        self.send(
347            MessageFromServer::NotificationFromServer(notification),
348            None,
349            None,
350        )
351        .await?;
352        Ok(())
353    }
354
355    /// Send log message notification from server to client.
356    /// If no logging/setLevel request has been sent from the client, the server MAY decide which messages to send automatically.
357    async fn notify_log_message(&self, params: LoggingMessageNotificationParams) -> SdkResult<()> {
358        self.send_notification(NotificationFromServer::LoggingMessageNotification(params))
359            .await
360    }
361
362    ///Send an optional notification from the server to the client, informing it that
363    /// the list of prompts it offers has changed.
364    /// This may be issued by servers without any previous subscription from the client.
365    async fn notify_prompt_list_changed(
366        &self,
367        params: Option<NotificationParams>,
368    ) -> SdkResult<()> {
369        self.send_notification(NotificationFromServer::PromptListChangedNotification(
370            params,
371        ))
372        .await
373    }
374
375    ///Send an optional notification from the server to the client,
376    /// informing it that the list of resources it can read from has changed.
377    /// This may be issued by servers without any previous subscription from the client.
378    async fn notify_resource_list_changed(
379        &self,
380        params: Option<NotificationParams>,
381    ) -> SdkResult<()> {
382        self.send_notification(NotificationFromServer::ResourceListChangedNotification(
383            params,
384        ))
385        .await
386    }
387
388    ///Send a notification from the server to the client, informing it that
389    /// a resource has changed and may need to be read again.
390    ///  This should only be sent if the client previously sent a resources/subscribe request.
391    async fn notify_resource_updated(
392        &self,
393        params: ResourceUpdatedNotificationParams,
394    ) -> SdkResult<()> {
395        self.send_notification(NotificationFromServer::ResourceUpdatedNotification(params))
396            .await
397    }
398
399    ///Send an optional notification from the server to the client, informing it that
400    /// the list of tools it offers has changed.
401    /// This may be issued by servers without any previous subscription from the client.
402    async fn notify_tool_list_changed(&self, params: Option<NotificationParams>) -> SdkResult<()> {
403        self.send_notification(NotificationFromServer::ToolListChangedNotification(params))
404            .await
405    }
406
407    /// This notification can be sent to indicate that it is cancelling a previously-issued request.
408    /// The request SHOULD still be in-flight, but due to communication latency, it is always possible that this notification MAY arrive after the request has already finished.
409    /// This notification indicates that the result will be unused, so any associated processing SHOULD cease.
410    /// A client MUST NOT attempt to cancel its initialize request.
411    /// For task cancellation, use the tasks/cancel request instead of this notification.
412    async fn notify_cancellation(&self, params: CancelledNotificationParams) -> SdkResult<()> {
413        self.send_notification(NotificationFromServer::CancelledNotification(params))
414            .await
415    }
416
417    ///Send an out-of-band notification used to inform the receiver of a progress update for a long-running request.
418    async fn notify_progress(&self, params: ProgressNotificationParams) -> SdkResult<()> {
419        self.send_notification(NotificationFromServer::ProgressNotification(params))
420            .await
421    }
422
423    /// Send an optional notification from the receiver to the requestor, informing them that a task's status has changed.
424    /// Receivers are not required to send these notifications.
425    async fn notify_task_status(&self, params: TaskStatusNotificationParams) -> SdkResult<()> {
426        self.send_notification(NotificationFromServer::TaskStatusNotification(params))
427            .await
428    }
429
430    ///An optional notification from the server to the client, informing it of a completion of a out-of-band elicitation request.
431    async fn notify_elicitation_completed(&self, params: ElicitCompleteParams) -> SdkResult<()> {
432        self.send_notification(NotificationFromServer::ElicitationCompleteNotification(
433            params,
434        ))
435        .await
436    }
437
438    ///Send a custom notification
439    async fn notify_custom(&self, params: CustomNotification) -> SdkResult<()> {
440        self.send_notification(NotificationFromServer::CustomNotification(params))
441            .await
442    }
443
444    #[deprecated(since = "0.8.0", note = "Use `request_root_list()` instead.")]
445    async fn list_roots(&self, params: Option<RequestParams>) -> SdkResult<ListRootsResult> {
446        let response = self
447            .request(RequestFromServer::ListRootsRequest(params), None)
448            .await?;
449        ListRootsResult::try_from(response).map_err(|err| err.into())
450    }
451
452    #[deprecated(since = "0.8.0", note = "Use `request_elicitation()` instead.")]
453    async fn elicit_input(&self, params: ElicitRequestParams) -> SdkResult<ElicitResult> {
454        let response = self
455            .request(RequestFromServer::ElicitRequest(params), None)
456            .await?;
457        ElicitResult::try_from(response).map_err(|err| err.into())
458    }
459
460    #[deprecated(since = "0.8.0", note = "Use `request_message_creation()` instead.")]
461    async fn create_message(
462        &self,
463        params: CreateMessageRequestParams,
464    ) -> SdkResult<CreateMessageResult> {
465        let response = self
466            .request(RequestFromServer::CreateMessageRequest(params), None)
467            .await?;
468        Ok(response.try_into()?)
469    }
470
471    #[deprecated(since = "0.8.0", note = "Use `notify_tool_list_changed()` instead.")]
472    async fn send_tool_list_changed(&self, params: Option<NotificationParams>) -> SdkResult<()> {
473        self.send_notification(NotificationFromServer::ToolListChangedNotification(params))
474            .await
475    }
476
477    #[deprecated(since = "0.8.0", note = "Use `notify_resource_updated()` instead.")]
478    async fn send_resource_updated(
479        &self,
480        params: ResourceUpdatedNotificationParams,
481    ) -> SdkResult<()> {
482        self.send_notification(NotificationFromServer::ResourceUpdatedNotification(params))
483            .await
484    }
485
486    #[deprecated(
487        since = "0.8.0",
488        note = "Use `notify_resource_list_changed()` instead."
489    )]
490    async fn send_resource_list_changed(
491        &self,
492        params: Option<NotificationParams>,
493    ) -> SdkResult<()> {
494        self.send_notification(NotificationFromServer::ResourceListChangedNotification(
495            params,
496        ))
497        .await
498    }
499
500    #[deprecated(since = "0.8.0", note = "Use `notify_prompt_list_changed()` instead.")]
501    async fn send_prompt_list_changed(&self, params: Option<NotificationParams>) -> SdkResult<()> {
502        self.send_notification(NotificationFromServer::PromptListChangedNotification(
503            params,
504        ))
505        .await
506    }
507
508    #[deprecated(since = "0.8.0", note = "Use `notify_log_message()` instead.")]
509    async fn send_logging_message(
510        &self,
511        params: LoggingMessageNotificationParams,
512    ) -> SdkResult<()> {
513        self.send_notification(NotificationFromServer::LoggingMessageNotification(params))
514            .await
515    }
516}