rust_mcp_transport/transport.rs
1use crate::{error::TransportResult, message_dispatcher::MessageDispatcher};
2use crate::{schema::RequestId, SessionId};
3use async_trait::async_trait;
4use std::{pin::Pin, sync::Arc, time::Duration};
5use tokio::{
6 sync::oneshot::{self, Sender},
7 task::JoinHandle,
8};
9
10/// Default Timeout in milliseconds
11const DEFAULT_TIMEOUT_MSEC: u64 = 60_000;
12
13/// Enum representing a stream that can either be readable or writable.
14/// This allows the reuse of the same traits for both MCP Server and MCP Client,
15/// where the data direction is reversed.
16///
17/// It encapsulates two types of I/O streams:
18/// - `Readable`: A stream that implements the `AsyncRead` trait for reading data asynchronously.
19/// - `Writable`: A stream that implements the `AsyncWrite` trait for writing data asynchronously.
20///
21pub enum IoStream {
22 Readable(Pin<Box<dyn tokio::io::AsyncRead + Send + Sync>>),
23 Writable(Pin<Box<dyn tokio::io::AsyncWrite + Send + Sync>>),
24}
25
26/// Maximum size (in bytes) of a single newline-delimited incoming message
27/// for all transports (stdio, SSE, streamable HTTP). Messages exceeding
28/// this limit are dropped silently; increase this value if you expect
29/// large tool results or responses.
30pub const DEFAULT_MAX_LINE_LENGTH: usize = 16 * 1024 * 1024;
31
32/// Configuration for the transport layer
33#[derive(Debug, Clone)]
34pub struct TransportOptions {
35 /// The timeout in milliseconds for requests.
36 ///
37 /// This value defines the maximum amount of time to wait for a response before
38 /// considering the request as timed out.
39 pub timeout: Duration,
40 /// Maximum size (in bytes) of a single newline-delimited incoming message.
41 ///
42 /// Messages exceeding this limit are dropped and logged with a warning;
43 /// the stream stays alive and resumes on the next line. If you expect
44 /// large tool results or responses, increase this value.
45 /// Default: 16 MiB.
46 pub max_line_length: usize,
47
48 /// Capacity of the incoming-message channel buffer.
49 ///
50 /// A larger value smooths out head-of-line jitter under bursty traffic at
51 /// the cost of more buffered memory. Defaults to 36.
52 pub channel_capacity: usize,
53}
54impl Default for TransportOptions {
55 fn default() -> Self {
56 Self {
57 timeout: Duration::from_millis(DEFAULT_TIMEOUT_MSEC),
58 max_line_length: DEFAULT_MAX_LINE_LENGTH,
59 channel_capacity: crate::mcp_stream::DEFAULT_MESSAGE_CHANNEL_CAPACITY,
60 }
61 }
62}
63
64/// A trait for dispatching MCP (Message Communication Protocol) messages.
65///
66/// This trait is designed to be implemented by components such as clients, servers, or transports
67/// that send and receive messages in the MCP protocol. It defines the interface for transmitting messages,
68/// optionally awaiting responses, writing raw payloads, and handling batch communication.
69///
70/// # Associated Types
71///
72/// - `R`: The response type expected from a message. This must implement deserialization and be safe
73/// for concurrent use in async contexts.
74/// - `S`: The type of the outgoing message sent directly to the wire. Must be serializable.
75/// - `M`: The internal message type used for responses received from a remote peer.
76/// - `OM`: The outgoing message type submitted to the dispatcher. This is the higher-level form of `S`
77/// used by clients or services submitting requests.
78///
79#[async_trait]
80pub trait McpDispatch<R, S, M, OM>: Send + Sync + 'static
81where
82 R: Clone + Send + Sync + serde::de::DeserializeOwned + 'static,
83 S: Clone + Send + Sync + serde::Serialize + 'static,
84 M: Clone + Send + Sync + serde::de::DeserializeOwned + 'static,
85 OM: Clone + Send + Sync + serde::de::DeserializeOwned + 'static,
86{
87 /// Sends a raw message represented by type `S` and optionally includes a `request_id`.
88 /// The `request_id` is used when sending a message in response to an MCP request.
89 /// It should match the `request_id` of the original request.
90 async fn send_message(
91 &self,
92 message: S,
93 request_timeout: Option<Duration>,
94 ) -> TransportResult<Option<R>>;
95
96 async fn send(&self, message: OM, timeout: Option<Duration>) -> TransportResult<Option<M>>;
97 async fn send_batch(
98 &self,
99 message: Vec<OM>,
100 timeout: Option<Duration>,
101 ) -> TransportResult<Option<Vec<M>>>;
102
103 /// Writes a string payload to the underlying asynchronous writable stream,
104 /// appending a newline character and flushing the stream afterward.
105 ///
106 async fn write_str(&self, payload: &str, skip_store: bool) -> TransportResult<()>;
107}
108
109/// A trait representing the transport layer for the MCP (Message Communication Protocol).
110///
111/// This trait abstracts the transport layer functionality required to send and receive messages
112/// within an MCP-based system. It provides methods to initialize the transport, send and receive
113/// messages, handle errors, manage pending requests, and implement keep-alive functionality.
114///
115/// # Associated Types
116///
117/// - `R`: The type of message expected to be received from the transport layer. Must be deserializable.
118/// - `S`: The type of message to be sent over the transport layer. Must be serializable.
119/// - `M`: The internal message type used by the dispatcher. Typically this wraps or transforms `R`.
120/// - `OR`: The outbound response type expected to be produced by the dispatcher when handling incoming messages.
121/// - `OM`: The outbound message type that the dispatcher expects to send as a reply to received messages.
122///
123#[async_trait]
124pub trait Transport<R, S, M, OR, OM>: Send + Sync + 'static
125where
126 R: Clone + Send + Sync + serde::de::DeserializeOwned + 'static,
127 S: Clone + Send + Sync + serde::Serialize + 'static,
128 M: Clone + Send + Sync + serde::de::DeserializeOwned + 'static,
129 OR: Clone + Send + Sync + serde::Serialize + 'static,
130 OM: Clone + Send + Sync + serde::de::DeserializeOwned + 'static,
131{
132 async fn start(&self) -> TransportResult<tokio_stream::wrappers::ReceiverStream<R>>
133 where
134 MessageDispatcher<M>: McpDispatch<R, OR, M, OM>;
135 fn message_sender(&self) -> Arc<tokio::sync::RwLock<Option<MessageDispatcher<M>>>>;
136 fn error_stream(&self) -> &tokio::sync::RwLock<Option<IoStream>>;
137 async fn shut_down(&self) -> TransportResult<()>;
138 async fn is_shut_down(&self) -> bool;
139 async fn consume_string_payload(&self, payload: &str) -> TransportResult<()>;
140 async fn pending_request_tx(&self, request_id: &RequestId) -> Option<Sender<M>>;
141 async fn keep_alive(
142 &self,
143 interval: Duration,
144 disconnect_tx: oneshot::Sender<()>,
145 ) -> TransportResult<JoinHandle<()>>;
146 async fn session_id(&self) -> Option<SessionId> {
147 None
148 }
149}
150
151/// A composite trait that combines both transport and dispatch capabilities for the MCP protocol.
152///
153/// `TransportDispatcher` unifies the functionality of [`Transport`] and [`McpDispatch`], allowing implementors
154/// to both manage the transport layer and handle message dispatch logic in a single abstraction.
155///
156/// This trait applies to components responsible for the following operations:
157/// - Handle low-level I/O (stream management, payload parsing, lifecycle control)
158/// - Dispatch and route messages, potentially awaiting or sending responses
159///
160/// # Supertraits
161///
162/// - [`Transport<R, S, M, OR, OM>`]: Provides the transport-level operations (starting, shutting down,
163/// receiving messages, etc.).
164/// - [`McpDispatch<R, OR, M, OM>`]: Provides message-sending and dispatching capabilities.
165///
166/// # Associated Types
167///
168/// - `R`: The raw message type expected to be received. Must be deserializable.
169/// - `S`: The message type sent over the transport (often serialized directly to wire).
170/// - `M`: The internal message type used within the dispatcher.
171/// - `OR`: The outbound response type returned from processing a received message.
172/// - `OM`: The outbound message type submitted by clients or application code.
173///
174pub trait TransportDispatcher<R, S, M, OR, OM>:
175 Transport<R, S, M, OR, OM> + McpDispatch<R, OR, M, OM>
176where
177 R: Clone + Send + Sync + serde::de::DeserializeOwned + 'static,
178 S: Clone + Send + Sync + serde::Serialize + 'static,
179 M: Clone + Send + Sync + serde::de::DeserializeOwned + 'static,
180 OR: Clone + Send + Sync + serde::Serialize + 'static,
181 OM: Clone + Send + Sync + serde::de::DeserializeOwned + 'static,
182{
183}
184
185// pub trait IntoClientTransport {
186// type TransportType: Transport<
187// ServerMessages,
188// MessageFromClient,
189// ServerMessage,
190// ClientMessages,
191// ClientMessage,
192// >;
193
194// fn into_transport(self, session_id: Option<SessionId>) -> TransportResult<Self::TransportType>;
195// }
196
197// impl<T> IntoClientTransport for T
198// where
199// T: Transport<ServerMessages, MessageFromClient, ServerMessage, ClientMessages, ClientMessage>,
200// {
201// type TransportType = T;
202
203// fn into_transport(self, _: Option<SessionId>) -> TransportResult<Self::TransportType> {
204// Ok(self)
205// }
206// }
207
208#[cfg(test)]
209mod tests {
210 use super::*;
211
212 #[test]
213 fn default_channel_capacity_matches_constant() {
214 assert_eq!(
215 TransportOptions::default().channel_capacity,
216 crate::mcp_stream::DEFAULT_MESSAGE_CHANNEL_CAPACITY
217 );
218 }
219
220 #[test]
221 fn channel_capacity_is_overridable() {
222 let options = TransportOptions {
223 channel_capacity: 256,
224 ..Default::default()
225 };
226 assert_eq!(options.channel_capacity, 256);
227 }
228}