ultrafast_mcp_core/protocol/
jsonrpc.rs1use crate::protocol::constants::{
2 JSONRPC_VERSION, MAX_REQUEST_ID_LENGTH, MAX_REQUEST_ID_NUMBER, MIN_REQUEST_ID_NUMBER,
3};
4use serde::{Deserialize, Serialize};
5use serde_json::Value;
6use std::borrow::Cow;
7use std::collections::HashMap;
8
9pub mod error_codes {
11 pub const PARSE_ERROR: i32 = -32700;
13 pub const INVALID_REQUEST: i32 = -32600;
15 pub const METHOD_NOT_FOUND: i32 = -32601;
17 pub const INVALID_PARAMS: i32 = -32602;
19 pub const INTERNAL_ERROR: i32 = -32603;
21 pub const SERVER_ERROR_START: i32 = -32000;
23 pub const SERVER_ERROR_END: i32 = -32099;
24}
25
26pub mod mcp_error_codes {
28 pub const INITIALIZATION_FAILED: i32 = -32000;
30 pub const CAPABILITY_NOT_SUPPORTED: i32 = -32001;
32 pub const RESOURCE_NOT_FOUND: i32 = -32002;
34 pub const TOOL_EXECUTION_ERROR: i32 = -32003;
36 pub const INVALID_URI: i32 = -32004;
38 pub const ACCESS_DENIED: i32 = -32005;
40 pub const REQUEST_TIMEOUT: i32 = -32006;
42 pub const PROTOCOL_VERSION_NOT_SUPPORTED: i32 = -32007;
44}
45
46#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
48#[serde(untagged)]
49pub enum RequestId {
50 String(String),
52 Number(i64),
54}
55
56impl RequestId {
57 pub fn string(s: impl Into<String>) -> Self {
59 Self::String(s.into())
60 }
61
62 pub fn number(n: i64) -> Self {
64 Self::Number(n)
65 }
66
67 pub fn validate(&self) -> Result<(), crate::error::ProtocolError> {
69 match self {
70 RequestId::String(s) => {
71 if s.is_empty() {
72 return Err(crate::error::ProtocolError::InvalidRequestId(
73 "Request ID string cannot be empty".to_string(),
74 ));
75 }
76 if s.len() > MAX_REQUEST_ID_LENGTH {
77 return Err(crate::error::ProtocolError::InvalidRequestId(format!(
78 "Request ID string too long (max {MAX_REQUEST_ID_LENGTH} characters)"
79 )));
80 }
81 }
82 RequestId::Number(n) => {
83 if *n < MIN_REQUEST_ID_NUMBER || *n > MAX_REQUEST_ID_NUMBER {
84 return Err(crate::error::ProtocolError::InvalidRequestId(format!(
85 "Request ID number out of range ({MIN_REQUEST_ID_NUMBER} to {MAX_REQUEST_ID_NUMBER})"
86 )));
87 }
88 }
89 }
90 Ok(())
91 }
92}
93
94impl std::fmt::Display for RequestId {
95 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
96 match self {
97 RequestId::String(s) => write!(f, "{s}"),
98 RequestId::Number(n) => write!(f, "{n}"),
99 }
100 }
101}
102
103impl From<String> for RequestId {
104 fn from(s: String) -> Self {
105 RequestId::String(s)
106 }
107}
108
109impl From<i64> for RequestId {
110 fn from(n: i64) -> Self {
111 RequestId::Number(n)
112 }
113}
114
115impl From<u64> for RequestId {
116 fn from(n: u64) -> Self {
117 RequestId::Number(n as i64)
118 }
119}
120
121#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
123pub struct JsonRpcRequest {
124 #[serde(rename = "jsonrpc")]
126 pub jsonrpc: Cow<'static, str>,
127 pub method: String,
129 #[serde(skip_serializing_if = "Option::is_none")]
131 pub params: Option<Value>,
132 #[serde(skip_serializing_if = "Option::is_none")]
134 pub id: Option<RequestId>,
135 #[serde(flatten)]
137 pub meta: HashMap<String, Value>,
138}
139
140impl JsonRpcRequest {
141 pub fn new(method: String, params: Option<Value>, id: Option<RequestId>) -> Self {
142 Self {
143 jsonrpc: Cow::Borrowed(JSONRPC_VERSION),
144 method,
145 params,
146 id,
147 meta: HashMap::new(),
148 }
149 }
150
151 pub fn notification(method: String, params: Option<Value>) -> Self {
152 Self::new(method, params, None)
153 }
154
155 pub fn is_notification(&self) -> bool {
156 self.id.is_none()
157 }
158
159 pub fn with_meta(mut self, key: String, value: Value) -> Self {
160 self.meta.insert(key, value);
161 self
162 }
163}
164
165#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
167pub struct JsonRpcResponse {
168 #[serde(rename = "jsonrpc")]
169 pub jsonrpc: Cow<'static, str>,
170 #[serde(skip_serializing_if = "Option::is_none")]
171 pub result: Option<Value>,
172 #[serde(skip_serializing_if = "Option::is_none")]
173 pub error: Option<JsonRpcError>,
174 pub id: Option<RequestId>,
175 #[serde(flatten)]
176 pub meta: HashMap<String, Value>,
177}
178
179impl JsonRpcResponse {
180 pub fn success(result: Value, id: Option<RequestId>) -> Self {
181 Self {
182 jsonrpc: Cow::Borrowed(JSONRPC_VERSION),
183 result: Some(result),
184 error: None,
185 id,
186 meta: HashMap::new(),
187 }
188 }
189
190 pub fn error(error: JsonRpcError, id: Option<RequestId>) -> Self {
191 Self {
192 jsonrpc: Cow::Borrowed(JSONRPC_VERSION),
193 result: None,
194 error: Some(error),
195 id,
196 meta: HashMap::new(),
197 }
198 }
199
200 pub fn with_meta(mut self, key: String, value: Value) -> Self {
201 self.meta.insert(key, value);
202 self
203 }
204}
205
206#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
208pub struct JsonRpcError {
209 pub code: i32,
210 pub message: String,
211 #[serde(skip_serializing_if = "Option::is_none")]
212 pub data: Option<Value>,
213}
214
215impl JsonRpcError {
216 pub fn new(code: i32, message: String) -> Self {
217 Self {
218 code,
219 message,
220 data: None,
221 }
222 }
223
224 pub fn with_data(mut self, data: Value) -> Self {
225 self.data = Some(data);
226 self
227 }
228
229 pub fn parse_error(message: Option<String>) -> Self {
230 let msg = message.unwrap_or_else(|| "Parse error".to_string());
231 Self::new(error_codes::PARSE_ERROR, msg)
232 }
233
234 pub fn invalid_request(message: Option<String>) -> Self {
235 let msg = message.unwrap_or_else(|| "Invalid request".to_string());
236 Self::new(error_codes::INVALID_REQUEST, msg)
237 }
238
239 pub fn method_not_found(method: String) -> Self {
240 Self::new(
241 error_codes::METHOD_NOT_FOUND,
242 format!("Method not found: {method}"),
243 )
244 }
245
246 pub fn invalid_params(message: Option<String>) -> Self {
247 let msg = message.unwrap_or_else(|| "Invalid parameters".to_string());
248 Self::new(error_codes::INVALID_PARAMS, msg)
249 }
250
251 pub fn internal_error(message: Option<String>) -> Self {
252 let msg = message.unwrap_or_else(|| "Internal error".to_string());
253 Self::new(error_codes::INTERNAL_ERROR, msg)
254 }
255
256 pub fn initialization_failed(message: String) -> Self {
257 Self::new(mcp_error_codes::INITIALIZATION_FAILED, message)
258 }
259
260 pub fn capability_not_supported(capability: String) -> Self {
261 Self::new(
262 mcp_error_codes::CAPABILITY_NOT_SUPPORTED,
263 format!("Capability not supported: {capability}"),
264 )
265 }
266
267 pub fn resource_not_found(uri: String) -> Self {
268 Self::new(
269 mcp_error_codes::RESOURCE_NOT_FOUND,
270 format!("Resource not found: {uri}"),
271 )
272 }
273
274 pub fn tool_execution_error(tool_name: String, error: String) -> Self {
275 Self::new(
276 mcp_error_codes::TOOL_EXECUTION_ERROR,
277 format!("Tool execution error for '{tool_name}': {error}"),
278 )
279 }
280
281 pub fn invalid_uri(uri: String) -> Self {
282 Self::new(mcp_error_codes::INVALID_URI, format!("Invalid URI: {uri}"))
283 }
284
285 pub fn access_denied(resource: String) -> Self {
286 Self::new(
287 mcp_error_codes::ACCESS_DENIED,
288 format!("Access denied to resource: {resource}"),
289 )
290 }
291
292 pub fn request_timeout() -> Self {
293 Self::new(
294 mcp_error_codes::REQUEST_TIMEOUT,
295 "Request timeout".to_string(),
296 )
297 }
298
299 pub fn protocol_version_not_supported(version: String) -> Self {
300 Self::new(
301 mcp_error_codes::PROTOCOL_VERSION_NOT_SUPPORTED,
302 format!("Protocol version not supported: {version}"),
303 )
304 }
305}
306
307#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
309#[serde(untagged)]
310pub enum JsonRpcMessage {
311 Request(JsonRpcRequest),
312 Response(JsonRpcResponse),
313 Notification(JsonRpcRequest), }
315
316impl JsonRpcMessage {
317 pub fn get_id(&self) -> Option<&RequestId> {
318 match self {
319 JsonRpcMessage::Request(req) => req.id.as_ref(),
320 JsonRpcMessage::Response(resp) => resp.id.as_ref(),
321 JsonRpcMessage::Notification(_) => None,
322 }
323 }
324
325 pub fn is_notification(&self) -> bool {
326 matches!(self, JsonRpcMessage::Notification(_))
327 }
328}
329
330pub fn validate_jsonrpc_message(
332 message: &JsonRpcMessage,
333) -> Result<(), crate::error::ProtocolError> {
334 match message {
335 JsonRpcMessage::Request(request) => {
336 if request.jsonrpc != JSONRPC_VERSION {
337 return Err(crate::error::ProtocolError::InvalidVersion(format!(
338 "Expected JSON-RPC version {}, got {}",
339 JSONRPC_VERSION, request.jsonrpc
340 )));
341 }
342
343 if request.method.is_empty() {
344 return Err(crate::error::ProtocolError::InvalidRequest(
345 "Method name cannot be empty".to_string(),
346 ));
347 }
348
349 if let Some(ref id) = request.id {
350 id.validate()?;
351 }
352 }
353 JsonRpcMessage::Response(response) => {
354 if response.jsonrpc != JSONRPC_VERSION {
355 return Err(crate::error::ProtocolError::InvalidVersion(format!(
356 "Expected JSON-RPC version {}, got {}",
357 JSONRPC_VERSION, response.jsonrpc
358 )));
359 }
360
361 if response.result.is_some() && response.error.is_some() {
362 return Err(crate::error::ProtocolError::InvalidResponse(
363 "Response cannot have both result and error".to_string(),
364 ));
365 }
366
367 if response.result.is_none() && response.error.is_none() {
368 return Err(crate::error::ProtocolError::InvalidResponse(
369 "Response must have either result or error".to_string(),
370 ));
371 }
372
373 if let Some(ref id) = response.id {
374 id.validate()?;
375 }
376 }
377 JsonRpcMessage::Notification(notification) => {
378 if notification.jsonrpc != JSONRPC_VERSION {
379 return Err(crate::error::ProtocolError::InvalidVersion(format!(
380 "Expected JSON-RPC version {}, got {}",
381 JSONRPC_VERSION, notification.jsonrpc
382 )));
383 }
384
385 if notification.method.is_empty() {
386 return Err(crate::error::ProtocolError::InvalidRequest(
387 "Method name cannot be empty".to_string(),
388 ));
389 }
390
391 if notification.id.is_some() {
392 return Err(crate::error::ProtocolError::InvalidRequest(
393 "Notification cannot have an ID".to_string(),
394 ));
395 }
396 }
397 }
398 Ok(())
399}
400
401#[cfg(test)]
402mod tests {
403 use super::*;
404
405 #[test]
406 fn test_request_serialization() {
407 let request = JsonRpcRequest::new(
408 "test_method".to_string(),
409 Some(serde_json::json!({"param": "value"})),
410 Some(RequestId::number(1)),
411 );
412
413 let serialized = serde_json::to_string(&request).expect("Failed to serialize request");
414 let deserialized: JsonRpcRequest =
415 serde_json::from_str(&serialized).expect("Failed to deserialize request");
416
417 assert_eq!(request, deserialized);
418 }
419
420 #[test]
421 fn test_notification() {
422 let notification = JsonRpcRequest::notification(
423 "test_notification".to_string(),
424 Some(serde_json::json!({"data": "value"})),
425 );
426
427 assert!(notification.is_notification());
428 assert_eq!(notification.id, None);
429 }
430
431 #[test]
432 fn test_response_success() {
433 let response = JsonRpcResponse::success(
434 serde_json::json!({"result": "success"}),
435 Some(RequestId::number(1)),
436 );
437
438 assert!(response.result.is_some());
439 assert!(response.error.is_none());
440 assert_eq!(response.id, Some(RequestId::number(1)));
441 }
442
443 #[test]
444 fn test_response_error() {
445 let error = JsonRpcError::method_not_found("unknown_method".to_string());
446 let response = JsonRpcResponse::error(error, Some(RequestId::number(1)));
447
448 assert!(response.result.is_none());
449 assert!(response.error.is_some());
450 assert_eq!(response.id, Some(RequestId::number(1)));
451 }
452
453 #[test]
454 fn test_request_id_validation() {
455 let valid_string = RequestId::string("valid_id");
457 assert!(valid_string.validate().is_ok());
458
459 let valid_number = RequestId::number(123);
461 assert!(valid_number.validate().is_ok());
462
463 let invalid_string = RequestId::string("");
465 assert!(invalid_string.validate().is_err());
466
467 let invalid_number = RequestId::number(9999999999);
469 assert!(invalid_number.validate().is_err());
470 }
471
472 #[test]
473 fn test_request_id_to_string() {
474 let string_id = RequestId::string("test_id");
475 assert_eq!(string_id.to_string(), "test_id");
476
477 let number_id = RequestId::number(123);
478 assert_eq!(number_id.to_string(), "123");
479 }
480
481 #[test]
482 fn test_jsonrpc_message_validation() {
483 let valid_request = JsonRpcMessage::Request(JsonRpcRequest::new(
485 "test_method".to_string(),
486 None,
487 Some(RequestId::number(1)),
488 ));
489 assert!(validate_jsonrpc_message(&valid_request).is_ok());
490
491 let invalid_request = JsonRpcMessage::Request(JsonRpcRequest::new(
493 "".to_string(),
494 None,
495 Some(RequestId::number(1)),
496 ));
497 assert!(validate_jsonrpc_message(&invalid_request).is_err());
498 }
499}