use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use uuid::Uuid;
pub mod client_base;
pub mod protocol;
pub mod server_base;
pub mod transport;
pub use client_base::*;
pub use protocol::*;
pub use server_base::*;
pub use transport::*;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ServerConfig {
pub name: String,
pub description: String,
pub version: String,
pub host: String,
pub port: u16,
pub max_connections: usize,
pub request_timeout_secs: u64,
pub log_requests: bool,
pub server_config: HashMap<String, serde_json::Value>,
}
impl Default for ServerConfig {
fn default() -> Self {
Self {
name: "MCP Server".to_string(),
description: "MCP Server powered by CoderLib".to_string(),
version: crate::VERSION.to_string(),
host: crate::DEFAULT_HOST.to_string(),
port: crate::DEFAULT_PORT,
max_connections: 100,
request_timeout_secs: 30,
log_requests: true,
server_config: HashMap::new(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ClientConfig {
pub name: String,
pub version: String,
pub server_url: String,
pub connect_timeout_secs: u64,
pub request_timeout_secs: u64,
pub log_requests: bool,
pub client_config: HashMap<String, serde_json::Value>,
}
impl Default for ClientConfig {
fn default() -> Self {
Self {
name: "MCP Client".to_string(),
version: crate::VERSION.to_string(),
server_url: format!("ws://{}:{}", crate::DEFAULT_HOST, crate::DEFAULT_PORT),
connect_timeout_secs: 10,
request_timeout_secs: 30,
log_requests: true,
client_config: HashMap::new(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct McpTool {
pub name: String,
pub description: String,
pub input_schema: serde_json::Value,
pub category: String,
pub requires_permission: bool,
pub permissions: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct McpToolRequest {
pub id: Uuid,
pub tool: String,
pub arguments: serde_json::Value,
pub session_id: String,
pub metadata: HashMap<String, serde_json::Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct McpToolResponse {
pub id: Uuid,
pub content: Vec<McpContent>,
pub is_error: bool,
pub error: Option<String>,
pub metadata: HashMap<String, serde_json::Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type")]
pub enum McpContent {
#[serde(rename = "text")]
Text { text: String },
#[serde(rename = "image")]
Image { data: String, mime_type: String },
#[serde(rename = "resource")]
Resource {
uri: String,
mime_type: Option<String>,
text: Option<String>,
},
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ServerCapabilities {
pub tools: Vec<McpTool>,
pub features: Vec<String>,
pub info: ServerInfo,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ServerInfo {
pub name: String,
pub version: String,
pub description: String,
pub coderlib_version: String,
pub protocol_version: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ClientCapabilities {
pub content_types: Vec<String>,
pub features: Vec<String>,
pub info: ClientInfo,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ClientInfo {
pub name: String,
pub version: String,
pub description: String,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum ConnectionStatus {
Disconnected,
Connecting,
Connected,
Error(String),
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ServerStats {
pub active_connections: usize,
pub total_requests: u64,
pub total_errors: u64,
pub uptime_secs: u64,
pub avg_request_duration_ms: f64,
}
impl McpContent {
pub fn text(text: impl Into<String>) -> Self {
Self::Text { text: text.into() }
}
pub fn image(data: impl Into<String>, mime_type: impl Into<String>) -> Self {
Self::Image {
data: data.into(),
mime_type: mime_type.into(),
}
}
pub fn resource(uri: impl Into<String>) -> Self {
Self::Resource {
uri: uri.into(),
mime_type: None,
text: None,
}
}
pub fn resource_with_type(uri: impl Into<String>, mime_type: impl Into<String>) -> Self {
Self::Resource {
uri: uri.into(),
mime_type: Some(mime_type.into()),
text: None,
}
}
}
impl McpToolResponse {
pub fn success(id: Uuid, content: Vec<McpContent>) -> Self {
Self {
id,
content,
is_error: false,
error: None,
metadata: HashMap::new(),
}
}
pub fn error(id: Uuid, error: impl Into<String>) -> Self {
Self {
id,
content: vec![],
is_error: true,
error: Some(error.into()),
metadata: HashMap::new(),
}
}
pub fn with_metadata(mut self, key: impl Into<String>, value: serde_json::Value) -> Self {
self.metadata.insert(key.into(), value);
self
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_server_config_default() {
let config = ServerConfig::default();
assert_eq!(config.name, "MCP Server");
assert_eq!(config.host, "127.0.0.1");
assert_eq!(config.port, 3000);
}
#[test]
fn test_client_config_default() {
let config = ClientConfig::default();
assert_eq!(config.name, "MCP Client");
assert!(config.server_url.contains("127.0.0.1:3000"));
}
#[test]
fn test_mcp_content_creation() {
let text = McpContent::text("Hello, world!");
match text {
McpContent::Text { text } => assert_eq!(text, "Hello, world!"),
_ => panic!("Expected text content"),
}
let image = McpContent::image("base64data", "image/png");
match image {
McpContent::Image { data, mime_type } => {
assert_eq!(data, "base64data");
assert_eq!(mime_type, "image/png");
}
_ => panic!("Expected image content"),
}
}
#[test]
fn test_mcp_tool_response() {
let id = Uuid::new_v4();
let response = McpToolResponse::success(id, vec![McpContent::text("Success")]);
assert_eq!(response.id, id);
assert!(!response.is_error);
assert!(response.error.is_none());
assert_eq!(response.content.len(), 1);
let error_response = McpToolResponse::error(id, "Something went wrong");
assert!(error_response.is_error);
assert!(error_response.error.is_some());
}
}