rust_mcp_sdk/mcp_runtimes/client_runtime/
mcp_client_runtime.rs

1use std::sync::Arc;
2
3use async_trait::async_trait;
4use rust_mcp_schema::{
5    schema_utils::{
6        MessageFromClient, NotificationFromServer, RequestFromServer, ResultFromClient,
7        ServerMessage,
8    },
9    InitializeRequestParams, RpcError,
10};
11use rust_mcp_transport::Transport;
12
13use crate::{
14    error::SdkResult, mcp_client::ClientHandler, mcp_traits::mcp_handler::McpClientHandler,
15    McpClient,
16};
17
18use super::ClientRuntime;
19
20/// Creates a new MCP client runtime with the specified configuration.
21///
22/// This function initializes a client for (MCP) by accepting , client details, a transport ,
23/// and a handler for client-side logic.
24///
25/// The resulting `ClientRuntime` is wrapped in an `Arc` for shared ownership across threads.
26///
27/// # Arguments
28/// * `client_details` - Client name , version and capabilities.
29/// * `transport` - An implementation of the `Transport` trait facilitating communication with the MCP server.
30/// * `handler` - An implementation of the `ClientHandler` trait that defines the client's
31///   core behavior and response logic.
32///
33/// # Returns
34/// An `Arc<ClientRuntime>` representing the initialized client, enabling shared access and
35/// asynchronous operation.
36///
37/// # Examples
38/// You can find a detailed example of how to use this function in the repository:
39///
40/// [Repository Example](https://github.com/rust-mcp-stack/rust-mcp-sdk/tree/main/examples/simple-mcp-client)
41pub fn create_client(
42    client_details: InitializeRequestParams,
43    transport: impl Transport<ServerMessage, MessageFromClient>,
44    handler: impl ClientHandler,
45) -> Arc<ClientRuntime> {
46    Arc::new(ClientRuntime::new(
47        client_details,
48        transport,
49        Box::new(ClientInternalHandler::new(Box::new(handler))),
50    ))
51}
52
53/// Internal handler that wraps a `ClientHandler` trait object.
54/// This is used to handle incoming requests and notifications for the client.
55struct ClientInternalHandler<H> {
56    handler: H,
57}
58impl ClientInternalHandler<Box<dyn ClientHandler>> {
59    pub fn new(handler: Box<dyn ClientHandler>) -> Self {
60        Self { handler }
61    }
62}
63
64/// Implementation of the `McpClientHandler` trait for `ClientInternalHandler`.
65/// This handles requests, notifications, and errors from the server by calling proper function of self.handler
66#[async_trait]
67impl McpClientHandler for ClientInternalHandler<Box<dyn ClientHandler>> {
68    /// Handles a request received from the server by passing the request to self.handler
69    async fn handle_request(
70        &self,
71        server_jsonrpc_request: RequestFromServer,
72        runtime: &dyn McpClient,
73    ) -> std::result::Result<ResultFromClient, RpcError> {
74        match server_jsonrpc_request {
75            RequestFromServer::ServerRequest(request) => match request {
76                rust_mcp_schema::ServerRequest::PingRequest(ping_request) => self
77                    .handler
78                    .handle_ping_request(ping_request, runtime)
79                    .await
80                    .map(|value| value.into()),
81                rust_mcp_schema::ServerRequest::CreateMessageRequest(create_message_request) => {
82                    self.handler
83                        .handle_create_message_request(create_message_request, runtime)
84                        .await
85                        .map(|value| value.into())
86                }
87                rust_mcp_schema::ServerRequest::ListRootsRequest(list_roots_request) => self
88                    .handler
89                    .handle_list_roots_request(list_roots_request, runtime)
90                    .await
91                    .map(|value| value.into()),
92            },
93            // Handles custom notifications received from the server by passing the request to self.handler
94            RequestFromServer::CustomRequest(custom_request) => self
95                .handler
96                .handle_custom_request(custom_request, runtime)
97                .await
98                .map(|value| value.into()),
99        }
100    }
101
102    /// Handles errors received from the server by passing the request to self.handler
103    async fn handle_error(
104        &self,
105        jsonrpc_error: RpcError,
106        runtime: &dyn McpClient,
107    ) -> SdkResult<()> {
108        self.handler.handle_error(jsonrpc_error, runtime).await?;
109        Ok(())
110    }
111
112    /// Handles notifications received from the server by passing the request to self.handler
113    async fn handle_notification(
114        &self,
115        server_jsonrpc_notification: NotificationFromServer,
116        runtime: &dyn McpClient,
117    ) -> SdkResult<()> {
118        match server_jsonrpc_notification {
119            NotificationFromServer::ServerNotification(server_notification) => {
120                match server_notification {
121                    rust_mcp_schema::ServerNotification::CancelledNotification(
122                        cancelled_notification,
123                    ) => {
124                        self.handler
125                            .handle_cancelled_notification(cancelled_notification, runtime)
126                            .await?;
127                    }
128                    rust_mcp_schema::ServerNotification::ProgressNotification(
129                        progress_notification,
130                    ) => {
131                        self.handler
132                            .handle_progress_notification(progress_notification, runtime)
133                            .await?;
134                    }
135                    rust_mcp_schema::ServerNotification::ResourceListChangedNotification(
136                        resource_list_changed_notification,
137                    ) => {
138                        self.handler
139                            .handle_resource_list_changed_notification(
140                                resource_list_changed_notification,
141                                runtime,
142                            )
143                            .await?;
144                    }
145                    rust_mcp_schema::ServerNotification::ResourceUpdatedNotification(
146                        resource_updated_notification,
147                    ) => {
148                        self.handler
149                            .handle_resource_updated_notification(
150                                resource_updated_notification,
151                                runtime,
152                            )
153                            .await?;
154                    }
155                    rust_mcp_schema::ServerNotification::PromptListChangedNotification(
156                        prompt_list_changed_notification,
157                    ) => {
158                        self.handler
159                            .handle_prompt_list_changed_notification(
160                                prompt_list_changed_notification,
161                                runtime,
162                            )
163                            .await?;
164                    }
165                    rust_mcp_schema::ServerNotification::ToolListChangedNotification(
166                        tool_list_changed_notification,
167                    ) => {
168                        self.handler
169                            .handle_tool_list_changed_notification(
170                                tool_list_changed_notification,
171                                runtime,
172                            )
173                            .await?;
174                    }
175                    rust_mcp_schema::ServerNotification::LoggingMessageNotification(
176                        logging_message_notification,
177                    ) => {
178                        self.handler
179                            .handle_logging_message_notification(
180                                logging_message_notification,
181                                runtime,
182                            )
183                            .await?;
184                    }
185                }
186            }
187            // Handles custom notifications received from the server by passing the request to self.handler
188            NotificationFromServer::CustomNotification(custom_notification) => {
189                self.handler
190                    .handle_custom_notification(custom_notification, runtime)
191                    .await?;
192            }
193        }
194        Ok(())
195    }
196
197    /// Handles process errors received from the server over stderr
198    async fn handle_process_error(
199        &self,
200        error_message: String,
201        runtime: &dyn McpClient,
202    ) -> SdkResult<()> {
203        self.handler
204            .handle_process_error(error_message, runtime)
205            .await
206            .map_err(|err| err.into())
207    }
208}