From 5292585bfe2d7bcea74684d1bcb735240f1e3655 Mon Sep 17 00:00:00 2001 From: Gitar Date: Wed, 16 Jul 2025 20:46:17 +0000 Subject: [PATCH 1/2] Gitar Agent Co-authored-by: gautam@gitar.ai --- summary.md | 73 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 summary.md diff --git a/summary.md b/summary.md new file mode 100644 index 0000000..1ad6865 --- /dev/null +++ b/summary.md @@ -0,0 +1,73 @@ +# mcp.rs Summary + +**mcp.rs** is a high-performance, type-safe Rust implementation of the Model Context Protocol (MCP) for enabling seamless communication between AI applications and their integrations. + +## Key Features + +- **Multiple Transport Types**: stdio, HTTP with SSE, extensible transport system +- **Resource Management**: File system resources, templating, real-time updates, subscriptions +- **Security**: Access controls, path traversal protection, rate limiting, CORS support +- **Flexible Configuration**: YAML/JSON files, environment variables, CLI arguments + +## Quick Commands + +### Client Usage +```bash +# List resources +cargo run --bin client list-resources + +# Read file +cargo run --bin client read-resource -u "file:///path/to/file" + +# Use prompt +cargo run --bin client get-prompt -n "code_review" -a '{"code": "fn main() {}", "language": "rust"}' + +# Call tool +cargo run --bin client call-tool --name "file_system" --args '{"operation": "read_file", "path": "Config.toml"}' +``` + +### Server Usage +```bash +# Run server with config +cargo run --bin server -- --config "../servers/test.json" + +# Run with stdio transport +mcp-server -t stdio + +# Run with SSE on port 3000 +mcp-server -t sse -p 3000 +``` + +## Architecture + +- **Transport Layer**: Handles client-server communication +- **Protocol Layer**: Implements MCP message format and routing +- **Resource Layer**: Manages external resource access +- **Configuration**: Server settings and capabilities + +## Installation + +Add to `Cargo.toml`: +```toml +[dependencies] +mcp = "0.1.0" +``` + +## Development + +- **Requirements**: Rust 1.70+, Cargo +- **Testing**: `cargo test` +- **Documentation**: `cargo doc --open` + +## Core Components + +- **McpServer**: Main server handling MCP protocol +- **ServerConfig**: Configuration management +- **RequestHandler**: Custom protocol handling +- **ResourceManager**: Resource access and management +- **ToolManager**: Tool execution +- **PromptManager**: Prompt templating + +## License + +MIT License \ No newline at end of file From 762c344706c2245d4b3565a83e1c1487691f704c Mon Sep 17 00:00:00 2001 From: Gitar Date: Wed, 16 Jul 2025 21:13:12 +0000 Subject: [PATCH 2/2] Fix CI failures: Test Suite and Clippy compilation errors --- src/error.rs | 2 +- src/logging/mod.rs | 2 +- src/prompts/mod.rs | 4 ++-- src/tools/calculator.rs | 6 +++++- src/tools/file_system/directory.rs | 4 ++-- src/tools/file_system/mod.rs | 16 ++++++++++++++-- src/tools/file_system/read.rs | 4 ++-- src/tools/file_system/search.rs | 3 +-- src/tools/mod.rs | 7 +++---- src/tools/test_tool.rs | 2 +- src/transport/sse.rs | 2 +- src/transport/ws.rs | 4 ++-- tests/tools.rs | 4 ++++ 13 files changed, 39 insertions(+), 21 deletions(-) diff --git a/src/error.rs b/src/error.rs index 0d1dd24..6ab2f93 100644 --- a/src/error.rs +++ b/src/error.rs @@ -77,7 +77,7 @@ impl fmt::Display for McpError { McpError::Custom { code, message } => write!(f, "Error {}: {}", code, message), McpError::ConnectionFailed => write!(f, "Connection failed"), McpError::ConnectionTimeout => write!(f, "Connection timed out"), - McpError::ServerError(msg) => write!(f, "Server error: {}", msg), + McpError::ServerError(msg) => write!(f, "Server error: {msg}"), } } } diff --git a/src/logging/mod.rs b/src/logging/mod.rs index 83cbff1..f135418 100644 --- a/src/logging/mod.rs +++ b/src/logging/mod.rs @@ -195,7 +195,7 @@ struct JsonVisitor(serde_json::Value); impl tracing::field::Visit for JsonVisitor { fn record_debug(&mut self, field: &tracing::field::Field, value: &dyn std::fmt::Debug) { - let value = format!("{:?}", value); + let value = format!("{value:?}"); if let serde_json::Value::Object(ref mut map) = self.0 { map.insert(field.name().to_string(), serde_json::Value::String(value)); } else { diff --git a/src/prompts/mod.rs b/src/prompts/mod.rs index 40a2e8d..5920634 100644 --- a/src/prompts/mod.rs +++ b/src/prompts/mod.rs @@ -127,7 +127,7 @@ impl PromptManager { let prompts = self.prompts.read().await; let prompt = prompts .get(name) - .ok_or_else(|| McpError::InvalidRequest(format!("Unknown prompt: {}", name)))?; + .ok_or_else(|| McpError::InvalidRequest(format!("Unknown prompt: {name}")))?; // Validate required arguments if let Some(args) = &arguments { @@ -174,7 +174,7 @@ impl PromptManager { .tx .send(notification) .await - .map_err(|e| McpError::InternalError(format!("Notification error: {}", e)))?; + .map_err(|e| McpError::InternalError(format!("Notification error: {e}")))?; } Ok(()) } diff --git a/src/tools/calculator.rs b/src/tools/calculator.rs index 20f9f2f..568e307 100644 --- a/src/tools/calculator.rs +++ b/src/tools/calculator.rs @@ -1,6 +1,7 @@ use async_trait::async_trait; use serde_json::{json, Value}; use std::{collections::HashMap, sync::Arc}; +use uuid::Uuid; use crate::{ error::McpError, @@ -309,6 +310,7 @@ mod tests { CallToolArgs::builder() .session_id(Some("session-1234".to_string())) .tool_id(Some("calculator-1234".to_string())) + .agent_id(Uuid::new_v4()) .build(), ), ) @@ -327,12 +329,13 @@ mod tests { "calculator", json!({ "operation": "ln", - "a": 2.718281828459045 + "a": std::f64::consts::E }), Some( CallToolArgs::builder() .session_id(Some("session-5678".to_string())) .tool_id(Some("calculator-5678".to_string())) + .agent_id(Uuid::new_v4()) .build(), ), ) @@ -370,6 +373,7 @@ mod tests { CallToolArgs::builder() .session_id(Some("session-9999".to_string())) .tool_id(Some("calculator-9999".to_string())) + .agent_id(Uuid::new_v4()) .build(), ), ) diff --git a/src/tools/file_system/directory.rs b/src/tools/file_system/directory.rs index b7a6b4f..e7f82c4 100644 --- a/src/tools/file_system/directory.rs +++ b/src/tools/file_system/directory.rs @@ -71,7 +71,7 @@ impl ToolProvider for DirectoryTool { Ok(ToolResult { content: vec![ToolContent::Text { - text: format!("Created directory: {}", path), + text: format!("Created directory: {path}"), }], is_error: false, _meta: None, @@ -118,7 +118,7 @@ impl ToolProvider for DirectoryTool { Ok(ToolResult { content: vec![ToolContent::Text { - text: format!("Moved {} to {}", source, destination), + text: format!("Moved {source} to {destination}"), }], is_error: false, _meta: None, diff --git a/src/tools/file_system/mod.rs b/src/tools/file_system/mod.rs index 87320cf..ebb0a7b 100644 --- a/src/tools/file_system/mod.rs +++ b/src/tools/file_system/mod.rs @@ -11,6 +11,7 @@ use async_trait::async_trait; use serde_json::Value; use std::path::PathBuf; use std::sync::Arc; +use uuid::Uuid; use super::CallToolArgs; @@ -165,6 +166,7 @@ mod tests { CallToolArgs::builder() .tool_id("write-to-file-1234".to_string()) .session_id("session-1234".to_string()) + .agent_id(Uuid::new_v4()) .build(), ), ) @@ -183,6 +185,7 @@ mod tests { CallToolArgs::builder() .tool_id("read-from-file-1234".to_string()) .session_id("session-1234".to_string()) + .agent_id(Uuid::new_v4()) .build(), ), ) @@ -211,6 +214,7 @@ mod tests { CallToolArgs::builder() .tool_id("create-directory-1234".to_string()) .session_id("session-1234".to_string()) + .agent_id(Uuid::new_v4()) .build(), ), ) @@ -229,6 +233,7 @@ mod tests { CallToolArgs::builder() .tool_id("list-directory-1234".to_string()) .session_id("session-1234".to_string()) + .agent_id(Uuid::new_v4()) .build(), ), ) @@ -260,6 +265,7 @@ mod tests { CallToolArgs::builder() .tool_id("write-to-file-1234".to_string()) .session_id("session-1234".to_string()) + .agent_id(Uuid::new_v4()) .build(), ), ) @@ -279,6 +285,7 @@ mod tests { CallToolArgs::builder() .tool_id("search-files-1234".to_string()) .session_id("session-1234".to_string()) + .agent_id(Uuid::new_v4()) .build(), ), ) @@ -313,6 +320,7 @@ mod tests { CallToolArgs::builder() .tool_id("write-to-file-1234".to_string()) .session_id("session-1234".to_string()) + .agent_id(Uuid::new_v4()) .build(), ), ) @@ -331,6 +339,7 @@ mod tests { CallToolArgs::builder() .tool_id("move-file-1234".to_string()) .session_id("session-1234".to_string()) + .agent_id(Uuid::new_v4()) .build(), ), ) @@ -360,6 +369,7 @@ mod tests { CallToolArgs::builder() .tool_id("write-to-file-1234".to_string()) .session_id("session-1234".to_string()) + .agent_id(Uuid::new_v4()) .build(), ), ) @@ -381,12 +391,13 @@ mod tests { json!({ "operation": "write_file", "path": path.to_str().unwrap(), - "content": format!("content {}", i), + "content": format!("content {i}"), }), Some( CallToolArgs::builder() - .tool_id(format!("write-to-file-{}", i).to_string()) + .tool_id(format!("write-to-file-{i}").to_string()) .session_id("session-1234".to_string()) + .agent_id(Uuid::new_v4()) .build(), ), ) @@ -403,6 +414,7 @@ mod tests { Some(CallToolArgs::builder() .tool_id("read-multiple-files-1234".to_string()) .session_id("session-1234".to_string()) + .agent_id(Uuid::new_v4()) .build()), ) .await diff --git a/src/tools/file_system/read.rs b/src/tools/file_system/read.rs index 2b7af69..7696add 100644 --- a/src/tools/file_system/read.rs +++ b/src/tools/file_system/read.rs @@ -113,10 +113,10 @@ impl ToolProvider for ReadFileTool { for (path, result) in results { match result { Ok(content) => contents.push(ToolContent::Text { - text: format!("File: {}\n{}", path, content), + text: format!("File: {path}\n{content}"), }), Err(e) => contents.push(ToolContent::Text { - text: format!("Error reading {}: {}", path, e), + text: format!("Error reading {path}: {e}"), }), } } diff --git a/src/tools/file_system/search.rs b/src/tools/file_system/search.rs index 7dbf354..fd50f9e 100644 --- a/src/tools/file_system/search.rs +++ b/src/tools/file_system/search.rs @@ -70,8 +70,7 @@ impl SearchTool { .as_secs(); Ok(format!( - "Type: {}\nSize: {} bytes\nLast Modified: {} seconds since epoch", - file_type, size, modified + "Type: {file_type}\nSize: {size} bytes\nLast Modified: {modified} seconds since epoch" )) } } diff --git a/src/tools/mod.rs b/src/tools/mod.rs index c3f3bf0..33c35b3 100644 --- a/src/tools/mod.rs +++ b/src/tools/mod.rs @@ -209,15 +209,14 @@ impl ToolManager { self.send_tool_update_notification( name, ToolUpdateType::Removed, - Some(format!("Tool '{}' unregistered", name)), + Some(format!("Tool '{name}' unregistered")), ) .await; } Ok(()) } else { Err(McpError::InvalidRequest(format!( - "Tool '{}' not found", - name + "Tool '{name}' not found" ))) } } @@ -295,7 +294,7 @@ impl ToolManager { let tools = self.tools.read().await; let provider = tools .get(name) - .ok_or_else(|| McpError::InvalidRequest(format!("Unknown tool: {}", name)))?; + .ok_or_else(|| McpError::InvalidRequest(format!("Unknown tool: {name}")))?; provider.execute(arguments, metadata).await } diff --git a/src/tools/test_tool.rs b/src/tools/test_tool.rs index e2652ea..2bba844 100644 --- a/src/tools/test_tool.rs +++ b/src/tools/test_tool.rs @@ -99,7 +99,7 @@ impl ToolProvider for PingTool { .get("server") .and_then(|s| s.as_str()) .unwrap_or("localhost"); - let res = reqwest::get(format!("http://{}", server)) + let res = reqwest::get(format!("http://{server}")) .await .map_err(|e| McpError::ToolExecutionError(e.to_string()))?; let body = res diff --git a/src/transport/sse.rs b/src/transport/sse.rs index eeced8b..365349a 100644 --- a/src/transport/sse.rs +++ b/src/transport/sse.rs @@ -167,7 +167,7 @@ impl SseTransport { event_tx: mpsc::Sender, ) { let client = reqwest::Client::new(); - let sse_url = format!("http://{}:{}/sse", host, port); + let sse_url = format!("http://{host}:{port}/sse"); tracing::debug!("Connecting to SSE endpoint: {}", sse_url); diff --git a/src/transport/ws.rs b/src/transport/ws.rs index bebcaff..f556ae4 100644 --- a/src/transport/ws.rs +++ b/src/transport/ws.rs @@ -47,7 +47,7 @@ impl WebSocketTransport { pub fn new_client(host: String, port: u16, buffer_size: usize) -> Self { Self { config: WsTransportConfig::Client { - url: format!("ws://{}:{}/ws", host, port), + url: format!("ws://{host}:{port}/ws"), }, buffer_size, auth_header: None, @@ -57,7 +57,7 @@ impl WebSocketTransport { pub fn new_wss_client(host: String, port: u16, buffer_size: usize) -> Self { Self { config: WsTransportConfig::Client { - url: format!("wss://{}:{}/ws", host, port), + url: format!("wss://{host}:{port}/ws"), }, buffer_size, auth_header: None, diff --git a/tests/tools.rs b/tests/tools.rs index a87da02..5b23c3e 100644 --- a/tests/tools.rs +++ b/tests/tools.rs @@ -1,6 +1,7 @@ use async_trait::async_trait; use serde_json::{json, Value}; use std::{collections::HashMap, sync::Arc}; +use uuid::Uuid; use mcp_rs::{ error::McpError, @@ -137,6 +138,7 @@ async fn test_tool_execution() { CallToolArgs::builder() .session_id("calculator-id-1234".to_string()) .tool_id("calculator-tool-id-1234".to_string()) + .agent_id(Uuid::new_v4()) .build(), ), ) @@ -163,6 +165,7 @@ async fn test_tool_execution() { CallToolArgs::builder() .session_id("calculator-id-1234".to_string()) .tool_id("calculator-tool-id-1234".to_string()) + .agent_id(Uuid::new_v4()) .build(), ), ) @@ -223,6 +226,7 @@ async fn test_invalid_arguments() { CallToolArgs::builder() .session_id("calculator-id-1234".to_string()) .tool_id("calculator-tool-id-1234".to_string()) + .agent_id(Uuid::new_v4()) .build(), ), )