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 payload - ensures mutual exclusion of result and error
59#[derive(Debug, Clone, Serialize, Deserialize)]
60#[serde(untagged)]
61pub enum JsonRpcResponsePayload {
62    /// Successful response with result
63    Success {
64        /// Response result
65        result: Value,
66    },
67    /// Error response
68    Error {
69        /// Response error
70        error: JsonRpcError,
71    },
72}
73
74/// JSON-RPC response message
75#[derive(Debug, Clone, Serialize, Deserialize)]
76pub struct JsonRpcResponse {
77    /// JSON-RPC version
78    pub jsonrpc: JsonRpcVersion,
79    /// Response payload (either result or error, never both)
80    #[serde(flatten)]
81    pub payload: JsonRpcResponsePayload,
82    /// Request identifier (required except for parse errors)
83    pub id: ResponseId,
84}
85
86/// Response ID - handles the special case where parse errors have null ID
87#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
88#[serde(transparent)]
89pub struct ResponseId(pub Option<RequestId>);
90
91impl ResponseId {
92    /// Create a response ID for a normal response
93    pub fn from_request(id: RequestId) -> Self {
94        Self(Some(id))
95    }
96
97    /// Create a null response ID for parse errors
98    pub fn null() -> Self {
99        Self(None)
100    }
101
102    /// Get the request ID if present
103    pub fn as_request_id(&self) -> Option<&RequestId> {
104        self.0.as_ref()
105    }
106
107    /// Check if this is a null ID (parse error)
108    pub fn is_null(&self) -> bool {
109        self.0.is_none()
110    }
111}
112
113/// JSON-RPC notification message (no response expected)
114#[derive(Debug, Clone, Serialize, Deserialize)]
115pub struct JsonRpcNotification {
116    /// JSON-RPC version
117    pub jsonrpc: JsonRpcVersion,
118    /// Notification method name
119    pub method: String,
120    /// Notification parameters
121    #[serde(skip_serializing_if = "Option::is_none")]
122    pub params: Option<Value>,
123}
124
125/// JSON-RPC error object
126#[derive(Debug, Clone, Serialize, Deserialize)]
127pub struct JsonRpcError {
128    /// Error code
129    pub code: i32,
130    /// Error message
131    pub message: String,
132    /// Additional error data
133    #[serde(skip_serializing_if = "Option::is_none")]
134    pub data: Option<Value>,
135}
136
137/// JSON-RPC batch request/response
138#[derive(Debug, Clone, Serialize, Deserialize)]
139#[serde(transparent)]
140pub struct JsonRpcBatch<T> {
141    /// Batch items
142    pub items: Vec<T>,
143}
144
145/// Standard JSON-RPC error codes
146#[derive(Debug, Clone, Copy, PartialEq, Eq)]
147pub enum JsonRpcErrorCode {
148    /// Parse error (-32700)
149    ParseError,
150    /// Invalid request (-32600)
151    InvalidRequest,
152    /// Method not found (-32601)
153    MethodNotFound,
154    /// Invalid params (-32602)
155    InvalidParams,
156    /// Internal error (-32603)
157    InternalError,
158    /// Application-defined error
159    ApplicationError(i32),
160}
161
162impl JsonRpcErrorCode {
163    /// Get the numeric error code
164    pub fn code(&self) -> i32 {
165        match self {
166            Self::ParseError => -32700,
167            Self::InvalidRequest => -32600,
168            Self::MethodNotFound => -32601,
169            Self::InvalidParams => -32602,
170            Self::InternalError => -32603,
171            Self::ApplicationError(code) => *code,
172        }
173    }
174
175    /// Get the standard error message
176    pub fn message(&self) -> &'static str {
177        match self {
178            Self::ParseError => "Parse error",
179            Self::InvalidRequest => "Invalid Request",
180            Self::MethodNotFound => "Method not found",
181            Self::InvalidParams => "Invalid params",
182            Self::InternalError => "Internal error",
183            Self::ApplicationError(_) => "Application error",
184        }
185    }
186}
187
188impl fmt::Display for JsonRpcErrorCode {
189    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
190        write!(f, "{} ({})", self.message(), self.code())
191    }
192}
193
194impl From<JsonRpcErrorCode> for JsonRpcError {
195    fn from(code: JsonRpcErrorCode) -> Self {
196        Self {
197            code: code.code(),
198            message: code.message().to_string(),
199            data: None,
200        }
201    }
202}
203
204impl From<i32> for JsonRpcErrorCode {
205    fn from(code: i32) -> Self {
206        match code {
207            -32700 => Self::ParseError,
208            -32600 => Self::InvalidRequest,
209            -32601 => Self::MethodNotFound,
210            -32602 => Self::InvalidParams,
211            -32603 => Self::InternalError,
212            other => Self::ApplicationError(other),
213        }
214    }
215}
216
217/// JSON-RPC message type (union of request, response, notification)
218#[derive(Debug, Clone, Serialize, Deserialize)]
219#[serde(untagged)]
220pub enum JsonRpcMessage {
221    /// Request message
222    Request(JsonRpcRequest),
223    /// Response message
224    Response(JsonRpcResponse),
225    /// Notification message
226    Notification(JsonRpcNotification),
227    /// Batch of messages
228    RequestBatch(JsonRpcBatch<JsonRpcRequest>),
229    /// Batch of responses
230    ResponseBatch(JsonRpcBatch<JsonRpcResponse>),
231    /// Mixed batch
232    MessageBatch(JsonRpcBatch<JsonRpcMessage>),
233}
234
235impl JsonRpcRequest {
236    /// Create a new JSON-RPC request
237    pub fn new(method: String, params: Option<Value>, id: RequestId) -> Self {
238        Self {
239            jsonrpc: JsonRpcVersion,
240            method,
241            params,
242            id,
243        }
244    }
245
246    /// Create a request with no parameters
247    pub fn without_params(method: String, id: RequestId) -> Self {
248        Self::new(method, None, id)
249    }
250
251    /// Create a request with parameters
252    pub fn with_params<P: Serialize>(
253        method: String,
254        params: P,
255        id: RequestId,
256    ) -> Result<Self, serde_json::Error> {
257        let params_value = serde_json::to_value(params)?;
258        Ok(Self::new(method, Some(params_value), id))
259    }
260}
261
262impl JsonRpcResponse {
263    /// Create a successful response
264    pub fn success(result: Value, id: RequestId) -> Self {
265        Self {
266            jsonrpc: JsonRpcVersion,
267            payload: JsonRpcResponsePayload::Success { result },
268            id: ResponseId::from_request(id),
269        }
270    }
271
272    /// Create an error response with request ID
273    pub fn error_response(error: JsonRpcError, id: RequestId) -> Self {
274        Self {
275            jsonrpc: JsonRpcVersion,
276            payload: JsonRpcResponsePayload::Error { error },
277            id: ResponseId::from_request(id),
278        }
279    }
280
281    /// Create a parse error response (id is null)
282    pub fn parse_error(message: Option<String>) -> Self {
283        let error = JsonRpcError {
284            code: JsonRpcErrorCode::ParseError.code(),
285            message: message.unwrap_or_else(|| JsonRpcErrorCode::ParseError.message().to_string()),
286            data: None,
287        };
288        Self {
289            jsonrpc: JsonRpcVersion,
290            payload: JsonRpcResponsePayload::Error { error },
291            id: ResponseId::null(),
292        }
293    }
294
295    /// Check if this is a successful response
296    pub fn is_success(&self) -> bool {
297        matches!(self.payload, JsonRpcResponsePayload::Success { .. })
298    }
299
300    /// Check if this is an error response
301    pub fn is_error(&self) -> bool {
302        matches!(self.payload, JsonRpcResponsePayload::Error { .. })
303    }
304
305    /// Get the result if this is a success response
306    pub fn result(&self) -> Option<&Value> {
307        match &self.payload {
308            JsonRpcResponsePayload::Success { result } => Some(result),
309            JsonRpcResponsePayload::Error { .. } => None,
310        }
311    }
312
313    /// Get the error if this is an error response
314    pub fn error(&self) -> Option<&JsonRpcError> {
315        match &self.payload {
316            JsonRpcResponsePayload::Success { .. } => None,
317            JsonRpcResponsePayload::Error { error } => Some(error),
318        }
319    }
320
321    /// Get the request ID if this is not a parse error
322    pub fn request_id(&self) -> Option<&RequestId> {
323        self.id.as_request_id()
324    }
325
326    /// Check if this response is for a parse error (has null ID)
327    pub fn is_parse_error(&self) -> bool {
328        self.id.is_null()
329    }
330
331    /// Get mutable reference to result if this is a success response
332    pub fn result_mut(&mut self) -> Option<&mut Value> {
333        match &mut self.payload {
334            JsonRpcResponsePayload::Success { result } => Some(result),
335            JsonRpcResponsePayload::Error { .. } => None,
336        }
337    }
338
339    /// Get mutable reference to error if this is an error response
340    pub fn error_mut(&mut self) -> Option<&mut JsonRpcError> {
341        match &mut self.payload {
342            JsonRpcResponsePayload::Success { .. } => None,
343            JsonRpcResponsePayload::Error { error } => Some(error),
344        }
345    }
346
347    /// Set the result for this response (converts to success response)
348    pub fn set_result(&mut self, result: Value) {
349        self.payload = JsonRpcResponsePayload::Success { result };
350    }
351
352    /// Set the error for this response (converts to error response)
353    pub fn set_error(&mut self, error: JsonRpcError) {
354        self.payload = JsonRpcResponsePayload::Error { error };
355    }
356}
357
358impl JsonRpcNotification {
359    /// Create a new JSON-RPC notification
360    pub fn new(method: String, params: Option<Value>) -> Self {
361        Self {
362            jsonrpc: JsonRpcVersion,
363            method,
364            params,
365        }
366    }
367
368    /// Create a notification with no parameters
369    pub fn without_params(method: String) -> Self {
370        Self::new(method, None)
371    }
372
373    /// Create a notification with parameters
374    pub fn with_params<P: Serialize>(method: String, params: P) -> Result<Self, serde_json::Error> {
375        let params_value = serde_json::to_value(params)?;
376        Ok(Self::new(method, Some(params_value)))
377    }
378}
379
380impl<T> JsonRpcBatch<T> {
381    /// Create a new batch
382    pub fn new(items: Vec<T>) -> Self {
383        Self { items }
384    }
385
386    /// Create an empty batch
387    pub fn empty() -> Self {
388        Self::new(Vec::new())
389    }
390
391    /// Add an item to the batch
392    pub fn push(&mut self, item: T) {
393        self.items.push(item);
394    }
395
396    /// Get the number of items in the batch
397    pub fn len(&self) -> usize {
398        self.items.len()
399    }
400
401    /// Check if the batch is empty
402    pub fn is_empty(&self) -> bool {
403        self.items.is_empty()
404    }
405
406    /// Iterate over batch items
407    pub fn iter(&self) -> impl Iterator<Item = &T> {
408        self.items.iter()
409    }
410}
411
412impl<T> IntoIterator for JsonRpcBatch<T> {
413    type Item = T;
414    type IntoIter = std::vec::IntoIter<T>;
415
416    fn into_iter(self) -> Self::IntoIter {
417        self.items.into_iter()
418    }
419}
420
421impl<T> From<Vec<T>> for JsonRpcBatch<T> {
422    fn from(items: Vec<T>) -> Self {
423        Self::new(items)
424    }
425}
426
427/// Utility functions for JSON-RPC message handling
428pub mod utils {
429    use super::*;
430
431    /// Parse a JSON-RPC message from a string
432    pub fn parse_message(json: &str) -> Result<JsonRpcMessage, serde_json::Error> {
433        serde_json::from_str(json)
434    }
435
436    /// Serialize a JSON-RPC message to a string
437    pub fn serialize_message(message: &JsonRpcMessage) -> Result<String, serde_json::Error> {
438        serde_json::to_string(message)
439    }
440
441    /// Check if a string looks like a JSON-RPC batch
442    pub fn is_batch(json: &str) -> bool {
443        json.trim_start().starts_with('[')
444    }
445
446    /// Extract the method name from a JSON-RPC message string
447    pub fn extract_method(json: &str) -> Option<String> {
448        // Simple regex-free method extraction for performance
449        if let Ok(value) = serde_json::from_str::<serde_json::Value>(json)
450            && let Some(method) = value.get("method")
451        {
452            return method.as_str().map(String::from);
453        }
454        None
455    }
456}
457
458#[cfg(test)]
459mod tests {
460    use super::*;
461    use serde_json::json;
462
463    #[test]
464    fn test_jsonrpc_version() {
465        let version = JsonRpcVersion;
466        let json = serde_json::to_string(&version).unwrap();
467        assert_eq!(json, "\"2.0\"");
468
469        let parsed: JsonRpcVersion = serde_json::from_str(&json).unwrap();
470        assert_eq!(parsed, version);
471    }
472
473    #[test]
474    fn test_request_creation() {
475        let request = JsonRpcRequest::new(
476            "test_method".to_string(),
477            Some(json!({"key": "value"})),
478            RequestId::String("test-id".to_string()),
479        );
480
481        assert_eq!(request.method, "test_method");
482        assert!(request.params.is_some());
483    }
484
485    #[test]
486    fn test_response_creation() {
487        let response = JsonRpcResponse::success(
488            json!({"result": "success"}),
489            RequestId::String("test-id".to_string()),
490        );
491
492        assert!(response.is_success());
493        assert!(!response.is_error());
494        assert!(response.result().is_some());
495        assert!(response.error().is_none());
496        assert!(!response.is_parse_error());
497    }
498
499    #[test]
500    fn test_error_response() {
501        let error = JsonRpcError::from(JsonRpcErrorCode::MethodNotFound);
502        let response =
503            JsonRpcResponse::error_response(error, RequestId::String("test-id".to_string()));
504
505        assert!(!response.is_success());
506        assert!(response.is_error());
507        assert!(response.result().is_none());
508        assert!(response.error().is_some());
509        assert!(!response.is_parse_error());
510    }
511
512    #[test]
513    fn test_parse_error_response() {
514        let response = JsonRpcResponse::parse_error(Some("Invalid JSON".to_string()));
515
516        assert!(!response.is_success());
517        assert!(response.is_error());
518        assert!(response.result().is_none());
519        assert!(response.error().is_some());
520        assert!(response.is_parse_error());
521        assert!(response.request_id().is_none());
522
523        // Verify the error details
524        let error = response.error().unwrap();
525        assert_eq!(error.code, JsonRpcErrorCode::ParseError.code());
526        assert_eq!(error.message, "Invalid JSON");
527    }
528
529    #[test]
530    fn test_notification() {
531        let notification = JsonRpcNotification::without_params("test_notification".to_string());
532        assert_eq!(notification.method, "test_notification");
533        assert!(notification.params.is_none());
534    }
535
536    #[test]
537    fn test_batch() {
538        let mut batch = JsonRpcBatch::<JsonRpcRequest>::empty();
539        assert!(batch.is_empty());
540
541        batch.push(JsonRpcRequest::without_params(
542            "method1".to_string(),
543            RequestId::String("1".to_string()),
544        ));
545        batch.push(JsonRpcRequest::without_params(
546            "method2".to_string(),
547            RequestId::String("2".to_string()),
548        ));
549
550        assert_eq!(batch.len(), 2);
551        assert!(!batch.is_empty());
552    }
553
554    #[test]
555    fn test_serialization() {
556        let request = JsonRpcRequest::new(
557            "test_method".to_string(),
558            Some(json!({"param": "value"})),
559            RequestId::String("123".to_string()),
560        );
561
562        let json = serde_json::to_string(&request).unwrap();
563        let parsed: JsonRpcRequest = serde_json::from_str(&json).unwrap();
564
565        assert_eq!(parsed.method, request.method);
566        assert_eq!(parsed.params, request.params);
567    }
568
569    #[test]
570    fn test_utils() {
571        let json = r#"{"jsonrpc":"2.0","method":"test","id":"123"}"#;
572
573        assert!(!utils::is_batch(json));
574        assert_eq!(utils::extract_method(json), Some("test".to_string()));
575
576        let batch_json = r#"[{"jsonrpc":"2.0","method":"test","id":"123"}]"#;
577        assert!(utils::is_batch(batch_json));
578    }
579
580    #[test]
581    fn test_error_codes() {
582        let parse_error = JsonRpcErrorCode::ParseError;
583        assert_eq!(parse_error.code(), -32700);
584        assert_eq!(parse_error.message(), "Parse error");
585
586        let app_error = JsonRpcErrorCode::ApplicationError(-32001);
587        assert_eq!(app_error.code(), -32001);
588    }
589}