1use serde::{Deserialize, Serialize};
4use serde_json::Value;
5
6pub const JSONRPC_VERSION: &str = "2.0";
8
9#[derive(Clone, Debug, Serialize, Deserialize)]
11pub struct JsonRpcRequest {
12 pub jsonrpc: String,
14 pub method: String,
16 #[serde(skip_serializing_if = "Option::is_none")]
18 pub params: Option<Value>,
19 pub id: RequestId,
21}
22
23impl JsonRpcRequest {
24 #[must_use]
26 pub fn new(method: impl Into<String>, params: Option<Value>, id: u64) -> Self {
27 Self {
28 jsonrpc: JSONRPC_VERSION.to_string(),
29 method: method.into(),
30 params,
31 id: RequestId::Number(id),
32 }
33 }
34}
35
36#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
38#[serde(untagged)]
39pub enum RequestId {
40 Number(u64),
42 String(String),
44}
45
46#[derive(Clone, Debug, Serialize, Deserialize)]
48pub struct JsonRpcResponse {
49 pub jsonrpc: String,
51 #[serde(skip_serializing_if = "Option::is_none")]
53 pub result: Option<Value>,
54 #[serde(skip_serializing_if = "Option::is_none")]
56 pub error: Option<JsonRpcError>,
57 pub id: RequestId,
59}
60
61impl JsonRpcResponse {
62 #[must_use]
64 pub const fn is_error(&self) -> bool {
65 self.error.is_some()
66 }
67
68 #[must_use]
70 pub const fn result(&self) -> Option<&Value> {
71 self.result.as_ref()
72 }
73}
74
75#[derive(Clone, Debug, Serialize, Deserialize)]
77pub struct JsonRpcError {
78 pub code: i32,
80 pub message: String,
82 #[serde(skip_serializing_if = "Option::is_none")]
84 pub data: Option<Value>,
85}
86
87pub mod error_codes {
89 pub const PARSE_ERROR: i32 = -32700;
91 pub const INVALID_REQUEST: i32 = -32600;
93 pub const METHOD_NOT_FOUND: i32 = -32601;
95 pub const INVALID_PARAMS: i32 = -32602;
97 pub const INTERNAL_ERROR: i32 = -32603;
99}
100
101#[derive(Clone, Debug, Serialize, Deserialize)]
103pub struct McpToolDefinition {
104 pub name: String,
106 #[serde(default)]
108 pub description: Option<String>,
109 #[serde(rename = "inputSchema")]
111 pub input_schema: Value,
112}
113
114#[derive(Clone, Debug, Serialize, Deserialize)]
116pub struct McpToolCallResult {
117 pub content: Vec<McpContent>,
119 #[serde(default, rename = "isError")]
121 pub is_error: bool,
122}
123
124#[derive(Clone, Debug, Serialize, Deserialize)]
126#[serde(tag = "type")]
127pub enum McpContent {
128 #[serde(rename = "text")]
130 Text {
131 text: String,
133 },
134 #[serde(rename = "image")]
136 Image {
137 data: String,
139 #[serde(rename = "mimeType")]
141 mime_type: String,
142 },
143 #[serde(rename = "resource")]
145 Resource {
146 uri: String,
148 #[serde(rename = "mimeType")]
150 mime_type: Option<String>,
151 text: Option<String>,
153 },
154}
155
156#[derive(Clone, Debug, Default, Serialize, Deserialize)]
158pub struct McpServerCapabilities {
159 #[serde(default)]
161 pub tools: Option<McpToolsCapability>,
162 #[serde(default)]
164 pub resources: Option<McpResourcesCapability>,
165 #[serde(default)]
167 pub prompts: Option<McpPromptsCapability>,
168}
169
170#[derive(Clone, Debug, Default, Serialize, Deserialize)]
172pub struct McpToolsCapability {
173 #[serde(default, rename = "listChanged")]
175 pub list_changed: bool,
176}
177
178#[derive(Clone, Debug, Default, Serialize, Deserialize)]
180pub struct McpResourcesCapability {
181 #[serde(default)]
183 pub subscribe: bool,
184 #[serde(default, rename = "listChanged")]
186 pub list_changed: bool,
187}
188
189#[derive(Clone, Debug, Default, Serialize, Deserialize)]
191pub struct McpPromptsCapability {
192 #[serde(default, rename = "listChanged")]
194 pub list_changed: bool,
195}
196
197#[derive(Clone, Debug, Serialize, Deserialize)]
199pub struct InitializeParams {
200 #[serde(rename = "protocolVersion")]
202 pub protocol_version: String,
203 pub capabilities: ClientCapabilities,
205 #[serde(rename = "clientInfo")]
207 pub client_info: ClientInfo,
208}
209
210#[derive(Clone, Debug, Default, Serialize, Deserialize)]
212pub struct ClientCapabilities {
213 #[serde(default)]
215 pub roots: Option<RootsCapability>,
216 #[serde(default)]
218 pub sampling: Option<SamplingCapability>,
219}
220
221#[derive(Clone, Debug, Default, Serialize, Deserialize)]
223pub struct RootsCapability {
224 #[serde(default, rename = "listChanged")]
226 pub list_changed: bool,
227}
228
229#[derive(Clone, Debug, Default, Serialize, Deserialize)]
231pub struct SamplingCapability {}
232
233#[derive(Clone, Debug, Serialize, Deserialize)]
235pub struct ClientInfo {
236 pub name: String,
238 pub version: String,
240}
241
242#[derive(Clone, Debug, Serialize, Deserialize)]
244pub struct InitializeResult {
245 #[serde(rename = "protocolVersion")]
247 pub protocol_version: String,
248 pub capabilities: McpServerCapabilities,
250 #[serde(rename = "serverInfo")]
252 pub server_info: ServerInfo,
253}
254
255#[derive(Clone, Debug, Serialize, Deserialize)]
257pub struct ServerInfo {
258 pub name: String,
260 #[serde(default)]
262 pub version: Option<String>,
263}
264
265#[derive(Clone, Debug, Serialize, Deserialize)]
267pub struct ToolsListResult {
268 pub tools: Vec<McpToolDefinition>,
270}
271
272#[derive(Clone, Debug, Serialize, Deserialize)]
274pub struct ToolCallParams {
275 pub name: String,
277 #[serde(default)]
279 pub arguments: Option<Value>,
280}
281
282#[cfg(test)]
283mod tests {
284 use super::*;
285
286 #[test]
287 fn test_json_rpc_request_serialization() {
288 let request =
289 JsonRpcRequest::new("test_method", Some(serde_json::json!({"key": "value"})), 1);
290
291 let json = serde_json::to_string(&request).expect("serialize");
292 assert!(json.contains("test_method"));
293 assert!(json.contains("2.0"));
294 }
295
296 #[test]
297 fn test_json_rpc_response_success() {
298 let response = JsonRpcResponse {
299 jsonrpc: JSONRPC_VERSION.to_string(),
300 result: Some(serde_json::json!({"success": true})),
301 error: None,
302 id: RequestId::Number(1),
303 };
304
305 assert!(!response.is_error());
306 assert!(response.result().is_some());
307 }
308
309 #[test]
310 fn test_json_rpc_response_error() {
311 let response = JsonRpcResponse {
312 jsonrpc: JSONRPC_VERSION.to_string(),
313 result: None,
314 error: Some(JsonRpcError {
315 code: error_codes::METHOD_NOT_FOUND,
316 message: "Method not found".to_string(),
317 data: None,
318 }),
319 id: RequestId::Number(1),
320 };
321
322 assert!(response.is_error());
323 assert!(response.result().is_none());
324 }
325
326 #[test]
327 fn test_mcp_tool_definition_deserialization() {
328 let json = r#"{
329 "name": "test_tool",
330 "description": "A test tool",
331 "inputSchema": {
332 "type": "object",
333 "properties": {}
334 }
335 }"#;
336
337 let tool: McpToolDefinition = serde_json::from_str(json).expect("deserialize");
338 assert_eq!(tool.name, "test_tool");
339 assert_eq!(tool.description.as_deref(), Some("A test tool"));
340 }
341
342 #[test]
343 fn test_mcp_content_text() {
344 let content = McpContent::Text {
345 text: "Hello".to_string(),
346 };
347
348 let json = serde_json::to_string(&content).expect("serialize");
349 assert!(json.contains("text"));
350 assert!(json.contains("Hello"));
351 }
352
353 #[test]
354 fn test_request_id_variants() {
355 let num_id = RequestId::Number(42);
356 let str_id = RequestId::String("req-1".to_string());
357
358 let json_num = serde_json::to_string(&num_id).expect("serialize");
359 let json_str = serde_json::to_string(&str_id).expect("serialize");
360
361 assert_eq!(json_num, "42");
362 assert_eq!(json_str, "\"req-1\"");
363 }
364}