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}