rust_mcp_sdk/mcp_runtimes/client_runtime/
mcp_client_runtime.rs

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