httpmcp_rust/
jsonrpc.rs

1use serde::{Deserialize, Serialize};
2use serde_json::Value;
3
4/// JSON-RPC 2.0 Request
5#[derive(Debug, Clone, Serialize, Deserialize)]
6pub struct JsonRpcRequest {
7    pub jsonrpc: String,
8    pub method: String,
9    #[serde(skip_serializing_if = "Option::is_none")]
10    pub params: Option<Value>,
11    #[serde(skip_serializing_if = "Option::is_none")]
12    pub id: Option<RequestId>,
13}
14
15/// JSON-RPC 2.0 Response
16#[derive(Debug, Clone, Serialize, Deserialize)]
17pub struct JsonRpcResponse {
18    pub jsonrpc: String,
19    #[serde(skip_serializing_if = "Option::is_none")]
20    pub result: Option<Value>,
21    #[serde(skip_serializing_if = "Option::is_none")]
22    pub error: Option<JsonRpcError>,
23    #[serde(skip_serializing_if = "Option::is_none")]
24    pub id: Option<RequestId>,
25}
26
27/// JSON-RPC 2.0 Error
28#[derive(Debug, Clone, Serialize, Deserialize)]
29pub struct JsonRpcError {
30    pub code: i32,
31    pub message: String,
32    #[serde(skip_serializing_if = "Option::is_none")]
33    pub data: Option<Value>,
34}
35
36/// Request ID can be string or number
37#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
38#[serde(untagged)]
39pub enum RequestId {
40    String(String),
41    Number(i64),
42}
43
44/// JSON-RPC Error Codes
45pub mod error_codes {
46    pub const PARSE_ERROR: i32 = -32700;
47    pub const INVALID_REQUEST: i32 = -32600;
48    pub const METHOD_NOT_FOUND: i32 = -32601;
49    pub const INVALID_PARAMS: i32 = -32602;
50    pub const INTERNAL_ERROR: i32 = -32603;
51
52    // MCP specific errors
53    pub const RESOURCE_NOT_FOUND: i32 = -32002;
54}
55
56impl JsonRpcRequest {
57    pub fn new(method: impl Into<String>, params: Option<Value>, id: Option<RequestId>) -> Self {
58        Self {
59            jsonrpc: "2.0".to_string(),
60            method: method.into(),
61            params,
62            id,
63        }
64    }
65
66    pub fn is_notification(&self) -> bool {
67        self.id.is_none()
68    }
69
70    pub fn validate(&self) -> Result<(), JsonRpcError> {
71        if self.jsonrpc != "2.0" {
72            return Err(JsonRpcError {
73                code: error_codes::INVALID_REQUEST,
74                message: "Invalid JSON-RPC version".to_string(),
75                data: None,
76            });
77        }
78
79        if self.method.is_empty() {
80            return Err(JsonRpcError {
81                code: error_codes::INVALID_REQUEST,
82                message: "Method cannot be empty".to_string(),
83                data: None,
84            });
85        }
86
87        Ok(())
88    }
89}
90
91impl JsonRpcResponse {
92    pub fn success(result: Value, id: Option<RequestId>) -> Self {
93        Self {
94            jsonrpc: "2.0".to_string(),
95            result: Some(result),
96            error: None,
97            id,
98        }
99    }
100
101    pub fn error(error: JsonRpcError, id: Option<RequestId>) -> Self {
102        Self {
103            jsonrpc: "2.0".to_string(),
104            result: None,
105            error: Some(error),
106            id,
107        }
108    }
109}
110
111impl JsonRpcError {
112    pub fn parse_error() -> Self {
113        Self {
114            code: error_codes::PARSE_ERROR,
115            message: "Parse error".to_string(),
116            data: None,
117        }
118    }
119
120    pub fn invalid_request(message: impl Into<String>) -> Self {
121        Self {
122            code: error_codes::INVALID_REQUEST,
123            message: message.into(),
124            data: None,
125        }
126    }
127
128    pub fn method_not_found(method: impl Into<String>) -> Self {
129        Self {
130            code: error_codes::METHOD_NOT_FOUND,
131            message: format!("Method not found: {}", method.into()),
132            data: None,
133        }
134    }
135
136    pub fn invalid_params(message: impl Into<String>) -> Self {
137        Self {
138            code: error_codes::INVALID_PARAMS,
139            message: message.into(),
140            data: None,
141        }
142    }
143
144    pub fn internal_error(message: impl Into<String>) -> Self {
145        Self {
146            code: error_codes::INTERNAL_ERROR,
147            message: message.into(),
148            data: None,
149        }
150    }
151}
152
153#[cfg(test)]
154mod tests {
155    use super::*;
156
157    #[test]
158    fn test_request_validation() {
159        let req = JsonRpcRequest::new("test", None, Some(RequestId::Number(1)));
160        assert!(req.validate().is_ok());
161
162        let invalid = JsonRpcRequest {
163            jsonrpc: "1.0".to_string(),
164            method: "test".to_string(),
165            params: None,
166            id: None,
167        };
168        assert!(invalid.validate().is_err());
169    }
170
171    #[test]
172    fn test_is_notification() {
173        let req = JsonRpcRequest::new("test", None, None);
174        assert!(req.is_notification());
175
176        let req = JsonRpcRequest::new("test", None, Some(RequestId::Number(1)));
177        assert!(!req.is_notification());
178    }
179}