1use serde::{Deserialize, Serialize};
6use serde_json::Value;
7
8pub const MCP_VERSION: &str = "2024-11-05";
10
11#[derive(Debug, Clone, Serialize, Deserialize)]
13#[serde(tag = "type", rename_all = "snake_case")]
14pub enum McpMessage {
15 Request(McpRequest),
17 Response(McpResponse),
19 Notification(McpNotification),
21 Error(McpError),
23}
24
25#[derive(Debug, Clone, Serialize, Deserialize)]
27pub struct McpRequest {
28 pub id: RequestId,
30 pub method: String,
32 #[serde(default)]
34 pub params: Option<Value>,
35}
36
37#[derive(Debug, Clone, Serialize, Deserialize)]
39pub struct McpResponse {
40 pub id: RequestId,
42 #[serde(skip_serializing_if = "Option::is_none")]
44 pub result: Option<Value>,
45 #[serde(skip_serializing_if = "Option::is_none")]
47 pub error: Option<McpErrorData>,
48}
49
50#[derive(Debug, Clone, Serialize, Deserialize)]
52pub struct McpNotification {
53 pub method: String,
55 #[serde(default)]
57 pub params: Option<Value>,
58}
59
60#[derive(Debug, Clone, Serialize, Deserialize)]
62pub struct McpError {
63 #[serde(skip_serializing_if = "Option::is_none")]
65 pub id: Option<RequestId>,
66 pub error: McpErrorData,
68}
69
70#[derive(Debug, Clone, Serialize, Deserialize)]
72pub struct McpErrorData {
73 pub code: i32,
75 pub message: String,
77 #[serde(skip_serializing_if = "Option::is_none")]
79 pub data: Option<Value>,
80}
81
82#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
84#[serde(untagged)]
85pub enum RequestId {
86 String(String),
87 Number(i64),
88}
89
90impl Default for RequestId {
91 fn default() -> Self {
92 RequestId::Number(0)
93 }
94}
95
96pub const METHOD_INITIALIZE: &str = "initialize";
100pub const METHOD_INITIALIZED: &str = "notifications/initialized";
102pub const METHOD_SHUTDOWN: &str = "shutdown";
104pub const METHOD_LIST_TOOLS: &str = "tools/list";
106pub const METHOD_CALL_TOOL: &str = "tools/call";
108pub const METHOD_LIST_RESOURCES: &str = "resources/list";
110pub const METHOD_READ_RESOURCE: &str = "resources/read";
112pub const METHOD_LIST_PROMPTS: &str = "prompts/list";
114pub const METHOD_GET_PROMPT: &str = "prompts/get";
116
117#[derive(Debug, Clone, Serialize, Deserialize)]
121pub struct ToolDefinition {
122 pub name: String,
124 #[serde(default)]
126 pub description: Option<String>,
127 #[serde(default)]
129 pub input_schema: Option<Value>,
130}
131
132#[derive(Debug, Clone, Serialize, Deserialize)]
134pub struct ToolResult {
135 #[serde(default)]
137 pub is_error: bool,
138 pub content: Vec<ContentBlock>,
140}
141
142#[derive(Debug, Clone, Serialize, Deserialize)]
144#[serde(tag = "type", rename_all = "snake_case")]
145pub enum ContentBlock {
146 Text { text: String },
148 Image { data: String, mime_type: String },
150 Resource { resource: ResourceContent },
152}
153
154#[derive(Debug, Clone, Serialize, Deserialize)]
156pub struct ResourceContent {
157 pub uri: String,
159 #[serde(default)]
161 pub mime_type: Option<String>,
162 #[serde(skip_serializing_if = "Option::is_none")]
164 pub text: Option<String>,
165 #[serde(skip_serializing_if = "Option::is_none")]
167 pub blob: Option<String>,
168}
169
170#[derive(Debug, Clone, Serialize, Deserialize)]
172pub struct InitializeParams {
173 #[serde(default = "default_protocol_version")]
175 pub protocol_version: String,
176 pub capabilities: ClientCapabilities,
178 #[serde(default)]
180 pub client_info: Option<Implementation>,
181}
182
183fn default_protocol_version() -> String {
184 MCP_VERSION.to_string()
185}
186
187#[derive(Debug, Clone, Default, Serialize, Deserialize)]
189pub struct ClientCapabilities {
190 #[serde(default)]
192 pub experimental: Option<Value>,
193 #[serde(default)]
195 pub roots: Option<RootsCapability>,
196 #[serde(default)]
198 pub sampling: Option<Value>,
199}
200
201#[derive(Debug, Clone, Default, Serialize, Deserialize)]
203pub struct RootsCapability {
204 #[serde(default)]
206 pub list_changed: Option<bool>,
207}
208
209#[derive(Debug, Clone, Serialize, Deserialize)]
211pub struct Implementation {
212 pub name: String,
214 pub version: String,
216}
217
218#[derive(Debug, Clone, Serialize, Deserialize)]
220pub struct InitializeResult {
221 pub protocol_version: String,
223 pub capabilities: ServerCapabilities,
225 pub server_info: Implementation,
227 #[serde(skip_serializing_if = "Option::is_none")]
229 pub instructions: Option<String>,
230}
231
232#[derive(Debug, Clone, Default, Serialize, Deserialize)]
234pub struct ServerCapabilities {
235 #[serde(default)]
237 pub experimental: Option<Value>,
238 #[serde(default)]
240 pub logging: Option<Value>,
241 #[serde(default)]
243 pub prompts: Option<PromptsCapability>,
244 #[serde(default)]
246 pub resources: Option<ResourcesCapability>,
247 #[serde(default)]
249 pub tools: Option<ToolsCapability>,
250}
251
252#[derive(Debug, Clone, Default, Serialize, Deserialize)]
254pub struct PromptsCapability {
255 #[serde(default)]
257 pub list_changed: Option<bool>,
258}
259
260#[derive(Debug, Clone, Default, Serialize, Deserialize)]
262pub struct ResourcesCapability {
263 #[serde(default)]
265 pub subscribe: Option<bool>,
266 #[serde(default)]
268 pub list_changed: Option<bool>,
269}
270
271#[derive(Debug, Clone, Default, Serialize, Deserialize)]
273pub struct ToolsCapability {
274 #[serde(default)]
276 pub list_changed: Option<bool>,
277}
278
279pub mod error_codes {
283 pub const PARSE_ERROR: i32 = -32700;
284 pub const INVALID_REQUEST: i32 = -32600;
285 pub const METHOD_NOT_FOUND: i32 = -32601;
286 pub const INVALID_PARAMS: i32 = -32602;
287 pub const INTERNAL_ERROR: i32 = -32603;
288
289 pub const SERVER_NOT_INITIALIZED: i32 = -32002;
291 pub const UNKNOWN_ERROR: i32 = -32001;
292}
293
294#[cfg(test)]
295mod tests {
296 use super::*;
297
298 #[test]
299 fn test_serialize_request() {
300 let request = McpRequest {
301 id: RequestId::Number(1),
302 method: "tools/list".to_string(),
303 params: None,
304 };
305
306 let json = serde_json::to_string(&request).unwrap();
307 assert!(json.contains("tools/list"));
308 }
309
310 #[test]
311 fn test_deserialize_response() {
312 let json = r#"{"id":1,"result":{"tools":[]}}"#;
313 let response: McpResponse = serde_json::from_str(json).unwrap();
314 assert_eq!(response.id, RequestId::Number(1));
315 }
316
317 #[test]
318 fn test_tool_definition() {
319 let tool = ToolDefinition {
320 name: "test_tool".to_string(),
321 description: Some("A test tool".to_string()),
322 input_schema: None,
323 };
324
325 let json = serde_json::to_string(&tool).unwrap();
326 assert!(json.contains("test_tool"));
327 }
328
329 #[test]
330 fn test_content_block_text() {
331 let block = ContentBlock::Text {
332 text: "Hello".to_string(),
333 };
334
335 let json = serde_json::to_string(&block).unwrap();
336 assert!(json.contains("text"));
337 assert!(json.contains("Hello"));
338 }
339}