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///
139/// **IMPORTANT**: JSON-RPC batching is NOT supported in MCP 2025-06-18 specification.
140/// This type exists only for defensive deserialization and will return errors if used.
141/// Per MCP spec changelog (PR #416), batch support was explicitly removed.
142///
143/// Do not use this type in new code. It will be removed in a future version.
144#[derive(Debug, Clone, Serialize, Deserialize)]
145#[serde(transparent)]
146#[deprecated(
147    since = "2.2.3",
148    note = "JSON-RPC batching removed from MCP 2025-06-18 spec (PR #416). This type exists only for defensive handling and will be removed."
149)]
150pub struct JsonRpcBatch<T> {
151    /// Batch items
152    pub items: Vec<T>,
153}
154
155/// Standard JSON-RPC error codes
156#[derive(Debug, Clone, Copy, PartialEq, Eq)]
157pub enum JsonRpcErrorCode {
158    /// Parse error (-32700)
159    ParseError,
160    /// Invalid request (-32600)
161    InvalidRequest,
162    /// Method not found (-32601)
163    MethodNotFound,
164    /// Invalid params (-32602)
165    InvalidParams,
166    /// Internal error (-32603)
167    InternalError,
168    /// Application-defined error
169    ApplicationError(i32),
170}
171
172impl JsonRpcErrorCode {
173    /// Get the numeric error code
174    pub fn code(&self) -> i32 {
175        match self {
176            Self::ParseError => -32700,
177            Self::InvalidRequest => -32600,
178            Self::MethodNotFound => -32601,
179            Self::InvalidParams => -32602,
180            Self::InternalError => -32603,
181            Self::ApplicationError(code) => *code,
182        }
183    }
184
185    /// Get the standard error message
186    pub fn message(&self) -> &'static str {
187        match self {
188            Self::ParseError => "Parse error",
189            Self::InvalidRequest => "Invalid Request",
190            Self::MethodNotFound => "Method not found",
191            Self::InvalidParams => "Invalid params",
192            Self::InternalError => "Internal error",
193            Self::ApplicationError(_) => "Application error",
194        }
195    }
196}
197
198impl fmt::Display for JsonRpcErrorCode {
199    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
200        write!(f, "{} ({})", self.message(), self.code())
201    }
202}
203
204impl From<JsonRpcErrorCode> for JsonRpcError {
205    fn from(code: JsonRpcErrorCode) -> Self {
206        Self {
207            code: code.code(),
208            message: code.message().to_string(),
209            data: None,
210        }
211    }
212}
213
214impl From<i32> for JsonRpcErrorCode {
215    fn from(code: i32) -> Self {
216        match code {
217            -32700 => Self::ParseError,
218            -32600 => Self::InvalidRequest,
219            -32601 => Self::MethodNotFound,
220            -32602 => Self::InvalidParams,
221            -32603 => Self::InternalError,
222            other => Self::ApplicationError(other),
223        }
224    }
225}
226
227/// JSON-RPC message type (union of request, response, notification)
228///
229/// **MCP 2025-06-18 Compliance Note:**
230/// Batch variants exist only for defensive deserialization and are NOT supported
231/// per MCP specification (PR #416 removed batch support). They will return errors if encountered.
232#[derive(Debug, Clone, Serialize, Deserialize)]
233#[serde(untagged)]
234pub enum JsonRpcMessage {
235    /// Request message (MCP-compliant)
236    Request(JsonRpcRequest),
237    /// Response message (MCP-compliant)
238    Response(JsonRpcResponse),
239    /// Notification message (MCP-compliant)
240    Notification(JsonRpcNotification),
241    /// Batch of messages (NOT SUPPORTED - defensive deserialization only)
242    ///
243    /// **Deprecated**: MCP 2025-06-18 removed batch support.
244    /// This variant exists only to return proper errors if batches are received.
245    #[deprecated(since = "2.2.3", note = "Batching removed from MCP spec")]
246    #[allow(deprecated)] // Internal use of deprecated batch type for defensive deserialization
247    RequestBatch(JsonRpcBatch<JsonRpcRequest>),
248    /// Batch of responses (NOT SUPPORTED - defensive deserialization only)
249    ///
250    /// **Deprecated**: MCP 2025-06-18 removed batch support.
251    /// This variant exists only to return proper errors if batches are received.
252    #[deprecated(since = "2.2.3", note = "Batching removed from MCP spec")]
253    #[allow(deprecated)] // Internal use of deprecated batch type for defensive deserialization
254    ResponseBatch(JsonRpcBatch<JsonRpcResponse>),
255    /// Mixed batch (NOT SUPPORTED - defensive deserialization only)
256    ///
257    /// **Deprecated**: MCP 2025-06-18 removed batch support.
258    /// This variant exists only to return proper errors if batches are received.
259    #[deprecated(since = "2.2.3", note = "Batching removed from MCP spec")]
260    #[allow(deprecated)] // Internal use of deprecated batch type for defensive deserialization
261    MessageBatch(JsonRpcBatch<JsonRpcMessage>),
262}
263
264impl JsonRpcRequest {
265    /// Create a new JSON-RPC request
266    pub fn new(method: String, params: Option<Value>, id: RequestId) -> Self {
267        Self {
268            jsonrpc: JsonRpcVersion,
269            method,
270            params,
271            id,
272        }
273    }
274
275    /// Create a request with no parameters
276    pub fn without_params(method: String, id: RequestId) -> Self {
277        Self::new(method, None, id)
278    }
279
280    /// Create a request with parameters
281    pub fn with_params<P: Serialize>(
282        method: String,
283        params: P,
284        id: RequestId,
285    ) -> Result<Self, serde_json::Error> {
286        let params_value = serde_json::to_value(params)?;
287        Ok(Self::new(method, Some(params_value), id))
288    }
289}
290
291impl JsonRpcResponse {
292    /// Create a successful response
293    pub fn success(result: Value, id: RequestId) -> Self {
294        Self {
295            jsonrpc: JsonRpcVersion,
296            payload: JsonRpcResponsePayload::Success { result },
297            id: ResponseId::from_request(id),
298        }
299    }
300
301    /// Create an error response with request ID
302    pub fn error_response(error: JsonRpcError, id: RequestId) -> Self {
303        Self {
304            jsonrpc: JsonRpcVersion,
305            payload: JsonRpcResponsePayload::Error { error },
306            id: ResponseId::from_request(id),
307        }
308    }
309
310    /// Create a parse error response (id is null)
311    pub fn parse_error(message: Option<String>) -> Self {
312        let error = JsonRpcError {
313            code: JsonRpcErrorCode::ParseError.code(),
314            message: message.unwrap_or_else(|| JsonRpcErrorCode::ParseError.message().to_string()),
315            data: None,
316        };
317        Self {
318            jsonrpc: JsonRpcVersion,
319            payload: JsonRpcResponsePayload::Error { error },
320            id: ResponseId::null(),
321        }
322    }
323
324    /// Check if this is a successful response
325    pub fn is_success(&self) -> bool {
326        matches!(self.payload, JsonRpcResponsePayload::Success { .. })
327    }
328
329    /// Check if this is an error response
330    pub fn is_error(&self) -> bool {
331        matches!(self.payload, JsonRpcResponsePayload::Error { .. })
332    }
333
334    /// Get the result if this is a success response
335    pub fn result(&self) -> Option<&Value> {
336        match &self.payload {
337            JsonRpcResponsePayload::Success { result } => Some(result),
338            JsonRpcResponsePayload::Error { .. } => None,
339        }
340    }
341
342    /// Get the error if this is an error response
343    pub fn error(&self) -> Option<&JsonRpcError> {
344        match &self.payload {
345            JsonRpcResponsePayload::Success { .. } => None,
346            JsonRpcResponsePayload::Error { error } => Some(error),
347        }
348    }
349
350    /// Get the request ID if this is not a parse error
351    pub fn request_id(&self) -> Option<&RequestId> {
352        self.id.as_request_id()
353    }
354
355    /// Check if this response is for a parse error (has null ID)
356    pub fn is_parse_error(&self) -> bool {
357        self.id.is_null()
358    }
359
360    /// Get mutable reference to result if this is a success response
361    pub fn result_mut(&mut self) -> Option<&mut Value> {
362        match &mut self.payload {
363            JsonRpcResponsePayload::Success { result } => Some(result),
364            JsonRpcResponsePayload::Error { .. } => None,
365        }
366    }
367
368    /// Get mutable reference to error if this is an error response
369    pub fn error_mut(&mut self) -> Option<&mut JsonRpcError> {
370        match &mut self.payload {
371            JsonRpcResponsePayload::Success { .. } => None,
372            JsonRpcResponsePayload::Error { error } => Some(error),
373        }
374    }
375
376    /// Set the result for this response (converts to success response)
377    pub fn set_result(&mut self, result: Value) {
378        self.payload = JsonRpcResponsePayload::Success { result };
379    }
380
381    /// Set the error for this response (converts to error response)
382    pub fn set_error(&mut self, error: JsonRpcError) {
383        self.payload = JsonRpcResponsePayload::Error { error };
384    }
385}
386
387impl JsonRpcNotification {
388    /// Create a new JSON-RPC notification
389    pub fn new(method: String, params: Option<Value>) -> Self {
390        Self {
391            jsonrpc: JsonRpcVersion,
392            method,
393            params,
394        }
395    }
396
397    /// Create a notification with no parameters
398    pub fn without_params(method: String) -> Self {
399        Self::new(method, None)
400    }
401
402    /// Create a notification with parameters
403    pub fn with_params<P: Serialize>(method: String, params: P) -> Result<Self, serde_json::Error> {
404        let params_value = serde_json::to_value(params)?;
405        Ok(Self::new(method, Some(params_value)))
406    }
407}
408
409// Allow deprecated warnings for internal implementation of deprecated batch types
410// External users will still see deprecation warnings, but implementation won't spam warnings
411#[allow(deprecated)]
412impl<T> JsonRpcBatch<T> {
413    /// Create a new batch
414    pub fn new(items: Vec<T>) -> Self {
415        Self { items }
416    }
417
418    /// Create an empty batch
419    pub fn empty() -> Self {
420        Self::new(Vec::new())
421    }
422
423    /// Add an item to the batch
424    pub fn push(&mut self, item: T) {
425        self.items.push(item);
426    }
427
428    /// Get the number of items in the batch
429    pub fn len(&self) -> usize {
430        self.items.len()
431    }
432
433    /// Check if the batch is empty
434    pub fn is_empty(&self) -> bool {
435        self.items.is_empty()
436    }
437
438    /// Iterate over batch items
439    pub fn iter(&self) -> impl Iterator<Item = &T> {
440        self.items.iter()
441    }
442}
443
444#[allow(deprecated)]
445impl<T> IntoIterator for JsonRpcBatch<T> {
446    type Item = T;
447    type IntoIter = std::vec::IntoIter<T>;
448
449    fn into_iter(self) -> Self::IntoIter {
450        self.items.into_iter()
451    }
452}
453
454#[allow(deprecated)]
455impl<T> From<Vec<T>> for JsonRpcBatch<T> {
456    fn from(items: Vec<T>) -> Self {
457        Self::new(items)
458    }
459}
460
461/// Utility functions for JSON-RPC message handling
462pub mod utils {
463    use super::*;
464
465    /// Parse a JSON-RPC message from a string
466    pub fn parse_message(json: &str) -> Result<JsonRpcMessage, serde_json::Error> {
467        serde_json::from_str(json)
468    }
469
470    /// Serialize a JSON-RPC message to a string
471    pub fn serialize_message(message: &JsonRpcMessage) -> Result<String, serde_json::Error> {
472        serde_json::to_string(message)
473    }
474
475    /// Check if a string looks like a JSON-RPC batch
476    pub fn is_batch(json: &str) -> bool {
477        json.trim_start().starts_with('[')
478    }
479
480    /// Extract the method name from a JSON-RPC message string
481    pub fn extract_method(json: &str) -> Option<String> {
482        // Simple regex-free method extraction for performance
483        if let Ok(value) = serde_json::from_str::<serde_json::Value>(json)
484            && let Some(method) = value.get("method")
485        {
486            return method.as_str().map(String::from);
487        }
488        None
489    }
490}
491
492#[cfg(test)]
493#[allow(deprecated)] // Tests cover deprecated batch functionality for defensive deserialization
494mod tests {
495    use super::*;
496    use serde_json::json;
497
498    #[test]
499    fn test_jsonrpc_version() {
500        let version = JsonRpcVersion;
501        let json = serde_json::to_string(&version).unwrap();
502        assert_eq!(json, "\"2.0\"");
503
504        let parsed: JsonRpcVersion = serde_json::from_str(&json).unwrap();
505        assert_eq!(parsed, version);
506    }
507
508    #[test]
509    fn test_request_creation() {
510        let request = JsonRpcRequest::new(
511            "test_method".to_string(),
512            Some(json!({"key": "value"})),
513            RequestId::String("test-id".to_string()),
514        );
515
516        assert_eq!(request.method, "test_method");
517        assert!(request.params.is_some());
518    }
519
520    #[test]
521    fn test_response_creation() {
522        let response = JsonRpcResponse::success(
523            json!({"result": "success"}),
524            RequestId::String("test-id".to_string()),
525        );
526
527        assert!(response.is_success());
528        assert!(!response.is_error());
529        assert!(response.result().is_some());
530        assert!(response.error().is_none());
531        assert!(!response.is_parse_error());
532    }
533
534    #[test]
535    fn test_error_response() {
536        let error = JsonRpcError::from(JsonRpcErrorCode::MethodNotFound);
537        let response =
538            JsonRpcResponse::error_response(error, RequestId::String("test-id".to_string()));
539
540        assert!(!response.is_success());
541        assert!(response.is_error());
542        assert!(response.result().is_none());
543        assert!(response.error().is_some());
544        assert!(!response.is_parse_error());
545    }
546
547    #[test]
548    fn test_parse_error_response() {
549        let response = JsonRpcResponse::parse_error(Some("Invalid JSON".to_string()));
550
551        assert!(!response.is_success());
552        assert!(response.is_error());
553        assert!(response.result().is_none());
554        assert!(response.error().is_some());
555        assert!(response.is_parse_error());
556        assert!(response.request_id().is_none());
557
558        // Verify the error details
559        let error = response.error().unwrap();
560        assert_eq!(error.code, JsonRpcErrorCode::ParseError.code());
561        assert_eq!(error.message, "Invalid JSON");
562    }
563
564    #[test]
565    fn test_notification() {
566        let notification = JsonRpcNotification::without_params("test_notification".to_string());
567        assert_eq!(notification.method, "test_notification");
568        assert!(notification.params.is_none());
569    }
570
571    #[test]
572    fn test_batch() {
573        let mut batch = JsonRpcBatch::<JsonRpcRequest>::empty();
574        assert!(batch.is_empty());
575
576        batch.push(JsonRpcRequest::without_params(
577            "method1".to_string(),
578            RequestId::String("1".to_string()),
579        ));
580        batch.push(JsonRpcRequest::without_params(
581            "method2".to_string(),
582            RequestId::String("2".to_string()),
583        ));
584
585        assert_eq!(batch.len(), 2);
586        assert!(!batch.is_empty());
587    }
588
589    #[test]
590    fn test_serialization() {
591        let request = JsonRpcRequest::new(
592            "test_method".to_string(),
593            Some(json!({"param": "value"})),
594            RequestId::String("123".to_string()),
595        );
596
597        let json = serde_json::to_string(&request).unwrap();
598        let parsed: JsonRpcRequest = serde_json::from_str(&json).unwrap();
599
600        assert_eq!(parsed.method, request.method);
601        assert_eq!(parsed.params, request.params);
602    }
603
604    #[test]
605    fn test_utils() {
606        let json = r#"{"jsonrpc":"2.0","method":"test","id":"123"}"#;
607
608        assert!(!utils::is_batch(json));
609        assert_eq!(utils::extract_method(json), Some("test".to_string()));
610
611        let batch_json = r#"[{"jsonrpc":"2.0","method":"test","id":"123"}]"#;
612        assert!(utils::is_batch(batch_json));
613    }
614
615    #[test]
616    fn test_error_codes() {
617        let parse_error = JsonRpcErrorCode::ParseError;
618        assert_eq!(parse_error.code(), -32700);
619        assert_eq!(parse_error.message(), "Parse error");
620
621        let app_error = JsonRpcErrorCode::ApplicationError(-32001);
622        assert_eq!(app_error.code(), -32001);
623    }
624}