Skip to main content

agentic_codebase/mcp/
protocol.rs

1//! JSON-RPC 2.0 protocol types for the MCP server.
2//!
3//! Defines the wire format for JSON-RPC messages used in the
4//! Model Context Protocol server implementation.
5
6use serde::{Deserialize, Serialize};
7use serde_json::Value;
8
9/// JSON-RPC 2.0 request message.
10#[derive(Debug, Clone, Serialize, Deserialize)]
11pub struct JsonRpcRequest {
12    /// Must be "2.0".
13    pub jsonrpc: String,
14    /// Request ID (number or string). Absent for notifications.
15    #[serde(default)]
16    pub id: Option<Value>,
17    /// Method name.
18    pub method: String,
19    /// Method parameters (optional).
20    #[serde(default)]
21    pub params: Value,
22}
23
24impl JsonRpcRequest {
25    /// Create a new JSON-RPC request.
26    pub fn new(id: impl Into<Value>, method: impl Into<String>) -> Self {
27        Self {
28            jsonrpc: "2.0".to_string(),
29            id: Some(id.into()),
30            method: method.into(),
31            params: Value::Null,
32        }
33    }
34
35    /// Create a new JSON-RPC request with parameters.
36    pub fn with_params(id: impl Into<Value>, method: impl Into<String>, params: Value) -> Self {
37        Self {
38            jsonrpc: "2.0".to_string(),
39            id: Some(id.into()),
40            method: method.into(),
41            params,
42        }
43    }
44}
45
46/// JSON-RPC 2.0 error object.
47#[derive(Debug, Clone, Serialize, Deserialize)]
48pub struct JsonRpcError {
49    /// Error code.
50    pub code: i32,
51    /// Error message.
52    pub message: String,
53    /// Additional error data (optional).
54    #[serde(skip_serializing_if = "Option::is_none")]
55    pub data: Option<Value>,
56}
57
58impl JsonRpcError {
59    /// Standard error: Parse error (-32700).
60    pub fn parse_error(detail: impl Into<String>) -> Self {
61        Self {
62            code: -32700,
63            message: "Parse error".to_string(),
64            data: Some(Value::String(detail.into())),
65        }
66    }
67
68    /// Standard error: Invalid request (-32600).
69    pub fn invalid_request(detail: impl Into<String>) -> Self {
70        Self {
71            code: -32600,
72            message: "Invalid Request".to_string(),
73            data: Some(Value::String(detail.into())),
74        }
75    }
76
77    /// Standard error: Method not found (-32601).
78    pub fn method_not_found(method: impl Into<String>) -> Self {
79        Self {
80            code: -32601,
81            message: "Method not found".to_string(),
82            data: Some(Value::String(method.into())),
83        }
84    }
85
86    /// Standard error: Invalid params (-32602).
87    pub fn invalid_params(detail: impl Into<String>) -> Self {
88        Self {
89            code: -32602,
90            message: "Invalid params".to_string(),
91            data: Some(Value::String(detail.into())),
92        }
93    }
94
95    /// Standard error: Internal error (-32603).
96    pub fn internal_error(detail: impl Into<String>) -> Self {
97        Self {
98            code: -32603,
99            message: "Internal error".to_string(),
100            data: Some(Value::String(detail.into())),
101        }
102    }
103}
104
105/// JSON-RPC 2.0 response message.
106#[derive(Debug, Clone, Serialize, Deserialize)]
107pub struct JsonRpcResponse {
108    /// Must be "2.0".
109    pub jsonrpc: String,
110    /// Must match the request ID.
111    pub id: Value,
112    /// The result (present on success).
113    #[serde(skip_serializing_if = "Option::is_none")]
114    pub result: Option<Value>,
115    /// The error (present on failure).
116    #[serde(skip_serializing_if = "Option::is_none")]
117    pub error: Option<JsonRpcError>,
118}
119
120impl JsonRpcResponse {
121    /// Create a success response.
122    pub fn success(id: Value, result: Value) -> Self {
123        Self {
124            jsonrpc: "2.0".to_string(),
125            id,
126            result: Some(result),
127            error: None,
128        }
129    }
130
131    /// Create an error response.
132    pub fn error(id: Value, error: JsonRpcError) -> Self {
133        Self {
134            jsonrpc: "2.0".to_string(),
135            id,
136            result: None,
137            error: Some(error),
138        }
139    }
140}
141
142/// Parse a raw JSON string into a JSON-RPC request.
143///
144/// Returns an error response if parsing fails.
145#[allow(clippy::result_large_err)]
146pub fn parse_request(raw: &str) -> Result<JsonRpcRequest, JsonRpcResponse> {
147    let value: Value = serde_json::from_str(raw).map_err(|e| {
148        JsonRpcResponse::error(Value::Null, JsonRpcError::parse_error(e.to_string()))
149    })?;
150
151    let request: JsonRpcRequest = serde_json::from_value(value).map_err(|e| {
152        JsonRpcResponse::error(Value::Null, JsonRpcError::invalid_request(e.to_string()))
153    })?;
154
155    if request.jsonrpc != "2.0" {
156        return Err(JsonRpcResponse::error(
157            request.id.unwrap_or(Value::Null),
158            JsonRpcError::invalid_request("jsonrpc must be \"2.0\""),
159        ));
160    }
161
162    Ok(request)
163}