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/// Standard JSON-RPC error codes as defined in the JSON-RPC 2.0 specification
18/// and the Model Context Protocol (MCP) documentation
19pub mod error_codes {
20    /// Parse error: Invalid JSON was received
21    pub const PARSE_ERROR: i32 = -32700;
22    /// Invalid Request: The JSON sent is not a valid Request object
23    pub const INVALID_REQUEST: i32 = -32600;
24    /// Method not found: The method does not exist / is not available
25    pub const METHOD_NOT_FOUND: i32 = -32601;
26    /// Invalid params: Invalid method parameter(s)
27    pub const INVALID_PARAMS: i32 = -32602;
28    /// Internal error: Internal JSON-RPC error
29    pub const INTERNAL_ERROR: i32 = -32603;
30
31    // Server-defined errors should be in the range -32000 to -32099
32    /// Generic server error for MCP-specific issues
33    pub const SERVER_ERROR: i32 = -32000;
34}
35
36/// A JSON-RPC message
37#[derive(Debug, Clone, Serialize, Deserialize)]
38#[serde(untagged)]
39pub enum JsonRpcMessage {
40    /// A JSON-RPC request
41    Request(JsonRpcRequest),
42    /// A JSON-RPC response
43    Response(JsonRpcResponse),
44    /// A JSON-RPC notification (request without ID)
45    Notification(JsonRpcNotification),
46}
47
48/// A JSON-RPC request
49#[derive(Debug, Clone, Serialize, Deserialize)]
50pub struct JsonRpcRequest {
51    /// JSON-RPC protocol version
52    pub jsonrpc: String,
53    /// Request ID
54    pub id: Value,
55    /// Method name
56    pub method: String,
57    /// Method parameters
58    #[serde(skip_serializing_if = "Option::is_none")]
59    pub params: Option<Value>,
60}
61
62impl JsonRpcRequest {
63    /// Create a new JSON-RPC request
64    pub fn new(id: impl Into<Value>, method: impl Into<String>, params: Option<Value>) -> Self {
65        Self {
66            jsonrpc: JSON_RPC_VERSION.to_string(),
67            id: id.into(),
68            method: method.into(),
69            params,
70        }
71    }
72
73    /// Create a request to list MCP tools
74    pub fn list_tools(id: impl Into<Value>) -> Self {
75        Self::new(id, "tools/list", None)
76    }
77
78    /// Create a request to call an MCP tool
79    pub fn call_tool(id: impl Into<Value>, name: impl Into<String>, args: Value) -> Self {
80        let params = serde_json::json!({
81            "name": name.into(),
82            "arguments": args
83        });
84        Self::new(id, "tools/call", Some(params))
85    }
86
87    /// Create a request to list MCP resources
88    pub fn list_resources(id: impl Into<Value>) -> Self {
89        Self::new(id, "resources/list", None)
90    }
91
92    /// Create a request to get an MCP resource
93    pub fn get_resource(id: impl Into<Value>, uri: impl Into<String>) -> Self {
94        let params = serde_json::json!({
95            "uri": uri.into()
96        });
97        Self::new(id, "resources/get", Some(params))
98    }
99}
100
101/// A JSON-RPC notification (request without ID)
102#[derive(Debug, Clone, Serialize, Deserialize)]
103pub struct JsonRpcNotification {
104    /// JSON-RPC protocol version
105    pub jsonrpc: String,
106    /// Method name
107    pub method: String,
108    /// Method parameters
109    #[serde(skip_serializing_if = "Option::is_none")]
110    pub params: Option<Value>,
111}
112
113impl JsonRpcNotification {
114    /// Create a new JSON-RPC notification
115    pub fn new(method: impl Into<String>, params: Option<Value>) -> Self {
116        Self {
117            jsonrpc: JSON_RPC_VERSION.to_string(),
118            method: method.into(),
119            params,
120        }
121    }
122
123    /// Create an 'initialized' notification
124    pub fn initialized() -> Self {
125        Self::new("notifications/initialized", None)
126    }
127}
128
129/// A JSON-RPC error
130#[derive(Debug, Clone, Serialize, Deserialize)]
131pub struct JsonRpcError {
132    /// Error code
133    pub code: i32,
134    /// Error message
135    pub message: String,
136    /// Error data
137    #[serde(skip_serializing_if = "Option::is_none")]
138    pub data: Option<Value>,
139}
140
141impl fmt::Display for JsonRpcError {
142    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
143        write!(f, "JSON-RPC error {}: {}", self.code, self.message)
144    }
145}
146
147/// A JSON-RPC response
148#[derive(Debug, Clone, Serialize, Deserialize)]
149pub struct JsonRpcResponse {
150    /// JSON-RPC protocol version
151    pub jsonrpc: String,
152    /// Request ID
153    pub id: Value,
154    /// Result (if successful)
155    #[serde(skip_serializing_if = "Option::is_none")]
156    pub result: Option<Value>,
157    /// Error (if failed)
158    #[serde(skip_serializing_if = "Option::is_none")]
159    pub error: Option<JsonRpcError>,
160}
161
162impl JsonRpcResponse {
163    /// Create a new successful JSON-RPC response
164    pub fn success(id: impl Into<Value>, result: Value) -> Self {
165        Self {
166            jsonrpc: JSON_RPC_VERSION.to_string(),
167            id: id.into(),
168            result: Some(result),
169            error: None,
170        }
171    }
172
173    /// Create a new error JSON-RPC response
174    pub fn error(
175        id: impl Into<Value>,
176        code: i32,
177        message: impl Into<String>,
178        data: Option<Value>,
179    ) -> Self {
180        Self {
181            jsonrpc: JSON_RPC_VERSION.to_string(),
182            id: id.into(),
183            result: None,
184            error: Some(JsonRpcError {
185                code,
186                message: message.into(),
187                data,
188            }),
189        }
190    }
191
192    /// Check if the response is successful
193    pub fn is_success(&self) -> bool {
194        self.error.is_none() && self.result.is_some()
195    }
196}