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: Cow<'de, str> = Cow::deserialize(deserializer)?;
26 if s == JSONRPC_VERSION {
27 Ok(Cow::Borrowed(JSONRPC_VERSION))
28 } else {
29 Ok(Cow::Owned(s.into_owned()))
30 }
31}
32
33#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
35#[serde(untagged)]
36pub enum RequestId {
37 Number(i64),
39 String(String),
41}
42
43impl From<i64> for RequestId {
44 fn from(id: i64) -> Self {
45 RequestId::Number(id)
46 }
47}
48
49impl From<String> for RequestId {
50 fn from(id: String) -> Self {
51 RequestId::String(id)
52 }
53}
54
55impl From<&str> for RequestId {
56 fn from(id: &str) -> Self {
57 RequestId::String(id.to_owned())
58 }
59}
60
61impl std::fmt::Display for RequestId {
62 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
63 match self {
64 RequestId::Number(n) => write!(f, "{n}"),
65 RequestId::String(s) => write!(f, "{s}"),
66 }
67 }
68}
69
70#[derive(Debug, Clone, Serialize, Deserialize)]
72pub struct JsonRpcRequest {
73 #[serde(
75 serialize_with = "serialize_jsonrpc_version",
76 deserialize_with = "deserialize_jsonrpc_version"
77 )]
78 pub jsonrpc: Cow<'static, str>,
79 pub method: String,
81 #[serde(skip_serializing_if = "Option::is_none")]
83 pub params: Option<Value>,
84 #[serde(skip_serializing_if = "Option::is_none")]
86 pub id: Option<RequestId>,
87}
88
89impl JsonRpcRequest {
90 #[must_use]
92 pub fn new(method: impl Into<String>, params: Option<Value>, id: impl Into<RequestId>) -> Self {
93 Self {
94 jsonrpc: Cow::Borrowed(JSONRPC_VERSION),
95 method: method.into(),
96 params,
97 id: Some(id.into()),
98 }
99 }
100
101 #[must_use]
103 pub fn notification(method: impl Into<String>, params: Option<Value>) -> Self {
104 Self {
105 jsonrpc: Cow::Borrowed(JSONRPC_VERSION),
106 method: method.into(),
107 params,
108 id: None,
109 }
110 }
111
112 #[must_use]
114 pub fn is_notification(&self) -> bool {
115 self.id.is_none()
116 }
117}
118
119#[derive(Debug, Clone, Serialize, Deserialize)]
121pub struct JsonRpcError {
122 pub code: i32,
124 pub message: String,
126 #[serde(skip_serializing_if = "Option::is_none")]
128 pub data: Option<Value>,
129}
130
131impl From<fastmcp_core::McpError> for JsonRpcError {
132 fn from(err: fastmcp_core::McpError) -> Self {
133 Self {
134 code: err.code.into(),
135 message: err.message,
136 data: err.data,
137 }
138 }
139}
140
141#[derive(Debug, Clone, Serialize, Deserialize)]
143pub struct JsonRpcResponse {
144 #[serde(
146 serialize_with = "serialize_jsonrpc_version",
147 deserialize_with = "deserialize_jsonrpc_version"
148 )]
149 pub jsonrpc: Cow<'static, str>,
150 #[serde(skip_serializing_if = "Option::is_none")]
152 pub result: Option<Value>,
153 #[serde(skip_serializing_if = "Option::is_none")]
155 pub error: Option<JsonRpcError>,
156 pub id: Option<RequestId>,
158}
159
160impl JsonRpcResponse {
161 #[must_use]
163 pub fn success(id: RequestId, result: Value) -> Self {
164 Self {
165 jsonrpc: Cow::Borrowed(JSONRPC_VERSION),
166 result: Some(result),
167 error: None,
168 id: Some(id),
169 }
170 }
171
172 #[must_use]
174 pub fn error(id: Option<RequestId>, error: JsonRpcError) -> Self {
175 Self {
176 jsonrpc: Cow::Borrowed(JSONRPC_VERSION),
177 result: None,
178 error: Some(error),
179 id,
180 }
181 }
182
183 #[must_use]
185 pub fn is_error(&self) -> bool {
186 self.error.is_some()
187 }
188}
189
190#[derive(Debug, Clone, Serialize, Deserialize)]
192#[serde(untagged)]
193pub enum JsonRpcMessage {
194 Request(JsonRpcRequest),
196 Response(JsonRpcResponse),
198}
199
200#[cfg(test)]
201mod tests {
202 use super::*;
203 use serde_json::json;
204
205 #[test]
210 fn request_id_number_serialization() {
211 let id = RequestId::Number(42);
212 let value = serde_json::to_value(&id).expect("serialize");
213 assert_eq!(value, 42);
214 }
215
216 #[test]
217 fn request_id_string_serialization() {
218 let id = RequestId::String("req-1".to_string());
219 let value = serde_json::to_value(&id).expect("serialize");
220 assert_eq!(value, "req-1");
221 }
222
223 #[test]
224 fn request_id_number_deserialization() {
225 let id: RequestId = serde_json::from_value(json!(99)).expect("deserialize");
226 assert_eq!(id, RequestId::Number(99));
227 }
228
229 #[test]
230 fn request_id_string_deserialization() {
231 let id: RequestId = serde_json::from_value(json!("abc")).expect("deserialize");
232 assert_eq!(id, RequestId::String("abc".to_string()));
233 }
234
235 #[test]
236 fn request_id_from_i64() {
237 let id: RequestId = 7i64.into();
238 assert_eq!(id, RequestId::Number(7));
239 }
240
241 #[test]
242 fn request_id_from_string() {
243 let id: RequestId = "test-id".to_string().into();
244 assert_eq!(id, RequestId::String("test-id".to_string()));
245 }
246
247 #[test]
248 fn request_id_from_str() {
249 let id: RequestId = "test-id".into();
250 assert_eq!(id, RequestId::String("test-id".to_string()));
251 }
252
253 #[test]
254 fn request_id_display() {
255 assert_eq!(format!("{}", RequestId::Number(42)), "42");
256 assert_eq!(
257 format!("{}", RequestId::String("req-1".to_string())),
258 "req-1"
259 );
260 }
261
262 #[test]
263 fn request_id_equality() {
264 assert_eq!(RequestId::Number(1), RequestId::Number(1));
265 assert_ne!(RequestId::Number(1), RequestId::Number(2));
266 assert_eq!(
267 RequestId::String("a".to_string()),
268 RequestId::String("a".to_string())
269 );
270 assert_ne!(RequestId::Number(1), RequestId::String("1".to_string()));
271 }
272
273 #[test]
278 fn jsonrpc_version_deserialize_borrows_static_for_request() {
279 let req: JsonRpcRequest =
280 serde_json::from_str(r#"{"jsonrpc":"2.0","method":"tools/list","id":1}"#)
281 .expect("deserialize");
282 assert!(matches!(req.jsonrpc, Cow::Borrowed(JSONRPC_VERSION)));
283 }
284
285 #[test]
286 fn jsonrpc_version_deserialize_owns_nonstandard_for_request() {
287 let req: JsonRpcRequest =
288 serde_json::from_str(r#"{"jsonrpc":"2.1","method":"tools/list","id":1}"#)
289 .expect("deserialize");
290 let owned = match req.jsonrpc {
291 Cow::Owned(s) => Some(s),
292 Cow::Borrowed(_) => None,
293 };
294 assert_eq!(owned.as_deref(), Some("2.1"));
295 }
296
297 #[test]
298 fn request_serialization() {
299 let req = JsonRpcRequest::new("tools/list", None, 1i64);
300 let json = serde_json::to_string(&req).unwrap();
301 assert!(json.contains("\"jsonrpc\":\"2.0\""));
302 assert!(json.contains("\"method\":\"tools/list\""));
303 assert!(json.contains("\"id\":1"));
304 }
305
306 #[test]
307 fn request_with_params() {
308 let params = json!({"name": "greet", "arguments": {"name": "World"}});
309 let req = JsonRpcRequest::new("tools/call", Some(params.clone()), 2i64);
310 let value = serde_json::to_value(&req).expect("serialize");
311 assert_eq!(value["jsonrpc"], "2.0");
312 assert_eq!(value["method"], "tools/call");
313 assert_eq!(value["params"]["name"], "greet");
314 assert_eq!(value["id"], 2);
315 }
316
317 #[test]
318 fn request_without_params_omits_field() {
319 let req = JsonRpcRequest::new("tools/list", None, 1i64);
320 let value = serde_json::to_value(&req).expect("serialize");
321 assert!(value.get("params").is_none());
322 }
323
324 #[test]
325 fn notification_has_no_id() {
326 let notif = JsonRpcRequest::notification("notifications/progress", None);
327 assert!(notif.is_notification());
328 assert!(notif.id.is_none());
329 let value = serde_json::to_value(¬if).expect("serialize");
330 assert!(value.get("id").is_none());
331 }
332
333 #[test]
334 fn notification_with_params() {
335 let params = json!({"uri": "file://changed.txt"});
336 let notif = JsonRpcRequest::notification("notifications/resources/updated", Some(params));
337 assert!(notif.is_notification());
338 let value = serde_json::to_value(¬if).expect("serialize");
339 assert_eq!(value["params"]["uri"], "file://changed.txt");
340 }
341
342 #[test]
343 fn request_is_not_notification() {
344 let req = JsonRpcRequest::new("tools/list", None, 1i64);
345 assert!(!req.is_notification());
346 }
347
348 #[test]
349 fn request_with_string_id() {
350 let req = JsonRpcRequest::new("tools/list", None, "req-abc");
351 let value = serde_json::to_value(&req).expect("serialize");
352 assert_eq!(value["id"], "req-abc");
353 }
354
355 #[test]
356 fn request_round_trip() {
357 let original = JsonRpcRequest::new(
358 "tools/call",
359 Some(json!({"name": "add", "arguments": {"a": 1, "b": 2}})),
360 42i64,
361 );
362 let json_str = serde_json::to_string(&original).expect("serialize");
363 let deserialized: JsonRpcRequest = serde_json::from_str(&json_str).expect("deserialize");
364 assert_eq!(deserialized.method, "tools/call");
365 assert_eq!(deserialized.id, Some(RequestId::Number(42)));
366 assert!(deserialized.params.is_some());
367 }
368
369 #[test]
374 fn jsonrpc_error_from_mcp_error_preserves_code_message_and_data() {
375 let err = fastmcp_core::McpError::with_data(
376 fastmcp_core::McpErrorCode::InvalidParams,
377 "bad params",
378 json!({"field":"name"}),
379 );
380 let rpc_err: JsonRpcError = err.into();
381 assert_eq!(rpc_err.code, -32602);
382 assert_eq!(rpc_err.message, "bad params");
383 assert_eq!(rpc_err.data, Some(json!({"field":"name"})));
384 }
385
386 #[test]
387 fn jsonrpc_error_serialization() {
388 let error = JsonRpcError {
389 code: -32600,
390 message: "Invalid Request".to_string(),
391 data: None,
392 };
393 let value = serde_json::to_value(&error).expect("serialize");
394 assert_eq!(value["code"], -32600);
395 assert_eq!(value["message"], "Invalid Request");
396 assert!(value.get("data").is_none());
397 }
398
399 #[test]
400 fn jsonrpc_error_with_data() {
401 let error = JsonRpcError {
402 code: -32602,
403 message: "Invalid params".to_string(),
404 data: Some(json!({"field": "name", "reason": "required"})),
405 };
406 let value = serde_json::to_value(&error).expect("serialize");
407 assert_eq!(value["code"], -32602);
408 assert_eq!(value["data"]["field"], "name");
409 }
410
411 #[test]
412 fn jsonrpc_error_standard_codes() {
413 let err = JsonRpcError {
415 code: -32700,
416 message: "Parse error".to_string(),
417 data: None,
418 };
419 assert_eq!(serde_json::to_value(&err).unwrap()["code"], -32700);
420
421 let err = JsonRpcError {
423 code: -32601,
424 message: "Method not found".to_string(),
425 data: None,
426 };
427 assert_eq!(serde_json::to_value(&err).unwrap()["code"], -32601);
428
429 let err = JsonRpcError {
431 code: -32603,
432 message: "Internal error".to_string(),
433 data: None,
434 };
435 assert_eq!(serde_json::to_value(&err).unwrap()["code"], -32603);
436 }
437
438 #[test]
443 fn jsonrpc_version_deserialize_borrows_static_for_response() {
444 let resp: JsonRpcResponse =
445 serde_json::from_str(r#"{"jsonrpc":"2.0","result":{"tools":[]},"id":1}"#)
446 .expect("deserialize");
447 assert!(matches!(resp.jsonrpc, Cow::Borrowed(JSONRPC_VERSION)));
448 }
449
450 #[test]
451 fn jsonrpc_version_deserialize_owns_nonstandard_for_response() {
452 let resp: JsonRpcResponse =
453 serde_json::from_str(r#"{"jsonrpc":"2.1","result":{"tools":[]},"id":1}"#)
454 .expect("deserialize");
455 let owned = match resp.jsonrpc {
456 Cow::Owned(s) => Some(s),
457 Cow::Borrowed(_) => None,
458 };
459 assert_eq!(owned.as_deref(), Some("2.1"));
460 }
461
462 #[test]
463 fn response_success() {
464 let resp = JsonRpcResponse::success(RequestId::Number(1), json!({"result": "ok"}));
465 let value = serde_json::to_value(&resp).expect("serialize");
466 assert_eq!(value["jsonrpc"], "2.0");
467 assert_eq!(value["result"]["result"], "ok");
468 assert_eq!(value["id"], 1);
469 assert!(value.get("error").is_none());
470 assert!(!resp.is_error());
471 }
472
473 #[test]
474 fn response_error() {
475 let error = JsonRpcError {
476 code: -32601,
477 message: "Method not found".to_string(),
478 data: None,
479 };
480 let resp = JsonRpcResponse::error(Some(RequestId::Number(1)), error);
481 let value = serde_json::to_value(&resp).expect("serialize");
482 assert_eq!(value["jsonrpc"], "2.0");
483 assert!(value.get("result").is_none());
484 assert_eq!(value["error"]["code"], -32601);
485 assert_eq!(value["error"]["message"], "Method not found");
486 assert_eq!(value["id"], 1);
487 assert!(resp.is_error());
488 }
489
490 #[test]
491 fn response_error_null_id() {
492 let error = JsonRpcError {
493 code: -32700,
494 message: "Parse error".to_string(),
495 data: None,
496 };
497 let resp = JsonRpcResponse::error(None, error);
498 let value = serde_json::to_value(&resp).expect("serialize");
499 assert!(value["id"].is_null());
500 }
501
502 #[test]
503 fn response_round_trip() {
504 let original =
505 JsonRpcResponse::success(RequestId::String("abc".to_string()), json!({"tools": []}));
506 let json_str = serde_json::to_string(&original).expect("serialize");
507 let deserialized: JsonRpcResponse = serde_json::from_str(&json_str).expect("deserialize");
508 assert!(!deserialized.is_error());
509 assert!(deserialized.result.is_some());
510 assert_eq!(deserialized.id, Some(RequestId::String("abc".to_string())));
511 }
512
513 #[test]
518 fn message_request_variant() {
519 let req = JsonRpcRequest::new("tools/list", None, 1i64);
520 let msg = JsonRpcMessage::Request(req);
521 let value = serde_json::to_value(&msg).expect("serialize");
522 assert_eq!(value["method"], "tools/list");
523 }
524
525 #[test]
526 fn message_response_variant() {
527 let resp = JsonRpcResponse::success(RequestId::Number(1), json!("ok"));
528 let msg = JsonRpcMessage::Response(resp);
529 let value = serde_json::to_value(&msg).expect("serialize");
530 assert_eq!(value["result"], "ok");
531 }
532
533 #[test]
534 fn message_deserialize_as_request() {
535 let json_str = r#"{"jsonrpc":"2.0","method":"tools/list","id":1}"#;
536 let msg: JsonRpcMessage = serde_json::from_str(json_str).expect("deserialize");
537 let (method, id) = match msg {
538 JsonRpcMessage::Request(req) => (req.method, req.id),
539 JsonRpcMessage::Response(_) => (String::new(), None),
540 };
541 assert_eq!(method, "tools/list");
542 assert_eq!(id, Some(RequestId::Number(1)));
543 }
544
545 #[test]
546 fn message_deserialize_as_response() {
547 let json_str = r#"{"jsonrpc":"2.0","result":{"tools":[]},"id":1}"#;
548 let msg: JsonRpcMessage = serde_json::from_str(json_str).expect("deserialize");
549 let (is_error, id) = match msg {
550 JsonRpcMessage::Response(resp) => (resp.is_error(), resp.id),
551 JsonRpcMessage::Request(_) => (true, None),
552 };
553 assert!(!is_error);
554 assert_eq!(id, Some(RequestId::Number(1)));
555 }
556
557 #[test]
562 fn jsonrpc_version_constant() {
563 assert_eq!(JSONRPC_VERSION, "2.0");
564 }
565}