mcp_protocol_sdk/protocol/
types.rs

1//! MCP protocol type definitions
2//!
3//! This module contains all the core types defined by the Model Context Protocol
4//! specification, including messages, capabilities, and content types.
5
6use serde::{Deserialize, Serialize};
7
8/// Information about an MCP server
9#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
10pub struct ServerInfo {
11    /// Name of the server
12    pub name: String,
13    /// Version of the server
14    pub version: String,
15}
16
17/// Information about an MCP client
18#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
19pub struct ClientInfo {
20    /// Name of the client
21    pub name: String,
22    /// Version of the client
23    pub version: String,
24}
25
26/// Capabilities advertised by an MCP server
27#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
28pub struct ServerCapabilities {
29    /// Prompt-related capabilities
30    #[serde(skip_serializing_if = "Option::is_none")]
31    pub prompts: Option<PromptsCapability>,
32    /// Resource-related capabilities
33    #[serde(skip_serializing_if = "Option::is_none")]
34    pub resources: Option<ResourcesCapability>,
35    /// Tool-related capabilities
36    #[serde(skip_serializing_if = "Option::is_none")]
37    pub tools: Option<ToolsCapability>,
38    /// Sampling-related capabilities
39    #[serde(skip_serializing_if = "Option::is_none")]
40    pub sampling: Option<SamplingCapability>,
41}
42
43/// Capabilities advertised by an MCP client
44#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
45pub struct ClientCapabilities {
46    /// Sampling-related capabilities
47    #[serde(skip_serializing_if = "Option::is_none")]
48    pub sampling: Option<SamplingCapability>,
49}
50
51/// Prompt-related server capabilities
52#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
53pub struct PromptsCapability {
54    /// Whether the server supports prompt list change notifications
55    #[serde(rename = "listChanged", skip_serializing_if = "Option::is_none")]
56    pub list_changed: Option<bool>,
57}
58
59/// Resource-related server capabilities
60#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
61pub struct ResourcesCapability {
62    /// Whether the server supports resource subscriptions
63    #[serde(skip_serializing_if = "Option::is_none")]
64    pub subscribe: Option<bool>,
65    /// Whether the server supports resource list change notifications
66    #[serde(rename = "listChanged", skip_serializing_if = "Option::is_none")]
67    pub list_changed: Option<bool>,
68}
69
70/// Tool-related server capabilities
71#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
72pub struct ToolsCapability {
73    /// Whether the server supports tool list change notifications
74    #[serde(rename = "listChanged", skip_serializing_if = "Option::is_none")]
75    pub list_changed: Option<bool>,
76}
77
78/// Sampling-related capabilities
79#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
80pub struct SamplingCapability {}
81
82/// Content that can be returned by tools, resources, or prompts
83#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
84#[serde(tag = "type")]
85pub enum Content {
86    /// Text content
87    #[serde(rename = "text")]
88    Text {
89        /// The text content
90        text: String,
91    },
92    /// Image content
93    #[serde(rename = "image")]
94    Image {
95        /// Base64-encoded image data
96        data: String,
97        /// MIME type of the image
98        #[serde(rename = "mimeType")]
99        mime_type: String,
100    },
101}
102
103/// Content of a resource
104#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
105pub struct ResourceContent {
106    /// URI of the resource
107    pub uri: String,
108    /// MIME type of the content
109    #[serde(rename = "mimeType", skip_serializing_if = "Option::is_none")]
110    pub mime_type: Option<String>,
111    /// Text content (for text resources)
112    #[serde(skip_serializing_if = "Option::is_none")]
113    pub text: Option<String>,
114    /// Binary content encoded as base64 (for binary resources)
115    #[serde(skip_serializing_if = "Option::is_none")]
116    pub blob: Option<String>,
117}
118
119/// Result of a tool execution
120#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
121pub struct ToolResult {
122    /// Content returned by the tool
123    pub content: Vec<Content>,
124    /// Whether this result represents an error
125    #[serde(rename = "isError", skip_serializing_if = "Option::is_none")]
126    pub is_error: Option<bool>,
127}
128
129/// Information about a tool
130#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
131pub struct ToolInfo {
132    /// Name of the tool
133    pub name: String,
134    /// Description of what the tool does
135    #[serde(skip_serializing_if = "Option::is_none")]
136    pub description: Option<String>,
137    /// JSON Schema describing the tool's input parameters
138    #[serde(rename = "inputSchema")]
139    pub input_schema: serde_json::Value,
140}
141
142/// Information about a resource
143#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
144pub struct ResourceInfo {
145    /// URI of the resource
146    pub uri: String,
147    /// Human-readable name of the resource
148    pub name: String,
149    /// Description of the resource
150    #[serde(skip_serializing_if = "Option::is_none")]
151    pub description: Option<String>,
152    /// MIME type of the resource
153    #[serde(rename = "mimeType", skip_serializing_if = "Option::is_none")]
154    pub mime_type: Option<String>,
155}
156
157/// Information about a prompt
158#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
159pub struct PromptInfo {
160    /// Name of the prompt
161    pub name: String,
162    /// Description of what the prompt does
163    #[serde(skip_serializing_if = "Option::is_none")]
164    pub description: Option<String>,
165    /// Arguments that the prompt accepts
166    #[serde(skip_serializing_if = "Option::is_none")]
167    pub arguments: Option<Vec<PromptArgument>>,
168}
169
170/// Argument for a prompt
171#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
172pub struct PromptArgument {
173    /// Name of the argument
174    pub name: String,
175    /// Description of the argument
176    #[serde(skip_serializing_if = "Option::is_none")]
177    pub description: Option<String>,
178    /// Whether this argument is required
179    pub required: bool,
180}
181
182/// Message in a prompt result
183#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
184pub struct PromptMessage {
185    /// Role of the message (e.g., "user", "assistant", "system")
186    pub role: String,
187    /// Content of the message
188    pub content: PromptContent,
189}
190
191/// Content of a prompt message
192#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
193#[serde(untagged)]
194pub enum PromptContent {
195    /// Text content
196    Text {
197        /// Type identifier
198        #[serde(rename = "type")]
199        content_type: String,
200        /// The text content
201        text: String,
202    },
203    /// Image content
204    Image {
205        /// Type identifier
206        #[serde(rename = "type")]
207        content_type: String,
208        /// Base64-encoded image data
209        data: String,
210        /// MIME type of the image
211        #[serde(rename = "mimeType")]
212        mime_type: String,
213    },
214}
215
216/// Result of prompt execution
217#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
218pub struct PromptResult {
219    /// Description of the prompt result
220    #[serde(skip_serializing_if = "Option::is_none")]
221    pub description: Option<String>,
222    /// Messages generated by the prompt
223    pub messages: Vec<PromptMessage>,
224}
225
226// JSON-RPC 2.0 message types
227
228/// JSON-RPC 2.0 request message
229#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
230pub struct JsonRpcRequest {
231    /// JSON-RPC version (always "2.0")
232    pub jsonrpc: String,
233    /// Request ID for correlation
234    pub id: serde_json::Value,
235    /// Method name being called
236    pub method: String,
237    /// Method parameters
238    #[serde(skip_serializing_if = "Option::is_none")]
239    pub params: Option<serde_json::Value>,
240}
241
242/// JSON-RPC 2.0 response message
243#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
244pub struct JsonRpcResponse {
245    /// JSON-RPC version (always "2.0")
246    pub jsonrpc: String,
247    /// Request ID for correlation
248    pub id: serde_json::Value,
249    /// Result of the method call (if successful)
250    #[serde(skip_serializing_if = "Option::is_none")]
251    pub result: Option<serde_json::Value>,
252    /// Error information (if unsuccessful)
253    #[serde(skip_serializing_if = "Option::is_none")]
254    pub error: Option<JsonRpcError>,
255}
256
257/// JSON-RPC 2.0 error information
258#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
259pub struct JsonRpcError {
260    /// Error code
261    pub code: i32,
262    /// Error message
263    pub message: String,
264    /// Additional error data
265    #[serde(skip_serializing_if = "Option::is_none")]
266    pub data: Option<serde_json::Value>,
267}
268
269/// JSON-RPC 2.0 notification message
270#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
271pub struct JsonRpcNotification {
272    /// JSON-RPC version (always "2.0")
273    pub jsonrpc: String,
274    /// Method name being called
275    pub method: String,
276    /// Method parameters
277    #[serde(skip_serializing_if = "Option::is_none")]
278    pub params: Option<serde_json::Value>,
279}
280
281// Standard JSON-RPC error codes
282/// Invalid JSON was received
283pub const PARSE_ERROR: i32 = -32700;
284/// The JSON sent is not a valid Request object
285pub const INVALID_REQUEST: i32 = -32600;
286/// The method does not exist / is not available
287pub const METHOD_NOT_FOUND: i32 = -32601;
288/// Invalid method parameter(s)
289pub const INVALID_PARAMS: i32 = -32602;
290/// Internal JSON-RPC error
291pub const INTERNAL_ERROR: i32 = -32603;
292
293// MCP-specific error codes
294/// Tool not found
295pub const TOOL_NOT_FOUND: i32 = -32000;
296/// Resource not found
297pub const RESOURCE_NOT_FOUND: i32 = -32001;
298/// Prompt not found
299pub const PROMPT_NOT_FOUND: i32 = -32002;
300
301impl JsonRpcRequest {
302    /// Create a new JSON-RPC request
303    pub fn new<T: Serialize>(
304        id: serde_json::Value,
305        method: String,
306        params: Option<T>,
307    ) -> Result<Self, serde_json::Error> {
308        let params = match params {
309            Some(p) => Some(serde_json::to_value(p)?),
310            None => None,
311        };
312
313        Ok(Self {
314            jsonrpc: "2.0".to_string(),
315            id,
316            method,
317            params,
318        })
319    }
320}
321
322impl JsonRpcResponse {
323    /// Create a successful JSON-RPC response
324    pub fn success<T: Serialize>(
325        id: serde_json::Value,
326        result: T,
327    ) -> Result<Self, serde_json::Error> {
328        Ok(Self {
329            jsonrpc: "2.0".to_string(),
330            id,
331            result: Some(serde_json::to_value(result)?),
332            error: None,
333        })
334    }
335
336    /// Create an error JSON-RPC response
337    pub fn error(
338        id: serde_json::Value,
339        code: i32,
340        message: String,
341        data: Option<serde_json::Value>,
342    ) -> Self {
343        Self {
344            jsonrpc: "2.0".to_string(),
345            id,
346            result: None,
347            error: Some(JsonRpcError {
348                code,
349                message,
350                data,
351            }),
352        }
353    }
354}
355
356impl JsonRpcNotification {
357    /// Create a new JSON-RPC notification
358    pub fn new<T: Serialize>(method: String, params: Option<T>) -> Result<Self, serde_json::Error> {
359        let params = match params {
360            Some(p) => Some(serde_json::to_value(p)?),
361            None => None,
362        };
363
364        Ok(Self {
365            jsonrpc: "2.0".to_string(),
366            method,
367            params,
368        })
369    }
370}
371
372// Helper functions for creating common content types
373
374impl Content {
375    /// Create text content
376    pub fn text<S: Into<String>>(text: S) -> Self {
377        Self::Text { text: text.into() }
378    }
379
380    /// Create image content
381    pub fn image<S: Into<String>>(data: S, mime_type: S) -> Self {
382        Self::Image {
383            data: data.into(),
384            mime_type: mime_type.into(),
385        }
386    }
387}
388
389impl PromptContent {
390    /// Create text prompt content
391    pub fn text<S: Into<String>>(text: S) -> Self {
392        Self::Text {
393            content_type: "text".to_string(),
394            text: text.into(),
395        }
396    }
397
398    /// Create image prompt content
399    pub fn image<S: Into<String>>(data: S, mime_type: S) -> Self {
400        Self::Image {
401            content_type: "image".to_string(),
402            data: data.into(),
403            mime_type: mime_type.into(),
404        }
405    }
406}
407
408#[cfg(test)]
409mod tests {
410    use super::*;
411    use serde_json::json;
412
413    #[test]
414    fn test_content_serialization() {
415        let text_content = Content::text("Hello, world!");
416        let json = serde_json::to_value(&text_content).unwrap();
417        assert_eq!(
418            json,
419            json!({
420                "type": "text",
421                "text": "Hello, world!"
422            })
423        );
424
425        let image_content = Content::image("data", "image/png");
426        let json = serde_json::to_value(&image_content).unwrap();
427        assert_eq!(
428            json,
429            json!({
430                "type": "image",
431                "data": "data",
432                "mimeType": "image/png"
433            })
434        );
435    }
436
437    #[test]
438    fn test_jsonrpc_request() {
439        let request = JsonRpcRequest::new(
440            json!(1),
441            "test_method".to_string(),
442            Some(json!({"param": "value"})),
443        )
444        .unwrap();
445
446        assert_eq!(request.jsonrpc, "2.0");
447        assert_eq!(request.method, "test_method");
448        assert_eq!(request.id, json!(1));
449    }
450
451    #[test]
452    fn test_jsonrpc_response() {
453        let response = JsonRpcResponse::success(json!(1), json!({"result": "success"})).unwrap();
454        assert_eq!(response.jsonrpc, "2.0");
455        assert_eq!(response.id, json!(1));
456        assert!(response.result.is_some());
457        assert!(response.error.is_none());
458
459        let error_response = JsonRpcResponse::error(
460            json!(1),
461            INVALID_PARAMS,
462            "Invalid parameters".to_string(),
463            None,
464        );
465        assert!(error_response.result.is_none());
466        assert!(error_response.error.is_some());
467    }
468}