1use serde::{Deserialize, Serialize};
4use serde_json::Value;
5
6#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
8#[serde(untagged)]
9pub enum RequestId {
10 String(String),
12 Number(i64),
14}
15
16impl From<i64> for RequestId {
17 fn from(n: i64) -> Self {
18 Self::Number(n)
19 }
20}
21
22impl From<String> for RequestId {
23 fn from(s: String) -> Self {
24 Self::String(s)
25 }
26}
27
28#[derive(Clone, Debug, Serialize, Deserialize)]
30pub struct JsonRpcError {
31 pub code: i32,
33 pub message: String,
35 #[serde(skip_serializing_if = "Option::is_none")]
37 pub data: Option<Value>,
38}
39
40impl JsonRpcError {
41 pub fn parse_error(msg: impl Into<String>) -> Self {
43 Self {
44 code: -32700,
45 message: msg.into(),
46 data: None,
47 }
48 }
49
50 pub fn invalid_request(msg: impl Into<String>) -> Self {
52 Self {
53 code: -32600,
54 message: msg.into(),
55 data: None,
56 }
57 }
58
59 pub fn method_not_found(method: &str) -> Self {
61 Self {
62 code: -32601,
63 message: format!("Method not found: {method}"),
64 data: None,
65 }
66 }
67
68 pub fn invalid_params(msg: impl Into<String>) -> Self {
70 Self {
71 code: -32602,
72 message: msg.into(),
73 data: None,
74 }
75 }
76
77 pub fn internal_error(msg: impl Into<String>) -> Self {
79 Self {
80 code: -32603,
81 message: msg.into(),
82 data: None,
83 }
84 }
85}
86
87#[derive(Clone, Debug, Serialize, Deserialize)]
89pub struct JsonRpcRequest {
90 pub jsonrpc: String,
92 pub id: RequestId,
94 pub method: String,
96 #[serde(default, skip_serializing_if = "Value::is_null")]
98 pub params: Value,
99}
100
101impl JsonRpcRequest {
102 pub fn new(id: impl Into<RequestId>, method: impl Into<String>, params: Value) -> Self {
104 Self {
105 jsonrpc: "2.0".into(),
106 id: id.into(),
107 method: method.into(),
108 params,
109 }
110 }
111}
112
113#[derive(Clone, Debug, Serialize, Deserialize)]
115pub struct JsonRpcResponse {
116 pub jsonrpc: String,
118 pub id: RequestId,
120 #[serde(skip_serializing_if = "Option::is_none")]
122 pub result: Option<Value>,
123 #[serde(skip_serializing_if = "Option::is_none")]
125 pub error: Option<JsonRpcError>,
126}
127
128impl JsonRpcResponse {
129 pub fn success(id: RequestId, result: Value) -> Self {
131 Self {
132 jsonrpc: "2.0".into(),
133 id,
134 result: Some(result),
135 error: None,
136 }
137 }
138
139 pub fn error(id: RequestId, error: JsonRpcError) -> Self {
141 Self {
142 jsonrpc: "2.0".into(),
143 id,
144 result: None,
145 error: Some(error),
146 }
147 }
148}
149
150#[derive(Clone, Debug, Serialize, Deserialize)]
152pub struct JsonRpcNotification {
153 pub jsonrpc: String,
155 pub method: String,
157 #[serde(default, skip_serializing_if = "Value::is_null")]
159 pub params: Value,
160}
161
162#[derive(Clone, Debug, Serialize, Deserialize)]
164pub struct ToolDefinition {
165 pub name: String,
167 #[serde(skip_serializing_if = "Option::is_none")]
169 pub description: Option<String>,
170 #[serde(rename = "inputSchema")]
172 pub input_schema: Value,
173}
174
175#[derive(Clone, Debug, Serialize, Deserialize)]
177pub struct ToolResult {
178 pub content: Vec<ContentBlock>,
180 #[serde(rename = "isError", skip_serializing_if = "Option::is_none")]
182 pub is_error: Option<bool>,
183}
184
185impl ToolResult {
186 pub fn text(s: impl Into<String>) -> Self {
188 Self {
189 content: vec![ContentBlock::Text { text: s.into() }],
190 is_error: Some(false),
191 }
192 }
193
194 pub fn error(msg: impl Into<String>) -> Self {
196 Self {
197 content: vec![ContentBlock::Text { text: msg.into() }],
198 is_error: Some(true),
199 }
200 }
201}
202
203#[derive(Clone, Debug, Serialize, Deserialize)]
205#[serde(tag = "type")]
206pub enum ContentBlock {
207 #[serde(rename = "text")]
209 Text {
210 text: String,
212 },
213 #[serde(rename = "image")]
215 Image {
216 data: String,
218 #[serde(rename = "mimeType")]
220 mime_type: String,
221 },
222 #[serde(rename = "resource")]
224 Resource {
225 uri: String,
227 #[serde(rename = "mimeType", skip_serializing_if = "Option::is_none")]
229 mime_type: Option<String>,
230 #[serde(skip_serializing_if = "Option::is_none")]
232 text: Option<String>,
233 },
234}
235
236#[derive(Debug, thiserror::Error)]
238pub enum McpError {
239 #[error("protocol error: {0}")]
241 Protocol(String),
242 #[error("tool execution failed: {0}")]
244 ToolFailure(String),
245 #[error("security policy violation: {0}")]
247 PolicyViolation(String),
248 #[error("transport error: {0}")]
250 Transport(String),
251 #[error("serialization error: {0}")]
253 Serialization(String),
254}
255
256impl From<serde_json::Error> for McpError {
257 fn from(e: serde_json::Error) -> Self {
258 Self::Serialization(e.to_string())
259 }
260}
261
262impl From<std::io::Error> for McpError {
263 fn from(e: std::io::Error) -> Self {
264 Self::Transport(e.to_string())
265 }
266}
267
268#[cfg(test)]
269mod tests {
270 use super::*;
271
272 #[test]
273 fn request_serialization() {
274 let req = JsonRpcRequest::new(1i64, "tools/call", serde_json::json!({"name": "echo"}));
275 let json = serde_json::to_string(&req).unwrap();
276 assert!(json.contains("\"jsonrpc\":\"2.0\""));
277 assert!(json.contains("\"method\":\"tools/call\""));
278 }
279
280 #[test]
281 fn response_success() {
282 let resp = JsonRpcResponse::success(RequestId::Number(1), serde_json::json!({"ok": true}));
283 assert!(resp.result.is_some());
284 assert!(resp.error.is_none());
285 }
286
287 #[test]
288 fn response_error() {
289 let resp = JsonRpcResponse::error(
290 RequestId::Number(1),
291 JsonRpcError::method_not_found("unknown"),
292 );
293 assert!(resp.result.is_none());
294 assert!(resp.error.is_some());
295 assert_eq!(resp.error.as_ref().unwrap().code, -32601);
296 }
297
298 #[test]
299 fn tool_result_text() {
300 let result = ToolResult::text("hello");
301 assert_eq!(result.content.len(), 1);
302 assert_eq!(result.is_error, Some(false));
303 }
304
305 #[test]
306 fn content_block_serialization() {
307 let block = ContentBlock::Text {
308 text: "hi".into(),
309 };
310 let json = serde_json::to_string(&block).unwrap();
311 assert!(json.contains("\"type\":\"text\""));
312 }
313}