Skip to main content

tower_mcp/client/
transport.rs

1//! Client transport trait for raw JSON message I/O.
2//!
3//! This module defines the [`ClientTransport`] trait, the low-level abstraction
4//! for sending and receiving JSON-RPC messages over a transport mechanism.
5//!
6//! Unlike the previous `ClientTransport` which bundled request/response
7//! correlation, this trait provides raw message I/O. The [`McpClient`](super::McpClient)
8//! handles correlation, multiplexing, and dispatch in its background task.
9
10use async_trait::async_trait;
11
12use crate::error::Result;
13use crate::protocol::RequestId;
14
15/// Low-level transport for sending and receiving raw JSON-RPC messages.
16///
17/// Implementations handle the physical I/O (stdio, HTTP, WebSocket) while
18/// the [`McpClient`](super::McpClient) handles JSON-RPC framing, request/response
19/// correlation, and server-initiated request dispatch.
20///
21/// # Implementing a Custom Transport
22///
23/// ```rust,ignore
24/// use async_trait::async_trait;
25/// use tower_mcp::client::ClientTransport;
26/// use tower_mcp::error::Result;
27///
28/// struct MyTransport { /* ... */ }
29///
30/// #[async_trait]
31/// impl ClientTransport for MyTransport {
32///     async fn send(&mut self, message: &str) -> Result<()> {
33///         // Write message to the transport
34///         Ok(())
35///     }
36///
37///     async fn recv(&mut self) -> Result<Option<String>> {
38///         // Read next message, None on EOF
39///         Ok(None)
40///     }
41///
42///     fn is_connected(&self) -> bool { true }
43///
44///     async fn close(&mut self) -> Result<()> { Ok(()) }
45/// }
46/// ```
47#[async_trait]
48pub trait ClientTransport: Send + 'static {
49    /// Send a raw JSON message to the server.
50    ///
51    /// The message is a complete JSON-RPC request, response, or notification
52    /// serialized as a string. The transport adds any necessary framing
53    /// (e.g., newline for stdio).
54    async fn send(&mut self, message: &str) -> Result<()>;
55
56    /// Receive the next raw JSON message from the server.
57    ///
58    /// Returns `Ok(Some(json))` for a message, `Ok(None)` for clean EOF/close,
59    /// or `Err(...)` for transport errors.
60    async fn recv(&mut self) -> Result<Option<String>>;
61
62    /// Check if the transport is still connected.
63    fn is_connected(&self) -> bool;
64
65    /// Close the transport gracefully.
66    ///
67    /// After calling this, `recv()` should return `Ok(None)`.
68    async fn close(&mut self) -> Result<()>;
69
70    /// Reset the transport's session state for re-initialization.
71    ///
72    /// Called when the server indicates the session has expired. The
73    /// transport should clear its session ID, stop any SSE streams,
74    /// and prepare for a new `initialize` handshake.
75    ///
76    /// The default implementation is a no-op (for transports like stdio
77    /// that don't have sessions).
78    async fn reset_session(&mut self) {}
79
80    /// Cancel one in-flight request.
81    ///
82    /// Message-oriented transports such as stdio use the protocol's
83    /// `notifications/cancelled` notification. HTTP overrides this method to
84    /// close only the response stream belonging to the request.
85    async fn cancel_request(&mut self, request_id: &RequestId) -> Result<()> {
86        self.send(
87            &serde_json::json!({
88                "jsonrpc": "2.0",
89                "method": "notifications/cancelled",
90                "params": {
91                    "requestId": request_id,
92                }
93            })
94            .to_string(),
95        )
96        .await
97    }
98
99    /// Whether this transport supports automatic session recovery.
100    ///
101    /// When true, the client will attempt to re-initialize and retry
102    /// failed operations when the server returns a session expired error.
103    ///
104    /// Default: `false`.
105    fn supports_session_recovery(&self) -> bool {
106        false
107    }
108}