spec_kit_mcp/mcp/
types.rs

1//! MCP Protocol Types
2//!
3//! This module defines the core types for the Model Context Protocol (MCP).
4
5use serde::{Deserialize, Serialize};
6use serde_json::Value;
7
8/// JSON-RPC 2.0 Request
9#[derive(Debug, Clone, Serialize, Deserialize)]
10pub struct JsonRpcRequest {
11    /// JSON-RPC version (must be "2.0")
12    pub jsonrpc: String,
13
14    /// Request ID (can be string or number)
15    pub id: RequestId,
16
17    /// Method name
18    pub method: String,
19
20    /// Parameters
21    #[serde(skip_serializing_if = "Option::is_none")]
22    pub params: Option<Value>,
23}
24
25/// JSON-RPC 2.0 Response
26#[derive(Debug, Clone, Serialize, Deserialize)]
27pub struct JsonRpcResponse {
28    /// JSON-RPC version (must be "2.0")
29    pub jsonrpc: String,
30
31    /// Request ID (matching the request)
32    pub id: RequestId,
33
34    /// Result (if successful)
35    #[serde(skip_serializing_if = "Option::is_none")]
36    pub result: Option<Value>,
37
38    /// Error (if failed)
39    #[serde(skip_serializing_if = "Option::is_none")]
40    pub error: Option<JsonRpcError>,
41}
42
43/// Request ID (can be string, number, or null)
44#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
45#[serde(untagged)]
46pub enum RequestId {
47    String(String),
48    Number(i64),
49    Null,
50}
51
52/// JSON-RPC Error
53#[derive(Debug, Clone, Serialize, Deserialize)]
54pub struct JsonRpcError {
55    /// Error code
56    pub code: i32,
57
58    /// Error message
59    pub message: String,
60
61    /// Additional error data
62    #[serde(skip_serializing_if = "Option::is_none")]
63    pub data: Option<Value>,
64}
65
66/// MCP Tool Definition
67#[derive(Debug, Clone, Serialize, Deserialize)]
68pub struct ToolDefinition {
69    /// Tool name (unique identifier)
70    pub name: String,
71
72    /// Human-readable description
73    pub description: String,
74
75    /// JSON Schema for input parameters
76    #[serde(rename = "inputSchema")]
77    pub input_schema: Value,
78}
79
80/// Tool call parameters
81#[derive(Debug, Clone, Serialize, Deserialize)]
82pub struct ToolCallParams {
83    /// Tool name
84    pub name: String,
85
86    /// Tool arguments
87    #[serde(default)]
88    pub arguments: Value,
89}
90
91/// Tool call result
92#[derive(Debug, Clone, Serialize, Deserialize)]
93pub struct ToolResult {
94    /// Result content
95    pub content: Vec<ContentBlock>,
96
97    /// Whether the operation is complete
98    #[serde(skip_serializing_if = "Option::is_none")]
99    pub is_error: Option<bool>,
100}
101
102/// Content block in a response
103#[derive(Debug, Clone, Serialize, Deserialize)]
104#[serde(tag = "type")]
105pub enum ContentBlock {
106    #[serde(rename = "text")]
107    Text { text: String },
108
109    #[serde(rename = "image")]
110    Image { data: String, mime_type: String },
111
112    #[serde(rename = "resource")]
113    Resource {
114        uri: String,
115        mime_type: Option<String>,
116        text: Option<String>,
117    },
118}
119
120impl ContentBlock {
121    /// Create a text content block
122    pub fn text(text: impl Into<String>) -> Self {
123        Self::Text { text: text.into() }
124    }
125}
126
127/// Standard JSON-RPC error codes
128pub mod error_codes {
129    pub const PARSE_ERROR: i32 = -32700;
130    pub const INVALID_REQUEST: i32 = -32600;
131    pub const METHOD_NOT_FOUND: i32 = -32601;
132    pub const INVALID_PARAMS: i32 = -32602;
133    pub const INTERNAL_ERROR: i32 = -32603;
134}
135
136impl JsonRpcResponse {
137    /// Create a success response
138    pub fn success(id: RequestId, result: Value) -> Self {
139        Self {
140            jsonrpc: "2.0".to_string(),
141            id,
142            result: Some(result),
143            error: None,
144        }
145    }
146
147    /// Create an error response
148    pub fn error(id: RequestId, error: JsonRpcError) -> Self {
149        Self {
150            jsonrpc: "2.0".to_string(),
151            id,
152            result: None,
153            error: Some(error),
154        }
155    }
156}
157
158impl JsonRpcError {
159    /// Create a new error
160    pub fn new(code: i32, message: impl Into<String>) -> Self {
161        Self {
162            code,
163            message: message.into(),
164            data: None,
165        }
166    }
167
168    /// Create an error with additional data
169    pub fn with_data(code: i32, message: impl Into<String>, data: Value) -> Self {
170        Self {
171            code,
172            message: message.into(),
173            data: Some(data),
174        }
175    }
176
177    /// Create a parse error
178    pub fn parse_error(message: impl Into<String>) -> Self {
179        Self::new(error_codes::PARSE_ERROR, message)
180    }
181
182    /// Create an invalid request error
183    pub fn invalid_request(message: impl Into<String>) -> Self {
184        Self::new(error_codes::INVALID_REQUEST, message)
185    }
186
187    /// Create a method not found error
188    pub fn method_not_found(method: &str) -> Self {
189        Self::new(
190            error_codes::METHOD_NOT_FOUND,
191            format!("Method not found: {}", method),
192        )
193    }
194
195    /// Create an invalid params error
196    pub fn invalid_params(message: impl Into<String>) -> Self {
197        Self::new(error_codes::INVALID_PARAMS, message)
198    }
199
200    /// Create an internal error
201    pub fn internal_error(message: impl Into<String>) -> Self {
202        Self::new(error_codes::INTERNAL_ERROR, message)
203    }
204}
205
206#[cfg(test)]
207mod tests {
208    use super::*;
209    use serde_json::json;
210
211    #[test]
212    fn test_parse_request() {
213        let json = r#"{
214            "jsonrpc": "2.0",
215            "id": 1,
216            "method": "tools/call",
217            "params": {
218                "name": "test_tool",
219                "arguments": {}
220            }
221        }"#;
222
223        let request: JsonRpcRequest = serde_json::from_str(json).unwrap();
224        assert_eq!(request.jsonrpc, "2.0");
225        assert_eq!(request.id, RequestId::Number(1));
226        assert_eq!(request.method, "tools/call");
227        assert!(request.params.is_some());
228    }
229
230    #[test]
231    fn test_success_response() {
232        let response = JsonRpcResponse::success(RequestId::Number(1), json!({"status": "ok"}));
233
234        assert_eq!(response.jsonrpc, "2.0");
235        assert_eq!(response.id, RequestId::Number(1));
236        assert!(response.result.is_some());
237        assert!(response.error.is_none());
238    }
239
240    #[test]
241    fn test_error_response() {
242        let error = JsonRpcError::method_not_found("test");
243        let response = JsonRpcResponse::error(RequestId::Number(1), error);
244
245        assert_eq!(response.jsonrpc, "2.0");
246        assert!(response.error.is_some());
247        assert!(response.result.is_none());
248    }
249
250    #[test]
251    fn test_content_block() {
252        let block = ContentBlock::text("Hello, world!");
253        let json = serde_json::to_value(&block).unwrap();
254
255        assert_eq!(json["type"], "text");
256        assert_eq!(json["text"], "Hello, world!");
257    }
258}