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