mcpkit_transport/traits.rs
1//! Transport traits for the MCP protocol.
2//!
3//! This module defines the core transport abstractions that are runtime-agnostic.
4//! Transports can be implemented for any async runtime (Tokio, async-std, smol).
5//!
6//! # Overview
7//!
8//! - [`Transport`]: Core trait for bidirectional message passing
9//! - [`TransportListener`]: Trait for server-side transport listeners
10//!
11//! # Example
12//!
13//! ```rust
14//! use mcpkit_transport::TransportMetadata;
15//!
16//! // Create metadata for a custom transport
17//! let metadata = TransportMetadata::new("my-transport")
18//! .remote_addr("peer:1234")
19//! .local_addr("local:5678")
20//! .bidirectional(true)
21//! .connected_now();
22//!
23//! assert_eq!(metadata.transport_type, "my-transport");
24//! assert!(metadata.connected_at.is_some());
25//! ```
26
27use mcpkit_core::protocol::Message;
28use std::future::Future;
29use std::time::Instant;
30
31/// Metadata about a transport connection.
32#[derive(Debug, Clone, Default)]
33pub struct TransportMetadata {
34 /// Transport type identifier (e.g., "stdio", "http", "websocket").
35 pub transport_type: String,
36 /// Remote address, if applicable.
37 pub remote_addr: Option<String>,
38 /// Local address, if applicable.
39 pub local_addr: Option<String>,
40 /// When the connection was established.
41 pub connected_at: Option<Instant>,
42 /// Whether the transport supports bidirectional communication.
43 pub bidirectional: bool,
44 /// Custom metadata specific to the transport type.
45 pub custom: Option<serde_json::Value>,
46}
47
48impl TransportMetadata {
49 /// Create new metadata for a transport type.
50 #[must_use]
51 pub fn new(transport_type: impl Into<String>) -> Self {
52 Self {
53 transport_type: transport_type.into(),
54 remote_addr: None,
55 local_addr: None,
56 connected_at: None,
57 bidirectional: true,
58 custom: None,
59 }
60 }
61
62 /// Set the remote address.
63 #[must_use]
64 pub fn remote_addr(mut self, addr: impl Into<String>) -> Self {
65 self.remote_addr = Some(addr.into());
66 self
67 }
68
69 /// Set the local address.
70 #[must_use]
71 pub fn local_addr(mut self, addr: impl Into<String>) -> Self {
72 self.local_addr = Some(addr.into());
73 self
74 }
75
76 /// Mark the connection time.
77 #[must_use]
78 pub fn connected_now(mut self) -> Self {
79 self.connected_at = Some(Instant::now());
80 self
81 }
82
83 /// Set bidirectional flag.
84 #[must_use]
85 pub const fn bidirectional(mut self, bidirectional: bool) -> Self {
86 self.bidirectional = bidirectional;
87 self
88 }
89}
90
91/// Core transport trait for MCP communication.
92///
93/// Transports provide bidirectional message passing between MCP clients
94/// and servers. This trait is runtime-agnostic and uses `impl Future`
95/// return types for flexibility.
96///
97/// # Implementing Transport
98///
99/// Implementations should be `Send + Sync` and handle concurrent access
100/// safely. The send and receive operations should be independent and
101/// can be called from different tasks.
102///
103/// # Example
104///
105/// Use one of the built-in transports:
106///
107/// ```rust
108/// use mcpkit_transport::{MemoryTransport, Transport};
109///
110/// // Create a pair of connected in-memory transports
111/// let (client, server) = MemoryTransport::pair();
112/// assert!(client.is_connected());
113/// ```
114pub trait Transport: Send + Sync {
115 /// The error type for transport operations.
116 type Error: std::error::Error + Send + Sync + 'static;
117
118 /// Send a message over the transport.
119 ///
120 /// # Errors
121 ///
122 /// Returns an error if the message could not be sent (e.g., connection closed,
123 /// serialization failed, I/O error).
124 fn send(&self, msg: Message) -> impl Future<Output = Result<(), Self::Error>> + Send;
125
126 /// Receive a message from the transport.
127 ///
128 /// Returns `Ok(None)` when the connection is cleanly closed.
129 /// Returns `Err` on transport errors.
130 ///
131 /// # Errors
132 ///
133 /// Returns an error if receiving failed (e.g., connection reset,
134 /// deserialization failed, I/O error).
135 fn recv(&self) -> impl Future<Output = Result<Option<Message>, Self::Error>> + Send;
136
137 /// Close the transport connection.
138 ///
139 /// This should perform a graceful shutdown if possible.
140 ///
141 /// # Errors
142 ///
143 /// Returns an error if the close operation failed.
144 fn close(&self) -> impl Future<Output = Result<(), Self::Error>> + Send;
145
146 /// Check if the transport is still connected.
147 fn is_connected(&self) -> bool;
148
149 /// Get metadata about the transport.
150 fn metadata(&self) -> TransportMetadata;
151}
152
153/// Listener trait for server-side transports.
154///
155/// Transport listeners accept incoming connections and produce
156/// transport instances for each connection.
157///
158/// Implementations include [`WebSocketListener`](crate::WebSocketListener)
159/// and [`UnixListener`](crate::unix::UnixListener) (Unix only).
160pub trait TransportListener: Send + Sync {
161 /// The type of transport produced by this listener.
162 type Transport: Transport;
163
164 /// The error type for listener operations.
165 type Error: std::error::Error + Send + Sync + 'static;
166
167 /// Accept an incoming connection.
168 ///
169 /// This method blocks until a new connection is available.
170 ///
171 /// # Errors
172 ///
173 /// Returns an error if accepting the connection failed.
174 fn accept(&self) -> impl Future<Output = Result<Self::Transport, Self::Error>> + Send;
175
176 /// Get the local address the listener is bound to, if available.
177 fn local_addr(&self) -> Option<String>;
178}
179
180/// Extension trait for transports with buffered operations.
181pub trait TransportExt: Transport {
182 /// Send multiple messages in a batch.
183 ///
184 /// This can be more efficient than sending messages individually
185 /// for transports that support batching.
186 fn send_batch(
187 &self,
188 msgs: Vec<Message>,
189 ) -> impl Future<Output = Result<(), Self::Error>> + Send {
190 async move {
191 for msg in msgs {
192 self.send(msg).await?;
193 }
194 Ok(())
195 }
196 }
197}
198
199impl<T: Transport> TransportExt for T {}
200
201#[cfg(test)]
202mod tests {
203 use super::*;
204
205 #[test]
206 fn test_metadata_builder() {
207 let meta = TransportMetadata::new("stdio")
208 .remote_addr("stdin")
209 .local_addr("stdout")
210 .bidirectional(true)
211 .connected_now();
212
213 assert_eq!(meta.transport_type, "stdio");
214 assert!(meta.remote_addr.is_some());
215 assert!(meta.connected_at.is_some());
216 }
217}