turbomcp_protocol/
jsonrpc.rs

1//! # JSON-RPC 2.0 Implementation
2//!
3//! This module provides a complete implementation of JSON-RPC 2.0 protocol
4//! with support for batching, streaming, and MCP-specific extensions.
5
6use serde::{Deserialize, Deserializer, Serialize, Serializer};
7use serde_json::Value;
8use std::fmt;
9
10use crate::types::RequestId;
11
12/// JSON-RPC version constant
13pub const JSONRPC_VERSION: &str = "2.0";
14
15/// JSON-RPC version type
16#[derive(Debug, Clone, PartialEq, Eq)]
17pub struct JsonRpcVersion;
18
19impl Serialize for JsonRpcVersion {
20    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
21    where
22        S: Serializer,
23    {
24        serializer.serialize_str(JSONRPC_VERSION)
25    }
26}
27
28impl<'de> Deserialize<'de> for JsonRpcVersion {
29    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
30    where
31        D: Deserializer<'de>,
32    {
33        let version = String::deserialize(deserializer)?;
34        if version == JSONRPC_VERSION {
35            Ok(JsonRpcVersion)
36        } else {
37            Err(serde::de::Error::custom(format!(
38                "Invalid JSON-RPC version: expected '{JSONRPC_VERSION}', got '{version}'"
39            )))
40        }
41    }
42}
43
44/// JSON-RPC request message
45#[derive(Debug, Clone, Serialize, Deserialize)]
46pub struct JsonRpcRequest {
47    /// JSON-RPC version
48    pub jsonrpc: JsonRpcVersion,
49    /// Request method name
50    pub method: String,
51    /// Request parameters
52    #[serde(skip_serializing_if = "Option::is_none")]
53    pub params: Option<Value>,
54    /// Request identifier
55    pub id: RequestId,
56}
57
58/// JSON-RPC response message
59#[derive(Debug, Clone, Serialize, Deserialize)]
60pub struct JsonRpcResponse {
61    /// JSON-RPC version
62    pub jsonrpc: JsonRpcVersion,
63    /// Response result (success case)
64    #[serde(skip_serializing_if = "Option::is_none")]
65    pub result: Option<Value>,
66    /// Response error (error case)
67    #[serde(skip_serializing_if = "Option::is_none")]
68    pub error: Option<JsonRpcError>,
69    /// Request identifier (null for parse errors)
70    pub id: Option<RequestId>,
71}
72
73/// JSON-RPC notification message (no response expected)
74#[derive(Debug, Clone, Serialize, Deserialize)]
75pub struct JsonRpcNotification {
76    /// JSON-RPC version
77    pub jsonrpc: JsonRpcVersion,
78    /// Notification method name
79    pub method: String,
80    /// Notification parameters
81    #[serde(skip_serializing_if = "Option::is_none")]
82    pub params: Option<Value>,
83}
84
85/// JSON-RPC error object
86#[derive(Debug, Clone, Serialize, Deserialize)]
87pub struct JsonRpcError {
88    /// Error code
89    pub code: i32,
90    /// Error message
91    pub message: String,
92    /// Additional error data
93    #[serde(skip_serializing_if = "Option::is_none")]
94    pub data: Option<Value>,
95}
96
97/// JSON-RPC batch request/response
98#[derive(Debug, Clone, Serialize, Deserialize)]
99#[serde(transparent)]
100pub struct JsonRpcBatch<T> {
101    /// Batch items
102    pub items: Vec<T>,
103}
104
105/// Standard JSON-RPC error codes
106#[derive(Debug, Clone, Copy, PartialEq, Eq)]
107pub enum JsonRpcErrorCode {
108    /// Parse error (-32700)
109    ParseError,
110    /// Invalid request (-32600)
111    InvalidRequest,
112    /// Method not found (-32601)
113    MethodNotFound,
114    /// Invalid params (-32602)
115    InvalidParams,
116    /// Internal error (-32603)
117    InternalError,
118    /// Application-defined error
119    ApplicationError(i32),
120}
121
122impl JsonRpcErrorCode {
123    /// Get the numeric error code
124    pub fn code(&self) -> i32 {
125        match self {
126            Self::ParseError => -32700,
127            Self::InvalidRequest => -32600,
128            Self::MethodNotFound => -32601,
129            Self::InvalidParams => -32602,
130            Self::InternalError => -32603,
131            Self::ApplicationError(code) => *code,
132        }
133    }
134
135    /// Get the standard error message
136    pub fn message(&self) -> &'static str {
137        match self {
138            Self::ParseError => "Parse error",
139            Self::InvalidRequest => "Invalid Request",
140            Self::MethodNotFound => "Method not found",
141            Self::InvalidParams => "Invalid params",
142            Self::InternalError => "Internal error",
143            Self::ApplicationError(_) => "Application error",
144        }
145    }
146}
147
148impl fmt::Display for JsonRpcErrorCode {
149    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
150        write!(f, "{} ({})", self.message(), self.code())
151    }
152}
153
154impl From<JsonRpcErrorCode> for JsonRpcError {
155    fn from(code: JsonRpcErrorCode) -> Self {
156        Self {
157            code: code.code(),
158            message: code.message().to_string(),
159            data: None,
160        }
161    }
162}
163
164impl From<i32> for JsonRpcErrorCode {
165    fn from(code: i32) -> Self {
166        match code {
167            -32700 => Self::ParseError,
168            -32600 => Self::InvalidRequest,
169            -32601 => Self::MethodNotFound,
170            -32602 => Self::InvalidParams,
171            -32603 => Self::InternalError,
172            other => Self::ApplicationError(other),
173        }
174    }
175}
176
177/// JSON-RPC message type (union of request, response, notification)
178#[derive(Debug, Clone, Serialize, Deserialize)]
179#[serde(untagged)]
180pub enum JsonRpcMessage {
181    /// Request message
182    Request(JsonRpcRequest),
183    /// Response message
184    Response(JsonRpcResponse),
185    /// Notification message
186    Notification(JsonRpcNotification),
187    /// Batch of messages
188    RequestBatch(JsonRpcBatch<JsonRpcRequest>),
189    /// Batch of responses
190    ResponseBatch(JsonRpcBatch<JsonRpcResponse>),
191    /// Mixed batch
192    MessageBatch(JsonRpcBatch<JsonRpcMessage>),
193}
194
195impl JsonRpcRequest {
196    /// Create a new JSON-RPC request
197    pub fn new(method: String, params: Option<Value>, id: RequestId) -> Self {
198        Self {
199            jsonrpc: JsonRpcVersion,
200            method,
201            params,
202            id,
203        }
204    }
205
206    /// Create a request with no parameters
207    pub fn without_params(method: String, id: RequestId) -> Self {
208        Self::new(method, None, id)
209    }
210
211    /// Create a request with parameters
212    pub fn with_params<P: Serialize>(
213        method: String,
214        params: P,
215        id: RequestId,
216    ) -> Result<Self, serde_json::Error> {
217        let params_value = serde_json::to_value(params)?;
218        Ok(Self::new(method, Some(params_value), id))
219    }
220}
221
222impl JsonRpcResponse {
223    /// Create a successful response
224    pub fn success(result: Value, id: RequestId) -> Self {
225        Self {
226            jsonrpc: JsonRpcVersion,
227            result: Some(result),
228            error: None,
229            id: Some(id),
230        }
231    }
232
233    /// Create an error response
234    pub fn error(error: JsonRpcError, id: Option<RequestId>) -> Self {
235        Self {
236            jsonrpc: JsonRpcVersion,
237            result: None,
238            error: Some(error),
239            id,
240        }
241    }
242
243    /// Create a parse error response (id is null)
244    pub fn parse_error(message: Option<String>) -> Self {
245        let error = JsonRpcError {
246            code: JsonRpcErrorCode::ParseError.code(),
247            message: message.unwrap_or_else(|| JsonRpcErrorCode::ParseError.message().to_string()),
248            data: None,
249        };
250        Self::error(error, None)
251    }
252
253    /// Check if this is a successful response
254    pub fn is_success(&self) -> bool {
255        self.error.is_none()
256    }
257
258    /// Check if this is an error response
259    pub fn is_error(&self) -> bool {
260        self.error.is_some()
261    }
262}
263
264impl JsonRpcNotification {
265    /// Create a new JSON-RPC notification
266    pub fn new(method: String, params: Option<Value>) -> Self {
267        Self {
268            jsonrpc: JsonRpcVersion,
269            method,
270            params,
271        }
272    }
273
274    /// Create a notification with no parameters
275    pub fn without_params(method: String) -> Self {
276        Self::new(method, None)
277    }
278
279    /// Create a notification with parameters
280    pub fn with_params<P: Serialize>(method: String, params: P) -> Result<Self, serde_json::Error> {
281        let params_value = serde_json::to_value(params)?;
282        Ok(Self::new(method, Some(params_value)))
283    }
284}
285
286impl<T> JsonRpcBatch<T> {
287    /// Create a new batch
288    pub fn new(items: Vec<T>) -> Self {
289        Self { items }
290    }
291
292    /// Create an empty batch
293    pub fn empty() -> Self {
294        Self::new(Vec::new())
295    }
296
297    /// Add an item to the batch
298    pub fn push(&mut self, item: T) {
299        self.items.push(item);
300    }
301
302    /// Get the number of items in the batch
303    pub fn len(&self) -> usize {
304        self.items.len()
305    }
306
307    /// Check if the batch is empty
308    pub fn is_empty(&self) -> bool {
309        self.items.is_empty()
310    }
311
312    /// Iterate over batch items
313    pub fn iter(&self) -> impl Iterator<Item = &T> {
314        self.items.iter()
315    }
316}
317
318impl<T> IntoIterator for JsonRpcBatch<T> {
319    type Item = T;
320    type IntoIter = std::vec::IntoIter<T>;
321
322    fn into_iter(self) -> Self::IntoIter {
323        self.items.into_iter()
324    }
325}
326
327impl<T> From<Vec<T>> for JsonRpcBatch<T> {
328    fn from(items: Vec<T>) -> Self {
329        Self::new(items)
330    }
331}
332
333/// Utility functions for JSON-RPC message handling
334pub mod utils {
335    use super::*;
336
337    /// Parse a JSON-RPC message from a string
338    pub fn parse_message(json: &str) -> Result<JsonRpcMessage, serde_json::Error> {
339        serde_json::from_str(json)
340    }
341
342    /// Serialize a JSON-RPC message to a string
343    pub fn serialize_message(message: &JsonRpcMessage) -> Result<String, serde_json::Error> {
344        serde_json::to_string(message)
345    }
346
347    /// Check if a string looks like a JSON-RPC batch
348    pub fn is_batch(json: &str) -> bool {
349        json.trim_start().starts_with('[')
350    }
351
352    /// Extract the method name from a JSON-RPC message string
353    pub fn extract_method(json: &str) -> Option<String> {
354        // Simple regex-free method extraction for performance
355        if let Ok(value) = serde_json::from_str::<serde_json::Value>(json)
356            && let Some(method) = value.get("method")
357        {
358            return method.as_str().map(String::from);
359        }
360        None
361    }
362}
363
364#[cfg(test)]
365mod tests {
366    use super::*;
367    use serde_json::json;
368
369    #[test]
370    fn test_jsonrpc_version() {
371        let version = JsonRpcVersion;
372        let json = serde_json::to_string(&version).unwrap();
373        assert_eq!(json, "\"2.0\"");
374
375        let parsed: JsonRpcVersion = serde_json::from_str(&json).unwrap();
376        assert_eq!(parsed, version);
377    }
378
379    #[test]
380    fn test_request_creation() {
381        let request = JsonRpcRequest::new(
382            "test_method".to_string(),
383            Some(json!({"key": "value"})),
384            RequestId::String("test-id".to_string()),
385        );
386
387        assert_eq!(request.method, "test_method");
388        assert!(request.params.is_some());
389    }
390
391    #[test]
392    fn test_response_creation() {
393        let response = JsonRpcResponse::success(
394            json!({"result": "success"}),
395            RequestId::String("test-id".to_string()),
396        );
397
398        assert!(response.is_success());
399        assert!(!response.is_error());
400        assert!(response.result.is_some());
401        assert!(response.error.is_none());
402    }
403
404    #[test]
405    fn test_error_response() {
406        let error = JsonRpcError::from(JsonRpcErrorCode::MethodNotFound);
407        let response =
408            JsonRpcResponse::error(error, Some(RequestId::String("test-id".to_string())));
409
410        assert!(!response.is_success());
411        assert!(response.is_error());
412        assert!(response.result.is_none());
413        assert!(response.error.is_some());
414    }
415
416    #[test]
417    fn test_notification() {
418        let notification = JsonRpcNotification::without_params("test_notification".to_string());
419        assert_eq!(notification.method, "test_notification");
420        assert!(notification.params.is_none());
421    }
422
423    #[test]
424    fn test_batch() {
425        let mut batch = JsonRpcBatch::<JsonRpcRequest>::empty();
426        assert!(batch.is_empty());
427
428        batch.push(JsonRpcRequest::without_params(
429            "method1".to_string(),
430            RequestId::String("1".to_string()),
431        ));
432        batch.push(JsonRpcRequest::without_params(
433            "method2".to_string(),
434            RequestId::String("2".to_string()),
435        ));
436
437        assert_eq!(batch.len(), 2);
438        assert!(!batch.is_empty());
439    }
440
441    #[test]
442    fn test_serialization() {
443        let request = JsonRpcRequest::new(
444            "test_method".to_string(),
445            Some(json!({"param": "value"})),
446            RequestId::String("123".to_string()),
447        );
448
449        let json = serde_json::to_string(&request).unwrap();
450        let parsed: JsonRpcRequest = serde_json::from_str(&json).unwrap();
451
452        assert_eq!(parsed.method, request.method);
453        assert_eq!(parsed.params, request.params);
454    }
455
456    #[test]
457    fn test_utils() {
458        let json = r#"{"jsonrpc":"2.0","method":"test","id":"123"}"#;
459
460        assert!(!utils::is_batch(json));
461        assert_eq!(utils::extract_method(json), Some("test".to_string()));
462
463        let batch_json = r#"[{"jsonrpc":"2.0","method":"test","id":"123"}]"#;
464        assert!(utils::is_batch(batch_json));
465    }
466
467    #[test]
468    fn test_error_codes() {
469        let parse_error = JsonRpcErrorCode::ParseError;
470        assert_eq!(parse_error.code(), -32700);
471        assert_eq!(parse_error.message(), "Parse error");
472
473        let app_error = JsonRpcErrorCode::ApplicationError(-32001);
474        assert_eq!(app_error.code(), -32001);
475    }
476}