Skip to main content

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", default)]
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///
60/// Per JSON-RPC 2.0 §5: a response object MUST contain `result` xor `error`,
61/// never both, never neither. The custom `Deserialize` impl enforces this:
62/// `{}` (neither) and `{ "result": ..., "error": ... }` (both) both fail with
63/// a clear, single-line error rather than serde's "missing field `result`"
64/// or silently dropping `error` (which the previous `#[serde(untagged)]`
65/// implementation would do — first variant wins).
66#[derive(Debug, Clone, Serialize)]
67#[serde(untagged)]
68pub enum JsonRpcResponsePayload {
69    /// Successful response with result
70    Success {
71        /// Response result
72        result: Value,
73    },
74    /// Error response
75    Error {
76        /// Response error
77        error: JsonRpcError,
78    },
79}
80
81impl<'de> Deserialize<'de> for JsonRpcResponsePayload {
82    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
83    where
84        D: serde::Deserializer<'de>,
85    {
86        let mut object = serde_json::Map::<String, Value>::deserialize(deserializer)?;
87        let result_present = object.contains_key("result");
88        let error_present = object.contains_key("error");
89
90        match (result_present, error_present) {
91            (true, false) => Ok(Self::Success {
92                result: object.remove("result").unwrap_or(Value::Null),
93            }),
94            (false, true) => {
95                let error_value = object.remove("error").unwrap_or(Value::Null);
96                let error =
97                    JsonRpcError::deserialize(error_value).map_err(serde::de::Error::custom)?;
98                Ok(Self::Error { error })
99            }
100            (true, true) => Err(serde::de::Error::custom(
101                "JSON-RPC response must contain exactly one of `result` or `error`, not both",
102            )),
103            (false, false) => Err(serde::de::Error::custom(
104                "JSON-RPC response must contain exactly one of `result` or `error`",
105            )),
106        }
107    }
108}
109
110/// JSON-RPC response message
111#[derive(Debug, Clone, Serialize, Deserialize)]
112pub struct JsonRpcResponse {
113    /// JSON-RPC version
114    pub jsonrpc: JsonRpcVersion,
115    /// Response payload (either result or error, never both)
116    #[serde(flatten)]
117    pub payload: JsonRpcResponsePayload,
118    /// Request identifier (required except for parse errors)
119    pub id: ResponseId,
120}
121
122/// Response ID - handles the special case where parse errors have null ID
123#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
124#[serde(transparent)]
125pub struct ResponseId(pub Option<RequestId>);
126
127impl ResponseId {
128    /// Create a response ID for a normal response
129    pub fn from_request(id: RequestId) -> Self {
130        Self(Some(id))
131    }
132
133    /// Create a null response ID for parse errors
134    pub fn null() -> Self {
135        Self(None)
136    }
137
138    /// Get the request ID if present
139    pub fn as_request_id(&self) -> Option<&RequestId> {
140        self.0.as_ref()
141    }
142
143    /// Check if this is a null ID (parse error)
144    pub fn is_null(&self) -> bool {
145        self.0.is_none()
146    }
147}
148
149/// JSON-RPC notification message (no response expected)
150#[derive(Debug, Clone, Serialize, Deserialize)]
151pub struct JsonRpcNotification {
152    /// JSON-RPC version
153    pub jsonrpc: JsonRpcVersion,
154    /// Notification method name
155    pub method: String,
156    /// Notification parameters
157    #[serde(skip_serializing_if = "Option::is_none", default)]
158    pub params: Option<Value>,
159}
160
161/// JSON-RPC error object
162#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
163pub struct JsonRpcError {
164    /// Error code
165    pub code: i32,
166    /// Error message
167    pub message: String,
168    /// Additional error data
169    #[serde(skip_serializing_if = "Option::is_none", default)]
170    pub data: Option<Value>,
171}
172
173impl JsonRpcError {
174    /// JSON-RPC 2.0 §5.1 reserves -32768..-32000 (the "Server error" range goes from
175    /// -32099 to -32000 inclusive). Custom server errors must fall in that range;
176    /// other application-level errors should be conveyed via `data`, not via codes
177    /// outside the reserved space, to avoid colliding with future spec assignments.
178    const SERVER_ERROR_RANGE: std::ops::RangeInclusive<i32> = -32099..=-32000;
179    /// Reserved codes already standardized (parse/invalid request/method not
180    /// found/invalid params/internal error). Outside the server-error range but
181    /// always allowed because they're spec-mandated.
182    const STANDARD_CODES: &'static [i32] = &[-32700, -32600, -32601, -32602, -32603];
183
184    /// Create a new JSON-RPC error.
185    ///
186    /// Custom application errors should use codes in `-32099..=-32000` (the server
187    /// error range). Codes outside that range and outside the well-known standard
188    /// codes are accepted but logged at WARN — they may collide with future spec
189    /// assignments. Use [`Self::with_validated_code`] to fail-fast instead.
190    pub fn new(code: i32, message: impl Into<String>) -> Self {
191        if !Self::is_valid_code(code) {
192            tracing::warn!(
193                code,
194                "JSON-RPC error code outside reserved server-error range -32099..=-32000 \
195                 and not a standardized JSON-RPC 2.0 code; this risks colliding with \
196                 future spec assignments"
197            );
198        }
199        Self {
200            code,
201            message: Self::cap_message(message.into()),
202            data: None,
203        }
204    }
205
206    /// Like [`Self::new`] but returns `Err` instead of warning when the code is out
207    /// of the JSON-RPC 2.0 reserved or standardized ranges.
208    pub fn with_validated_code(
209        code: i32,
210        message: impl Into<String>,
211    ) -> Result<Self, &'static str> {
212        if !Self::is_valid_code(code) {
213            return Err(
214                "JSON-RPC error code must be a standardized code or in the -32099..=-32000 server-error range",
215            );
216        }
217        Ok(Self {
218            code,
219            message: Self::cap_message(message.into()),
220            data: None,
221        })
222    }
223
224    fn is_valid_code(code: i32) -> bool {
225        Self::SERVER_ERROR_RANGE.contains(&code) || Self::STANDARD_CODES.contains(&code)
226    }
227
228    /// Soft cap on the on-wire `message` field, in bytes.
229    ///
230    /// JSON-RPC error messages routinely include user-supplied `details` (see
231    /// `invalid_params`, `parse_error_with_details`, `invalid_request_with_reason`).
232    /// A naive caller passing a multi-MiB payload would amplify the response
233    /// and risk leaking the offending input back to a third party. We truncate
234    /// at a UTF-8 char boundary and append a `…[truncated, N bytes elided]`
235    /// suffix; the `data` field carries the same cap.
236    const MESSAGE_BYTE_CAP: usize = 1024;
237
238    /// Truncate `s` at a UTF-8 char boundary, preserving the first
239    /// `MESSAGE_BYTE_CAP` bytes and appending an ellipsis with the elision
240    /// count. Cheap no-op for short messages.
241    fn cap_message(s: String) -> String {
242        if s.len() <= Self::MESSAGE_BYTE_CAP {
243            return s;
244        }
245        let mut end = Self::MESSAGE_BYTE_CAP;
246        while end > 0 && !s.is_char_boundary(end) {
247            end -= 1;
248        }
249        let elided = s.len() - end;
250        let mut out = String::with_capacity(end + 32);
251        out.push_str(&s[..end]);
252        out.push_str(&format!("…[truncated, {elided} bytes elided]"));
253        out
254    }
255
256    /// Same cap applied to a `data` payload's string fields.
257    fn cap_data_value(data: Value) -> Value {
258        match data {
259            Value::String(s) => Value::String(Self::cap_message(s)),
260            Value::Array(values) => {
261                Value::Array(values.into_iter().map(Self::cap_data_value).collect())
262            }
263            Value::Object(map) => {
264                let capped = map
265                    .into_iter()
266                    .map(|(k, v)| (k, Self::cap_data_value(v)))
267                    .collect();
268                Value::Object(capped)
269            }
270            other => other,
271        }
272    }
273
274    /// Create a new JSON-RPC error with additional data
275    pub fn with_data(code: i32, message: impl Into<String>, data: Value) -> Self {
276        if !Self::is_valid_code(code) {
277            tracing::warn!(
278                code,
279                "JSON-RPC error code outside reserved server-error range -32099..=-32000"
280            );
281        }
282        Self {
283            code,
284            message: Self::cap_message(message.into()),
285            data: Some(Self::cap_data_value(data)),
286        }
287    }
288
289    /// Create a parse error (-32700)
290    pub fn parse_error() -> Self {
291        Self::new(-32700, "Parse error")
292    }
293
294    /// Create a parse error with details
295    pub fn parse_error_with_details(details: impl Into<String>) -> Self {
296        Self::with_data(
297            -32700,
298            "Parse error",
299            serde_json::json!({ "details": details.into() }),
300        )
301    }
302
303    /// Create an invalid request error (-32600)
304    pub fn invalid_request() -> Self {
305        Self::new(-32600, "Invalid Request")
306    }
307
308    /// Create an invalid request error with reason
309    pub fn invalid_request_with_reason(reason: impl Into<String>) -> Self {
310        Self::with_data(
311            -32600,
312            "Invalid Request",
313            serde_json::json!({ "reason": reason.into() }),
314        )
315    }
316
317    /// Create a method not found error (-32601)
318    pub fn method_not_found(method: &str) -> Self {
319        Self::new(-32601, format!("Method not found: {method}"))
320    }
321
322    /// Create an invalid params error (-32602)
323    pub fn invalid_params(details: &str) -> Self {
324        Self::new(-32602, format!("Invalid params: {details}"))
325    }
326
327    /// Create an internal error (-32603)
328    pub fn internal_error(details: &str) -> Self {
329        Self::new(-32603, format!("Internal error: {details}"))
330    }
331
332    /// Check if this is a parse error
333    pub fn is_parse_error(&self) -> bool {
334        self.code == -32700
335    }
336
337    /// Check if this is an invalid request error
338    pub fn is_invalid_request(&self) -> bool {
339        self.code == -32600
340    }
341
342    /// Check if this is a method-not-found error (-32601)
343    pub fn is_method_not_found(&self) -> bool {
344        self.code == -32601
345    }
346
347    /// Check if this is an invalid-params error (-32602)
348    pub fn is_invalid_params(&self) -> bool {
349        self.code == -32602
350    }
351
352    /// Check if this is an internal error (-32603)
353    pub fn is_internal_error(&self) -> bool {
354        self.code == -32603
355    }
356
357    /// Check if this code falls in JSON-RPC 2.0's implementation-defined
358    /// server-error range `-32099..=-32000` (§5.1).
359    pub fn is_server_error(&self) -> bool {
360        (-32099..=-32000).contains(&self.code)
361    }
362
363    /// Get the error code
364    pub fn code(&self) -> i32 {
365        self.code
366    }
367
368    /// Classify this error against the JSON-RPC standard error enum.
369    /// Returns `None` for codes outside the JSON-RPC reserved range.
370    pub fn standard_kind(&self) -> Option<JsonRpcErrorCode> {
371        match self.code {
372            -32700 => Some(JsonRpcErrorCode::ParseError),
373            -32600 => Some(JsonRpcErrorCode::InvalidRequest),
374            -32601 => Some(JsonRpcErrorCode::MethodNotFound),
375            -32602 => Some(JsonRpcErrorCode::InvalidParams),
376            -32603 => Some(JsonRpcErrorCode::InternalError),
377            _ => None,
378        }
379    }
380}
381
382/// Standard JSON-RPC error codes
383#[derive(Debug, Clone, Copy, PartialEq, Eq)]
384pub enum JsonRpcErrorCode {
385    /// Parse error (-32700)
386    ParseError,
387    /// Invalid request (-32600)
388    InvalidRequest,
389    /// Method not found (-32601)
390    MethodNotFound,
391    /// Invalid params (-32602)
392    InvalidParams,
393    /// Internal error (-32603)
394    InternalError,
395    /// Application-defined error
396    ApplicationError(i32),
397}
398
399impl JsonRpcErrorCode {
400    /// Get the numeric error code
401    pub fn code(&self) -> i32 {
402        match self {
403            Self::ParseError => -32700,
404            Self::InvalidRequest => -32600,
405            Self::MethodNotFound => -32601,
406            Self::InvalidParams => -32602,
407            Self::InternalError => -32603,
408            Self::ApplicationError(code) => *code,
409        }
410    }
411
412    /// Get the standard error message
413    pub fn message(&self) -> &'static str {
414        match self {
415            Self::ParseError => "Parse error",
416            Self::InvalidRequest => "Invalid Request",
417            Self::MethodNotFound => "Method not found",
418            Self::InvalidParams => "Invalid params",
419            Self::InternalError => "Internal error",
420            Self::ApplicationError(_) => "Application error",
421        }
422    }
423}
424
425impl fmt::Display for JsonRpcErrorCode {
426    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
427        write!(f, "{} ({})", self.message(), self.code())
428    }
429}
430
431impl From<JsonRpcErrorCode> for JsonRpcError {
432    fn from(code: JsonRpcErrorCode) -> Self {
433        Self {
434            code: code.code(),
435            message: code.message().to_string(),
436            data: None,
437        }
438    }
439}
440
441impl From<i32> for JsonRpcErrorCode {
442    fn from(code: i32) -> Self {
443        match code {
444            -32700 => Self::ParseError,
445            -32600 => Self::InvalidRequest,
446            -32601 => Self::MethodNotFound,
447            -32602 => Self::InvalidParams,
448            -32603 => Self::InternalError,
449            other => Self::ApplicationError(other),
450        }
451    }
452}
453
454/// JSON-RPC message type (union of request, response, notification)
455///
456/// Per the current MCP specification, batch operations are not supported.
457#[derive(Debug, Clone, Serialize, Deserialize)]
458#[serde(untagged)]
459pub enum JsonRpcMessage {
460    /// Request message
461    Request(JsonRpcRequest),
462    /// Response message
463    Response(JsonRpcResponse),
464    /// Notification message
465    Notification(JsonRpcNotification),
466}
467
468impl JsonRpcRequest {
469    /// Create a new JSON-RPC request
470    pub fn new(method: String, params: Option<Value>, id: RequestId) -> Self {
471        Self {
472            jsonrpc: JsonRpcVersion,
473            method,
474            params,
475            id,
476        }
477    }
478
479    /// Create a request with no parameters
480    pub fn without_params(method: String, id: RequestId) -> Self {
481        Self::new(method, None, id)
482    }
483
484    /// Create a request with parameters
485    pub fn with_params<P: Serialize>(
486        method: String,
487        params: P,
488        id: RequestId,
489    ) -> Result<Self, serde_json::Error> {
490        let params_value = serde_json::to_value(params)?;
491        Ok(Self::new(method, Some(params_value), id))
492    }
493}
494
495impl JsonRpcResponse {
496    /// Create a successful response
497    pub fn success(result: Value, id: RequestId) -> Self {
498        Self {
499            jsonrpc: JsonRpcVersion,
500            payload: JsonRpcResponsePayload::Success { result },
501            id: ResponseId::from_request(id),
502        }
503    }
504
505    /// Create an error response with request ID
506    pub fn error_response(error: JsonRpcError, id: RequestId) -> Self {
507        Self {
508            jsonrpc: JsonRpcVersion,
509            payload: JsonRpcResponsePayload::Error { error },
510            id: ResponseId::from_request(id),
511        }
512    }
513
514    /// Create a parse error response (id is null)
515    pub fn parse_error(message: Option<String>) -> Self {
516        let error = JsonRpcError {
517            code: JsonRpcErrorCode::ParseError.code(),
518            message: message.unwrap_or_else(|| JsonRpcErrorCode::ParseError.message().to_string()),
519            data: None,
520        };
521        Self {
522            jsonrpc: JsonRpcVersion,
523            payload: JsonRpcResponsePayload::Error { error },
524            id: ResponseId::null(),
525        }
526    }
527
528    /// Check if this is a successful response
529    pub fn is_success(&self) -> bool {
530        matches!(self.payload, JsonRpcResponsePayload::Success { .. })
531    }
532
533    /// Check if this is an error response
534    pub fn is_error(&self) -> bool {
535        matches!(self.payload, JsonRpcResponsePayload::Error { .. })
536    }
537
538    /// Get the result if this is a success response
539    pub fn result(&self) -> Option<&Value> {
540        match &self.payload {
541            JsonRpcResponsePayload::Success { result } => Some(result),
542            JsonRpcResponsePayload::Error { .. } => None,
543        }
544    }
545
546    /// Get the error if this is an error response
547    pub fn error(&self) -> Option<&JsonRpcError> {
548        match &self.payload {
549            JsonRpcResponsePayload::Success { .. } => None,
550            JsonRpcResponsePayload::Error { error } => Some(error),
551        }
552    }
553
554    /// Get the request ID if this is not a parse error
555    pub fn request_id(&self) -> Option<&RequestId> {
556        self.id.as_request_id()
557    }
558
559    /// Check if this response is for a parse error (has null ID)
560    pub fn is_parse_error(&self) -> bool {
561        self.id.is_null()
562    }
563
564    /// Get mutable reference to result if this is a success response
565    pub fn result_mut(&mut self) -> Option<&mut Value> {
566        match &mut self.payload {
567            JsonRpcResponsePayload::Success { result } => Some(result),
568            JsonRpcResponsePayload::Error { .. } => None,
569        }
570    }
571
572    /// Get mutable reference to error if this is an error response
573    pub fn error_mut(&mut self) -> Option<&mut JsonRpcError> {
574        match &mut self.payload {
575            JsonRpcResponsePayload::Success { .. } => None,
576            JsonRpcResponsePayload::Error { error } => Some(error),
577        }
578    }
579
580    /// Set the result for this response (converts to success response)
581    pub fn set_result(&mut self, result: Value) {
582        self.payload = JsonRpcResponsePayload::Success { result };
583    }
584
585    /// Set the error for this response (converts to error response)
586    pub fn set_error(&mut self, error: JsonRpcError) {
587        self.payload = JsonRpcResponsePayload::Error { error };
588    }
589}
590
591impl JsonRpcNotification {
592    /// Create a new JSON-RPC notification
593    pub fn new(method: String, params: Option<Value>) -> Self {
594        Self {
595            jsonrpc: JsonRpcVersion,
596            method,
597            params,
598        }
599    }
600
601    /// Create a notification with no parameters
602    pub fn without_params(method: String) -> Self {
603        Self::new(method, None)
604    }
605
606    /// Create a notification with parameters
607    pub fn with_params<P: Serialize>(method: String, params: P) -> Result<Self, serde_json::Error> {
608        let params_value = serde_json::to_value(params)?;
609        Ok(Self::new(method, Some(params_value)))
610    }
611}
612
613/// Utility functions for JSON-RPC message handling
614pub mod utils {
615    use super::*;
616
617    /// Error returned by [`parse_message_typed`] for the cases the untagged
618    /// `JsonRpcMessage` enum can't distinguish on its own.
619    ///
620    /// `JsonRpcMessage` is `#[serde(untagged)]` over Request/Response/Notification.
621    /// A top-level JSON array (a JSON-RPC §7 batch) fails serde's variant
622    /// match with a generic "data did not match any variant" diagnostic;
623    /// MCP 2025-11-25 deprecates batches, so callers want to respond with a
624    /// clear `-32600 Invalid Request` rather than echoing serde's text.
625    #[derive(Debug)]
626    pub enum ParseMessageError {
627        /// Top-level JSON array (deprecated batch shape).
628        BatchUnsupported,
629        /// Anything else (parse error, unknown variant, etc.).
630        Json(serde_json::Error),
631    }
632
633    impl core::fmt::Display for ParseMessageError {
634        fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
635            match self {
636                Self::BatchUnsupported => {
637                    f.write_str("JSON-RPC batches are not supported in MCP 2025-11-25")
638                }
639                Self::Json(e) => write!(f, "{e}"),
640            }
641        }
642    }
643
644    impl std::error::Error for ParseMessageError {
645        fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
646            match self {
647                Self::BatchUnsupported => None,
648                Self::Json(e) => Some(e),
649            }
650        }
651    }
652
653    impl From<serde_json::Error> for ParseMessageError {
654        fn from(e: serde_json::Error) -> Self {
655            Self::Json(e)
656        }
657    }
658
659    /// Parse a JSON-RPC message from a string.
660    ///
661    /// Kept on the original signature for backwards compatibility — callers
662    /// who need to distinguish "batch not supported" from generic parse
663    /// errors should use [`parse_message_typed`].
664    pub fn parse_message(json: &str) -> Result<JsonRpcMessage, serde_json::Error> {
665        serde_json::from_str(json)
666    }
667
668    /// Parse a JSON-RPC message and surface batch arrays as a distinct error.
669    ///
670    /// Returns `ParseMessageError::BatchUnsupported` when the input's first
671    /// non-whitespace byte is `[` (a JSON array). Otherwise behaves like
672    /// [`parse_message`]. Callers can map the typed error to a JSON-RPC
673    /// `-32600 Invalid Request` response with the stable message.
674    pub fn parse_message_typed(json: &str) -> Result<JsonRpcMessage, ParseMessageError> {
675        if json.trim_start().as_bytes().first() == Some(&b'[') {
676            return Err(ParseMessageError::BatchUnsupported);
677        }
678        Ok(serde_json::from_str(json)?)
679    }
680
681    /// Serialize a JSON-RPC message to a string
682    pub fn serialize_message(message: &JsonRpcMessage) -> Result<String, serde_json::Error> {
683        serde_json::to_string(message)
684    }
685
686    /// Extract the method name from a JSON-RPC message string
687    pub fn extract_method(json: &str) -> Option<String> {
688        // Simple regex-free method extraction for performance
689        if let Ok(value) = serde_json::from_str::<serde_json::Value>(json)
690            && let Some(method) = value.get("method")
691        {
692            return method.as_str().map(String::from);
693        }
694        None
695    }
696}
697
698/// HTTP boundary types for lenient JSON-RPC parsing
699///
700/// These types are designed for parsing JSON-RPC messages at HTTP boundaries where
701/// the input may not be strictly compliant. They accept any valid JSON structure
702/// and can be converted to the canonical types after validation.
703///
704/// # Usage
705///
706/// ```rust
707/// use turbomcp_protocol::jsonrpc::http::{HttpJsonRpcRequest, HttpJsonRpcResponse};
708/// use turbomcp_protocol::jsonrpc::JsonRpcError;
709///
710/// // Parse lenient request
711/// let raw_json = r#"{"jsonrpc":"2.0","method":"test","id":1}"#;
712/// let request: HttpJsonRpcRequest = serde_json::from_str(raw_json).unwrap();
713///
714/// // Validate and use
715/// if request.jsonrpc != "2.0" {
716///     // Return error with the id we managed to extract
717/// }
718/// ```
719pub mod http {
720    use serde::{Deserialize, Serialize};
721    use serde_json::Value;
722
723    /// Lenient JSON-RPC request for HTTP boundary parsing
724    ///
725    /// This type accepts any string for `jsonrpc` and any JSON value for `id`,
726    /// allowing proper error handling when clients send non-compliant requests.
727    #[derive(Debug, Clone, Serialize, Deserialize)]
728    pub struct HttpJsonRpcRequest {
729        /// JSON-RPC version (should be "2.0" but accepts any string for error handling)
730        pub jsonrpc: String,
731        /// Request ID (can be string, number, or null)
732        #[serde(default)]
733        pub id: Option<Value>,
734        /// Method name
735        pub method: String,
736        /// Method parameters
737        #[serde(default)]
738        pub params: Option<Value>,
739    }
740
741    impl HttpJsonRpcRequest {
742        /// Check if this is a valid JSON-RPC 2.0 request
743        pub fn is_valid(&self) -> bool {
744            self.jsonrpc == "2.0" && !self.method.is_empty()
745        }
746
747        /// Check if this is a notification (no id)
748        pub fn is_notification(&self) -> bool {
749            self.id.is_none()
750        }
751
752        /// Get the id as a string if it's a string, or convert number to string
753        pub fn id_string(&self) -> Option<String> {
754            self.id.as_ref().map(|v| match v {
755                Value::String(s) => s.clone(),
756                Value::Number(n) => n.to_string(),
757                _ => v.to_string(),
758            })
759        }
760    }
761
762    /// Lenient JSON-RPC response for HTTP boundary
763    ///
764    /// Uses separate result/error fields for compatibility with various JSON-RPC
765    /// implementations.
766    #[derive(Debug, Clone, Serialize, Deserialize)]
767    pub struct HttpJsonRpcResponse {
768        /// JSON-RPC version
769        pub jsonrpc: String,
770        /// Response ID
771        #[serde(default)]
772        pub id: Option<Value>,
773        /// Success result
774        #[serde(skip_serializing_if = "Option::is_none")]
775        pub result: Option<Value>,
776        /// Error information
777        #[serde(skip_serializing_if = "Option::is_none")]
778        pub error: Option<super::JsonRpcError>,
779    }
780
781    impl HttpJsonRpcResponse {
782        /// Create a success response
783        pub fn success(id: Option<Value>, result: Value) -> Self {
784            Self {
785                jsonrpc: "2.0".to_string(),
786                id,
787                result: Some(result),
788                error: None,
789            }
790        }
791
792        /// Create an error response
793        pub fn error(id: Option<Value>, error: super::JsonRpcError) -> Self {
794            Self {
795                jsonrpc: "2.0".to_string(),
796                id,
797                result: None,
798                error: Some(error),
799            }
800        }
801
802        /// Create an error response from error code
803        pub fn error_from_code(id: Option<Value>, code: i32, message: impl Into<String>) -> Self {
804            Self::error(id, super::JsonRpcError::new(code, message))
805        }
806
807        /// Create an invalid request error response
808        pub fn invalid_request(id: Option<Value>, reason: impl Into<String>) -> Self {
809            Self::error(id, super::JsonRpcError::invalid_request_with_reason(reason))
810        }
811
812        /// Create a parse error response (id is always null for parse errors)
813        pub fn parse_error(details: Option<String>) -> Self {
814            Self::error(
815                None,
816                details
817                    .map(super::JsonRpcError::parse_error_with_details)
818                    .unwrap_or_else(super::JsonRpcError::parse_error),
819            )
820        }
821
822        /// Create an internal error response
823        pub fn internal_error(id: Option<Value>, details: &str) -> Self {
824            Self::error(id, super::JsonRpcError::internal_error(details))
825        }
826
827        /// Create a method not found error response
828        pub fn method_not_found(id: Option<Value>, method: &str) -> Self {
829            Self::error(id, super::JsonRpcError::method_not_found(method))
830        }
831
832        /// Check if this is an error response
833        pub fn is_error(&self) -> bool {
834            self.error.is_some()
835        }
836
837        /// Check if this is a success response
838        pub fn is_success(&self) -> bool {
839            self.result.is_some() && self.error.is_none()
840        }
841    }
842
843    #[cfg(test)]
844    mod tests {
845        use super::*;
846
847        #[test]
848        fn test_http_request_parsing() {
849            let json = r#"{"jsonrpc":"2.0","method":"test","id":1,"params":{"key":"value"}}"#;
850            let request: HttpJsonRpcRequest = serde_json::from_str(json).unwrap();
851            assert!(request.is_valid());
852            assert!(!request.is_notification());
853            assert_eq!(request.method, "test");
854        }
855
856        #[test]
857        fn test_http_request_invalid_version() {
858            let json = r#"{"jsonrpc":"1.0","method":"test","id":1}"#;
859            let request: HttpJsonRpcRequest = serde_json::from_str(json).unwrap();
860            assert!(!request.is_valid());
861        }
862
863        #[test]
864        fn test_http_response_success() {
865            let response = HttpJsonRpcResponse::success(
866                Some(Value::Number(1.into())),
867                serde_json::json!({"result": "ok"}),
868            );
869            assert!(response.is_success());
870            assert!(!response.is_error());
871        }
872
873        #[test]
874        fn test_http_response_error() {
875            let response = HttpJsonRpcResponse::invalid_request(
876                Some(Value::String("req-1".into())),
877                "jsonrpc must be 2.0",
878            );
879            assert!(!response.is_success());
880            assert!(response.is_error());
881        }
882
883        #[test]
884        fn test_http_response_serialization() {
885            let response = HttpJsonRpcResponse::success(
886                Some(Value::Number(1.into())),
887                serde_json::json!({"data": "test"}),
888            );
889            let json = serde_json::to_string(&response).unwrap();
890            assert!(json.contains(r#""jsonrpc":"2.0""#));
891            assert!(json.contains(r#""result""#));
892            assert!(!json.contains(r#""error""#));
893        }
894    }
895}
896
897// Additional integration-style tests live in `jsonrpc/tests.rs`. The split
898// originated from a file refactor; both modules cover distinct cases (e.g.
899// `tests.rs` exercises wire-shape regressions like `id:null` for parse errors)
900// so neither should be dropped until they are merged.
901#[cfg(test)]
902#[path = "jsonrpc/tests.rs"]
903mod extended_tests;
904
905#[cfg(test)]
906mod tests {
907    use super::*;
908    use serde_json::json;
909
910    #[test]
911    fn test_jsonrpc_version() {
912        let version = JsonRpcVersion;
913        let json = serde_json::to_string(&version).unwrap();
914        assert_eq!(json, "\"2.0\"");
915
916        let parsed: JsonRpcVersion = serde_json::from_str(&json).unwrap();
917        assert_eq!(parsed, version);
918    }
919
920    #[test]
921    fn test_request_creation() {
922        let request = JsonRpcRequest::new(
923            "test_method".to_string(),
924            Some(json!({"key": "value"})),
925            RequestId::String("test-id".to_string()),
926        );
927
928        assert_eq!(request.method, "test_method");
929        assert!(request.params.is_some());
930    }
931
932    #[test]
933    fn test_response_creation() {
934        let response = JsonRpcResponse::success(
935            json!({"result": "success"}),
936            RequestId::String("test-id".to_string()),
937        );
938
939        assert!(response.is_success());
940        assert!(!response.is_error());
941        assert!(response.result().is_some());
942        assert!(response.error().is_none());
943        assert!(!response.is_parse_error());
944    }
945
946    #[test]
947    fn test_error_response() {
948        let error = JsonRpcError::from(JsonRpcErrorCode::MethodNotFound);
949        let response =
950            JsonRpcResponse::error_response(error, RequestId::String("test-id".to_string()));
951
952        assert!(!response.is_success());
953        assert!(response.is_error());
954        assert!(response.result().is_none());
955        assert!(response.error().is_some());
956        assert!(!response.is_parse_error());
957    }
958
959    #[test]
960    fn test_response_payload_accepts_null_result() {
961        let raw = r#"{"jsonrpc":"2.0","id":1,"result":null}"#;
962        let response: JsonRpcResponse = serde_json::from_str(raw).unwrap();
963        assert!(response.is_success());
964        assert_eq!(response.result(), Some(&Value::Null));
965    }
966
967    #[test]
968    fn test_parse_error_response() {
969        let response = JsonRpcResponse::parse_error(Some("Invalid JSON".to_string()));
970
971        assert!(!response.is_success());
972        assert!(response.is_error());
973        assert!(response.result().is_none());
974        assert!(response.error().is_some());
975        assert!(response.is_parse_error());
976        assert!(response.request_id().is_none());
977
978        // Verify the error details
979        let error = response.error().unwrap();
980        assert_eq!(error.code, JsonRpcErrorCode::ParseError.code());
981        assert_eq!(error.message, "Invalid JSON");
982    }
983
984    #[test]
985    fn test_notification() {
986        let notification = JsonRpcNotification::without_params("test_notification".to_string());
987        assert_eq!(notification.method, "test_notification");
988        assert!(notification.params.is_none());
989    }
990
991    #[test]
992    fn test_serialization() {
993        let request = JsonRpcRequest::new(
994            "test_method".to_string(),
995            Some(json!({"param": "value"})),
996            RequestId::String("123".to_string()),
997        );
998
999        let json = serde_json::to_string(&request).unwrap();
1000        let parsed: JsonRpcRequest = serde_json::from_str(&json).unwrap();
1001
1002        assert_eq!(parsed.method, request.method);
1003        assert_eq!(parsed.params, request.params);
1004    }
1005
1006    #[test]
1007    fn test_utils() {
1008        let json = r#"{"jsonrpc":"2.0","method":"test","id":"123"}"#;
1009        assert_eq!(utils::extract_method(json), Some("test".to_string()));
1010    }
1011
1012    #[test]
1013    fn test_error_codes() {
1014        let parse_error = JsonRpcErrorCode::ParseError;
1015        assert_eq!(parse_error.code(), -32700);
1016        assert_eq!(parse_error.message(), "Parse error");
1017
1018        let app_error = JsonRpcErrorCode::ApplicationError(-32001);
1019        assert_eq!(app_error.code(), -32001);
1020    }
1021}