turul_mcp_protocol_2025_06_18/
json_rpc.rs

1//! JSON-RPC 2.0 Implementation for MCP 2025-06-18
2//!
3//! This module provides JSON-RPC structures that are fully compliant with the
4//! MCP 2025-06-18 specification, including proper _meta field handling.
5
6use serde::{Deserialize, Serialize};
7use serde_json::Value;
8use std::collections::HashMap;
9
10use crate::meta::{Meta, ProgressToken};
11use crate::traits::{
12    HasData, HasDataParam, HasMeta, HasMetaParam, HasProgressTokenParam, Params, RpcResult,
13};
14
15/// JSON-RPC version constant
16pub const JSONRPC_VERSION: &str = "2.0";
17
18/// JSON-RPC `params` object with optional `_meta` and method-specific arguments
19#[derive(Debug, Clone, Deserialize, Serialize)]
20#[serde(rename_all = "camelCase")]
21pub struct RequestParams {
22    /// Optional MCP `_meta` section
23    #[serde(default, skip_serializing_if = "Option::is_none", alias = "_meta")]
24    pub meta: Option<Meta>,
25
26    /// All other method-specific parameters
27    #[serde(flatten)]
28    pub other: HashMap<String, Value>,
29}
30
31impl Params for RequestParams {}
32
33impl HasMeta for RequestParams {
34    fn meta(&self) -> Option<HashMap<String, Value>> {
35        self.meta.as_ref().map(|m| {
36            let mut map = HashMap::new();
37            if let Some(ref token) = m.progress_token {
38                map.insert(
39                    "progressToken".to_string(),
40                    Value::String(token.as_str().to_string()),
41                );
42            }
43            if let Some(ref cursor) = m.cursor {
44                map.insert(
45                    "cursor".to_string(),
46                    Value::String(cursor.as_str().to_string()),
47                );
48            }
49            if let Some(total) = m.total {
50                map.insert("total".to_string(), Value::Number(total.into()));
51            }
52            if let Some(has_more) = m.has_more {
53                map.insert("hasMore".to_string(), Value::Bool(has_more));
54            }
55            if let Some(estimated_remaining) = m.estimated_remaining_seconds {
56                map.insert(
57                    "estimatedRemainingSeconds".to_string(),
58                    Value::Number(serde_json::Number::from_f64(estimated_remaining).unwrap()),
59                );
60            }
61            if let Some(progress) = m.progress {
62                map.insert(
63                    "progress".to_string(),
64                    Value::Number(serde_json::Number::from_f64(progress).unwrap()),
65                );
66            }
67            if let Some(current_step) = m.current_step {
68                map.insert(
69                    "currentStep".to_string(),
70                    Value::Number(current_step.into()),
71                );
72            }
73            if let Some(total_steps) = m.total_steps {
74                map.insert("totalSteps".to_string(), Value::Number(total_steps.into()));
75            }
76            map
77        })
78    }
79}
80
81impl HasProgressTokenParam for RequestParams {
82    fn progress_token(&self) -> Option<&ProgressToken> {
83        self.meta.as_ref()?.progress_token.as_ref()
84    }
85}
86
87impl HasDataParam for RequestParams {
88    fn data(&self) -> &HashMap<String, Value> {
89        &self.other
90    }
91}
92
93impl HasMetaParam for RequestParams {
94    fn meta(&self) -> Option<&HashMap<String, Value>> {
95        // This is different from HasMeta::meta() - this returns a reference to raw meta
96        // For now, we'll just return None since we store structured Meta
97        None
98    }
99}
100
101/// A generic result wrapper that combines data with optional _meta information
102#[derive(Debug, Clone, Serialize, Deserialize)]
103pub struct ResultWithMeta {
104    /// The result data
105    #[serde(flatten)]
106    pub data: HashMap<String, Value>,
107
108    /// Optional _meta information
109    #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
110    pub meta: Option<HashMap<String, Value>>,
111}
112
113impl ResultWithMeta {
114    pub fn new(data: HashMap<String, Value>) -> Self {
115        Self { data, meta: None }
116    }
117
118    pub fn with_meta(mut self, meta: HashMap<String, Value>) -> Self {
119        self.meta = Some(meta);
120        self
121    }
122
123    pub fn from_value(value: Value) -> Self {
124        match value {
125            Value::Object(map) => Self {
126                data: map.into_iter().collect(),
127                meta: None,
128            },
129            _ => Self {
130                data: HashMap::new(),
131                meta: None,
132            },
133        }
134    }
135}
136
137impl HasData for ResultWithMeta {
138    fn data(&self) -> HashMap<String, Value> {
139        self.data.clone()
140    }
141}
142
143impl HasMeta for ResultWithMeta {
144    fn meta(&self) -> Option<HashMap<String, Value>> {
145        self.meta.clone()
146    }
147}
148
149impl RpcResult for ResultWithMeta {}
150
151/// A standard JSON-RPC 2.0 request
152#[derive(Debug, Clone, Serialize, Deserialize)]
153pub struct JsonRpcRequest {
154    pub jsonrpc: String,
155    pub id: Value,
156    pub method: String,
157    #[serde(skip_serializing_if = "Option::is_none")]
158    pub params: Option<RequestParams>,
159}
160
161impl JsonRpcRequest {
162    pub fn new(id: Value, method: String) -> Self {
163        Self {
164            jsonrpc: JSONRPC_VERSION.to_string(),
165            id,
166            method,
167            params: None,
168        }
169    }
170
171    pub fn with_params(mut self, params: RequestParams) -> Self {
172        self.params = Some(params);
173        self
174    }
175}
176
177/// A standard JSON-RPC 2.0 response
178#[derive(Debug, Clone, Serialize, Deserialize)]
179pub struct JsonRpcResponse {
180    pub jsonrpc: String,
181    pub id: Value,
182    #[serde(skip_serializing_if = "Option::is_none")]
183    pub result: Option<ResultWithMeta>,
184    #[serde(skip_serializing_if = "Option::is_none")]
185    pub error: Option<JsonRpcError>,
186}
187
188impl JsonRpcResponse {
189    pub fn success(id: Value, result: ResultWithMeta) -> Self {
190        Self {
191            jsonrpc: JSONRPC_VERSION.to_string(),
192            id,
193            result: Some(result),
194            error: None,
195        }
196    }
197
198    pub fn error(id: Value, error: JsonRpcError) -> Self {
199        Self {
200            jsonrpc: JSONRPC_VERSION.to_string(),
201            id,
202            result: None,
203            error: Some(error),
204        }
205    }
206}
207
208/// JSON-RPC 2.0 error object
209#[derive(Debug, Clone, Serialize, Deserialize)]
210pub struct JsonRpcError {
211    pub code: i32,
212    pub message: String,
213    #[serde(skip_serializing_if = "Option::is_none")]
214    pub data: Option<Value>,
215}
216
217impl JsonRpcError {
218    pub fn new(code: i32, message: String) -> Self {
219        Self {
220            code,
221            message,
222            data: None,
223        }
224    }
225
226    pub fn with_data(mut self, data: Value) -> Self {
227        self.data = Some(data);
228        self
229    }
230
231    // Standard JSON-RPC error codes
232    pub fn parse_error() -> Self {
233        Self::new(-32700, "Parse error".to_string())
234    }
235
236    pub fn invalid_request() -> Self {
237        Self::new(-32600, "Invalid Request".to_string())
238    }
239
240    pub fn method_not_found() -> Self {
241        Self::new(-32601, "Method not found".to_string())
242    }
243
244    pub fn invalid_params() -> Self {
245        Self::new(-32602, "Invalid params".to_string())
246    }
247
248    pub fn internal_error() -> Self {
249        Self::new(-32603, "Internal error".to_string())
250    }
251}
252
253/// A JSON-RPC 2.0 notification (no response expected)
254#[derive(Debug, Clone, Serialize, Deserialize)]
255pub struct JsonRpcNotification {
256    pub jsonrpc: String,
257    pub method: String,
258    #[serde(skip_serializing_if = "Option::is_none")]
259    pub params: Option<RequestParams>,
260}
261
262impl JsonRpcNotification {
263    pub fn new(method: String) -> Self {
264        Self {
265            jsonrpc: JSONRPC_VERSION.to_string(),
266            method,
267            params: None,
268        }
269    }
270
271    pub fn with_params(mut self, params: RequestParams) -> Self {
272        self.params = Some(params);
273        self
274    }
275}
276
277/// Unified JSON-RPC message type
278#[derive(Debug, Clone, Serialize, Deserialize)]
279#[serde(untagged)]
280pub enum JsonRpcMessage {
281    Request(JsonRpcRequest),
282    Response(JsonRpcResponse),
283    Notification(JsonRpcNotification),
284    Error(JsonRpcError),
285}
286
287#[cfg(test)]
288mod tests {
289    use super::*;
290    use serde_json::json;
291
292    #[test]
293    fn test_request_params_with_meta() {
294        let params = RequestParams {
295            meta: Some(Meta {
296                progress_token: Some(ProgressToken::new("test-token")),
297                cursor: Some(crate::meta::Cursor::new("cursor-123")),
298                total: Some(100),
299                has_more: Some(true),
300                ..Default::default()
301            }),
302            other: {
303                let mut map = HashMap::new();
304                map.insert("name".to_string(), json!("test"));
305                map
306            },
307        };
308
309        // Test serialization
310        let json_str = serde_json::to_string(&params).unwrap();
311        assert!(json_str.contains("progressToken"));
312        assert!(json_str.contains("test-token"));
313        assert!(json_str.contains("cursor"));
314        assert!(json_str.contains("cursor-123"));
315        assert!(json_str.contains("name"));
316        assert!(json_str.contains("test"));
317
318        // Test deserialization
319        let parsed: RequestParams = serde_json::from_str(&json_str).unwrap();
320        assert!(parsed.meta.is_some());
321        assert_eq!(
322            parsed
323                .meta
324                .as_ref()
325                .unwrap()
326                .progress_token
327                .as_ref()
328                .unwrap()
329                .as_str(),
330            "test-token"
331        );
332    }
333
334    #[test]
335    fn test_result_with_meta() {
336        let mut data = HashMap::new();
337        data.insert("result".to_string(), json!("success"));
338
339        let mut meta = HashMap::new();
340        meta.insert("total".to_string(), json!(42));
341
342        let result = ResultWithMeta::new(data).with_meta(meta);
343
344        // Test traits
345        assert!(result.data().contains_key("result"));
346        assert!(result.meta().unwrap().contains_key("total"));
347
348        // Test serialization
349        let json_str = serde_json::to_string(&result).unwrap();
350        assert!(json_str.contains("result"));
351        assert!(json_str.contains("_meta"));
352        assert!(json_str.contains("total"));
353    }
354}