Skip to main content

mcp_protocol/
types.rs

1//! # MCP Protocol Types
2//!
3//! **JSON-RPC 2.0 and MCP specification compliant** data structures
4//! - Fully compatible with FastMCP and other MCP implementations
5//! - Standard OAuth 2.1 Bearer token authentication support
6//! - Comprehensive tool, memory, and capability definitions
7
8use serde::{Deserialize, Serialize};
9
10/// **JSON-RPC 2.0 Request** - Standard wire format for MCP
11#[derive(Debug, Clone, Serialize, Deserialize)]
12pub struct JsonRpcRequest {
13    /// JSON-RPC version (always "2.0")
14    pub jsonrpc: String,
15    /// Request ID (optional for notifications)
16    #[serde(skip_serializing_if = "Option::is_none")]
17    pub id: Option<serde_json::Value>,
18    /// Method name (e.g., "tools/list", "tools/call")
19    pub method: String,
20    /// Method parameters (optional)
21    #[serde(skip_serializing_if = "Option::is_none")]
22    pub params: Option<serde_json::Value>,
23}
24
25/// **JSON-RPC 2.0 Response** - Standard response format
26#[derive(Debug, Clone, Serialize, Deserialize)]
27pub struct JsonRpcResponse {
28    /// JSON-RPC version (always "2.0")
29    pub jsonrpc: String,
30    /// Response ID (matches request ID)
31    pub id: Option<serde_json::Value>,
32    /// Success result (mutually exclusive with error)
33    #[serde(skip_serializing_if = "Option::is_none")]
34    pub result: Option<serde_json::Value>,
35    /// Error information (mutually exclusive with result)
36    #[serde(skip_serializing_if = "Option::is_none")]
37    pub error: Option<JsonRpcError>,
38}
39
40/// **JSON-RPC 2.0 Error** - Standard error format
41#[derive(Debug, Clone, Serialize, Deserialize)]
42pub struct JsonRpcError {
43    /// Error code (standard JSON-RPC error codes)
44    pub code: i32,
45    /// Error message
46    pub message: String,
47    /// Additional error data (optional)
48    #[serde(skip_serializing_if = "Option::is_none")]
49    pub data: Option<serde_json::Value>,
50}
51
52impl JsonRpcRequest {
53    /// Create new JSON-RPC request
54    pub fn new(
55        method: &str,
56        params: Option<serde_json::Value>,
57        id: Option<serde_json::Value>,
58    ) -> Self {
59        Self {
60            jsonrpc: "2.0".to_string(),
61            method: method.to_string(),
62            params,
63            id,
64        }
65    }
66
67    /// Create notification (no response expected)
68    pub fn notification(method: &str, params: Option<serde_json::Value>) -> Self {
69        Self::new(method, params, None)
70    }
71
72    /// Create request with string ID
73    pub fn with_id(method: &str, params: Option<serde_json::Value>, id: &str) -> Self {
74        Self::new(
75            method,
76            params,
77            Some(serde_json::Value::String(id.to_string())),
78        )
79    }
80}
81
82impl JsonRpcResponse {
83    /// Create success response
84    pub fn success(id: Option<serde_json::Value>, result: serde_json::Value) -> Self {
85        Self {
86            jsonrpc: "2.0".to_string(),
87            id,
88            result: Some(result),
89            error: None,
90        }
91    }
92
93    /// Create error response
94    pub fn error(id: Option<serde_json::Value>, error: JsonRpcError) -> Self {
95        Self {
96            jsonrpc: "2.0".to_string(),
97            id,
98            result: None,
99            error: Some(error),
100        }
101    }
102}
103
104impl JsonRpcError {
105    /// Method not found (-32601)
106    pub fn method_not_found(message: &str) -> Self {
107        Self {
108            code: -32601,
109            message: message.to_string(),
110            data: None,
111        }
112    }
113
114    /// Invalid params (-32602)
115    pub fn invalid_params(message: &str) -> Self {
116        Self {
117            code: -32602,
118            message: message.to_string(),
119            data: None,
120        }
121    }
122
123    /// Internal error (-32603)
124    pub fn internal_error(message: &str) -> Self {
125        Self {
126            code: -32603,
127            message: message.to_string(),
128            data: None,
129        }
130    }
131}
132
133// === MCP Protocol-Specific Types ===
134
135/// **Initialize Request** - MCP handshake
136#[derive(Debug, Clone, Serialize, Deserialize)]
137#[serde(rename_all = "camelCase")]
138pub struct InitializeRequest {
139    /// Protocol version requested by client
140    #[serde(alias = "protocol_version")]
141    pub protocol_version: String,
142    /// Client capabilities
143    pub capabilities: ClientCapabilities,
144    /// Client information
145    #[serde(alias = "client_info")]
146    pub client_info: ClientInfo,
147}
148
149/// **Initialize Result** - MCP handshake response
150#[derive(Debug, Clone, Serialize, Deserialize)]
151#[serde(rename_all = "camelCase")]
152pub struct InitializeResult {
153    /// Protocol version supported by server
154    #[serde(alias = "protocol_version")]
155    pub protocol_version: String,
156    /// Server capabilities
157    pub capabilities: ServerCapabilities,
158    /// Server information
159    #[serde(alias = "server_info")]
160    pub server_info: ServerInfo,
161}
162
163/// **Client Information**
164#[derive(Debug, Clone, Serialize, Deserialize)]
165#[serde(rename_all = "camelCase")]
166pub struct ClientInfo {
167    /// Client name
168    pub name: String,
169    /// Client version
170    pub version: String,
171    /// Client description (optional)
172    #[serde(skip_serializing_if = "Option::is_none")]
173    pub description: Option<String>,
174}
175
176/// **Server Information**
177#[derive(Debug, Clone, Serialize, Deserialize)]
178#[serde(rename_all = "camelCase")]
179pub struct ServerInfo {
180    /// Server name
181    pub name: String,
182    /// Server version
183    pub version: String,
184    /// Server description (optional)
185    #[serde(skip_serializing_if = "Option::is_none")]
186    pub description: Option<String>,
187}
188
189/// **Client Capabilities**
190#[derive(Debug, Clone, Serialize, Deserialize, Default)]
191#[serde(rename_all = "camelCase")]
192pub struct ClientCapabilities {
193    /// Tool execution support
194    #[serde(skip_serializing_if = "Option::is_none")]
195    pub tools: Option<ToolCapabilities>,
196}
197
198/// **Server Capabilities**
199#[derive(Debug, Clone, Serialize, Deserialize, Default)]
200#[serde(rename_all = "camelCase")]
201pub struct ServerCapabilities {
202    /// Tool execution support
203    #[serde(skip_serializing_if = "Option::is_none")]
204    pub tools: Option<ToolCapabilities>,
205}
206
207/// **Tool Capabilities**
208#[derive(Debug, Clone, Serialize, Deserialize)]
209#[serde(rename_all = "camelCase")]
210pub struct ToolCapabilities {
211    /// Tool listing support
212    pub supported: bool,
213}
214
215// === Tool Operations ===
216
217/// **List Tools Result**
218#[derive(Debug, Clone, Serialize, Deserialize)]
219pub struct ListToolsResult {
220    /// Available tools
221    pub tools: Vec<Tool>,
222}
223
224/// **Tool Definition**
225#[derive(Debug, Clone, Serialize, Deserialize)]
226#[serde(rename_all = "camelCase")]
227pub struct Tool {
228    /// Tool name
229    pub name: String,
230    /// Tool description
231    pub description: String,
232    /// JSON Schema for tool parameters
233    #[serde(alias = "input_schema")]
234    pub input_schema: serde_json::Value,
235}
236
237/// **Call Tool Request**
238#[derive(Debug, Clone, Serialize, Deserialize)]
239pub struct CallToolRequest {
240    /// Tool name to execute
241    pub name: String,
242    /// Tool arguments
243    #[serde(skip_serializing_if = "Option::is_none")]
244    pub arguments: Option<serde_json::Value>,
245    /// Optional MCP request metadata.
246    #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
247    pub meta: Option<serde_json::Value>,
248}
249
250/// **Call Tool Result**
251#[derive(Debug, Clone, Serialize, Deserialize)]
252#[serde(rename_all = "camelCase")]
253pub struct CallToolResult {
254    /// Tool execution result content
255    pub content: Vec<Content>,
256    /// Whether execution resulted in error
257    #[serde(skip_serializing_if = "Option::is_none")]
258    #[serde(alias = "is_error")]
259    pub is_error: Option<bool>,
260}
261
262/// **Content** - Multi-modal content representation
263#[derive(Debug, Clone, Serialize, Deserialize)]
264#[serde(tag = "type")]
265pub enum Content {
266    #[serde(rename = "text")]
267    Text {
268        /// Text content
269        text: String,
270    },
271}
272
273/// **Bearer Token Authentication**
274#[derive(Debug, Clone)]
275pub struct BearerToken {
276    /// OAuth 2.1 access token
277    pub access_token: String,
278    /// Token type (always "Bearer")
279    pub token_type: String,
280}
281
282impl BearerToken {
283    /// Create new bearer token
284    pub fn new(access_token: &str) -> Self {
285        Self {
286            access_token: access_token.to_string(),
287            token_type: "Bearer".to_string(),
288        }
289    }
290
291    /// Format as Authorization header value
292    pub fn to_authorization_header(&self) -> String {
293        format!("{} {}", self.token_type, self.access_token)
294    }
295}
296
297impl Tool {
298    /// Create simple tool with string parameters
299    pub fn simple(name: &str, description: &str, parameters: &[&str]) -> Self {
300        let mut properties = serde_json::Map::new();
301        for param in parameters {
302            properties.insert(
303                param.to_string(),
304                serde_json::json!({
305                    "type": "string",
306                    "description": format!("Parameter: {}", param)
307                }),
308            );
309        }
310
311        let schema = serde_json::json!({
312            "type": "object",
313            "properties": properties,
314            "required": parameters
315        });
316
317        Self {
318            name: name.to_string(),
319            description: description.to_string(),
320            input_schema: schema,
321        }
322    }
323}
324
325impl Content {
326    /// Create text content
327    pub fn text(text: &str) -> Self {
328        Content::Text {
329            text: text.to_string(),
330        }
331    }
332}