1use serde::{Deserialize, Serialize};
7use serde_json::Value;
8use std::fmt;
9
10pub const MCP_PROTOCOL_VERSION: &str = "2024-11-05";
12
13#[derive(Debug, Clone, Serialize, Deserialize)]
15pub struct JsonRpcRequest {
16 pub jsonrpc: String,
17 pub id: JsonRpcId,
18 pub method: String,
19 #[serde(skip_serializing_if = "Option::is_none")]
20 pub params: Option<Value>,
21}
22
23impl JsonRpcRequest {
24 pub fn new(id: JsonRpcId, method: impl Into<String>, params: Option<Value>) -> Self {
25 Self {
26 jsonrpc: "2.0".to_string(),
27 id,
28 method: method.into(),
29 params,
30 }
31 }
32}
33
34#[derive(Debug, Clone, Serialize, Deserialize)]
36pub struct JsonRpcResponse {
37 pub jsonrpc: String,
38 pub id: JsonRpcId,
39 #[serde(flatten)]
40 pub payload: JsonRpcPayload,
41}
42
43impl JsonRpcResponse {
44 pub fn success(id: impl Into<JsonRpcId>, result: Value) -> Self {
46 Self {
47 jsonrpc: "2.0".to_string(),
48 id: id.into(),
49 payload: JsonRpcPayload::Success { result },
50 }
51 }
52
53 pub fn error(
55 id: impl Into<JsonRpcId>,
56 code: i32,
57 message: impl Into<String>,
58 data: Option<Value>,
59 ) -> Self {
60 Self {
61 jsonrpc: "2.0".to_string(),
62 id: id.into(),
63 payload: JsonRpcPayload::Error {
64 error: JsonRpcError {
65 code,
66 message: message.into(),
67 data,
68 },
69 },
70 }
71 }
72
73 pub fn result(&self) -> Option<&Value> {
75 match &self.payload {
76 JsonRpcPayload::Success { result } => Some(result),
77 JsonRpcPayload::Error { .. } => None,
78 }
79 }
80
81 pub fn error_info(&self) -> Option<&JsonRpcError> {
83 match &self.payload {
84 JsonRpcPayload::Success { .. } => None,
85 JsonRpcPayload::Error { error } => Some(error),
86 }
87 }
88}
89
90#[derive(Debug, Clone, Serialize, Deserialize)]
91#[serde(untagged)]
92pub enum JsonRpcPayload {
93 Success { result: Value },
94 Error { error: JsonRpcError },
95}
96
97#[derive(Debug, Clone, Serialize, Deserialize)]
99pub struct JsonRpcError {
100 pub code: i32,
101 pub message: String,
102 #[serde(skip_serializing_if = "Option::is_none")]
103 pub data: Option<Value>,
104}
105
106impl fmt::Display for JsonRpcError {
107 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
108 write!(f, "JSON-RPC Error {}: {}", self.code, self.message)
109 }
110}
111
112impl std::error::Error for JsonRpcError {}
113
114pub mod error_codes {
116 pub const PARSE_ERROR: i32 = -32700;
118 pub const INVALID_REQUEST: i32 = -32600;
120 pub const METHOD_NOT_FOUND: i32 = -32601;
122 pub const INVALID_PARAMS: i32 = -32602;
124 pub const INTERNAL_ERROR: i32 = -32603;
126}
127
128#[derive(Debug, Clone, Serialize, Deserialize)]
134pub struct McpServerInfo {
135 pub name: String,
136 pub version: String,
137}
138
139#[derive(Debug, Clone, Serialize, Deserialize, Default)]
141pub struct McpCapabilities {
142 #[serde(skip_serializing_if = "Option::is_none")]
143 pub tools: Option<Value>,
144 #[serde(skip_serializing_if = "Option::is_none")]
145 pub resources: Option<Value>,
146 #[serde(skip_serializing_if = "Option::is_none")]
147 pub prompts: Option<Value>,
148 #[serde(skip_serializing_if = "Option::is_none")]
149 pub logging: Option<Value>,
150}
151
152#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
158#[serde(untagged)]
159pub enum JsonRpcId {
160 String(String),
161 Number(i64),
162 Null,
163}
164
165impl From<i64> for JsonRpcId {
166 fn from(n: i64) -> Self {
167 JsonRpcId::Number(n)
168 }
169}
170
171impl From<String> for JsonRpcId {
172 fn from(s: String) -> Self {
173 JsonRpcId::String(s)
174 }
175}
176
177impl From<&str> for JsonRpcId {
178 fn from(s: &str) -> Self {
179 JsonRpcId::String(s.to_string())
180 }
181}
182
183impl From<Value> for JsonRpcId {
184 fn from(v: Value) -> Self {
185 match v {
186 Value::String(s) => JsonRpcId::String(s),
187 Value::Number(n) => JsonRpcId::Number(n.as_i64().unwrap_or(0)),
188 Value::Null => JsonRpcId::Null,
189 _ => JsonRpcId::Null,
190 }
191 }
192}
193
194impl From<JsonRpcId> for Value {
195 fn from(id: JsonRpcId) -> Self {
196 match id {
197 JsonRpcId::String(s) => Value::String(s),
198 JsonRpcId::Number(n) => Value::Number(n.into()),
199 JsonRpcId::Null => Value::Null,
200 }
201 }
202}
203
204impl fmt::Display for JsonRpcId {
205 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
206 match self {
207 JsonRpcId::String(s) => write!(f, "{}", s),
208 JsonRpcId::Number(n) => write!(f, "{}", n),
209 JsonRpcId::Null => write!(f, "null"),
210 }
211 }
212}
213
214#[derive(Debug, Clone, Serialize, Deserialize)]
220pub struct McpToolDef {
221 pub name: String,
222 #[serde(skip_serializing_if = "Option::is_none")]
223 pub description: Option<String>,
224 #[serde(skip_serializing_if = "Option::is_none")]
225 pub group: Option<String>,
226 #[serde(default = "default_input_schema")]
227 pub input_schema: Value,
228}
229
230fn default_input_schema() -> Value {
231 serde_json::json!({"type": "object"})
232}
233
234impl McpToolDef {
235 pub fn new(name: impl Into<String>) -> Self {
237 Self {
238 name: name.into(),
239 description: None,
240 group: None,
241 input_schema: default_input_schema(),
242 }
243 }
244
245 pub fn with_group(mut self, group: impl Into<String>) -> Self {
247 self.group = Some(group.into());
248 self
249 }
250
251 pub fn with_description(mut self, desc: impl Into<String>) -> Self {
253 self.description = Some(desc.into());
254 self
255 }
256
257 pub fn with_schema(mut self, schema: Value) -> Self {
259 self.input_schema = schema;
260 self
261 }
262}
263
264#[derive(Debug, Clone, Serialize, Deserialize, Default)]
266pub struct ToolInputSchema {
267 #[serde(rename = "type")]
268 pub schema_type: String,
269 #[serde(skip_serializing_if = "Option::is_none")]
270 pub properties: Option<Value>,
271 #[serde(skip_serializing_if = "Option::is_none")]
272 pub required: Option<Vec<String>>,
273}
274
275#[derive(Debug, Clone, Serialize, Deserialize, Default)]
277pub struct ListToolsParams {
278 #[serde(skip_serializing_if = "Option::is_none")]
279 pub cursor: Option<String>,
280}
281
282#[derive(Debug, Clone, Serialize, Deserialize)]
284pub struct ListToolsResult {
285 pub tools: Vec<McpToolDef>,
286 #[serde(skip_serializing_if = "Option::is_none")]
287 pub next_cursor: Option<String>,
288}
289
290#[derive(Debug, Clone, Serialize, Deserialize)]
292pub struct CallToolParams {
293 pub name: String,
294 #[serde(skip_serializing_if = "Option::is_none")]
295 pub arguments: Option<Value>,
296}
297
298#[derive(Debug, Clone, Serialize, Deserialize)]
300pub struct CallToolResult {
301 #[serde(default)]
302 pub content: Vec<ToolContent>,
303 #[serde(skip_serializing_if = "Option::is_none")]
304 pub is_error: Option<bool>,
305}
306
307#[derive(Debug, Clone, Serialize, Deserialize)]
309#[serde(tag = "type")]
310pub enum ToolContent {
311 #[serde(rename = "text")]
312 Text { text: String },
313 #[serde(rename = "image")]
314 Image { data: String, mime_type: String },
315 #[serde(rename = "resource")]
316 Resource {
317 uri: String,
318 mime_type: Option<String>,
319 },
320}
321
322impl ToolContent {
323 pub fn text(text: impl Into<String>) -> Self {
325 Self::Text { text: text.into() }
326 }
327
328 pub fn image(data: impl Into<String>, mime_type: impl Into<String>) -> Self {
330 Self::Image {
331 data: data.into(),
332 mime_type: mime_type.into(),
333 }
334 }
335
336 pub fn resource(uri: impl Into<String>, mime_type: Option<String>) -> Self {
338 Self::Resource {
339 uri: uri.into(),
340 mime_type,
341 }
342 }
343
344 pub fn as_text(&self) -> Option<&str> {
346 match self {
347 Self::Text { text } => Some(text),
348 _ => None,
349 }
350 }
351
352 pub fn is_text(&self) -> bool {
354 matches!(self, Self::Text { .. })
355 }
356}
357
358#[derive(Debug, Clone, Serialize, Deserialize)]
363pub struct ToolDefinition {
364 pub name: String,
365 pub description: String,
366 pub parameters: Value,
367}
368
369impl From<McpToolDef> for ToolDefinition {
370 fn from(tool: McpToolDef) -> Self {
371 Self {
372 name: tool.name,
373 description: tool.description.unwrap_or_default(),
374 parameters: serde_json::to_value(&tool.input_schema).unwrap_or_default(),
375 }
376 }
377}
378
379#[cfg(test)]
380mod tests {
381 use super::*;
382
383 #[test]
384 fn test_jsonrpc_request_serialization() {
385 let req = JsonRpcRequest::new(JsonRpcId::Number(1), "tools/list", None);
386
387 let json = serde_json::to_string(&req).unwrap();
388 assert!(json.contains(r#""jsonrpc":"2.0""#));
389 assert!(json.contains(r#""id":1"#));
390 assert!(json.contains(r#""method":"tools/list""#));
391 }
392
393 #[test]
394 fn test_jsonrpc_response_success() {
395 let json = r#"{"jsonrpc":"2.0","id":1,"result":{"tools":[]}}"#;
396 let resp: JsonRpcResponse = serde_json::from_str(json).unwrap();
397
398 assert!(matches!(resp.payload, JsonRpcPayload::Success { .. }));
399 }
400
401 #[test]
402 fn test_jsonrpc_response_error() {
403 let json =
404 r#"{"jsonrpc":"2.0","id":1,"error":{"code":-32601,"message":"Method not found"}}"#;
405 let resp: JsonRpcResponse = serde_json::from_str(json).unwrap();
406
407 assert!(matches!(resp.payload, JsonRpcPayload::Error { .. }));
408 }
409
410 #[test]
411 fn test_tool_content_text() {
412 let content = ToolContent::text("Hello, world!");
413 assert_eq!(content.as_text(), Some("Hello, world!"));
414
415 let json = serde_json::to_string(&content).unwrap();
416 assert!(json.contains(r#""type":"text""#));
417 assert!(json.contains(r#""text":"Hello, world!""#));
418 }
419
420 #[test]
421 fn test_jsonrpc_id_display() {
422 assert_eq!(JsonRpcId::Number(42).to_string(), "42");
423 assert_eq!(JsonRpcId::String("test".to_string()).to_string(), "test");
424 assert_eq!(JsonRpcId::Null.to_string(), "null");
425 }
426
427 #[test]
428 fn test_mcp_tool_deserialization() {
429 let json = r#"{
430 "name": "test_tool",
431 "description": "A test tool",
432 "input_schema": {
433 "type": "object",
434 "properties": {
435 "name": {"type": "string"}
436 },
437 "required": ["name"]
438 }
439 }"#;
440
441 let tool: McpToolDef = serde_json::from_str(json).unwrap();
442 assert_eq!(tool.name, "test_tool");
443 assert_eq!(tool.description, Some("A test tool".to_string()));
444 assert_eq!(tool.input_schema["type"], "object");
445 }
446}