viam_mcp_sdk/
json_rpc.rs

1use serde::{Deserialize, Serialize};
2use serde_json::Value;
3use std::fmt;
4
5#[derive(Debug, Deserialize)]
6pub struct Request {
7    pub jsonrpc: String,
8    pub method: String,
9    #[serde(default)]
10    pub params: Value,
11    pub id: Option<Value>,
12}
13
14#[derive(Debug, Serialize)]
15pub struct Response {
16    pub jsonrpc: String,
17    #[serde(skip_serializing_if = "Option::is_none")]
18    pub result: Option<Value>,
19    #[serde(skip_serializing_if = "Option::is_none")]
20    pub error: Option<Error>,
21    pub id: Option<Value>,
22}
23
24#[derive(Debug, Serialize)]
25pub struct Error {
26    pub code: i32,
27    pub message: String,
28    #[serde(skip_serializing_if = "Option::is_none")]
29    pub data: Option<Value>,
30}
31
32// Error codes as per JSON-RPC 2.0 spec
33pub mod error_codes {
34    pub const PARSE_ERROR: i32 = -32700;
35    pub const INVALID_REQUEST: i32 = -32600;
36    pub const METHOD_NOT_FOUND: i32 = -32601;
37    pub const INVALID_PARAMS: i32 = -32602;
38    pub const INTERNAL_ERROR: i32 = -32603;
39    // MCP-specific error codes could be added here
40}
41
42impl Response {
43    pub fn success(result: Value, id: Option<Value>) -> Self {
44        Self {
45            jsonrpc: "2.0".to_string(),
46            result: Some(result),
47            error: None,
48            id,
49        }
50    }
51
52    pub fn error(code: i32, message: String, id: Option<Value>) -> Self {
53        Self {
54            jsonrpc: "2.0".to_string(),
55            result: None,
56            error: Some(Error {
57                code,
58                message,
59                data: None,
60            }),
61            id,
62        }
63    }
64
65    pub fn to_string(&self) -> Result<String, String> {
66        serde_json::to_string(self).map_err(|e| format!("Failed to serialize response: {}", e))
67    }
68}
69
70// Helper functions for common responses
71pub fn success(result: Value, id: Option<Value>) -> Result<String, String> {
72    Response::success(result, id).to_string()
73}
74
75pub fn error(code: i32, message: &str, id: Option<Value>) -> Result<String, String> {
76    Response::error(code, message.to_string(), id).to_string()
77}
78
79pub fn parse_request(message: &str) -> Result<Request, String> {
80    // Trim any whitespace and check if the message is empty
81    let trimmed = message.trim();
82    if trimmed.is_empty() {
83        return Err("Empty JSON-RPC message".to_string());
84    }
85    
86    // Try to parse the message as a JSON-RPC request
87    match serde_json::from_str::<Request>(trimmed) {
88        Ok(request) => {
89            // Validate the request
90            if request.jsonrpc != "2.0" {
91                return Err(format!("Invalid JSON-RPC version: {}", request.jsonrpc));
92            }
93            
94            // Check if method is empty
95            if request.method.is_empty() {
96                return Err("Method cannot be empty".to_string());
97            }
98            
99            Ok(request)
100        },
101        Err(e) => {
102            // Provide more detailed error messages for common JSON parsing errors
103            let error_msg = match e.classify() {
104                serde_json::error::Category::Io => "I/O error while reading JSON",
105                serde_json::error::Category::Syntax => "JSON syntax error",
106                serde_json::error::Category::Data => "JSON data error (missing or invalid fields)",
107                serde_json::error::Category::Eof => "Unexpected end of JSON input",
108            };
109            
110            Err(format!("Invalid JSON-RPC message: {} - {}", error_msg, e))
111        }
112    }
113}
114
115// Custom error type for JSON-RPC errors
116#[derive(Debug)]
117pub struct JsonRpcError {
118    pub code: i32,
119    pub message: String,
120    pub id: Option<Value>,
121}
122
123impl fmt::Display for JsonRpcError {
124    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
125        write!(f, "JSON-RPC Error {}: {}", self.code, self.message)
126    }
127}
128
129impl std::error::Error for JsonRpcError {}
130
131// Convert to a proper JSON-RPC response
132impl From<JsonRpcError> for Response {
133    fn from(err: JsonRpcError) -> Self {
134        Response::error(err.code, err.message, err.id)
135    }
136}