1use 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#[derive(Debug, Clone, Serialize, Deserialize)]
19pub struct ServerConfig {
20 pub name: String,
22 pub description: String,
24 pub version: String,
26 pub host: String,
28 pub port: u16,
30 pub max_connections: usize,
32 pub request_timeout_secs: u64,
34 pub log_requests: bool,
36 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#[derive(Debug, Clone, Serialize, Deserialize)]
58pub struct ClientConfig {
59 pub name: String,
61 pub version: String,
63 pub server_url: String,
65 pub connect_timeout_secs: u64,
67 pub request_timeout_secs: u64,
69 pub log_requests: bool,
71 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#[derive(Debug, Clone, Serialize, Deserialize)]
91pub struct McpTool {
92 pub name: String,
94 pub description: String,
96 pub input_schema: serde_json::Value,
98 pub category: String,
100 pub requires_permission: bool,
102 pub permissions: Vec<String>,
104}
105
106#[derive(Debug, Clone, Serialize, Deserialize)]
108pub struct McpToolRequest {
109 pub id: Uuid,
111 pub tool: String,
113 pub arguments: serde_json::Value,
115 pub session_id: String,
117 pub metadata: HashMap<String, serde_json::Value>,
119}
120
121#[derive(Debug, Clone, Serialize, Deserialize)]
123pub struct McpToolResponse {
124 pub id: Uuid,
126 pub content: Vec<McpContent>,
128 pub is_error: bool,
130 pub error: Option<String>,
132 pub metadata: HashMap<String, serde_json::Value>,
134}
135
136#[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#[derive(Debug, Clone, Serialize, Deserialize)]
154pub struct ServerCapabilities {
155 pub tools: Vec<McpTool>,
157 pub features: Vec<String>,
159 pub info: ServerInfo,
161}
162
163#[derive(Debug, Clone, Serialize, Deserialize)]
165pub struct ServerInfo {
166 pub name: String,
168 pub version: String,
170 pub description: String,
172 pub coderlib_version: String,
174 pub protocol_version: String,
176}
177
178#[derive(Debug, Clone, Serialize, Deserialize)]
180pub struct ClientCapabilities {
181 pub content_types: Vec<String>,
183 pub features: Vec<String>,
185 pub info: ClientInfo,
187}
188
189#[derive(Debug, Clone, Serialize, Deserialize)]
191pub struct ClientInfo {
192 pub name: String,
194 pub version: String,
196 pub description: String,
198}
199
200#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
202pub enum ConnectionStatus {
203 Disconnected,
204 Connecting,
205 Connected,
206 Error(String),
207}
208
209#[derive(Debug, Clone, Serialize, Deserialize)]
211pub struct ServerStats {
212 pub active_connections: usize,
214 pub total_requests: u64,
216 pub total_errors: u64,
218 pub uptime_secs: u64,
220 pub avg_request_duration_ms: f64,
222}
223
224impl McpContent {
226 pub fn text(text: impl Into<String>) -> Self {
228 Self::Text { text: text.into() }
229 }
230
231 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 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 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 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 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 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}