mcp_tools/common/
mod.rs

1//! Common types and utilities shared across MCP Tools
2
3use serde::{Deserialize, Serialize};
4use std::collections::HashMap;
5use uuid::Uuid;
6
7pub mod client_base;
8pub mod protocol;
9pub mod server_base;
10pub mod transport;
11
12pub use client_base::*;
13pub use protocol::*;
14pub use server_base::*;
15pub use transport::*;
16
17/// Standard MCP server configuration
18#[derive(Debug, Clone, Serialize, Deserialize)]
19pub struct ServerConfig {
20    /// Server name
21    pub name: String,
22    /// Server description
23    pub description: String,
24    /// Server version
25    pub version: String,
26    /// Host to bind to
27    pub host: String,
28    /// Port to bind to
29    pub port: u16,
30    /// Maximum number of concurrent connections
31    pub max_connections: usize,
32    /// Request timeout in seconds
33    pub request_timeout_secs: u64,
34    /// Enable request logging
35    pub log_requests: bool,
36    /// Server-specific configuration
37    pub server_config: HashMap<String, serde_json::Value>,
38}
39
40impl Default for ServerConfig {
41    fn default() -> Self {
42        Self {
43            name: "MCP Server".to_string(),
44            description: "MCP Server powered by CoderLib".to_string(),
45            version: crate::VERSION.to_string(),
46            host: crate::DEFAULT_HOST.to_string(),
47            port: crate::DEFAULT_PORT,
48            max_connections: 100,
49            request_timeout_secs: 30,
50            log_requests: true,
51            server_config: HashMap::new(),
52        }
53    }
54}
55
56/// Standard MCP client configuration
57#[derive(Debug, Clone, Serialize, Deserialize)]
58pub struct ClientConfig {
59    /// Client name
60    pub name: String,
61    /// Client version
62    pub version: String,
63    /// Server URL to connect to
64    pub server_url: String,
65    /// Connection timeout in seconds
66    pub connect_timeout_secs: u64,
67    /// Request timeout in seconds
68    pub request_timeout_secs: u64,
69    /// Enable request logging
70    pub log_requests: bool,
71    /// Client-specific configuration
72    pub client_config: HashMap<String, serde_json::Value>,
73}
74
75impl Default for ClientConfig {
76    fn default() -> Self {
77        Self {
78            name: "MCP Client".to_string(),
79            version: crate::VERSION.to_string(),
80            server_url: format!("ws://{}:{}", crate::DEFAULT_HOST, crate::DEFAULT_PORT),
81            connect_timeout_secs: 10,
82            request_timeout_secs: 30,
83            log_requests: true,
84            client_config: HashMap::new(),
85        }
86    }
87}
88
89/// MCP tool definition
90#[derive(Debug, Clone, Serialize, Deserialize)]
91pub struct McpTool {
92    /// Tool name
93    pub name: String,
94    /// Tool description
95    pub description: String,
96    /// Input schema
97    pub input_schema: serde_json::Value,
98    /// Tool category
99    pub category: String,
100    /// Whether tool requires permissions
101    pub requires_permission: bool,
102    /// Required permissions
103    pub permissions: Vec<String>,
104}
105
106/// MCP tool execution request
107#[derive(Debug, Clone, Serialize, Deserialize)]
108pub struct McpToolRequest {
109    /// Request ID
110    pub id: Uuid,
111    /// Tool name
112    pub tool: String,
113    /// Tool arguments
114    pub arguments: serde_json::Value,
115    /// Session ID
116    pub session_id: String,
117    /// Request metadata
118    pub metadata: HashMap<String, serde_json::Value>,
119}
120
121/// MCP tool execution response
122#[derive(Debug, Clone, Serialize, Deserialize)]
123pub struct McpToolResponse {
124    /// Request ID
125    pub id: Uuid,
126    /// Response content
127    pub content: Vec<McpContent>,
128    /// Whether the operation was successful
129    pub is_error: bool,
130    /// Error message if any
131    pub error: Option<String>,
132    /// Response metadata
133    pub metadata: HashMap<String, serde_json::Value>,
134}
135
136/// MCP content types
137#[derive(Debug, Clone, Serialize, Deserialize)]
138#[serde(tag = "type")]
139pub enum McpContent {
140    #[serde(rename = "text")]
141    Text { text: String },
142    #[serde(rename = "image")]
143    Image { data: String, mime_type: String },
144    #[serde(rename = "resource")]
145    Resource {
146        uri: String,
147        mime_type: Option<String>,
148        text: Option<String>,
149    },
150}
151
152/// Server capabilities
153#[derive(Debug, Clone, Serialize, Deserialize)]
154pub struct ServerCapabilities {
155    /// Available tools
156    pub tools: Vec<McpTool>,
157    /// Supported features
158    pub features: Vec<String>,
159    /// Server information
160    pub info: ServerInfo,
161}
162
163/// Server information
164#[derive(Debug, Clone, Serialize, Deserialize)]
165pub struct ServerInfo {
166    /// Server name
167    pub name: String,
168    /// Server version
169    pub version: String,
170    /// Server description
171    pub description: String,
172    /// CoderLib version
173    pub coderlib_version: String,
174    /// Supported protocol version
175    pub protocol_version: String,
176}
177
178/// Client capabilities
179#[derive(Debug, Clone, Serialize, Deserialize)]
180pub struct ClientCapabilities {
181    /// Supported content types
182    pub content_types: Vec<String>,
183    /// Supported features
184    pub features: Vec<String>,
185    /// Client information
186    pub info: ClientInfo,
187}
188
189/// Client information
190#[derive(Debug, Clone, Serialize, Deserialize)]
191pub struct ClientInfo {
192    /// Client name
193    pub name: String,
194    /// Client version
195    pub version: String,
196    /// Client description
197    pub description: String,
198}
199
200/// Connection status
201#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
202pub enum ConnectionStatus {
203    Disconnected,
204    Connecting,
205    Connected,
206    Error(String),
207}
208
209/// Server statistics
210#[derive(Debug, Clone, Serialize, Deserialize)]
211pub struct ServerStats {
212    /// Number of active connections
213    pub active_connections: usize,
214    /// Total requests processed
215    pub total_requests: u64,
216    /// Total errors
217    pub total_errors: u64,
218    /// Server uptime in seconds
219    pub uptime_secs: u64,
220    /// Average request duration in milliseconds
221    pub avg_request_duration_ms: f64,
222}
223
224/// Utility functions
225impl McpContent {
226    /// Create text content
227    pub fn text(text: impl Into<String>) -> Self {
228        Self::Text { text: text.into() }
229    }
230
231    /// Create image content
232    pub fn image(data: impl Into<String>, mime_type: impl Into<String>) -> Self {
233        Self::Image {
234            data: data.into(),
235            mime_type: mime_type.into(),
236        }
237    }
238
239    /// Create resource content
240    pub fn resource(uri: impl Into<String>) -> Self {
241        Self::Resource {
242            uri: uri.into(),
243            mime_type: None,
244            text: None,
245        }
246    }
247
248    /// Create resource content with MIME type
249    pub fn resource_with_type(uri: impl Into<String>, mime_type: impl Into<String>) -> Self {
250        Self::Resource {
251            uri: uri.into(),
252            mime_type: Some(mime_type.into()),
253            text: None,
254        }
255    }
256}
257
258impl McpToolResponse {
259    /// Create successful response
260    pub fn success(id: Uuid, content: Vec<McpContent>) -> Self {
261        Self {
262            id,
263            content,
264            is_error: false,
265            error: None,
266            metadata: HashMap::new(),
267        }
268    }
269
270    /// Create error response
271    pub fn error(id: Uuid, error: impl Into<String>) -> Self {
272        Self {
273            id,
274            content: vec![],
275            is_error: true,
276            error: Some(error.into()),
277            metadata: HashMap::new(),
278        }
279    }
280
281    /// Add metadata
282    pub fn with_metadata(mut self, key: impl Into<String>, value: serde_json::Value) -> Self {
283        self.metadata.insert(key.into(), value);
284        self
285    }
286}
287
288#[cfg(test)]
289mod tests {
290    use super::*;
291
292    #[test]
293    fn test_server_config_default() {
294        let config = ServerConfig::default();
295        assert_eq!(config.name, "MCP Server");
296        assert_eq!(config.host, "127.0.0.1");
297        assert_eq!(config.port, 3000);
298    }
299
300    #[test]
301    fn test_client_config_default() {
302        let config = ClientConfig::default();
303        assert_eq!(config.name, "MCP Client");
304        assert!(config.server_url.contains("127.0.0.1:3000"));
305    }
306
307    #[test]
308    fn test_mcp_content_creation() {
309        let text = McpContent::text("Hello, world!");
310        match text {
311            McpContent::Text { text } => assert_eq!(text, "Hello, world!"),
312            _ => panic!("Expected text content"),
313        }
314
315        let image = McpContent::image("base64data", "image/png");
316        match image {
317            McpContent::Image { data, mime_type } => {
318                assert_eq!(data, "base64data");
319                assert_eq!(mime_type, "image/png");
320            }
321            _ => panic!("Expected image content"),
322        }
323    }
324
325    #[test]
326    fn test_mcp_tool_response() {
327        let id = Uuid::new_v4();
328        let response = McpToolResponse::success(id, vec![McpContent::text("Success")]);
329
330        assert_eq!(response.id, id);
331        assert!(!response.is_error);
332        assert!(response.error.is_none());
333        assert_eq!(response.content.len(), 1);
334
335        let error_response = McpToolResponse::error(id, "Something went wrong");
336        assert!(error_response.is_error);
337        assert!(error_response.error.is_some());
338    }
339}