mcp_protocol_sdk/transport/traits.rs
1//! Transport layer traits and abstractions
2//!
3//! This module defines the core transport traits that enable MCP communication
4//! over different protocols like STDIO, HTTP, and WebSocket.
5
6use crate::core::error::McpResult;
7use crate::protocol::types::{JsonRpcNotification, JsonRpcRequest, JsonRpcResponse};
8use async_trait::async_trait;
9
10/// Transport trait for MCP clients
11///
12/// This trait defines the interface for sending requests and receiving responses
13/// in a client-side MCP connection.
14#[async_trait]
15pub trait Transport: Send + Sync {
16 /// Send a JSON-RPC request and wait for a response
17 ///
18 /// # Arguments
19 /// * `request` - The JSON-RPC request to send
20 ///
21 /// # Returns
22 /// Result containing the JSON-RPC response or an error
23 async fn send_request(&mut self, request: JsonRpcRequest) -> McpResult<JsonRpcResponse>;
24
25 /// Send a JSON-RPC notification (no response expected)
26 ///
27 /// # Arguments
28 /// * `notification` - The JSON-RPC notification to send
29 ///
30 /// # Returns
31 /// Result indicating success or an error
32 async fn send_notification(&mut self, notification: JsonRpcNotification) -> McpResult<()>;
33
34 /// Receive a notification from the server (non-blocking)
35 ///
36 /// # Returns
37 /// Result containing an optional notification or an error
38 async fn receive_notification(&mut self) -> McpResult<Option<JsonRpcNotification>>;
39
40 /// Close the transport connection
41 ///
42 /// # Returns
43 /// Result indicating success or an error
44 async fn close(&mut self) -> McpResult<()>;
45
46 /// Check if the transport is connected
47 ///
48 /// # Returns
49 /// True if the transport is connected and ready for communication
50 fn is_connected(&self) -> bool {
51 true // Default implementation - assume connected
52 }
53
54 /// Get connection information for debugging
55 ///
56 /// # Returns
57 /// String describing the connection
58 fn connection_info(&self) -> String {
59 "Unknown transport".to_string()
60 }
61}
62
63/// Transport trait for MCP servers
64///
65/// This trait defines the interface for handling incoming requests and
66/// sending responses in a server-side MCP connection.
67#[async_trait]
68pub trait ServerTransport: Send + Sync {
69 /// Start the server transport and begin listening for connections
70 ///
71 /// # Returns
72 /// Result indicating success or an error
73 async fn start(&mut self) -> McpResult<()>;
74
75 /// Handle an incoming JSON-RPC request and return a response
76 ///
77 /// # Arguments
78 /// * `request` - The incoming JSON-RPC request
79 ///
80 /// # Returns
81 /// Result containing the JSON-RPC response or an error
82 async fn handle_request(&mut self, request: JsonRpcRequest) -> McpResult<JsonRpcResponse>;
83
84 /// Send a JSON-RPC notification to the client
85 ///
86 /// # Arguments
87 /// * `notification` - The JSON-RPC notification to send
88 ///
89 /// # Returns
90 /// Result indicating success or an error
91 async fn send_notification(&mut self, notification: JsonRpcNotification) -> McpResult<()>;
92
93 /// Stop the server transport
94 ///
95 /// # Returns
96 /// Result indicating success or an error
97 async fn stop(&mut self) -> McpResult<()>;
98
99 /// Check if the server is running
100 ///
101 /// # Returns
102 /// True if the server is running and accepting connections
103 fn is_running(&self) -> bool {
104 true // Default implementation - assume running
105 }
106
107 /// Get server information for debugging
108 ///
109 /// # Returns
110 /// String describing the server state
111 fn server_info(&self) -> String {
112 "Unknown server transport".to_string()
113 }
114}
115
116/// Transport configuration options
117#[derive(Debug, Clone)]
118pub struct TransportConfig {
119 /// Connection timeout in milliseconds
120 pub connect_timeout_ms: Option<u64>,
121 /// Read timeout in milliseconds
122 pub read_timeout_ms: Option<u64>,
123 /// Write timeout in milliseconds
124 pub write_timeout_ms: Option<u64>,
125 /// Maximum message size in bytes
126 pub max_message_size: Option<usize>,
127 /// Keep-alive interval in milliseconds
128 pub keep_alive_ms: Option<u64>,
129 /// Whether to enable compression
130 pub compression: bool,
131 /// Custom headers for HTTP-based transports
132 pub headers: std::collections::HashMap<String, String>,
133}
134
135impl Default for TransportConfig {
136 fn default() -> Self {
137 Self {
138 connect_timeout_ms: Some(30_000), // 30 seconds
139 read_timeout_ms: Some(60_000), // 60 seconds
140 write_timeout_ms: Some(30_000), // 30 seconds
141 max_message_size: Some(16 * 1024 * 1024), // 16 MB
142 keep_alive_ms: Some(30_000), // 30 seconds
143 compression: false,
144 headers: std::collections::HashMap::new(),
145 }
146 }
147}
148
149/// Connection state for transports
150#[derive(Debug, Clone, PartialEq)]
151pub enum ConnectionState {
152 /// Transport is disconnected
153 Disconnected,
154 /// Transport is connecting
155 Connecting,
156 /// Transport is connected and ready
157 Connected,
158 /// Transport is reconnecting after an error
159 Reconnecting,
160 /// Transport is closing
161 Closing,
162 /// Transport has encountered an error
163 Error(String),
164}
165
166/// Transport statistics for monitoring
167#[derive(Debug, Clone, Default)]
168pub struct TransportStats {
169 /// Number of requests sent
170 pub requests_sent: u64,
171 /// Number of responses received
172 pub responses_received: u64,
173 /// Number of notifications sent
174 pub notifications_sent: u64,
175 /// Number of notifications received
176 pub notifications_received: u64,
177 /// Number of connection errors
178 pub connection_errors: u64,
179 /// Number of protocol errors
180 pub protocol_errors: u64,
181 /// Total bytes sent
182 pub bytes_sent: u64,
183 /// Total bytes received
184 pub bytes_received: u64,
185 /// Connection uptime in milliseconds
186 pub uptime_ms: u64,
187}
188
189/// Trait for transports that support statistics
190pub trait TransportStats_: Send + Sync {
191 /// Get current transport statistics
192 fn stats(&self) -> TransportStats;
193
194 /// Reset transport statistics
195 fn reset_stats(&mut self);
196}
197
198/// Trait for transports that support reconnection
199#[async_trait]
200pub trait ReconnectableTransport: Transport {
201 /// Attempt to reconnect the transport
202 ///
203 /// # Returns
204 /// Result indicating success or an error
205 async fn reconnect(&mut self) -> McpResult<()>;
206
207 /// Set the reconnection configuration
208 ///
209 /// # Arguments
210 /// * `config` - Reconnection configuration
211 fn set_reconnect_config(&mut self, config: ReconnectConfig);
212
213 /// Get the current connection state
214 fn connection_state(&self) -> ConnectionState;
215}
216
217/// Configuration for automatic reconnection
218#[derive(Debug, Clone)]
219pub struct ReconnectConfig {
220 /// Whether automatic reconnection is enabled
221 pub enabled: bool,
222 /// Maximum number of reconnection attempts
223 pub max_attempts: Option<u32>,
224 /// Initial delay before first reconnection attempt (milliseconds)
225 pub initial_delay_ms: u64,
226 /// Maximum delay between reconnection attempts (milliseconds)
227 pub max_delay_ms: u64,
228 /// Multiplier for exponential backoff
229 pub backoff_multiplier: f64,
230 /// Jitter factor for randomizing delays (0.0 to 1.0)
231 pub jitter_factor: f64,
232}
233
234impl Default for ReconnectConfig {
235 fn default() -> Self {
236 Self {
237 enabled: true,
238 max_attempts: Some(5),
239 initial_delay_ms: 1000, // 1 second
240 max_delay_ms: 30_000, // 30 seconds
241 backoff_multiplier: 2.0,
242 jitter_factor: 0.1,
243 }
244 }
245}
246
247/// Trait for transports that support message filtering
248pub trait FilterableTransport: Send + Sync {
249 /// Set a message filter function
250 ///
251 /// # Arguments
252 /// * `filter` - Function that returns true if message should be processed
253 fn set_message_filter(&mut self, filter: Box<dyn Fn(&JsonRpcRequest) -> bool + Send + Sync>);
254
255 /// Clear the message filter
256 fn clear_message_filter(&mut self);
257}
258
259/// Transport event for monitoring and debugging
260#[derive(Debug, Clone)]
261pub enum TransportEvent {
262 /// Connection established
263 Connected,
264 /// Connection lost
265 Disconnected,
266 /// Message sent
267 MessageSent {
268 /// Message type
269 message_type: String,
270 /// Message size in bytes
271 size: usize,
272 },
273 /// Message received
274 MessageReceived {
275 /// Message type
276 message_type: String,
277 /// Message size in bytes
278 size: usize,
279 },
280 /// Error occurred
281 Error {
282 /// Error message
283 message: String,
284 },
285}
286
287/// Trait for transports that support event listeners
288pub trait EventEmittingTransport: Send + Sync {
289 /// Add an event listener
290 ///
291 /// # Arguments
292 /// * `listener` - Event listener function
293 fn add_event_listener(&mut self, listener: Box<dyn Fn(TransportEvent) + Send + Sync>);
294
295 /// Remove all event listeners
296 fn clear_event_listeners(&mut self);
297}
298
299#[cfg(test)]
300mod tests {
301 use super::*;
302
303 #[test]
304 fn test_transport_config_default() {
305 let config = TransportConfig::default();
306 assert_eq!(config.connect_timeout_ms, Some(30_000));
307 assert_eq!(config.read_timeout_ms, Some(60_000));
308 assert_eq!(config.max_message_size, Some(16 * 1024 * 1024));
309 assert!(!config.compression);
310 }
311
312 #[test]
313 fn test_reconnect_config_default() {
314 let config = ReconnectConfig::default();
315 assert!(config.enabled);
316 assert_eq!(config.max_attempts, Some(5));
317 assert_eq!(config.initial_delay_ms, 1000);
318 assert_eq!(config.max_delay_ms, 30_000);
319 assert_eq!(config.backoff_multiplier, 2.0);
320 assert_eq!(config.jitter_factor, 0.1);
321 }
322
323 #[test]
324 fn test_connection_state_equality() {
325 assert_eq!(ConnectionState::Connected, ConnectionState::Connected);
326 assert_eq!(ConnectionState::Disconnected, ConnectionState::Disconnected);
327 assert_ne!(ConnectionState::Connected, ConnectionState::Disconnected);
328
329 let error1 = ConnectionState::Error("test".to_string());
330 let error2 = ConnectionState::Error("test".to_string());
331 let error3 = ConnectionState::Error("other".to_string());
332 assert_eq!(error1, error2);
333 assert_ne!(error1, error3);
334 }
335
336 #[test]
337 fn test_transport_stats_default() {
338 let stats = TransportStats::default();
339 assert_eq!(stats.requests_sent, 0);
340 assert_eq!(stats.responses_received, 0);
341 assert_eq!(stats.bytes_sent, 0);
342 assert_eq!(stats.bytes_received, 0);
343 }
344}