turul_mcp_protocol_2025_06_18/
ping.rs

1//! MCP Ping Protocol Types
2//!
3//! This module defines types for the ping functionality in MCP.
4
5use serde::{Deserialize, Serialize};
6use serde_json::Value;
7use std::collections::HashMap;
8
9/// Request for ping (per MCP spec)
10#[derive(Debug, Clone, Serialize, Deserialize)]
11#[serde(rename_all = "camelCase")]
12pub struct PingRequest {
13    /// Method name (always "ping")
14    pub method: String,
15    /// No parameters for ping
16    #[serde(skip_serializing_if = "Option::is_none")]
17    pub params: Option<Value>,
18}
19
20impl PingRequest {
21    pub fn new() -> Self {
22        Self {
23            method: "ping".to_string(),
24            params: None,
25        }
26    }
27}
28
29impl Default for PingRequest {
30    fn default() -> Self {
31        Self::new()
32    }
33}
34
35/// Empty result for successful operations (per MCP spec)
36#[derive(Debug, Clone, Serialize, Deserialize)]
37#[serde(rename_all = "camelCase")]
38pub struct EmptyResult {
39    /// Meta information (follows MCP Result interface)
40    #[serde(
41        default,
42        skip_serializing_if = "Option::is_none",
43        alias = "_meta",
44        rename = "_meta"
45    )]
46    pub meta: Option<HashMap<String, Value>>,
47}
48
49impl EmptyResult {
50    pub fn new() -> Self {
51        Self { meta: None }
52    }
53
54    pub fn with_meta(mut self, meta: HashMap<String, Value>) -> Self {
55        self.meta = Some(meta);
56        self
57    }
58}
59
60impl Default for EmptyResult {
61    fn default() -> Self {
62        Self::new()
63    }
64}
65
66// Trait implementations for EmptyResult
67use crate::traits::{HasData, HasMeta, RpcResult};
68
69impl HasData for EmptyResult {
70    fn data(&self) -> HashMap<String, Value> {
71        HashMap::new()
72    }
73}
74
75impl HasMeta for EmptyResult {
76    fn meta(&self) -> Option<HashMap<String, Value>> {
77        self.meta.clone()
78    }
79}
80
81impl RpcResult for EmptyResult {}
82
83// Trait implementations for protocol compliance
84use crate::traits::Params;
85
86#[derive(Debug, Clone, Serialize, Deserialize, Default)]
87pub struct EmptyParams;
88
89impl Params for EmptyParams {}
90
91// Note: PingRequest contains method field which is handled at the request level
92// The actual ping params would be EmptyParams in the params field
93
94#[cfg(test)]
95mod tests {
96    use super::*;
97    use serde_json::json;
98
99    #[test]
100    fn test_ping_request() {
101        let ping = PingRequest::new();
102        assert_eq!(ping.method, "ping");
103        assert!(ping.params.is_none());
104
105        let json = serde_json::to_value(&ping).unwrap();
106        assert_eq!(json["method"], "ping");
107    }
108
109    #[test]
110    fn test_empty_result() {
111        let result = EmptyResult::new();
112        assert!(result.meta.is_none());
113
114        let meta = HashMap::from([("test".to_string(), json!("value"))]);
115        let result_with_meta = EmptyResult::new().with_meta(meta.clone());
116        assert_eq!(result_with_meta.meta, Some(meta));
117    }
118
119    #[test]
120    fn test_empty_result_serialization() {
121        let result = EmptyResult::new();
122        let json = serde_json::to_value(&result).unwrap();
123
124        // Should serialize to empty object (no _meta field when None)
125        assert_eq!(json, json!({}));
126
127        let meta = HashMap::from([("progressToken".to_string(), json!("test-123"))]);
128        let result_with_meta = EmptyResult::new().with_meta(meta);
129        let json_with_meta = serde_json::to_value(&result_with_meta).unwrap();
130        assert!(json_with_meta["_meta"].is_object());
131    }
132}