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}