1use std::borrow::Cow;
4
5use serde::{Deserialize, Deserializer, Serialize, Serializer};
6use serde_json::Value;
7
8pub const JSONRPC_VERSION: &str = "2.0";
10
11fn serialize_jsonrpc_version<S>(value: &str, serializer: S) -> Result<S::Ok, S::Error>
13where
14 S: Serializer,
15{
16 serializer.serialize_str(value)
17}
18
19fn deserialize_jsonrpc_version<'de, D>(deserializer: D) -> Result<Cow<'static, str>, D::Error>
21where
22 D: Deserializer<'de>,
23{
24 let s = String::deserialize(deserializer)?;
25 if s == JSONRPC_VERSION {
26 Ok(Cow::Borrowed(JSONRPC_VERSION))
27 } else {
28 Ok(Cow::Owned(s))
29 }
30}
31
32#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
34#[serde(untagged)]
35pub enum RequestId {
36 Number(i64),
38 String(String),
40}
41
42impl From<i64> for RequestId {
43 fn from(id: i64) -> Self {
44 RequestId::Number(id)
45 }
46}
47
48impl From<String> for RequestId {
49 fn from(id: String) -> Self {
50 RequestId::String(id)
51 }
52}
53
54impl From<&str> for RequestId {
55 fn from(id: &str) -> Self {
56 RequestId::String(id.to_owned())
57 }
58}
59
60impl std::fmt::Display for RequestId {
61 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
62 match self {
63 RequestId::Number(n) => write!(f, "{n}"),
64 RequestId::String(s) => write!(f, "{s}"),
65 }
66 }
67}
68
69#[derive(Debug, Clone, Serialize, Deserialize)]
71pub struct JsonRpcRequest {
72 #[serde(
74 serialize_with = "serialize_jsonrpc_version",
75 deserialize_with = "deserialize_jsonrpc_version"
76 )]
77 pub jsonrpc: Cow<'static, str>,
78 pub method: String,
80 #[serde(skip_serializing_if = "Option::is_none")]
82 pub params: Option<Value>,
83 #[serde(skip_serializing_if = "Option::is_none")]
85 pub id: Option<RequestId>,
86}
87
88impl JsonRpcRequest {
89 #[must_use]
91 pub fn new(method: impl Into<String>, params: Option<Value>, id: impl Into<RequestId>) -> Self {
92 Self {
93 jsonrpc: Cow::Borrowed(JSONRPC_VERSION),
94 method: method.into(),
95 params,
96 id: Some(id.into()),
97 }
98 }
99
100 #[must_use]
102 pub fn notification(method: impl Into<String>, params: Option<Value>) -> Self {
103 Self {
104 jsonrpc: Cow::Borrowed(JSONRPC_VERSION),
105 method: method.into(),
106 params,
107 id: None,
108 }
109 }
110
111 #[must_use]
113 pub fn is_notification(&self) -> bool {
114 self.id.is_none()
115 }
116}
117
118#[derive(Debug, Clone, Serialize, Deserialize)]
120pub struct JsonRpcError {
121 pub code: i32,
123 pub message: String,
125 #[serde(skip_serializing_if = "Option::is_none")]
127 pub data: Option<Value>,
128}
129
130impl From<fastmcp_core::McpError> for JsonRpcError {
131 fn from(err: fastmcp_core::McpError) -> Self {
132 Self {
133 code: err.code.into(),
134 message: err.message,
135 data: err.data,
136 }
137 }
138}
139
140#[derive(Debug, Clone, Serialize, Deserialize)]
142pub struct JsonRpcResponse {
143 #[serde(
145 serialize_with = "serialize_jsonrpc_version",
146 deserialize_with = "deserialize_jsonrpc_version"
147 )]
148 pub jsonrpc: Cow<'static, str>,
149 #[serde(skip_serializing_if = "Option::is_none")]
151 pub result: Option<Value>,
152 #[serde(skip_serializing_if = "Option::is_none")]
154 pub error: Option<JsonRpcError>,
155 pub id: Option<RequestId>,
157}
158
159impl JsonRpcResponse {
160 #[must_use]
162 pub fn success(id: RequestId, result: Value) -> Self {
163 Self {
164 jsonrpc: Cow::Borrowed(JSONRPC_VERSION),
165 result: Some(result),
166 error: None,
167 id: Some(id),
168 }
169 }
170
171 #[must_use]
173 pub fn error(id: Option<RequestId>, error: JsonRpcError) -> Self {
174 Self {
175 jsonrpc: Cow::Borrowed(JSONRPC_VERSION),
176 result: None,
177 error: Some(error),
178 id,
179 }
180 }
181
182 #[must_use]
184 pub fn is_error(&self) -> bool {
185 self.error.is_some()
186 }
187}
188
189#[derive(Debug, Clone, Serialize, Deserialize)]
191#[serde(untagged)]
192pub enum JsonRpcMessage {
193 Request(JsonRpcRequest),
195 Response(JsonRpcResponse),
197}
198
199#[cfg(test)]
200mod tests {
201 use super::*;
202 use serde_json::json;
203
204 #[test]
209 fn request_id_number_serialization() {
210 let id = RequestId::Number(42);
211 let value = serde_json::to_value(&id).expect("serialize");
212 assert_eq!(value, 42);
213 }
214
215 #[test]
216 fn request_id_string_serialization() {
217 let id = RequestId::String("req-1".to_string());
218 let value = serde_json::to_value(&id).expect("serialize");
219 assert_eq!(value, "req-1");
220 }
221
222 #[test]
223 fn request_id_number_deserialization() {
224 let id: RequestId = serde_json::from_value(json!(99)).expect("deserialize");
225 assert_eq!(id, RequestId::Number(99));
226 }
227
228 #[test]
229 fn request_id_string_deserialization() {
230 let id: RequestId = serde_json::from_value(json!("abc")).expect("deserialize");
231 assert_eq!(id, RequestId::String("abc".to_string()));
232 }
233
234 #[test]
235 fn request_id_from_i64() {
236 let id: RequestId = 7i64.into();
237 assert_eq!(id, RequestId::Number(7));
238 }
239
240 #[test]
241 fn request_id_from_string() {
242 let id: RequestId = "test-id".to_string().into();
243 assert_eq!(id, RequestId::String("test-id".to_string()));
244 }
245
246 #[test]
247 fn request_id_from_str() {
248 let id: RequestId = "test-id".into();
249 assert_eq!(id, RequestId::String("test-id".to_string()));
250 }
251
252 #[test]
253 fn request_id_display() {
254 assert_eq!(format!("{}", RequestId::Number(42)), "42");
255 assert_eq!(
256 format!("{}", RequestId::String("req-1".to_string())),
257 "req-1"
258 );
259 }
260
261 #[test]
262 fn request_id_equality() {
263 assert_eq!(RequestId::Number(1), RequestId::Number(1));
264 assert_ne!(RequestId::Number(1), RequestId::Number(2));
265 assert_eq!(
266 RequestId::String("a".to_string()),
267 RequestId::String("a".to_string())
268 );
269 assert_ne!(RequestId::Number(1), RequestId::String("1".to_string()));
270 }
271
272 #[test]
277 fn request_serialization() {
278 let req = JsonRpcRequest::new("tools/list", None, 1i64);
279 let json = serde_json::to_string(&req).unwrap();
280 assert!(json.contains("\"jsonrpc\":\"2.0\""));
281 assert!(json.contains("\"method\":\"tools/list\""));
282 assert!(json.contains("\"id\":1"));
283 }
284
285 #[test]
286 fn request_with_params() {
287 let params = json!({"name": "greet", "arguments": {"name": "World"}});
288 let req = JsonRpcRequest::new("tools/call", Some(params.clone()), 2i64);
289 let value = serde_json::to_value(&req).expect("serialize");
290 assert_eq!(value["jsonrpc"], "2.0");
291 assert_eq!(value["method"], "tools/call");
292 assert_eq!(value["params"]["name"], "greet");
293 assert_eq!(value["id"], 2);
294 }
295
296 #[test]
297 fn request_without_params_omits_field() {
298 let req = JsonRpcRequest::new("tools/list", None, 1i64);
299 let value = serde_json::to_value(&req).expect("serialize");
300 assert!(value.get("params").is_none());
301 }
302
303 #[test]
304 fn notification_has_no_id() {
305 let notif = JsonRpcRequest::notification("notifications/progress", None);
306 assert!(notif.is_notification());
307 assert!(notif.id.is_none());
308 let value = serde_json::to_value(¬if).expect("serialize");
309 assert!(value.get("id").is_none());
310 }
311
312 #[test]
313 fn notification_with_params() {
314 let params = json!({"uri": "file://changed.txt"});
315 let notif = JsonRpcRequest::notification("notifications/resources/updated", Some(params));
316 assert!(notif.is_notification());
317 let value = serde_json::to_value(¬if).expect("serialize");
318 assert_eq!(value["params"]["uri"], "file://changed.txt");
319 }
320
321 #[test]
322 fn request_is_not_notification() {
323 let req = JsonRpcRequest::new("tools/list", None, 1i64);
324 assert!(!req.is_notification());
325 }
326
327 #[test]
328 fn request_with_string_id() {
329 let req = JsonRpcRequest::new("tools/list", None, "req-abc");
330 let value = serde_json::to_value(&req).expect("serialize");
331 assert_eq!(value["id"], "req-abc");
332 }
333
334 #[test]
335 fn request_round_trip() {
336 let original = JsonRpcRequest::new(
337 "tools/call",
338 Some(json!({"name": "add", "arguments": {"a": 1, "b": 2}})),
339 42i64,
340 );
341 let json_str = serde_json::to_string(&original).expect("serialize");
342 let deserialized: JsonRpcRequest = serde_json::from_str(&json_str).expect("deserialize");
343 assert_eq!(deserialized.method, "tools/call");
344 assert_eq!(deserialized.id, Some(RequestId::Number(42)));
345 assert!(deserialized.params.is_some());
346 }
347
348 #[test]
353 fn jsonrpc_error_serialization() {
354 let error = JsonRpcError {
355 code: -32600,
356 message: "Invalid Request".to_string(),
357 data: None,
358 };
359 let value = serde_json::to_value(&error).expect("serialize");
360 assert_eq!(value["code"], -32600);
361 assert_eq!(value["message"], "Invalid Request");
362 assert!(value.get("data").is_none());
363 }
364
365 #[test]
366 fn jsonrpc_error_with_data() {
367 let error = JsonRpcError {
368 code: -32602,
369 message: "Invalid params".to_string(),
370 data: Some(json!({"field": "name", "reason": "required"})),
371 };
372 let value = serde_json::to_value(&error).expect("serialize");
373 assert_eq!(value["code"], -32602);
374 assert_eq!(value["data"]["field"], "name");
375 }
376
377 #[test]
378 fn jsonrpc_error_standard_codes() {
379 let err = JsonRpcError {
381 code: -32700,
382 message: "Parse error".to_string(),
383 data: None,
384 };
385 assert_eq!(serde_json::to_value(&err).unwrap()["code"], -32700);
386
387 let err = JsonRpcError {
389 code: -32601,
390 message: "Method not found".to_string(),
391 data: None,
392 };
393 assert_eq!(serde_json::to_value(&err).unwrap()["code"], -32601);
394
395 let err = JsonRpcError {
397 code: -32603,
398 message: "Internal error".to_string(),
399 data: None,
400 };
401 assert_eq!(serde_json::to_value(&err).unwrap()["code"], -32603);
402 }
403
404 #[test]
409 fn response_success() {
410 let resp = JsonRpcResponse::success(RequestId::Number(1), json!({"result": "ok"}));
411 let value = serde_json::to_value(&resp).expect("serialize");
412 assert_eq!(value["jsonrpc"], "2.0");
413 assert_eq!(value["result"]["result"], "ok");
414 assert_eq!(value["id"], 1);
415 assert!(value.get("error").is_none());
416 assert!(!resp.is_error());
417 }
418
419 #[test]
420 fn response_error() {
421 let error = JsonRpcError {
422 code: -32601,
423 message: "Method not found".to_string(),
424 data: None,
425 };
426 let resp = JsonRpcResponse::error(Some(RequestId::Number(1)), error);
427 let value = serde_json::to_value(&resp).expect("serialize");
428 assert_eq!(value["jsonrpc"], "2.0");
429 assert!(value.get("result").is_none());
430 assert_eq!(value["error"]["code"], -32601);
431 assert_eq!(value["error"]["message"], "Method not found");
432 assert_eq!(value["id"], 1);
433 assert!(resp.is_error());
434 }
435
436 #[test]
437 fn response_error_null_id() {
438 let error = JsonRpcError {
439 code: -32700,
440 message: "Parse error".to_string(),
441 data: None,
442 };
443 let resp = JsonRpcResponse::error(None, error);
444 let value = serde_json::to_value(&resp).expect("serialize");
445 assert!(value["id"].is_null());
446 }
447
448 #[test]
449 fn response_round_trip() {
450 let original =
451 JsonRpcResponse::success(RequestId::String("abc".to_string()), json!({"tools": []}));
452 let json_str = serde_json::to_string(&original).expect("serialize");
453 let deserialized: JsonRpcResponse = serde_json::from_str(&json_str).expect("deserialize");
454 assert!(!deserialized.is_error());
455 assert!(deserialized.result.is_some());
456 assert_eq!(deserialized.id, Some(RequestId::String("abc".to_string())));
457 }
458
459 #[test]
464 fn message_request_variant() {
465 let req = JsonRpcRequest::new("tools/list", None, 1i64);
466 let msg = JsonRpcMessage::Request(req);
467 let value = serde_json::to_value(&msg).expect("serialize");
468 assert_eq!(value["method"], "tools/list");
469 }
470
471 #[test]
472 fn message_response_variant() {
473 let resp = JsonRpcResponse::success(RequestId::Number(1), json!("ok"));
474 let msg = JsonRpcMessage::Response(resp);
475 let value = serde_json::to_value(&msg).expect("serialize");
476 assert_eq!(value["result"], "ok");
477 }
478
479 #[test]
480 fn message_deserialize_as_request() {
481 let json_str = r#"{"jsonrpc":"2.0","method":"tools/list","id":1}"#;
482 let msg: JsonRpcMessage = serde_json::from_str(json_str).expect("deserialize");
483 match msg {
484 JsonRpcMessage::Request(req) => {
485 assert_eq!(req.method, "tools/list");
486 assert_eq!(req.id, Some(RequestId::Number(1)));
487 }
488 _ => panic!("Expected request variant"),
489 }
490 }
491
492 #[test]
493 fn message_deserialize_as_response() {
494 let json_str = r#"{"jsonrpc":"2.0","result":{"tools":[]},"id":1}"#;
495 let msg: JsonRpcMessage = serde_json::from_str(json_str).expect("deserialize");
496 match msg {
497 JsonRpcMessage::Response(resp) => {
498 assert!(!resp.is_error());
499 assert_eq!(resp.id, Some(RequestId::Number(1)));
500 }
501 JsonRpcMessage::Request(_) => {
503 }
505 }
506 }
507
508 #[test]
513 fn jsonrpc_version_constant() {
514 assert_eq!(JSONRPC_VERSION, "2.0");
515 }
516}