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, LoggingLevel,
10    LoggingMessageNotificationParams, NotificationParams, ProgressToken, 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    fn session_id(&self) -> Option<SessionId>;
104
105    async fn send(
106        &self,
107        message: MessageFromServer,
108        request_id: Option<RequestId>,
109        request_timeout: Option<Duration>,
110    ) -> SdkResult<Option<ClientMessage>>;
111
112    async fn send_batch(
113        &self,
114        messages: Vec<ServerMessage>,
115        request_timeout: Option<Duration>,
116    ) -> SdkResult<Option<Vec<ClientMessage>>>;
117
118    /// Checks whether the server has been initialized with client
119    fn is_initialized(&self) -> bool {
120        self.client_info().is_some()
121    }
122
123    /// Returns the client's name and version information once initialization is complete.
124    /// This method retrieves the client details, if available, after successful initialization.
125    fn client_version(&self) -> Option<Implementation> {
126        self.client_info()
127            .map(|client_details| client_details.client_info)
128    }
129
130    /// Returns the server's capabilities.
131    fn capabilities(&self) -> &ServerCapabilities {
132        &self.server_info().capabilities
133    }
134
135    /*******************
136          Requests
137    *******************/
138
139    /// Sends a request to the client and processes the response.
140    ///
141    /// This function sends a `RequestFromServer` message to the client, waits for the response,
142    /// and handles the result. If the response is empty or of an invalid type, an error is returned.
143    /// Otherwise, it returns the result from the client.
144    async fn request(
145        &self,
146        request: RequestFromServer,
147        timeout: Option<Duration>,
148    ) -> SdkResult<ResultFromClient> {
149        // keep a clone of the request for the task store
150        let request_clone = if request.is_task_augmented() {
151            Some(request.clone())
152        } else {
153            None
154        };
155        // Send the request and receive the response.
156        let response = self
157            .send(MessageFromServer::RequestFromServer(request), None, timeout)
158            .await?;
159
160        let client_message = response.ok_or_else(|| {
161            RpcError::internal_error()
162                .with_message("An empty response was received from the client.".to_string())
163        })?;
164
165        if client_message.is_error() {
166            return Err(client_message.as_error()?.error.into());
167        }
168
169        let client_response = client_message.as_response()?;
170
171        // track awaiting tasks in the client_task_store
172        // CreateTaskResult indicates that a task-augmented request was sent
173        // we keep request tasks in client_task_store and poll until task is in terminal status
174        if let ResultFromClient::CreateTaskResult(create_task_result) = &client_response.result {
175            if let Some(request_to_store) = request_clone {
176                if let Some(client_task_store) = self.client_task_store() {
177                    let session_id = self.session_id();
178                    client_task_store
179                        .create_task(
180                            CreateTaskOptions {
181                                ttl: create_task_result.task.ttl,
182                                poll_interval: create_task_result.task.poll_interval,
183                                meta: create_task_result.meta.clone(),
184                            },
185                            client_response.id.clone(),
186                            ServerJsonrpcRequest::new(client_response.id, request_to_store),
187                            session_id,
188                        )
189                        .await;
190                }
191            } else {
192                return Err(RpcError::internal_error()
193                    .with_message("No eligible request found for task storage.".to_string())
194                    .into());
195            }
196        }
197
198        return Ok(client_response.result);
199    }
200
201    /// Sends an elicitation request to the client to prompt user input and returns the received response.
202    ///
203    /// The requested_schema argument allows servers to define the structure of the expected response using a restricted subset of JSON Schema.
204    /// To simplify client user experience, elicitation schemas are limited to flat objects with primitive properties only
205    async fn request_elicitation(&self, params: ElicitRequestParams) -> SdkResult<ElicitResult> {
206        let response = self
207            .request(RequestFromServer::ElicitRequest(params), None)
208            .await?;
209        ElicitResult::try_from(response).map_err(|err| err.into())
210    }
211
212    async fn request_elicitation_task(
213        &self,
214        params: ElicitRequestParams,
215    ) -> SdkResult<CreateTaskResult> {
216        if !params.is_task_augmented() {
217            return Err(RpcError::invalid_params()
218                .with_message(
219                    "Invalid parameters: the request is not identified as task-augmented."
220                        .to_string(),
221                )
222                .into());
223        }
224        let response = self
225            .request(RequestFromServer::ElicitRequest(params), None)
226            .await?;
227
228        let response = CreateTaskResult::try_from(response)?;
229
230        Ok(response)
231    }
232
233    /// Request a list of root URIs from the client. Roots allow
234    /// servers to ask for specific directories or files to operate on. A common example
235    /// for roots is providing a set of repositories or directories a server should operate on.
236    /// This request is typically used when the server needs to understand the file system
237    /// structure or access specific locations that the client has permission to read from
238    async fn request_root_list(&self, params: Option<RequestParams>) -> SdkResult<ListRootsResult> {
239        let response = self
240            .request(RequestFromServer::ListRootsRequest(params), None)
241            .await?;
242        ListRootsResult::try_from(response).map_err(|err| err.into())
243    }
244
245    /// A ping request to check that the other party is still alive.
246    /// The receiver must promptly respond, or else may be disconnected.
247    ///
248    /// This function creates a `PingRequest` with no specific parameters, sends the request and awaits the response
249    /// Once the response is received, it attempts to convert it into the expected
250    /// result type.
251    ///
252    /// # Returns
253    /// A `SdkResult` containing the `rust_mcp_schema::Result` if the request is successful.
254    /// If the request or conversion fails, an error is returned.
255    async fn ping(
256        &self,
257        params: Option<RequestParams>,
258        timeout: Option<Duration>,
259    ) -> SdkResult<crate::schema::Result> {
260        let response = self
261            .request(RequestFromServer::PingRequest(params), timeout)
262            .await?;
263        Ok(response.try_into()?)
264    }
265
266    /// A request from the server to sample an LLM via the client.
267    /// The client has full discretion over which model to select.
268    /// The client should also inform the user before beginning sampling,
269    /// to allow them to inspect the request (human in the loop)
270    /// and decide whether to approve it.
271    async fn request_message_creation(
272        &self,
273        params: CreateMessageRequestParams,
274    ) -> SdkResult<CreateMessageResult> {
275        let response = self
276            .request(RequestFromServer::CreateMessageRequest(params), None)
277            .await?;
278        Ok(response.try_into()?)
279    }
280
281    ///Send a request to retrieve the state of a task.
282    async fn request_get_task(&self, params: GetTaskParams) -> SdkResult<GetTaskResult> {
283        let response = self
284            .request(RequestFromServer::GetTaskRequest(params), None)
285            .await?;
286        Ok(response.try_into()?)
287    }
288
289    ///Send a request to retrieve the result of a completed task.
290    async fn request_get_task_payload(
291        &self,
292        params: GetTaskPayloadParams,
293    ) -> SdkResult<ClientTaskResult> {
294        let response = self
295            .request(RequestFromServer::GetTaskPayloadRequest(params), None)
296            .await?;
297        Ok(response.try_into()?)
298    }
299
300    ///Send a request to cancel a task.
301    async fn request_task_cancellation(
302        &self,
303        params: CancelTaskParams,
304    ) -> SdkResult<CancelTaskResult> {
305        let response = self
306            .request(RequestFromServer::CancelTaskRequest(params), None)
307            .await?;
308        Ok(response.try_into()?)
309    }
310
311    ///A request to retrieve a list of tasks.
312    async fn request_task_list(
313        &self,
314        params: Option<PaginatedRequestParams>,
315    ) -> SdkResult<ListTasksResult> {
316        let response = self
317            .request(RequestFromServer::ListTasksRequest(params), None)
318            .await?;
319        Ok(response.try_into()?)
320    }
321
322    ///Send a custom request with a custom method name and params
323    async fn request_custom(&self, params: CustomRequest) -> SdkResult<GenericResult> {
324        let response = self
325            .request(RequestFromServer::CustomRequest(params), None)
326            .await?;
327        Ok(response.try_into()?)
328    }
329
330    /*******************
331        Notifications
332    *******************/
333
334    /// Sends a notification. This is a one-way message that is not expected
335    /// to return any response. The method asynchronously sends the notification using
336    /// the transport layer and does not wait for any acknowledgement or result.
337    async fn send_notification(&self, notification: NotificationFromServer) -> SdkResult<()> {
338        self.send(
339            MessageFromServer::NotificationFromServer(notification),
340            None,
341            None,
342        )
343        .await?;
344        Ok(())
345    }
346
347    /// Send log message notification from server to client.
348    /// If no logging/setLevel request has been sent from the client, the server MAY decide which messages to send automatically.
349    async fn notify_log_message(&self, params: LoggingMessageNotificationParams) -> SdkResult<()> {
350        self.send_notification(NotificationFromServer::LoggingMessageNotification(params))
351            .await
352    }
353
354    ///Send an optional notification from the server to the client, informing it that
355    /// the list of prompts it offers has changed.
356    /// This may be issued by servers without any previous subscription from the client.
357    async fn notify_prompt_list_changed(
358        &self,
359        params: Option<NotificationParams>,
360    ) -> SdkResult<()> {
361        self.send_notification(NotificationFromServer::PromptListChangedNotification(
362            params,
363        ))
364        .await
365    }
366
367    ///Send an optional notification from the server to the client,
368    /// informing it that the list of resources it can read from has changed.
369    /// This may be issued by servers without any previous subscription from the client.
370    async fn notify_resource_list_changed(
371        &self,
372        params: Option<NotificationParams>,
373    ) -> SdkResult<()> {
374        self.send_notification(NotificationFromServer::ResourceListChangedNotification(
375            params,
376        ))
377        .await
378    }
379
380    ///Send a notification from the server to the client, informing it that
381    /// a resource has changed and may need to be read again.
382    ///  This should only be sent if the client previously sent a resources/subscribe request.
383    async fn notify_resource_updated(
384        &self,
385        params: ResourceUpdatedNotificationParams,
386    ) -> SdkResult<()> {
387        self.send_notification(NotificationFromServer::ResourceUpdatedNotification(params))
388            .await
389    }
390
391    ///Send an optional notification from the server to the client, informing it that
392    /// the list of tools it offers has changed.
393    /// This may be issued by servers without any previous subscription from the client.
394    async fn notify_tool_list_changed(&self, params: Option<NotificationParams>) -> SdkResult<()> {
395        self.send_notification(NotificationFromServer::ToolListChangedNotification(params))
396            .await
397    }
398
399    /// This notification can be sent to indicate that it is cancelling a previously-issued request.
400    /// 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.
401    /// This notification indicates that the result will be unused, so any associated processing SHOULD cease.
402    /// A client MUST NOT attempt to cancel its initialize request.
403    /// For task cancellation, use the tasks/cancel request instead of this notification.
404    async fn notify_cancellation(&self, params: CancelledNotificationParams) -> SdkResult<()> {
405        self.send_notification(NotificationFromServer::CancelledNotification(params))
406            .await
407    }
408
409    ///Send an out-of-band notification used to inform the receiver of a progress update for a long-running request.
410    async fn notify_progress(&self, params: ProgressNotificationParams) -> SdkResult<()> {
411        self.send_notification(NotificationFromServer::ProgressNotification(params))
412            .await
413    }
414
415    /// Convenience shortcut for [`Self::notify_progress`].
416    ///
417    /// Sends a progress update for a long-running request without requiring
418    /// the caller to construct a full [`ProgressNotificationParams`].
419    ///
420    /// # Arguments
421    /// - `progress_token` — the token supplied by the client in the original
422    ///   request's `_meta.progressToken`. If `None`, the notification is
423    ///   skipped (the server has no token to address).
424    /// - `progress` — current progress value.
425    /// - `total` — optional total value the progress is approaching.
426    /// - `message` — optional human-readable status message.
427    async fn report_progress(
428        &self,
429        progress_token: Option<ProgressToken>,
430        progress: f64,
431        total: Option<f64>,
432        message: Option<String>,
433    ) -> SdkResult<()> {
434        let Some(progress_token) = progress_token else {
435            return Ok(());
436        };
437        self.notify_progress(ProgressNotificationParams {
438            progress_token,
439            progress,
440            total,
441            message,
442            meta: None,
443        })
444        .await
445    }
446
447    /// Convenience shortcut for [`Self::notify_log_message`] at [`LoggingLevel::Debug`].
448    ///
449    /// The message is sent as a JSON string in the notification's `data`
450    /// field. Use [`Self::notify_log_message`] directly for structured data
451    /// or a custom `logger` name.
452    async fn log_debug(&self, message: String) -> SdkResult<()> {
453        self.notify_log_message(LoggingMessageNotificationParams {
454            level: LoggingLevel::Debug,
455            data: ::serde_json::Value::String(message),
456            logger: None,
457            meta: None,
458        })
459        .await
460    }
461
462    /// Convenience shortcut for [`Self::notify_log_message`] at [`LoggingLevel::Info`].
463    async fn log_info(&self, message: String) -> SdkResult<()> {
464        self.notify_log_message(LoggingMessageNotificationParams {
465            level: LoggingLevel::Info,
466            data: ::serde_json::Value::String(message),
467            logger: None,
468            meta: None,
469        })
470        .await
471    }
472
473    /// Convenience shortcut for [`Self::notify_log_message`] at [`LoggingLevel::Warning`].
474    async fn log_warn(&self, message: String) -> SdkResult<()> {
475        self.notify_log_message(LoggingMessageNotificationParams {
476            level: LoggingLevel::Warning,
477            data: ::serde_json::Value::String(message),
478            logger: None,
479            meta: None,
480        })
481        .await
482    }
483
484    /// Convenience shortcut for [`Self::notify_log_message`] at [`LoggingLevel::Error`].
485    async fn log_error(&self, message: String) -> SdkResult<()> {
486        self.notify_log_message(LoggingMessageNotificationParams {
487            level: LoggingLevel::Error,
488            data: ::serde_json::Value::String(message),
489            logger: None,
490            meta: None,
491        })
492        .await
493    }
494
495    /// Send an optional notification from the receiver to the requestor, informing them that a task's status has changed.
496    /// Receivers are not required to send these notifications.
497    async fn notify_task_status(&self, params: TaskStatusNotificationParams) -> SdkResult<()> {
498        self.send_notification(NotificationFromServer::TaskStatusNotification(params))
499            .await
500    }
501
502    ///An optional notification from the server to the client, informing it of a completion of a out-of-band elicitation request.
503    async fn notify_elicitation_completed(&self, params: ElicitCompleteParams) -> SdkResult<()> {
504        self.send_notification(NotificationFromServer::ElicitationCompleteNotification(
505            params,
506        ))
507        .await
508    }
509
510    ///Send a custom notification
511    async fn notify_custom(&self, params: CustomNotification) -> SdkResult<()> {
512        self.send_notification(NotificationFromServer::CustomNotification(params))
513            .await
514    }
515
516    #[deprecated(since = "0.8.0", note = "Use `request_root_list()` instead.")]
517    async fn list_roots(&self, params: Option<RequestParams>) -> SdkResult<ListRootsResult> {
518        let response = self
519            .request(RequestFromServer::ListRootsRequest(params), None)
520            .await?;
521        ListRootsResult::try_from(response).map_err(|err| err.into())
522    }
523
524    #[deprecated(since = "0.8.0", note = "Use `request_elicitation()` instead.")]
525    async fn elicit_input(&self, params: ElicitRequestParams) -> SdkResult<ElicitResult> {
526        let response = self
527            .request(RequestFromServer::ElicitRequest(params), None)
528            .await?;
529        ElicitResult::try_from(response).map_err(|err| err.into())
530    }
531
532    #[deprecated(since = "0.8.0", note = "Use `request_message_creation()` instead.")]
533    async fn create_message(
534        &self,
535        params: CreateMessageRequestParams,
536    ) -> SdkResult<CreateMessageResult> {
537        let response = self
538            .request(RequestFromServer::CreateMessageRequest(params), None)
539            .await?;
540        Ok(response.try_into()?)
541    }
542
543    #[deprecated(since = "0.8.0", note = "Use `notify_tool_list_changed()` instead.")]
544    async fn send_tool_list_changed(&self, params: Option<NotificationParams>) -> SdkResult<()> {
545        self.send_notification(NotificationFromServer::ToolListChangedNotification(params))
546            .await
547    }
548
549    #[deprecated(since = "0.8.0", note = "Use `notify_resource_updated()` instead.")]
550    async fn send_resource_updated(
551        &self,
552        params: ResourceUpdatedNotificationParams,
553    ) -> SdkResult<()> {
554        self.send_notification(NotificationFromServer::ResourceUpdatedNotification(params))
555            .await
556    }
557
558    #[deprecated(
559        since = "0.8.0",
560        note = "Use `notify_resource_list_changed()` instead."
561    )]
562    async fn send_resource_list_changed(
563        &self,
564        params: Option<NotificationParams>,
565    ) -> SdkResult<()> {
566        self.send_notification(NotificationFromServer::ResourceListChangedNotification(
567            params,
568        ))
569        .await
570    }
571
572    #[deprecated(since = "0.8.0", note = "Use `notify_prompt_list_changed()` instead.")]
573    async fn send_prompt_list_changed(&self, params: Option<NotificationParams>) -> SdkResult<()> {
574        self.send_notification(NotificationFromServer::PromptListChangedNotification(
575            params,
576        ))
577        .await
578    }
579
580    #[deprecated(since = "0.8.0", note = "Use `notify_log_message()` instead.")]
581    async fn send_logging_message(
582        &self,
583        params: LoggingMessageNotificationParams,
584    ) -> SdkResult<()> {
585        self.send_notification(NotificationFromServer::LoggingMessageNotification(params))
586            .await
587    }
588}