mcp_runner/transport/
json_rpc.rs

1//! JSON-RPC implementation for MCP communication.
2//!
3//! This module provides types and utilities for JSON-RPC 2.0 communication
4//! with MCP servers. It includes structures for requests, responses, notifications,
5//! and error handling, along with helper methods for creating common MCP request types.
6//!
7//! The implementation follows the JSON-RPC 2.0 specification and adapts it specifically
8//! for the Model Context Protocol's requirements.
9
10use serde::{Deserialize, Serialize};
11use serde_json::Value;
12use std::fmt;
13
14/// JSON-RPC protocol version
15pub const JSON_RPC_VERSION: &str = "2.0";
16
17/// A JSON-RPC message
18#[derive(Debug, Clone, Serialize, Deserialize)]
19#[serde(untagged)]
20pub enum JsonRpcMessage {
21    /// A JSON-RPC request
22    Request(JsonRpcRequest),
23    /// A JSON-RPC response
24    Response(JsonRpcResponse),
25    /// A JSON-RPC notification (request without ID)
26    Notification(JsonRpcNotification),
27}
28
29/// A JSON-RPC request
30#[derive(Debug, Clone, Serialize, Deserialize)]
31pub struct JsonRpcRequest {
32    /// JSON-RPC protocol version
33    pub jsonrpc: String,
34    /// Request ID
35    pub id: Value,
36    /// Method name
37    pub method: String,
38    /// Method parameters
39    #[serde(skip_serializing_if = "Option::is_none")]
40    pub params: Option<Value>,
41}
42
43impl JsonRpcRequest {
44    /// Create a new JSON-RPC request
45    pub fn new(id: impl Into<Value>, method: impl Into<String>, params: Option<Value>) -> Self {
46        Self {
47            jsonrpc: JSON_RPC_VERSION.to_string(),
48            id: id.into(),
49            method: method.into(),
50            params,
51        }
52    }
53
54    /// Create a request to list MCP tools
55    pub fn list_tools(id: impl Into<Value>) -> Self {
56        Self::new(id, "tools/list", None)
57    }
58
59    /// Create a request to call an MCP tool
60    pub fn call_tool(id: impl Into<Value>, name: impl Into<String>, args: Value) -> Self {
61        let params = serde_json::json!({
62            "name": name.into(),
63            "arguments": args
64        });
65        Self::new(id, "tools/call", Some(params))
66    }
67
68    /// Create a request to list MCP resources
69    pub fn list_resources(id: impl Into<Value>) -> Self {
70        Self::new(id, "resources/list", None)
71    }
72
73    /// Create a request to get an MCP resource
74    pub fn get_resource(id: impl Into<Value>, uri: impl Into<String>) -> Self {
75        let params = serde_json::json!({
76            "uri": uri.into()
77        });
78        Self::new(id, "resources/get", Some(params))
79    }
80}
81
82/// A JSON-RPC notification (request without ID)
83#[derive(Debug, Clone, Serialize, Deserialize)]
84pub struct JsonRpcNotification {
85    /// JSON-RPC protocol version
86    pub jsonrpc: String,
87    /// Method name
88    pub method: String,
89    /// Method parameters
90    #[serde(skip_serializing_if = "Option::is_none")]
91    pub params: Option<Value>,
92}
93
94impl JsonRpcNotification {
95    /// Create a new JSON-RPC notification
96    pub fn new(method: impl Into<String>, params: Option<Value>) -> Self {
97        Self {
98            jsonrpc: JSON_RPC_VERSION.to_string(),
99            method: method.into(),
100            params,
101        }
102    }
103
104    /// Create an 'initialized' notification
105    pub fn initialized() -> Self {
106        Self::new("notifications/initialized", None)
107    }
108}
109
110/// A JSON-RPC error
111#[derive(Debug, Clone, Serialize, Deserialize)]
112pub struct JsonRpcError {
113    /// Error code
114    pub code: i32,
115    /// Error message
116    pub message: String,
117    /// Error data
118    #[serde(skip_serializing_if = "Option::is_none")]
119    pub data: Option<Value>,
120}
121
122impl fmt::Display for JsonRpcError {
123    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
124        write!(f, "JSON-RPC error {}: {}", self.code, self.message)
125    }
126}
127
128/// A JSON-RPC response
129#[derive(Debug, Clone, Serialize, Deserialize)]
130pub struct JsonRpcResponse {
131    /// JSON-RPC protocol version
132    pub jsonrpc: String,
133    /// Request ID
134    pub id: Value,
135    /// Result (if successful)
136    #[serde(skip_serializing_if = "Option::is_none")]
137    pub result: Option<Value>,
138    /// Error (if failed)
139    #[serde(skip_serializing_if = "Option::is_none")]
140    pub error: Option<JsonRpcError>,
141}
142
143impl JsonRpcResponse {
144    /// Create a new successful JSON-RPC response
145    pub fn success(id: impl Into<Value>, result: Value) -> Self {
146        Self {
147            jsonrpc: JSON_RPC_VERSION.to_string(),
148            id: id.into(),
149            result: Some(result),
150            error: None,
151        }
152    }
153
154    /// Create a new error JSON-RPC response
155    pub fn error(
156        id: impl Into<Value>,
157        code: i32,
158        message: impl Into<String>,
159        data: Option<Value>,
160    ) -> Self {
161        Self {
162            jsonrpc: JSON_RPC_VERSION.to_string(),
163            id: id.into(),
164            result: None,
165            error: Some(JsonRpcError {
166                code,
167                message: message.into(),
168                data,
169            }),
170        }
171    }
172
173    /// Check if the response is successful
174    pub fn is_success(&self) -> bool {
175        self.error.is_none() && self.result.is_some()
176    }
177}