async_mcp/transport/
mod.rs

1//! Transport layer for the MCP protocol
2//! handles the serialization and deserialization of message
3//! handles send and receive of messages
4//! defines transport layer types
5use anyhow::Result;
6use async_trait::async_trait;
7use serde::{Deserialize, Serialize};
8
9mod stdio_transport;
10pub use stdio_transport::*;
11mod inmemory_transport;
12pub use inmemory_transport::*;
13mod sse_transport;
14pub use sse_transport::*;
15mod ws_transport;
16pub use ws_transport::*;
17mod http_transport;
18pub use http_transport::*;
19/// only JsonRpcMessage is supported for now
20/// https://spec.modelcontextprotocol.io/specification/basic/messages/
21pub type Message = JsonRpcMessage;
22
23#[async_trait]
24pub trait Transport: Send + Sync + 'static {
25    /// Send a message to the transport
26    async fn send(&self, message: &Message) -> Result<()>;
27
28    /// Receive a message from the transport
29    /// this is blocking call
30    async fn receive(&self) -> Result<Option<Message>>;
31
32    /// open the transport
33    async fn open(&self) -> Result<()>;
34
35    /// Close the transport
36    async fn close(&self) -> Result<()>;
37}
38
39/// Request ID type
40pub type RequestId = u64;
41/// JSON RPC version type
42#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
43#[serde(transparent)]
44pub struct JsonRpcVersion(String);
45
46impl Default for JsonRpcVersion {
47    fn default() -> Self {
48        JsonRpcVersion("2.0".to_owned())
49    }
50}
51
52impl JsonRpcVersion {
53    pub fn as_str(&self) -> &str {
54        &self.0
55    }
56}
57
58#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
59#[serde(deny_unknown_fields)]
60#[serde(untagged)]
61pub enum JsonRpcMessage {
62    Response(JsonRpcResponse),
63    Request(JsonRpcRequest),
64    Notification(JsonRpcNotification),
65}
66
67// json rpc types
68#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)]
69#[serde(deny_unknown_fields)]
70pub struct JsonRpcRequest {
71    pub id: RequestId,
72    pub method: String,
73    #[serde(skip_serializing_if = "Option::is_none")]
74    pub params: Option<serde_json::Value>,
75    pub jsonrpc: JsonRpcVersion,
76}
77
78#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)]
79#[serde(rename_all = "camelCase")]
80#[serde(deny_unknown_fields)]
81#[serde(default)]
82pub struct JsonRpcNotification {
83    pub method: String,
84    #[serde(skip_serializing_if = "Option::is_none")]
85    pub params: Option<serde_json::Value>,
86    pub jsonrpc: JsonRpcVersion,
87}
88
89#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)]
90#[serde(deny_unknown_fields)]
91#[serde(rename_all = "camelCase")]
92#[serde(default)]
93pub struct JsonRpcResponse {
94    /// The request ID this response corresponds to
95    pub id: RequestId,
96    /// The result of the request, if successful
97    #[serde(skip_serializing_if = "Option::is_none")]
98    pub result: Option<serde_json::Value>,
99    /// The error, if the request failed
100    #[serde(skip_serializing_if = "Option::is_none")]
101    pub error: Option<JsonRpcError>,
102    /// The JSON-RPC version
103    pub jsonrpc: JsonRpcVersion,
104}
105
106#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)]
107#[serde(rename_all = "camelCase")]
108#[serde(default)]
109pub struct JsonRpcError {
110    /// Error code
111    pub code: i32,
112    /// Error message
113    pub message: String,
114    /// Optional additional error data
115    #[serde(skip_serializing_if = "Option::is_none")]
116    pub data: Option<serde_json::Value>,
117}
118
119#[cfg(test)]
120mod tests {
121    use super::*;
122    #[test]
123    fn test_deserialize_initialize_request() {
124        let json = r#"{"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"claude-ai","version":"0.1.0"}},"jsonrpc":"2.0","id":0}"#;
125
126        let message: Message = serde_json::from_str(json).unwrap();
127        match message {
128            JsonRpcMessage::Request(req) => {
129                assert_eq!(req.jsonrpc.as_str(), "2.0");
130                assert_eq!(req.id, 0);
131                assert_eq!(req.method, "initialize");
132
133                // Verify params exist and are an object
134                let params = req.params.expect("params should exist");
135                assert!(params.is_object());
136
137                let params_obj = params.as_object().unwrap();
138                assert_eq!(params_obj["protocolVersion"], "2024-11-05");
139
140                let client_info = params_obj["clientInfo"].as_object().unwrap();
141                assert_eq!(client_info["name"], "claude-ai");
142                assert_eq!(client_info["version"], "0.1.0");
143            }
144            _ => panic!("Expected Request variant"),
145        }
146    }
147}