airsprotocols_mcp/protocol/message.rs
1//! JSON-RPC 2.0 and MCP Protocol Message Implementation
2//!
3//! This module provides a complete implementation of JSON-RPC 2.0 message types
4//! with shared serialization behavior through traits, plus MCP-specific message
5//! structures built on top of the JSON-RPC foundation.
6//!
7//! # Architecture
8//!
9//! The message layer is organized as follows:
10//! - Core JSON-RPC 2.0 message types with JsonRpcMessage trait
11//! - High-performance streaming JSON parser for large messages
12//! - MCP-specific message structures for protocol operations
13//! - Zero-copy optimizations for high-throughput scenarios
14//!
15//! # Examples
16//!
17//! ```rust
18//! use airsprotocols_mcp::protocol::{JsonRpcRequest, JsonRpcMessageTrait, RequestId};
19//! use serde_json::json;
20//!
21//! let request = JsonRpcRequest::new(
22//! "ping",
23//! Some(json!({"message": "hello"})),
24//! RequestId::new_string("req-123")
25//! );
26//!
27//! // Use trait methods for consistent serialization
28//! let json = request.to_json().unwrap();
29//! let pretty_json = request.to_json_pretty().unwrap();
30//! let parsed = JsonRpcRequest::from_json(&json).unwrap();
31//!
32//! assert_eq!(request, parsed);
33//! ```
34
35// Layer 1: Standard library imports
36use std::fmt;
37
38// Layer 2: Third-party crate imports
39use bytes::{BufMut, Bytes, BytesMut};
40use serde::{Deserialize, Serialize};
41use serde_json::Value;
42
43// Layer 3: Internal module imports
44use super::constants::error_codes::{
45 INTERNAL_ERROR, INVALID_PARAMS, INVALID_REQUEST, METHOD_NOT_FOUND, PARSE_ERROR,
46};
47use super::errors::JsonRpcError;
48
49/// JSON-RPC 2.0 message validation and utilities
50impl JsonRpcMessage {
51 /// Validate a JSON-RPC message according to JSON-RPC 2.0 specification
52 ///
53 /// This function performs comprehensive validation including:
54 /// - JSON-RPC version validation (must be exactly "2.0")
55 /// - Method field validation (must be present and non-empty for requests/notifications)
56 /// - Request ID validation (proper format checking)
57 /// - Mutual exclusion of result/error in responses
58 ///
59 /// # Returns
60 ///
61 /// * `Ok(())` - Message is valid according to JSON-RPC 2.0 spec
62 /// * `Err(JsonRpcError)` - Message violates JSON-RPC 2.0 specification
63 ///
64 /// # Examples
65 ///
66 /// ```rust
67 /// use airsprotocols_mcp::protocol::{JsonRpcMessage, JsonRpcRequest, RequestId};
68 /// use serde_json::json;
69 ///
70 /// let request = JsonRpcMessage::Request(JsonRpcRequest::new(
71 /// "ping",
72 /// None,
73 /// RequestId::new_number(1)
74 /// ));
75 ///
76 /// assert!(request.validate().is_ok());
77 /// ```
78 pub fn validate(&self) -> Result<(), JsonRpcError> {
79 match self {
80 JsonRpcMessage::Request(req) => Self::validate_request(req),
81 JsonRpcMessage::Response(resp) => Self::validate_response(resp),
82 JsonRpcMessage::Notification(notif) => Self::validate_notification(notif),
83 }
84 }
85
86 /// Validate a JSON-RPC request message
87 fn validate_request(request: &JsonRpcRequest) -> Result<(), JsonRpcError> {
88 // Validate JSON-RPC version
89 if request.jsonrpc != "2.0" {
90 return Err(JsonRpcError::invalid_request(format!(
91 "Invalid JSON-RPC version: expected '2.0', got '{}'",
92 request.jsonrpc
93 )));
94 }
95
96 // Validate method field
97 if request.method.is_empty() {
98 return Err(JsonRpcError::invalid_request(
99 "Method field cannot be empty",
100 ));
101 }
102
103 // Method names beginning with "rpc." are reserved for rpc-internal methods
104 if request.method.starts_with("rpc.") {
105 return Err(JsonRpcError::invalid_request(format!(
106 "Method name '{}' is reserved for JSON-RPC internal methods",
107 request.method
108 )));
109 }
110
111 Ok(())
112 }
113
114 /// Validate a JSON-RPC response message
115 fn validate_response(response: &JsonRpcResponse) -> Result<(), JsonRpcError> {
116 // Validate JSON-RPC version
117 if response.jsonrpc != "2.0" {
118 return Err(JsonRpcError::invalid_request(format!(
119 "Invalid JSON-RPC version: expected '2.0', got '{}'",
120 response.jsonrpc
121 )));
122 }
123
124 // Validate mutual exclusion of result and error
125 match (&response.result, &response.error) {
126 (Some(_), Some(_)) => {
127 return Err(JsonRpcError::invalid_request(
128 "Response cannot have both result and error fields",
129 ));
130 }
131 (None, None) => {
132 return Err(JsonRpcError::invalid_request(
133 "Response must have either result or error field",
134 ));
135 }
136 _ => {} // Valid: exactly one of result or error is present
137 }
138
139 Ok(())
140 }
141
142 /// Validate a JSON-RPC notification message
143 fn validate_notification(notification: &JsonRpcNotification) -> Result<(), JsonRpcError> {
144 // Validate JSON-RPC version
145 if notification.jsonrpc != "2.0" {
146 return Err(JsonRpcError::invalid_request(format!(
147 "Invalid JSON-RPC version: expected '2.0', got '{}'",
148 notification.jsonrpc
149 )));
150 }
151
152 // Validate method field
153 if notification.method.is_empty() {
154 return Err(JsonRpcError::invalid_request(
155 "Method field cannot be empty",
156 ));
157 }
158
159 // Method names beginning with "rpc." are reserved for rpc-internal methods
160 if notification.method.starts_with("rpc.") {
161 return Err(JsonRpcError::invalid_request(format!(
162 "Method name '{}' is reserved for JSON-RPC internal methods",
163 notification.method
164 )));
165 }
166
167 Ok(())
168 }
169
170 /// Create a standardized JSON-RPC error response
171 ///
172 /// This function creates a properly formatted JSON-RPC 2.0 error response
173 /// according to the specification with the correct error object structure.
174 ///
175 /// # Arguments
176 ///
177 /// * `code` - JSON-RPC error code (use constants from error_codes module)
178 /// * `message` - Human-readable error message
179 /// * `data` - Optional additional error data
180 /// * `id` - Request ID from the original request (None for parse errors)
181 ///
182 /// # Examples
183 ///
184 /// ```rust
185 /// use airsprotocols_mcp::protocol::{JsonRpcMessage, RequestId};
186 /// use airsprotocols_mcp::protocol::constants::error_codes;
187 ///
188 /// let error_response = JsonRpcMessage::create_error_response(
189 /// error_codes::INVALID_REQUEST,
190 /// "Missing required field",
191 /// None,
192 /// Some(RequestId::new_number(1))
193 /// );
194 /// ```
195 pub fn create_error_response(
196 code: i32,
197 message: &str,
198 data: Option<Value>,
199 id: Option<RequestId>,
200 ) -> Self {
201 let mut error_obj = serde_json::json!({
202 "code": code,
203 "message": message
204 });
205
206 if let Some(data) = data {
207 error_obj["data"] = data;
208 }
209
210 JsonRpcMessage::Response(JsonRpcResponse {
211 jsonrpc: "2.0".to_string(),
212 result: None,
213 error: Some(error_obj),
214 id,
215 })
216 }
217
218 /// Parse and validate a JSON-RPC message from a byte slice
219 ///
220 /// This method combines parsing and validation in a single step,
221 /// providing proper JSON-RPC error responses for invalid messages.
222 /// Works directly with byte slices for optimal performance.
223 ///
224 /// # Performance Benefits
225 /// - No intermediate string allocation
226 /// - Single UTF-8 validation + JSON parsing step
227 /// - Better cache locality for large messages
228 ///
229 /// # Arguments
230 /// * `data` - Raw byte slice containing JSON data
231 ///
232 /// # Returns
233 /// `Ok(JsonRpcMessage)` if parsing and validation succeed,
234 /// `Err(JsonRpcMessage)` containing a JSON-RPC error response if validation fails
235 ///
236 /// # Examples
237 ///
238 /// ```rust
239 /// use airsprotocols_mcp::protocol::JsonRpcMessage;
240 ///
241 /// // Parse from byte slice (most efficient)
242 /// let data = br#"{"jsonrpc":"2.0","method":"ping","id":1}"#;
243 /// let result = JsonRpcMessage::parse_and_validate_from_slice(data);
244 /// assert!(result.is_ok());
245 ///
246 /// // Parse from string (convert to bytes first)
247 /// let json_str = r#"{"jsonrpc":"2.0","method":"ping","id":1}"#;
248 /// let result = JsonRpcMessage::parse_and_validate_from_slice(json_str.as_bytes());
249 /// assert!(result.is_ok());
250 /// ```
251 pub fn parse_and_validate_from_slice(data: &[u8]) -> Result<Self, Self> {
252 // Parse JSON directly from byte slice (handles UTF-8 validation internally)
253 let message = match serde_json::from_slice::<JsonRpcMessage>(data) {
254 Ok(msg) => msg,
255 Err(parse_err) => {
256 return Err(Self::create_error_response(
257 PARSE_ERROR,
258 "Parse error",
259 Some(serde_json::json!({
260 "details": parse_err.to_string()
261 })),
262 None,
263 ));
264 }
265 };
266
267 // Then validate the parsed message
268 if let Err(validation_err) = message.validate() {
269 let error_code = match validation_err {
270 JsonRpcError::ParseError { .. } => PARSE_ERROR,
271 JsonRpcError::InvalidRequest { .. } => INVALID_REQUEST,
272 JsonRpcError::MethodNotFound { .. } => METHOD_NOT_FOUND,
273 JsonRpcError::InvalidParams { .. } => INVALID_PARAMS,
274 JsonRpcError::InternalError { .. } => INTERNAL_ERROR,
275 JsonRpcError::ServerError { code, .. } => code,
276 };
277
278 // Extract request ID if possible for proper error response
279 let request_id = match &message {
280 JsonRpcMessage::Request(req) => Some(req.id.clone()),
281 JsonRpcMessage::Response(resp) => resp.id.clone(),
282 JsonRpcMessage::Notification(_) => None, // Notifications don't have IDs
283 };
284
285 return Err(Self::create_error_response(
286 error_code,
287 &validation_err.to_string(),
288 None,
289 request_id,
290 ));
291 }
292
293 Ok(message)
294 }
295}
296
297/// JSON-RPC message types supporting requests, responses, and notifications
298///
299/// This enum unifies all JSON-RPC 2.0 message types into a single type
300/// for transport and handling. Each variant preserves the specific structure
301/// of its message type while providing unified serialization.
302#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
303#[serde(untagged)]
304pub enum JsonRpcMessage {
305 /// JSON-RPC request message
306 Request(JsonRpcRequest),
307 /// JSON-RPC response message
308 Response(JsonRpcResponse),
309 /// JSON-RPC notification message
310 Notification(JsonRpcNotification),
311}
312
313/// Trait for JSON-RPC message serialization and deserialization
314///
315/// This trait provides common functionality for all JSON-RPC message types,
316/// eliminating code duplication and ensuring consistent serialization behavior.
317///
318/// Any type that implements `Serialize + Deserialize` automatically gets
319/// the default implementations for JSON conversion methods.
320///
321/// # Examples
322///
323/// ```rust
324/// use airsprotocols_mcp::protocol::{JsonRpcMessageTrait, JsonRpcRequest, RequestId};
325///
326/// let request = JsonRpcRequest::new("ping", None, RequestId::new_number(1));
327///
328/// // Uses trait method with default implementation
329/// let json = request.to_json().unwrap();
330/// let parsed = JsonRpcRequest::from_json(&json).unwrap();
331///
332/// assert_eq!(request, parsed);
333/// ```
334#[allow(dead_code)] // Library trait methods - will be used by consuming code
335pub trait JsonRpcMessageTrait: Serialize + for<'de> Deserialize<'de> {
336 /// Serialize this message to JSON string
337 ///
338 /// # Errors
339 ///
340 /// Returns `serde_json::Error` if serialization fails, which should be rare
341 /// given the controlled structure of JSON-RPC messages.
342 ///
343 /// # Examples
344 ///
345 /// ```rust
346 /// use airsprotocols_mcp::protocol::{JsonRpcMessageTrait, JsonRpcNotification};
347 ///
348 /// let notification = JsonRpcNotification::new("heartbeat", None);
349 /// let json = notification.to_json().unwrap();
350 /// assert!(json.contains("heartbeat"));
351 /// ```
352 fn to_json(&self) -> Result<String, serde_json::Error> {
353 serde_json::to_string(self)
354 }
355
356 /// Serialize this message to pretty-printed JSON
357 ///
358 /// Useful for debugging and logging.
359 ///
360 /// # Examples
361 ///
362 /// ```rust
363 /// use airsprotocols_mcp::protocol::{JsonRpcMessageTrait, JsonRpcRequest, RequestId};
364 ///
365 /// let request = JsonRpcRequest::new("ping", None, RequestId::new_number(1));
366 /// let pretty = request.to_json_pretty().unwrap();
367 /// println!("{}", pretty);
368 /// ```
369 fn to_json_pretty(&self) -> Result<String, serde_json::Error> {
370 serde_json::to_string_pretty(self)
371 }
372
373 /// Deserialize from JSON string
374 ///
375 /// # Examples
376 ///
377 /// ```rust
378 /// use airsprotocols_mcp::protocol::{JsonRpcMessageTrait, JsonRpcRequest};
379 ///
380 /// let json = r#"{"jsonrpc":"2.0","method":"ping","id":1}"#;
381 /// let request = JsonRpcRequest::from_json(json).unwrap();
382 /// assert_eq!(request.method, "ping");
383 /// ```
384 fn from_json(json: &str) -> Result<Self, serde_json::Error> {
385 serde_json::from_str(json)
386 }
387
388 /// Zero-copy serialization to buffer
389 ///
390 /// Efficiently serializes the message directly to a buffer, avoiding
391 /// intermediate string allocation. Ideal for high-performance scenarios.
392 ///
393 /// # Arguments
394 ///
395 /// * `buffer` - Target buffer to write JSON data to
396 ///
397 /// # Returns
398 ///
399 /// * `Ok(())` - Message serialized successfully
400 /// * `Err(serde_json::Error)` - Serialization failed
401 ///
402 /// # Performance
403 ///
404 /// More efficient than `to_json().into_bytes()` as it avoids the intermediate String.
405 ///
406 /// # Examples
407 ///
408 /// ```rust
409 /// use airsprotocols_mcp::protocol::{JsonRpcMessageTrait, JsonRpcRequest, RequestId};
410 /// use bytes::BytesMut;
411 ///
412 /// let request = JsonRpcRequest::new("test", None, RequestId::new_string("1"));
413 /// let mut buffer = BytesMut::new();
414 /// request.serialize_to_buffer(&mut buffer).unwrap();
415 ///
416 /// // Can be sent directly over transport without additional allocations
417 /// assert!(buffer.len() > 0);
418 /// ```
419 fn serialize_to_buffer(&self, buffer: &mut BytesMut) -> Result<(), serde_json::Error> {
420 serde_json::to_writer(buffer.writer(), self)
421 }
422
423 /// Serialize this message to bytes
424 ///
425 /// # Returns
426 ///
427 /// * `Ok(Bytes)` - Message serialized successfully
428 /// * `Err(serde_json::Error)` - Serialization failed
429 ///
430 /// # Performance
431 ///
432 /// More efficient than `to_json().into_bytes()` as it avoids the intermediate String.
433 ///
434 /// # Examples
435 ///
436 /// ```rust
437 /// use airsprotocols_mcp::protocol::{JsonRpcMessageTrait, JsonRpcRequest, RequestId};
438 ///
439 /// let request = JsonRpcRequest::new("test", None, RequestId::new_string("1"));
440 /// let bytes = request.to_bytes().unwrap();
441 ///
442 /// // Can be sent directly over transport without additional allocations
443 /// assert!(bytes.len() > 0);
444 /// ```
445 fn to_bytes(&self) -> Result<Bytes, serde_json::Error> {
446 let mut buffer = BytesMut::with_capacity(256);
447 self.serialize_to_buffer(&mut buffer)?;
448 Ok(buffer.freeze())
449 }
450
451 /// Deserialize a message from JSON bytes
452 ///
453 /// More efficient than string-based parsing when working with byte streams.
454 ///
455 /// # Examples
456 ///
457 /// ```rust
458 /// use airsprotocols_mcp::protocol::{JsonRpcMessageTrait, JsonRpcRequest};
459 ///
460 /// let json_bytes = br#"{"jsonrpc":"2.0","method":"ping","id":1}"#;
461 /// let request = JsonRpcRequest::from_json_bytes(json_bytes).unwrap();
462 ///
463 /// assert_eq!(request.method, "ping");
464 /// ```
465 fn from_json_bytes(json: &[u8]) -> Result<Self, serde_json::Error> {
466 serde_json::from_slice(json)
467 }
468}
469
470#[allow(dead_code)] // Library convenience methods - will be used by consuming code
471impl JsonRpcMessage {
472 /// Create a new notification message
473 pub fn from_notification(method: &str, params: Option<Value>) -> Self {
474 JsonRpcMessage::Notification(JsonRpcNotification {
475 jsonrpc: "2.0".to_string(),
476 method: method.to_string(),
477 params,
478 })
479 }
480
481 /// Create a new request message
482 pub fn from_request(method: &str, params: Option<Value>, id: RequestId) -> Self {
483 JsonRpcMessage::Request(JsonRpcRequest {
484 jsonrpc: "2.0".to_string(),
485 method: method.to_string(),
486 params,
487 id,
488 })
489 }
490
491 /// Create a new response message
492 pub fn from_response(
493 result: Option<Value>,
494 error: Option<Value>,
495 id: Option<RequestId>,
496 ) -> Self {
497 JsonRpcMessage::Response(JsonRpcResponse {
498 jsonrpc: "2.0".to_string(),
499 result,
500 error,
501 id,
502 })
503 }
504}
505
506/// Request ID supporting string, numeric, and null formats per JSON-RPC 2.0 specification
507///
508/// The JSON-RPC 2.0 specification allows request IDs to be strings, numbers, or null.
509/// This enum supports all three variants for complete JSON-RPC 2.0 compliance.
510///
511/// # Examples
512///
513/// ```rust
514/// use airsprotocols_mcp::protocol::RequestId;
515///
516/// let string_id = RequestId::String("req-123".to_string());
517/// let numeric_id = RequestId::Number(42);
518/// let null_id = RequestId::Null;
519///
520/// // Serialization preserves the original format
521/// assert_eq!(serde_json::to_string(&string_id).unwrap(), r#""req-123""#);
522/// assert_eq!(serde_json::to_string(&numeric_id).unwrap(), "42");
523/// assert_eq!(serde_json::to_string(&null_id).unwrap(), "null");
524/// ```
525#[derive(Debug, Clone, PartialEq, Eq, Hash)]
526pub enum RequestId {
527 /// String-based request identifier
528 String(String),
529 /// Numeric request identifier
530 Number(i64),
531 /// Null request identifier
532 Null,
533}
534
535impl Serialize for RequestId {
536 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
537 where
538 S: serde::Serializer,
539 {
540 match self {
541 RequestId::String(s) => serializer.serialize_str(s),
542 RequestId::Number(n) => serializer.serialize_i64(*n),
543 RequestId::Null => serializer.serialize_unit(),
544 }
545 }
546}
547
548impl<'de> Deserialize<'de> for RequestId {
549 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
550 where
551 D: serde::Deserializer<'de>,
552 {
553 use serde::de::{self, Visitor};
554
555 struct RequestIdVisitor;
556
557 impl<'de> Visitor<'de> for RequestIdVisitor {
558 type Value = RequestId;
559
560 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
561 formatter.write_str("a string, number, or null")
562 }
563
564 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
565 where
566 E: de::Error,
567 {
568 Ok(RequestId::String(value.to_string()))
569 }
570
571 fn visit_i64<E>(self, value: i64) -> Result<Self::Value, E>
572 where
573 E: de::Error,
574 {
575 Ok(RequestId::Number(value))
576 }
577
578 fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E>
579 where
580 E: de::Error,
581 {
582 if value <= i64::MAX as u64 {
583 Ok(RequestId::Number(value as i64))
584 } else {
585 Err(E::custom("number too large for i64"))
586 }
587 }
588
589 fn visit_unit<E>(self) -> Result<Self::Value, E>
590 where
591 E: de::Error,
592 {
593 Ok(RequestId::Null)
594 }
595 }
596
597 deserializer.deserialize_any(RequestIdVisitor)
598 }
599}
600
601impl RequestId {
602 /// Create a new string-based request ID
603 ///
604 /// # Examples
605 ///
606 /// ```rust
607 /// use airsprotocols_mcp::protocol::RequestId;
608 ///
609 /// let id = RequestId::new_string("my-request-id");
610 /// ```
611 pub fn new_string(id: impl Into<String>) -> Self {
612 RequestId::String(id.into())
613 }
614
615 /// Create a new numeric request ID
616 ///
617 /// # Examples
618 ///
619 /// ```rust
620 /// use airsprotocols_mcp::protocol::RequestId;
621 ///
622 /// let id = RequestId::new_number(123);
623 /// ```
624 pub fn new_number(id: i64) -> Self {
625 RequestId::Number(id)
626 }
627
628 /// Create a new null request ID
629 ///
630 /// # Examples
631 ///
632 /// ```rust
633 /// use airsprotocols_mcp::protocol::RequestId;
634 ///
635 /// let id = RequestId::new_null();
636 /// ```
637 pub fn new_null() -> Self {
638 RequestId::Null
639 }
640}
641
642impl fmt::Display for RequestId {
643 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
644 match self {
645 RequestId::String(s) => write!(f, "{s}"),
646 RequestId::Number(n) => write!(f, "{n}"),
647 RequestId::Null => write!(f, "null"),
648 }
649 }
650}
651
652/// JSON-RPC 2.0 Request Message
653///
654/// Represents a request to invoke a method on the remote peer. All fields are required
655/// except for params, which may be omitted if the method takes no parameters.
656///
657/// # JSON-RPC 2.0 Specification Compliance
658///
659/// - `jsonrpc`: MUST be exactly "2.0"
660/// - `method`: MUST be a String containing the name of the method to invoke
661/// - `params`: MAY be omitted. If present, MUST be Structured values (Object) or Ordered values (Array)
662/// - `id`: MUST be a String, Number, or NULL value
663///
664/// # Examples
665///
666/// ```rust
667/// use airsprotocols_mcp::protocol::{JsonRpcRequest, JsonRpcMessageTrait, RequestId};
668/// use serde_json::json;
669///
670/// // Request with parameters
671/// let request = JsonRpcRequest::new(
672/// "subtract",
673/// Some(json!([42, 23])),
674/// RequestId::new_number(1)
675/// );
676///
677/// // Use trait methods for serialization
678/// let json = request.to_json().unwrap();
679/// let parsed = JsonRpcRequest::from_json(&json).unwrap();
680/// assert_eq!(request, parsed);
681/// ```
682#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
683pub struct JsonRpcRequest {
684 /// Protocol version - always "2.0" for JSON-RPC 2.0 compliance
685 pub jsonrpc: String,
686
687 /// Name of the method to invoke
688 pub method: String,
689
690 /// Parameters for the method (null, object, or array)
691 #[serde(skip_serializing_if = "Option::is_none")]
692 pub params: Option<Value>,
693
694 /// Unique identifier for this request
695 pub id: RequestId,
696}
697
698impl JsonRpcRequest {
699 /// Create a new JSON-RPC 2.0 request
700 ///
701 /// # Parameters
702 ///
703 /// - `method`: Name of the method to invoke
704 /// - `params`: Optional parameters (will be serialized as JSON)
705 /// - `id`: Unique request identifier
706 ///
707 /// # Examples
708 ///
709 /// ```rust
710 /// use airsprotocols_mcp::protocol::{JsonRpcRequest, RequestId};
711 /// use serde_json::json;
712 ///
713 /// let request = JsonRpcRequest::new(
714 /// "calculate",
715 /// Some(json!({"operation": "add", "values": [1, 2, 3]})),
716 /// RequestId::new_string("calc-123")
717 /// );
718 /// ```
719 pub fn new(method: impl Into<String>, params: Option<Value>, id: RequestId) -> Self {
720 Self {
721 jsonrpc: "2.0".to_string(),
722 method: method.into(),
723 params,
724 id,
725 }
726 }
727
728 /// Create and validate a new JSON-RPC 2.0 request
729 ///
730 /// This function performs validation during construction to catch
731 /// invalid requests early.
732 ///
733 /// # Parameters
734 ///
735 /// - `method`: Name of the method to invoke (must be non-empty)
736 /// - `params`: Optional parameters (will be serialized as JSON)
737 /// - `id`: Unique request identifier
738 ///
739 /// # Returns
740 ///
741 /// * `Ok(JsonRpcRequest)` - Valid request created successfully
742 /// * `Err(JsonRpcError)` - Request violates JSON-RPC 2.0 specification
743 ///
744 /// # Examples
745 ///
746 /// ```rust
747 /// use airsprotocols_mcp::protocol::{JsonRpcRequest, RequestId};
748 /// use serde_json::json;
749 ///
750 /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
751 /// let request = JsonRpcRequest::new_validated(
752 /// "calculate",
753 /// Some(json!({"operation": "add", "values": [1, 2, 3]})),
754 /// RequestId::new_string("calc-123")
755 /// )?;
756 /// # Ok(())
757 /// # }
758 /// ```
759 pub fn new_validated(
760 method: impl Into<String>,
761 params: Option<Value>,
762 id: RequestId,
763 ) -> Result<Self, JsonRpcError> {
764 let method_str = method.into();
765
766 // Validate method field
767 if method_str.is_empty() {
768 return Err(JsonRpcError::invalid_request(
769 "Method field cannot be empty",
770 ));
771 }
772
773 // Method names beginning with "rpc." are reserved for rpc-internal methods
774 if method_str.starts_with("rpc.") {
775 return Err(JsonRpcError::invalid_request(format!(
776 "Method name '{method_str}' is reserved for JSON-RPC internal methods"
777 )));
778 }
779
780 Ok(Self {
781 jsonrpc: "2.0".to_string(),
782 method: method_str,
783 params,
784 id,
785 })
786 }
787}
788
789// Automatic trait implementation - no more duplicated code!
790impl JsonRpcMessageTrait for JsonRpcRequest {}
791
792/// JSON-RPC 2.0 Response Message
793///
794/// Represents the response to a JSON-RPC request. Contains either a successful result
795/// or error information, but never both (mutual exclusion enforced by JSON-RPC spec).
796///
797/// # JSON-RPC 2.0 Specification Compliance
798///
799/// - `jsonrpc`: MUST be exactly "2.0"
800/// - `result`: MUST exist and contain the result if the call succeeded (omitted on error)
801/// - `error`: MUST exist and contain error details if the call failed (omitted on success)
802/// - `id`: MUST be the same as the request that triggered this response, or null for parse errors
803///
804/// # Examples
805///
806/// ```rust
807/// use airsprotocols_mcp::protocol::{JsonRpcResponse, JsonRpcMessageTrait, RequestId};
808/// use serde_json::json;
809///
810/// // Success response
811/// let success = JsonRpcResponse::success(
812/// json!({"result": "operation completed"}),
813/// RequestId::new_number(1)
814/// );
815///
816/// // Use trait methods for serialization
817/// let json = success.to_json().unwrap();
818/// let parsed = JsonRpcResponse::from_json(&json).unwrap();
819/// assert_eq!(success, parsed);
820/// ```
821#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
822pub struct JsonRpcResponse {
823 /// Protocol version - always "2.0" for JSON-RPC 2.0 compliance
824 pub jsonrpc: String,
825
826 /// Result of successful method invocation (mutually exclusive with error)
827 #[serde(skip_serializing_if = "Option::is_none")]
828 pub result: Option<Value>,
829
830 /// Error information for failed method invocation (mutually exclusive with result)
831 #[serde(skip_serializing_if = "Option::is_none")]
832 pub error: Option<Value>,
833
834 /// Request identifier from the original request (null for parse errors)
835 #[serde(skip_serializing_if = "Option::is_none")]
836 pub id: Option<RequestId>,
837}
838
839impl JsonRpcResponse {
840 /// Create a successful JSON-RPC 2.0 response
841 ///
842 /// # Parameters
843 ///
844 /// - `result`: The successful result of the method invocation
845 /// - `id`: Request identifier from the original request
846 ///
847 /// # Examples
848 ///
849 /// ```rust
850 /// use airsprotocols_mcp::protocol::{JsonRpcResponse, RequestId};
851 /// use serde_json::json;
852 ///
853 /// let response = JsonRpcResponse::success(
854 /// json!({"status": "ok", "data": [1, 2, 3]}),
855 /// RequestId::new_string("req-456")
856 /// );
857 /// ```
858 pub fn success(result: Value, id: RequestId) -> Self {
859 Self {
860 jsonrpc: "2.0".to_string(),
861 result: Some(result),
862 error: None,
863 id: Some(id),
864 }
865 }
866
867 /// Create an error JSON-RPC 2.0 response
868 ///
869 /// # Parameters
870 ///
871 /// - `error`: Error information (should conform to JSON-RPC error object structure)
872 /// - `id`: Request identifier from the original request (None for parse errors)
873 ///
874 /// # Examples
875 ///
876 /// ```rust
877 /// use airsprotocols_mcp::protocol::{JsonRpcResponse, RequestId};
878 /// use serde_json::json;
879 ///
880 /// let response = JsonRpcResponse::error(
881 /// json!({"code": -32602, "message": "Invalid params"}),
882 /// Some(RequestId::new_number(789))
883 /// );
884 /// ```
885 pub fn error(error: Value, id: Option<RequestId>) -> Self {
886 Self {
887 jsonrpc: "2.0".to_string(),
888 result: None,
889 error: Some(error),
890 id,
891 }
892 }
893
894 /// Create a standardized JSON-RPC 2.0 error response
895 ///
896 /// This function creates a properly formatted JSON-RPC 2.0 error response
897 /// with the correct error object structure according to the specification.
898 ///
899 /// # Parameters
900 ///
901 /// - `code`: JSON-RPC error code (use constants from error_codes module)
902 /// - `message`: Human-readable error message
903 /// - `data`: Optional additional error data
904 /// - `id`: Request identifier from the original request (None for parse errors)
905 ///
906 /// # Examples
907 ///
908 /// ```rust
909 /// use airsprotocols_mcp::protocol::{JsonRpcResponse, RequestId};
910 /// use airsprotocols_mcp::protocol::constants::error_codes;
911 ///
912 /// let response = JsonRpcResponse::error_standard(
913 /// error_codes::INVALID_PARAMS,
914 /// "Missing required parameter 'name'",
915 /// Some(serde_json::json!({"parameter": "name"})),
916 /// Some(RequestId::new_number(123))
917 /// );
918 /// ```
919 pub fn error_standard(
920 code: i32,
921 message: &str,
922 data: Option<Value>,
923 id: Option<RequestId>,
924 ) -> Self {
925 let mut error_obj = serde_json::json!({
926 "code": code,
927 "message": message
928 });
929
930 if let Some(data) = data {
931 error_obj["data"] = data;
932 }
933
934 Self {
935 jsonrpc: "2.0".to_string(),
936 result: None,
937 error: Some(error_obj),
938 id,
939 }
940 }
941
942 /// Create a parse error response (-32700)
943 pub fn parse_error(message: &str, data: Option<Value>) -> Self {
944 Self::error_standard(
945 PARSE_ERROR,
946 message,
947 data,
948 None, // Parse errors don't have request IDs
949 )
950 }
951
952 /// Create an invalid request error response (-32600)
953 pub fn invalid_request(message: &str, data: Option<Value>, id: Option<RequestId>) -> Self {
954 Self::error_standard(INVALID_REQUEST, message, data, id)
955 }
956
957 /// Create a method not found error response (-32601)
958 pub fn method_not_found(method: &str, id: Option<RequestId>) -> Self {
959 Self::error_standard(
960 METHOD_NOT_FOUND,
961 &format!("Method '{method}' not found"),
962 Some(serde_json::json!({"method": method})),
963 id,
964 )
965 }
966
967 /// Create an invalid params error response (-32602)
968 pub fn invalid_params(message: &str, data: Option<Value>, id: Option<RequestId>) -> Self {
969 Self::error_standard(INVALID_PARAMS, message, data, id)
970 }
971
972 /// Create an internal error response (-32603)
973 pub fn internal_error(message: &str, data: Option<Value>, id: Option<RequestId>) -> Self {
974 Self::error_standard(INTERNAL_ERROR, message, data, id)
975 }
976}
977
978// Automatic trait implementation - elegant and DRY!
979impl JsonRpcMessageTrait for JsonRpcResponse {}
980
981/// JSON-RPC 2.0 Notification Message
982///
983/// Represents a notification - a request that does not expect a response.
984/// Notifications are "fire and forget" messages used for events or one-way communication.
985///
986/// # JSON-RPC 2.0 Specification Compliance
987///
988/// - `jsonrpc`: MUST be exactly "2.0"
989/// - `method`: MUST be a String containing the name of the notification method
990/// - `params`: MAY be omitted. If present, MUST be Structured values (Object) or Ordered values (Array)
991/// - `id`: MUST NOT be present (this is what distinguishes notifications from requests)
992///
993/// # Examples
994///
995/// ```rust
996/// use airsprotocols_mcp::protocol::{JsonRpcNotification, JsonRpcMessageTrait};
997/// use serde_json::json;
998///
999/// // Notification with parameters
1000/// let notification = JsonRpcNotification::new(
1001/// "user_logged_in",
1002/// Some(json!({"user_id": 12345, "timestamp": "2025-07-28T10:30:00Z"}))
1003/// );
1004///
1005/// // Use trait methods for serialization
1006/// let json = notification.to_json().unwrap();
1007/// let parsed = JsonRpcNotification::from_json(&json).unwrap();
1008/// assert_eq!(notification, parsed);
1009/// ```
1010#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1011pub struct JsonRpcNotification {
1012 /// Protocol version - always "2.0" for JSON-RPC 2.0 compliance
1013 pub jsonrpc: String,
1014
1015 /// Name of the notification method
1016 pub method: String,
1017
1018 /// Parameters for the notification (null, object, or array)
1019 #[serde(skip_serializing_if = "Option::is_none")]
1020 pub params: Option<Value>,
1021 // Note: No `id` field - this is what makes it a notification instead of a request
1022}
1023
1024impl JsonRpcNotification {
1025 /// Create a new JSON-RPC 2.0 notification
1026 ///
1027 /// # Parameters
1028 ///
1029 /// - `method`: Name of the notification method
1030 /// - `params`: Optional parameters (will be serialized as JSON)
1031 ///
1032 /// # Examples
1033 ///
1034 /// ```rust
1035 /// use airsprotocols_mcp::protocol::JsonRpcNotification;
1036 /// use serde_json::json;
1037 ///
1038 /// let notification = JsonRpcNotification::new(
1039 /// "status_changed",
1040 /// Some(json!({"old_status": "pending", "new_status": "active"}))
1041 /// );
1042 /// ```
1043 pub fn new(method: impl Into<String>, params: Option<Value>) -> Self {
1044 Self {
1045 jsonrpc: "2.0".to_string(),
1046 method: method.into(),
1047 params,
1048 }
1049 }
1050
1051 /// Create and validate a new JSON-RPC 2.0 notification
1052 ///
1053 /// This function performs validation during construction to catch
1054 /// invalid notifications early.
1055 ///
1056 /// # Parameters
1057 ///
1058 /// - `method`: Name of the notification method (must be non-empty)
1059 /// - `params`: Optional parameters (will be serialized as JSON)
1060 ///
1061 /// # Returns
1062 ///
1063 /// * `Ok(JsonRpcNotification)` - Valid notification created successfully
1064 /// * `Err(JsonRpcError)` - Notification violates JSON-RPC 2.0 specification
1065 ///
1066 /// # Examples
1067 ///
1068 /// ```rust
1069 /// use airsprotocols_mcp::protocol::JsonRpcNotification;
1070 /// use serde_json::json;
1071 ///
1072 /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
1073 /// let notification = JsonRpcNotification::new_validated(
1074 /// "status_changed",
1075 /// Some(json!({"old_status": "pending", "new_status": "active"}))
1076 /// )?;
1077 /// # Ok(())
1078 /// # }
1079 /// ```
1080 pub fn new_validated(
1081 method: impl Into<String>,
1082 params: Option<Value>,
1083 ) -> Result<Self, JsonRpcError> {
1084 let method_str = method.into();
1085
1086 // Validate method field
1087 if method_str.is_empty() {
1088 return Err(JsonRpcError::invalid_request(
1089 "Method field cannot be empty",
1090 ));
1091 }
1092
1093 // Method names beginning with "rpc." are reserved for rpc-internal methods
1094 if method_str.starts_with("rpc.") {
1095 return Err(JsonRpcError::invalid_request(format!(
1096 "Method name '{method_str}' is reserved for JSON-RPC internal methods"
1097 )));
1098 }
1099
1100 Ok(Self {
1101 jsonrpc: "2.0".to_string(),
1102 method: method_str,
1103 params,
1104 })
1105 }
1106}
1107
1108// Automatic trait implementation - consistency without repetition!
1109impl JsonRpcMessageTrait for JsonRpcNotification {}
1110
1111// Implement the trait for the unified message enum
1112impl JsonRpcMessageTrait for JsonRpcMessage {}
1113
1114// TODO(DEBT-ARCH): Add MCP-specific message structures and protocol optimizations
1115// Will be implemented once the core migration is complete
1116// Reference: MCP protocol specification for message structures