main
hkr04 2025-03-24 04:20:19 +08:00
commit 3fe1e049fb
6 changed files with 280 additions and 284 deletions

162
README.md
View File

@ -1,77 +1,77 @@
# MCP Protocol Framework # MCP Protocol Framework
[Model Context Protocol (MCP)](https://spec.modelcontextprotocol.io/specification/2024-11-05/architecture/) 是一个开放协议为AI模型和代理提供与各种资源、工具和服务交互的标准化方式。本框架实现了MCP协议的核心功能符合2024-11-05基本协议规范。 [Model Context Protocol (MCP)](https://spec.modelcontextprotocol.io/specification/2024-11-05/architecture/) is an open protocol that provides a standardized way for AI models and agents to interact with various resources, tools, and services. This framework implements the core functionality of the MCP protocol, conforming to the 2024-11-05 basic protocol specification.
## 核心特性 ## Core Features
- **JSON-RPC 2.0通信**: 基于JSON-RPC 2.0标准的请求/响应通信 - **JSON-RPC 2.0 Communication**: Request/response communication based on JSON-RPC 2.0 standard
- **资源抽象**: 文件、API等资源的标准接口 - **Resource Abstraction**: Standard interfaces for resources such as files, APIs, etc.
- **工具注册**: 注册和调用带有结构化参数的工具 - **Tool Registration**: Register and call tools with structured parameters
- **可扩展架构**: 易于扩展新的资源类型和工具 - **Extensible Architecture**: Easy to extend with new resource types and tools
- **多传输支持**: 支持HTTP和标准输入/输出(stdio)通信方式 - **Multi-Transport Support**: Supports HTTP and standard input/output (stdio) communication methods
## 组件 ## Components
MCP C++库包含以下主要组件: The MCP C++ library includes the following main components:
### 核心组件 ### Core Components
#### 客户端接口 (`mcp_client.h`) #### Client Interface (`mcp_client.h`)
定义了MCP客户端的抽象接口所有具体的客户端实现都继承自这个接口。 Defines the abstract interface for MCP clients, which all concrete client implementations inherit from.
#### SSE客户端 (`mcp_sse_client.h`, `mcp_sse_client.cpp`) #### SSE Client (`mcp_sse_client.h`, `mcp_sse_client.cpp`)
使用HTTP和Server-Sent Events (SSE)与MCP服务器通信的客户端实现。 Client implementation that communicates with MCP servers using HTTP and Server-Sent Events (SSE).
#### Stdio客户端 (`mcp_stdio_client.h`, `mcp_stdio_client.cpp`) #### Stdio Client (`mcp_stdio_client.h`, `mcp_stdio_client.cpp`)
使用标准输入/输出与MCP服务器通信的客户端实现可以启动子进程并与之通信。 Client implementation that communicates with MCP servers using standard input/output, capable of launching subprocesses and communicating with them.
#### 消息处理 (`mcp_message.h`, `mcp_message.cpp`) #### Message Processing (`mcp_message.h`, `mcp_message.cpp`)
处理JSON-RPC消息的序列化和反序列化。 Handles serialization and deserialization of JSON-RPC messages.
#### 工具管理 (`mcp_tool.h`, `mcp_tool.cpp`) #### Tool Management (`mcp_tool.h`, `mcp_tool.cpp`)
管理和调用MCP工具。 Manages and invokes MCP tools.
#### 资源管理 (`mcp_resource.h`, `mcp_resource.cpp`) #### Resource Management (`mcp_resource.h`, `mcp_resource.cpp`)
管理MCP资源。 Manages MCP resources.
#### 服务器 (`mcp_server.h`, `mcp_server.cpp`) #### Server (`mcp_server.h`, `mcp_server.cpp`)
实现MCP服务器功能。 Implements MCP server functionality.
## 示例 ## Examples
### HTTP服务器示例 (`examples/server_example.cpp`) ### HTTP Server Example (`examples/server_example.cpp`)
MCP服务器实现示例带有自定义工具: Example MCP server implementation with custom tools:
- 时间工具: 获取当前时间 - Time tool: Get the current time
- 计算器工具: 执行数学运算 - Calculator tool: Perform mathematical operations
- 回显工具: 处理和分析文本 - Echo tool: Process and analyze text
- 招呼工具:返回`Hello, `+传入名字+`!`,默认返回`Hello, World!` - Greeting tool: Returns `Hello, `+input name+`!`, defaults to `Hello, World!`
### HTTP客户端示例 (`examples/client_example.cpp`) ### HTTP Client Example (`examples/client_example.cpp`)
连接到服务器的MCP客户端示例: Example MCP client connecting to a server:
- 获取服务器信息 - Get server information
- 列出可用工具 - List available tools
- 使用参数调用工具 - Call tools with parameters
- 访问资源 - Access resources
### Stdio客户端示例 (`examples/stdio_client_example.cpp`) ### Stdio Client Example (`examples/stdio_client_example.cpp`)
展示如何使用stdio客户端与本地服务器通信: Demonstrates how to use the stdio client to communicate with a local server:
- 启动本地服务器进程 - Launch a local server process
- 访问文件系统资源 - Access filesystem resources
- 调用服务器工具 - Call server tools
## 如何使用 ## How to Use
### 设置HTTP服务器 ### Setting up an HTTP Server
```cpp ```cpp
// 创建并配置服务器 // Create and configure the server
mcp::server server("localhost", 8080); mcp::server server("localhost", 8080);
server.set_server_info("MCP Example Server", "2024-11-05"); server.set_server_info("MCP Example Server", "2024-11-05");
// 注册工具 // Register tools
mcp::json hello_handler(const mcp::json& params) { mcp::json hello_handler(const mcp::json& params) {
std::string name = params.contains("name") ? params["name"].get<std::string>() : "World"; std::string name = params.contains("name") ? params["name"].get<std::string>() : "World";
return { return {
@ -89,24 +89,24 @@ mcp::tool hello_tool = mcp::tool_builder("hello")
server.register_tool(hello_tool, hello_handler); server.register_tool(hello_tool, hello_handler);
// 注册资源 // Register resources
auto file_resource = std::make_shared<mcp::file_resource>("<file_path>"); auto file_resource = std::make_shared<mcp::file_resource>("<file_path>");
server.register_resource("file://<file_path>", file_resource); server.register_resource("file://<file_path>", file_resource);
// 启动服务器 // Start the server
server.start(true); // 阻塞模式 server.start(true); // Blocking mode
``` ```
### 创建HTTP客户端 ### Creating an HTTP Client
```cpp ```cpp
// 连接到服务器 // Connect to the server
mcp::sse_client client("localhost", 8080); mcp::sse_client client("localhost", 8080);
// 初始化连接 // Initialize the connection
client.initialize("My Client", "1.0.0"); client.initialize("My Client", "1.0.0");
// 调用工具 // Call a tool
mcp::json params = { mcp::json params = {
{"name", "Client"} {"name", "Client"}
}; };
@ -114,83 +114,83 @@ mcp::json params = {
mcp::json result = client.call_tool("hello", params); mcp::json result = client.call_tool("hello", params);
``` ```
### 使用SSE客户端 ### Using the SSE Client
SSE客户端使用HTTP和Server-Sent Events (SSE) 与MCP服务器通信。这是一种基于Web标准的通信方式适合与支持HTTP/SSE的服务器通信。 The SSE client uses HTTP and Server-Sent Events (SSE) to communicate with MCP servers. This is a communication method based on Web standards, suitable for communicating with servers that support HTTP/SSE.
```cpp ```cpp
#include "mcp_sse_client.h" #include "mcp_sse_client.h"
// 创建客户端,指定服务器地址和端口 // Create a client, specifying the server address and port
mcp::sse_client client("localhost", 8080); mcp::sse_client client("localhost", 8080);
// 或者使用基础URL // Or use a base URL
// mcp::sse_client client("http://localhost:8080"); // mcp::sse_client client("http://localhost:8080");
// 设置认证令牌(如果需要) // Set an authentication token (if needed)
client.set_auth_token("your_auth_token"); client.set_auth_token("your_auth_token");
// 设置自定义请求头(如果需要) // Set custom request headers (if needed)
client.set_header("X-Custom-Header", "value"); client.set_header("X-Custom-Header", "value");
// 初始化客户端 // Initialize the client
if (!client.initialize("My Client", "1.0.0")) { if (!client.initialize("My Client", "1.0.0")) {
// 初始化失败处理 // Handle initialization failure
} }
// 调用工具 // Call a tool
json result = client.call_tool("tool_name", { json result = client.call_tool("tool_name", {
{"param1", "value1"}, {"param1", "value1"},
{"param2", 42} {"param2", 42}
}); });
``` ```
### 使用Stdio客户端 ### Using the Stdio Client
Stdio客户端可以与任何支持stdio传输的MCP服务器进行通信例如 The Stdio client can communicate with any MCP server that supports stdio transport, such as:
- @modelcontextprotocol/server-everything - 示例服务器 - @modelcontextprotocol/server-everything - Example server
- @modelcontextprotocol/server-filesystem - 文件系统服务器 - @modelcontextprotocol/server-filesystem - Filesystem server
- 其他支持stdio传输的[MCP服务器](https://www.pulsemcp.com/servers) - Other [MCP servers](https://www.pulsemcp.com/servers) that support stdio transport
```cpp ```cpp
#include "mcp_stdio_client.h" #include "mcp_stdio_client.h"
// 创建客户端,指定服务器命令 // Create a client, specifying the server command
mcp::stdio_client client("npx -y @modelcontextprotocol/server-everything"); mcp::stdio_client client("npx -y @modelcontextprotocol/server-everything");
// mcp::stdio_client client("npx -y @modelcontextprotocol/server-filesystem /path/to/directory"); // mcp::stdio_client client("npx -y @modelcontextprotocol/server-filesystem /path/to/directory");
// 初始化客户端 // Initialize the client
if (!client.initialize("My Client", "1.0.0")) { if (!client.initialize("My Client", "1.0.0")) {
// 初始化失败处理 // Handle initialization failure
} }
// 访问资源 // Access resources
json resources = client.list_resources(); json resources = client.list_resources();
json content = client.read_resource("resource://uri"); json content = client.read_resource("resource://uri");
// 调用工具 // Call a tool
json result = client.call_tool("tool_name", { json result = client.call_tool("tool_name", {
{"param1", "value1"}, {"param1", "value1"},
{"param2", "value2"} {"param2", "value2"}
}); });
``` ```
## 构建框架 ## Building the Framework
框架依赖以下库: The framework depends on the following libraries:
- httplib.h - HTTP服务器和客户端 - httplib.h - HTTP server and client
- json.hpp - JSON解析和生成 - json.hpp - JSON parsing and generation
- gtest - 测试 - gtest - Testing
所有依赖项都包含在仓库中。 All dependencies are included in the repository.
使用CMake构建示例: Example of building with CMake:
```bash ```bash
cmake -B build cmake -B build
cmake --build build --config Release cmake --build build --config Release
``` ```
## 许可证 ## License
本框架根据MIT许可证提供。有关详细信息请参阅LICENSE文件。 This framework is provided under the MIT license. For details, please see the LICENSE file.

View File

@ -18,7 +18,7 @@ add_library(${TARGET} STATIC
target_link_libraries(${TARGET} PUBLIC ${CMAKE_THREAD_LIBS_INIT}) target_link_libraries(${TARGET} PUBLIC ${CMAKE_THREAD_LIBS_INIT})
# OpenSSLOpenSSL # If OpenSSL is found, link the OpenSSL libraries
if(OPENSSL_FOUND) if(OPENSSL_FOUND)
target_link_libraries(${TARGET} PUBLIC ${OPENSSL_LIBRARIES}) target_link_libraries(${TARGET} PUBLIC ${OPENSSL_LIBRARIES})
endif() endif()

View File

@ -26,7 +26,7 @@ bool server::start(bool blocking) {
LOG_INFO("Starting MCP server on ", host_, ":", port_); LOG_INFO("Starting MCP server on ", host_, ":", port_);
// 设置CORS处理 // Setup CORS handling
http_server_->Options(".*", [](const httplib::Request& req, httplib::Response& res) { http_server_->Options(".*", [](const httplib::Request& req, httplib::Response& res) {
res.set_header("Access-Control-Allow-Origin", "*"); res.set_header("Access-Control-Allow-Origin", "*");
res.set_header("Access-Control-Allow-Methods", "GET, POST, OPTIONS"); res.set_header("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
@ -34,23 +34,23 @@ bool server::start(bool blocking) {
res.status = 204; // No Content res.status = 204; // No Content
}); });
// 设置JSON-RPC端点 // Setup JSON-RPC endpoint
http_server_->Post(msg_endpoint_.c_str(), [this](const httplib::Request& req, httplib::Response& res) { http_server_->Post(msg_endpoint_.c_str(), [this](const httplib::Request& req, httplib::Response& res) {
this->handle_jsonrpc(req, res); this->handle_jsonrpc(req, res);
LOG_INFO(req.remote_addr, ":", req.remote_port, " - \"POST ", req.path, " HTTP/1.1\" ", res.status); LOG_INFO(req.remote_addr, ":", req.remote_port, " - \"POST ", req.path, " HTTP/1.1\" ", res.status);
}); });
// 设置SSE端点 // Setup SSE endpoint
http_server_->Get(sse_endpoint_.c_str(), [this](const httplib::Request& req, httplib::Response& res) { http_server_->Get(sse_endpoint_.c_str(), [this](const httplib::Request& req, httplib::Response& res) {
this->handle_sse(req, res); this->handle_sse(req, res);
LOG_INFO(req.remote_addr, ":", req.remote_port, " - \"GET ", req.path, " HTTP/1.1\" ", res.status); LOG_INFO(req.remote_addr, ":", req.remote_port, " - \"GET ", req.path, " HTTP/1.1\" ", res.status);
}); });
// 启动资源检查线程(优化:只在非阻塞模式下启动) // Start resource check thread (only start in non-blocking mode)
if (!blocking) { if (!blocking) {
maintenance_thread_ = std::make_unique<std::thread>([this]() { maintenance_thread_ = std::make_unique<std::thread>([this]() {
while (running_) { while (running_) {
// 每60秒检查一次不活跃的会话 // Check inactive sessions every 60 seconds
std::this_thread::sleep_for(std::chrono::seconds(60)); std::this_thread::sleep_for(std::chrono::seconds(60));
if (running_) { if (running_) {
try { try {
@ -65,7 +65,7 @@ bool server::start(bool blocking) {
}); });
} }
// 启动服务器 // Start server
if (blocking) { if (blocking) {
running_ = true; running_ = true;
LOG_INFO("Starting server in blocking mode"); LOG_INFO("Starting server in blocking mode");
@ -76,7 +76,7 @@ bool server::start(bool blocking) {
} }
return true; return true;
} else { } else {
// 在单独的线程中启动 // Start server in a separate thread
server_thread_ = std::make_unique<std::thread>([this]() { server_thread_ = std::make_unique<std::thread>([this]() {
LOG_INFO("Starting server in separate thread"); LOG_INFO("Starting server in separate thread");
if (!http_server_->listen(host_.c_str(), port_)) { if (!http_server_->listen(host_.c_str(), port_)) {
@ -98,7 +98,7 @@ void server::stop() {
LOG_INFO("Stopping MCP server on ", host_, ":", port_); LOG_INFO("Stopping MCP server on ", host_, ":", port_);
running_ = false; running_ = false;
// 关闭维护线程 // Close maintenance thread
if (maintenance_thread_ && maintenance_thread_->joinable()) { if (maintenance_thread_ && maintenance_thread_->joinable()) {
try { try {
maintenance_thread_->join(); maintenance_thread_->join();
@ -107,20 +107,20 @@ void server::stop() {
} }
} }
// 复制所有分发器和线程,避免长时间持有锁 // Copy all dispatchers and threads to avoid holding the lock for too long
std::vector<std::shared_ptr<event_dispatcher>> dispatchers_to_close; std::vector<std::shared_ptr<event_dispatcher>> dispatchers_to_close;
std::vector<std::unique_ptr<std::thread>> threads_to_join; std::vector<std::unique_ptr<std::thread>> threads_to_join;
{ {
std::lock_guard<std::mutex> lock(mutex_); std::lock_guard<std::mutex> lock(mutex_);
// 复制所有分发器 // Copy all dispatchers
dispatchers_to_close.reserve(session_dispatchers_.size()); dispatchers_to_close.reserve(session_dispatchers_.size());
for (const auto& [_, dispatcher] : session_dispatchers_) { for (const auto& [_, dispatcher] : session_dispatchers_) {
dispatchers_to_close.push_back(dispatcher); dispatchers_to_close.push_back(dispatcher);
} }
// 复制所有线程 // Copy all threads
threads_to_join.reserve(sse_threads_.size()); threads_to_join.reserve(sse_threads_.size());
for (auto& [_, thread] : sse_threads_) { for (auto& [_, thread] : sse_threads_) {
if (thread && thread->joinable()) { if (thread && thread->joinable()) {
@ -128,27 +128,27 @@ void server::stop() {
} }
} }
// 清空映射表 // Clear the maps
session_dispatchers_.clear(); session_dispatchers_.clear();
sse_threads_.clear(); sse_threads_.clear();
session_initialized_.clear(); session_initialized_.clear();
} }
// 在锁外关闭所有分发器 // Close all dispatchers outside the lock
for (auto& dispatcher : dispatchers_to_close) { for (auto& dispatcher : dispatchers_to_close) {
if (dispatcher && !dispatcher->is_closed()) { if (dispatcher && !dispatcher->is_closed()) {
try { try {
dispatcher->close(); dispatcher->close();
} catch (...) { } catch (...) {
// 忽略异常 // Ignore exceptions
} }
} }
} }
// 给线程一些时间处理关闭事件 // Give threads some time to handle close events
std::this_thread::sleep_for(std::chrono::milliseconds(300)); std::this_thread::sleep_for(std::chrono::milliseconds(300));
// 在锁外等待线程结束(有超时限制) // Wait for threads to finish outside the lock (with timeout limit)
const auto timeout_point = std::chrono::steady_clock::now() + std::chrono::seconds(2); const auto timeout_point = std::chrono::steady_clock::now() + std::chrono::seconds(2);
for (auto& thread : threads_to_join) { for (auto& thread : threads_to_join) {
@ -157,20 +157,20 @@ void server::stop() {
} }
if (std::chrono::steady_clock::now() >= timeout_point) { if (std::chrono::steady_clock::now() >= timeout_point) {
// 如果已经超时detach剩余线程 // If timeout reached, detach remaining threads
LOG_WARNING("Thread join timeout reached, detaching remaining threads"); LOG_WARNING("Thread join timeout reached, detaching remaining threads");
thread->detach(); thread->detach();
continue; continue;
} }
// 尝试使用超时的join // Try using timeout join
bool joined = false; bool joined = false;
try { try {
// 创建future和promise用于实现thread join的超时处理 // Create future and promise for timeout join
std::promise<void> thread_done; std::promise<void> thread_done;
auto future = thread_done.get_future(); auto future = thread_done.get_future();
// 在另一个线程中尝试join // Try join in another thread
std::thread join_helper([&thread, &thread_done]() { std::thread join_helper([&thread, &thread_done]() {
try { try {
thread->join(); thread->join();
@ -182,13 +182,13 @@ void server::stop() {
} }
}); });
// 等待join完成或超时 // Wait for join to complete or timeout
if (future.wait_for(std::chrono::milliseconds(100)) == std::future_status::ready) { if (future.wait_for(std::chrono::milliseconds(100)) == std::future_status::ready) {
future.get(); // 获取可能的异常 future.get(); // Get possible exception
joined = true; joined = true;
} }
// 处理join_helper线程 // Process join_helper thread
if (join_helper.joinable()) { if (join_helper.joinable()) {
if (joined) { if (joined) {
join_helper.join(); join_helper.join();
@ -200,12 +200,12 @@ void server::stop() {
joined = false; joined = false;
} }
// 如果join失败detach // If join fails, then detach
if (!joined) { if (!joined) {
try { try {
thread->detach(); thread->detach();
} catch (...) { } catch (...) {
// 忽略异常 // Ignore exceptions
} }
} }
} }
@ -382,37 +382,37 @@ void server::handle_sse(const httplib::Request& req, httplib::Response& res) {
std::string session_id = generate_session_id(); std::string session_id = generate_session_id();
std::string session_uri = msg_endpoint_ + "?session_id=" + session_id; std::string session_uri = msg_endpoint_ + "?session_id=" + session_id;
// 设置SSE响应头 // Setup SSE response headers
res.set_header("Content-Type", "text/event-stream"); res.set_header("Content-Type", "text/event-stream");
res.set_header("Cache-Control", "no-cache"); res.set_header("Cache-Control", "no-cache");
res.set_header("Connection", "keep-alive"); res.set_header("Connection", "keep-alive");
res.set_header("Access-Control-Allow-Origin", "*"); res.set_header("Access-Control-Allow-Origin", "*");
// 创建会话特定的事件分发器 // Create session-specific event dispatcher
auto session_dispatcher = std::make_shared<event_dispatcher>(); auto session_dispatcher = std::make_shared<event_dispatcher>();
// 初始化活动时间 // Initialize activity time
session_dispatcher->update_activity(); session_dispatcher->update_activity();
// 添加会话分发器到映射表 // Add session dispatcher to mapping table
{ {
std::lock_guard<std::mutex> lock(mutex_); std::lock_guard<std::mutex> lock(mutex_);
session_dispatchers_[session_id] = session_dispatcher; session_dispatchers_[session_id] = session_dispatcher;
} }
// 创建会话线程 // Create session thread
auto thread = std::make_unique<std::thread>([this, res, session_id, session_uri, session_dispatcher]() { auto thread = std::make_unique<std::thread>([this, res, session_id, session_uri, session_dispatcher]() {
try { try {
// 发送初始会话URI // Send initial session URI
std::this_thread::sleep_for(std::chrono::milliseconds(500)); std::this_thread::sleep_for(std::chrono::milliseconds(500));
std::stringstream ss; std::stringstream ss;
ss << "event: endpoint\r\ndata: " << session_uri << "\r\n\r\n"; ss << "event: endpoint\r\ndata: " << session_uri << "\r\n\r\n";
session_dispatcher->send_event(ss.str()); session_dispatcher->send_event(ss.str());
// 更新活动时间(发送消息后) // Update activity time (after sending message)
session_dispatcher->update_activity(); session_dispatcher->update_activity();
// 定期发送心跳,检测连接状态 // Send periodic heartbeats to detect connection status
int heartbeat_count = 0; int heartbeat_count = 0;
while (running_ && !session_dispatcher->is_closed()) { while (running_ && !session_dispatcher->is_closed()) {
std::this_thread::sleep_for(std::chrono::seconds(5) + std::chrono::milliseconds(rand() % 500)); // NOTE: DO NOT set it the same as the timeout of wait_event std::this_thread::sleep_for(std::chrono::seconds(5) + std::chrono::milliseconds(rand() % 500)); // NOTE: DO NOT set it the same as the timeout of wait_event
@ -431,7 +431,7 @@ void server::handle_sse(const httplib::Request& req, httplib::Response& res) {
break; break;
} }
// 更新活动时间(心跳成功) // Update activity time (heartbeat successful)
session_dispatcher->update_activity(); session_dispatcher->update_activity();
} catch (const std::exception& e) { } catch (const std::exception& e) {
LOG_ERROR("Failed to send heartbeat: ", e.what()); LOG_ERROR("Failed to send heartbeat: ", e.what());
@ -442,39 +442,39 @@ void server::handle_sse(const httplib::Request& req, httplib::Response& res) {
LOG_ERROR("SSE session thread exception: ", session_id, ", ", e.what()); LOG_ERROR("SSE session thread exception: ", session_id, ", ", e.what());
} }
// 安全地清理资源 // Clean up resources safely
try { try {
// 先复制需要处理的资源指针 // Copy resources to be processed
std::shared_ptr<event_dispatcher> dispatcher_to_close; std::shared_ptr<event_dispatcher> dispatcher_to_close;
std::unique_ptr<std::thread> thread_to_release; std::unique_ptr<std::thread> thread_to_release;
{ {
std::lock_guard<std::mutex> lock(mutex_); std::lock_guard<std::mutex> lock(mutex_);
// 获取dispatcher指针 // Get dispatcher pointer
auto dispatcher_it = session_dispatchers_.find(session_id); auto dispatcher_it = session_dispatchers_.find(session_id);
if (dispatcher_it != session_dispatchers_.end()) { if (dispatcher_it != session_dispatchers_.end()) {
dispatcher_to_close = dispatcher_it->second; dispatcher_to_close = dispatcher_it->second;
session_dispatchers_.erase(dispatcher_it); session_dispatchers_.erase(dispatcher_it);
} }
// 获取线程指针 // Get thread pointer
auto thread_it = sse_threads_.find(session_id); auto thread_it = sse_threads_.find(session_id);
if (thread_it != sse_threads_.end()) { if (thread_it != sse_threads_.end()) {
thread_to_release = std::move(thread_it->second); thread_to_release = std::move(thread_it->second);
sse_threads_.erase(thread_it); sse_threads_.erase(thread_it);
} }
// 清理初始化状态 // Clean up initialization status
session_initialized_.erase(session_id); session_initialized_.erase(session_id);
} }
// 在锁外关闭dispatcher // Close dispatcher outside the lock
if (dispatcher_to_close && !dispatcher_to_close->is_closed()) { if (dispatcher_to_close && !dispatcher_to_close->is_closed()) {
dispatcher_to_close->close(); dispatcher_to_close->close();
} }
// 释放线程资源 // Release thread resources
if (thread_to_release) { if (thread_to_release) {
thread_to_release.release(); thread_to_release.release();
} }
@ -485,42 +485,42 @@ void server::handle_sse(const httplib::Request& req, httplib::Response& res) {
} }
}); });
// 存储线程 // Store thread
{ {
std::lock_guard<std::mutex> lock(mutex_); std::lock_guard<std::mutex> lock(mutex_);
sse_threads_[session_id] = std::move(thread); sse_threads_[session_id] = std::move(thread);
} }
// 设置分块内容提供者 // Setup chunked content provider
res.set_chunked_content_provider("text/event-stream", [this, session_id, session_dispatcher](size_t /* offset */, httplib::DataSink& sink) { res.set_chunked_content_provider("text/event-stream", [this, session_id, session_dispatcher](size_t /* offset */, httplib::DataSink& sink) {
try { try {
// 检查会话是否已关闭 - 直接从分发器获取状态,减少锁冲突 // Check if session is closed - directly get status from dispatcher, reduce lock contention
if (session_dispatcher->is_closed()) { if (session_dispatcher->is_closed()) {
return false; return false;
} }
// 更新活动时间(接收到请求) // Update activity time (received request)
session_dispatcher->update_activity(); session_dispatcher->update_activity();
// 等待事件 // Wait for event
bool result = session_dispatcher->wait_event(&sink); bool result = session_dispatcher->wait_event(&sink);
if (!result) { if (!result) {
LOG_WARNING("Failed to wait for event, closing connection: ", session_id); LOG_WARNING("Failed to wait for event, closing connection: ", session_id);
// 直接关闭分发器,无需加锁 // Close dispatcher directly, no need to lock
session_dispatcher->close(); session_dispatcher->close();
return false; return false;
} }
// 更新活动时间(成功接收消息) // Update activity time (successfully received message)
session_dispatcher->update_activity(); session_dispatcher->update_activity();
return true; return true;
} catch (const std::exception& e) { } catch (const std::exception& e) {
LOG_ERROR("SSE content provider exception: ", e.what()); LOG_ERROR("SSE content provider exception: ", e.what());
// 直接关闭分发器,无需加锁 // Close dispatcher directly, no need to lock
session_dispatcher->close(); session_dispatcher->close();
return false; return false;
@ -529,23 +529,23 @@ void server::handle_sse(const httplib::Request& req, httplib::Response& res) {
} }
void server::handle_jsonrpc(const httplib::Request& req, httplib::Response& res) { void server::handle_jsonrpc(const httplib::Request& req, httplib::Response& res) {
// 设置响应头 // Setup response headers
res.set_header("Content-Type", "application/json"); res.set_header("Content-Type", "application/json");
res.set_header("Access-Control-Allow-Origin", "*"); res.set_header("Access-Control-Allow-Origin", "*");
res.set_header("Access-Control-Allow-Methods", "POST, OPTIONS"); res.set_header("Access-Control-Allow-Methods", "POST, OPTIONS");
res.set_header("Access-Control-Allow-Headers", "Content-Type"); res.set_header("Access-Control-Allow-Headers", "Content-Type");
// 处理OPTIONS请求CORS预检 // Handle OPTIONS request (CORS pre-flight)
if (req.method == "OPTIONS") { if (req.method == "OPTIONS") {
res.status = 204; // No Content res.status = 204; // No Content
return; return;
} }
// 获取会话ID // Get session ID
auto it = req.params.find("session_id"); auto it = req.params.find("session_id");
std::string session_id = it != req.params.end() ? it->second : ""; std::string session_id = it != req.params.end() ? it->second : "";
// 更新会话活动时间 // Update session activity time
if (!session_id.empty()) { if (!session_id.empty()) {
std::shared_ptr<event_dispatcher> dispatcher; std::shared_ptr<event_dispatcher> dispatcher;
{ {
@ -561,7 +561,7 @@ void server::handle_jsonrpc(const httplib::Request& req, httplib::Response& res)
} }
} }
// 解析请求 // Parse request
json req_json; json req_json;
try { try {
req_json = json::parse(req.body); req_json = json::parse(req.body);
@ -572,13 +572,13 @@ void server::handle_jsonrpc(const httplib::Request& req, httplib::Response& res)
return; return;
} }
// 检查会话是否存在 // Check if session exists
std::shared_ptr<event_dispatcher> dispatcher; std::shared_ptr<event_dispatcher> dispatcher;
{ {
std::lock_guard<std::mutex> lock(mutex_); std::lock_guard<std::mutex> lock(mutex_);
auto disp_it = session_dispatchers_.find(session_id); auto disp_it = session_dispatchers_.find(session_id);
if (disp_it == session_dispatchers_.end()) { if (disp_it == session_dispatchers_.end()) {
// 处理ping请求 // Handle ping request
if (req_json["method"] == "ping") { if (req_json["method"] == "ping") {
res.status = 202; res.status = 202;
res.set_content("Accepted", "text/plain"); res.set_content("Accepted", "text/plain");
@ -592,7 +592,7 @@ void server::handle_jsonrpc(const httplib::Request& req, httplib::Response& res)
dispatcher = disp_it->second; dispatcher = disp_it->second;
} }
// 创建请求对象 // Create request object
request mcp_req; request mcp_req;
try { try {
mcp_req.jsonrpc = req_json["jsonrpc"].get<std::string>(); mcp_req.jsonrpc = req_json["jsonrpc"].get<std::string>();
@ -610,25 +610,25 @@ void server::handle_jsonrpc(const httplib::Request& req, httplib::Response& res)
return; return;
} }
// 如果是通知没有ID直接处理并返回202状态码 // If it is a notification (no ID), process it directly and return 202 status code
if (mcp_req.is_notification()) { if (mcp_req.is_notification()) {
// 在线程池中异步处理通知 // Process it asynchronously in the thread pool
thread_pool_.enqueue([this, mcp_req, session_id]() { thread_pool_.enqueue([this, mcp_req, session_id]() {
process_request(mcp_req, session_id); process_request(mcp_req, session_id);
}); });
// 返回202 Accepted // Return 202 Accepted
res.status = 202; res.status = 202;
res.set_content("Accepted", "text/plain"); res.set_content("Accepted", "text/plain");
return; return;
} }
// 对于有ID的请求在线程池中处理并通过SSE返回结果 // For requests with ID, process it asynchronously in the thread pool and return the result via SSE
thread_pool_.enqueue([this, mcp_req, session_id, dispatcher]() { thread_pool_.enqueue([this, mcp_req, session_id, dispatcher]() {
// 处理请求 // Process the request
json response_json = process_request(mcp_req, session_id); json response_json = process_request(mcp_req, session_id);
// 通过SSE发送响应 // Send response via SSE
std::stringstream ss; std::stringstream ss;
ss << "event: message\r\ndata: " << response_json.dump() << "\r\n\r\n"; ss << "event: message\r\ndata: " << response_json.dump() << "\r\n\r\n";
bool result = dispatcher->send_event(ss.str()); bool result = dispatcher->send_event(ss.str());
@ -638,13 +638,13 @@ void server::handle_jsonrpc(const httplib::Request& req, httplib::Response& res)
} }
}); });
// 返回202 Accepted // Return 202 Accepted
res.status = 202; res.status = 202;
res.set_content("Accepted", "text/plain"); res.set_content("Accepted", "text/plain");
} }
json server::process_request(const request& req, const std::string& session_id) { json server::process_request(const request& req, const std::string& session_id) {
// 检查是否是通知 // Check if it is a notification
if (req.is_notification()) { if (req.is_notification()) {
if (req.method == "notifications/initialized") { if (req.method == "notifications/initialized") {
set_session_initialized(session_id, true); set_session_initialized(session_id, true);
@ -652,11 +652,11 @@ json server::process_request(const request& req, const std::string& session_id)
return json::object(); return json::object();
} }
// 处理方法调用 // Process method call
try { try {
LOG_INFO("Processing method call: ", req.method); LOG_INFO("Processing method call: ", req.method);
// 特殊情况:初始化 // Special case: initialization
if (req.method == "initialize") { if (req.method == "initialize") {
return handle_initialize(req, session_id); return handle_initialize(req, session_id);
} else if (req.method == "ping") { } else if (req.method == "ping") {
@ -672,7 +672,7 @@ json server::process_request(const request& req, const std::string& session_id)
).to_json(); ).to_json();
} }
// 查找注册的方法处理器 // Find registered method handler
std::function<json(const json&)> handler; std::function<json(const json&)> handler;
{ {
std::lock_guard<std::mutex> lock(mutex_); std::lock_guard<std::mutex> lock(mutex_);
@ -683,19 +683,19 @@ json server::process_request(const request& req, const std::string& session_id)
} }
if (handler) { if (handler) {
// 调用处理器 // Call handler
LOG_INFO("Calling method handler: ", req.method); LOG_INFO("Calling method handler: ", req.method);
auto future = thread_pool_.enqueue([handler, params = req.params]() -> json { auto future = thread_pool_.enqueue([handler, params = req.params]() -> json {
return handler(params); return handler(params);
}); });
json result = future.get(); json result = future.get();
// 创建成功响应 // Create success response
LOG_INFO("Method call successful: ", req.method); LOG_INFO("Method call successful: ", req.method);
return response::create_success(req.id, result).to_json(); return response::create_success(req.id, result).to_json();
} }
// 方法未找到 // Method not found
LOG_WARNING("Method not found: ", req.method); LOG_WARNING("Method not found: ", req.method);
return response::create_error( return response::create_error(
req.id, req.id,
@ -703,7 +703,7 @@ json server::process_request(const request& req, const std::string& session_id)
"Method not found: " + req.method "Method not found: " + req.method
).to_json(); ).to_json();
} catch (const mcp_exception& e) { } catch (const mcp_exception& e) {
// MCP异常 // MCP exception
LOG_ERROR("MCP exception: ", e.what(), ", code: ", static_cast<int>(e.code())); LOG_ERROR("MCP exception: ", e.what(), ", code: ", static_cast<int>(e.code()));
return response::create_error( return response::create_error(
req.id, req.id,
@ -711,7 +711,7 @@ json server::process_request(const request& req, const std::string& session_id)
e.what() e.what()
).to_json(); ).to_json();
} catch (const std::exception& e) { } catch (const std::exception& e) {
// 其他异常 // Other exceptions
LOG_ERROR("Exception while processing request: ", e.what()); LOG_ERROR("Exception while processing request: ", e.what());
return response::create_error( return response::create_error(
req.id, req.id,
@ -719,7 +719,7 @@ json server::process_request(const request& req, const std::string& session_id)
"Internal error: " + std::string(e.what()) "Internal error: " + std::string(e.what())
).to_json(); ).to_json();
} catch (...) { } catch (...) {
// 未知异常 // Unknown exception
LOG_ERROR("Unknown exception while processing request"); LOG_ERROR("Unknown exception while processing request");
return response::create_error( return response::create_error(
req.id, req.id,
@ -792,7 +792,7 @@ json server::handle_initialize(const request& req, const std::string& session_id
} }
void server::send_request(const std::string& session_id, const std::string& method, const json& params) { void server::send_request(const std::string& session_id, const std::string& method, const json& params) {
// 检查会话ID是否有效 // Check if session ID is valid
if (session_id.empty()) { if (session_id.empty()) {
LOG_WARNING("Cannot send request to empty session_id"); LOG_WARNING("Cannot send request to empty session_id");
return; return;
@ -810,7 +810,7 @@ void server::send_request(const std::string& session_id, const std::string& meth
// Create request // Create request
request req = request::create(method, params); request req = request::create(method, params);
// 获取会话分发器 // Get session dispatcher
std::shared_ptr<event_dispatcher> dispatcher; std::shared_ptr<event_dispatcher> dispatcher;
{ {
std::lock_guard<std::mutex> lock(mutex_); std::lock_guard<std::mutex> lock(mutex_);
@ -822,13 +822,13 @@ void server::send_request(const std::string& session_id, const std::string& meth
dispatcher = it->second; dispatcher = it->second;
} }
// 确认dispatcher仍然有效 // Confirm dispatcher is still valid
if (!dispatcher || dispatcher->is_closed()) { if (!dispatcher || dispatcher->is_closed()) {
LOG_WARNING("Cannot send to closed session: ", session_id); LOG_WARNING("Cannot send to closed session: ", session_id);
return; return;
} }
// 发送请求 // Send request
std::stringstream ss; std::stringstream ss;
ss << "event: message\r\ndata: " << req.to_json().dump() << "\r\n\r\n"; ss << "event: message\r\ndata: " << req.to_json().dump() << "\r\n\r\n";
bool result = dispatcher->send_event(ss.str()); bool result = dispatcher->send_event(ss.str());
@ -839,7 +839,7 @@ void server::send_request(const std::string& session_id, const std::string& meth
} }
bool server::is_session_initialized(const std::string& session_id) const { bool server::is_session_initialized(const std::string& session_id) const {
// 检查会话ID是否有效 // Check if session ID is valid
if (session_id.empty()) { if (session_id.empty()) {
return false; return false;
} }
@ -855,7 +855,7 @@ bool server::is_session_initialized(const std::string& session_id) const {
} }
void server::set_session_initialized(const std::string& session_id, bool initialized) { void server::set_session_initialized(const std::string& session_id, bool initialized) {
// 检查会话ID是否有效 // Check if session ID is valid
if (session_id.empty()) { if (session_id.empty()) {
LOG_WARNING("Cannot set initialization state for empty session_id"); LOG_WARNING("Cannot set initialization state for empty session_id");
return; return;
@ -863,7 +863,7 @@ void server::set_session_initialized(const std::string& session_id, bool initial
try { try {
std::lock_guard<std::mutex> lock(mutex_); std::lock_guard<std::mutex> lock(mutex_);
// 检查会话是否仍然存在 // Check if session still exists
auto it = session_dispatchers_.find(session_id); auto it = session_dispatchers_.find(session_id);
if (it == session_dispatchers_.end()) { if (it == session_dispatchers_.end()) {
LOG_WARNING("Cannot set initialization state for non-existent session: ", session_id); LOG_WARNING("Cannot set initialization state for non-existent session: ", session_id);
@ -915,7 +915,7 @@ void server::check_inactive_sessions() {
if (!running_) return; if (!running_) return;
const auto now = std::chrono::steady_clock::now(); const auto now = std::chrono::steady_clock::now();
const auto timeout = std::chrono::minutes(3); // 3分钟不活跃则关闭 const auto timeout = std::chrono::minutes(60); // 1 hour inactive then close
std::vector<std::string> sessions_to_close; std::vector<std::string> sessions_to_close;
@ -923,13 +923,13 @@ void server::check_inactive_sessions() {
std::lock_guard<std::mutex> lock(mutex_); std::lock_guard<std::mutex> lock(mutex_);
for (const auto& [session_id, dispatcher] : session_dispatchers_) { for (const auto& [session_id, dispatcher] : session_dispatchers_) {
if (now - dispatcher->last_activity() > timeout) { if (now - dispatcher->last_activity() > timeout) {
// 超过闲置时间限制 // Exceeded idle time limit
sessions_to_close.push_back(session_id); sessions_to_close.push_back(session_id);
} }
} }
} }
// 关闭不活跃的会话 // Close inactive sessions
for (const auto& session_id : sessions_to_close) { for (const auto& session_id : sessions_to_close) {
LOG_INFO("Closing inactive session: ", session_id); LOG_INFO("Closing inactive session: ", session_id);

View File

@ -542,12 +542,15 @@ json sse_client::send_jsonrpc(const request& req) {
if (status == std::future_status::ready) { if (status == std::future_status::ready) {
json response = response_future.get(); json response = response_future.get();
if (response.contains("isError") && response["isError"].get<bool>()) { if (response.contains("isError") && response["isError"].is_boolean() && response["isError"].get<bool>()) {
int code = response["error"]["code"]; if (response.contains("error") && response["error"].is_object()) {
std::string message = response["error"]["message"]; const auto& err_obj = response["error"];
int code = err_obj.contains("code") ? err_obj["code"].get<int>() : static_cast<int>(error_code::internal_error);
std::string message = err_obj.value("message", "");
// Handle error
throw mcp_exception(static_cast<error_code>(code), message); throw mcp_exception(static_cast<error_code>(code), message);
} }
}
return response; return response;
} else { } else {

View File

@ -219,13 +219,13 @@ bool stdio_client::start_server_process() {
}; };
#if defined(_WIN32) || defined(_WIN64) #if defined(_WIN32) || defined(_WIN64)
// Windows实现 // Windows implementation
SECURITY_ATTRIBUTES sa; SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(SECURITY_ATTRIBUTES); sa.nLength = sizeof(SECURITY_ATTRIBUTES);
sa.bInheritHandle = TRUE; sa.bInheritHandle = TRUE;
sa.lpSecurityDescriptor = NULL; sa.lpSecurityDescriptor = NULL;
// 创建管道 // Create pipes
HANDLE child_stdin_read = NULL; HANDLE child_stdin_read = NULL;
HANDLE child_stdin_write = NULL; HANDLE child_stdin_write = NULL;
HANDLE child_stdout_read = NULL; HANDLE child_stdout_read = NULL;
@ -259,7 +259,7 @@ bool stdio_client::start_server_process() {
return false; return false;
} }
// 准备进程启动信息 // Prepare process startup info
STARTUPINFOA si; STARTUPINFOA si;
PROCESS_INFORMATION pi; PROCESS_INFORMATION pi;
@ -272,7 +272,7 @@ bool stdio_client::start_server_process() {
ZeroMemory(&pi, sizeof(PROCESS_INFORMATION)); ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
// 准备环境变量 // Prepare environment variables
std::string env_block; std::string env_block;
if (!env_vars_.empty()) { if (!env_vars_.empty()) {
for (const auto& [key, value] : env_vars_.items()) { for (const auto& [key, value] : env_vars_.items()) {
@ -282,21 +282,21 @@ bool stdio_client::start_server_process() {
env_block += '\0'; env_block += '\0';
} }
// 创建子进程 // Create child process
std::string cmd_line = command_; std::string cmd_line = command_;
char* cmd_str = const_cast<char*>(cmd_line.c_str()); char* cmd_str = const_cast<char*>(cmd_line.c_str());
BOOL success = CreateProcessA( BOOL success = CreateProcessA(
NULL, // 应用程序名称 NULL, // Application name
cmd_str, // 命令行 cmd_str, // Command line
NULL, // 进程安全属性 NULL, // Process security attributes
NULL, // 线程安全属性 NULL, // Thread security attributes
TRUE, // 继承句柄 TRUE, // Inherit handles
CREATE_NO_WINDOW, // 创建标志 CREATE_NO_WINDOW, // Creation flags
env_vars_.empty() ? NULL : (LPVOID)env_block.c_str(), // 环境变量 env_vars_.empty() ? NULL : (LPVOID)env_block.c_str(), // Environment variables
NULL, // 当前目录 NULL, // Current directory
&si, // 启动信息 &si, // Startup info
&pi // 进程信息 &pi // Process info
); );
if (!success) { if (!success) {
@ -308,12 +308,12 @@ bool stdio_client::start_server_process() {
return false; return false;
} }
// 关闭不需要的句柄 // Close unnecessary handles
CloseHandle(child_stdin_read); CloseHandle(child_stdin_read);
CloseHandle(child_stdout_write); CloseHandle(child_stdout_write);
CloseHandle(pi.hThread); CloseHandle(pi.hThread);
// 保存进程信息 // Save process info
process_id_ = pi.dwProcessId; process_id_ = pi.dwProcessId;
process_handle_ = pi.hProcess; process_handle_ = pi.hProcess;
stdin_pipe_[0] = NULL; stdin_pipe_[0] = NULL;
@ -321,13 +321,13 @@ bool stdio_client::start_server_process() {
stdout_pipe_[0] = child_stdout_read; stdout_pipe_[0] = child_stdout_read;
stdout_pipe_[1] = NULL; stdout_pipe_[1] = NULL;
// 设置非阻塞模式 // Set non-blocking mode
DWORD mode = PIPE_NOWAIT; DWORD mode = PIPE_NOWAIT;
SetNamedPipeHandleState(stdout_pipe_[0], &mode, NULL, NULL); SetNamedPipeHandleState(stdout_pipe_[0], &mode, NULL, NULL);
#else #else
// POSIX实现 // POSIX implementation
// 创建管道 // Create pipes
if (pipe(stdin_pipe_) == -1) { if (pipe(stdin_pipe_) == -1) {
LOG_ERROR("Failed to create stdin pipe: ", strerror(errno)); LOG_ERROR("Failed to create stdin pipe: ", strerror(errno));
return false; return false;
@ -340,7 +340,7 @@ bool stdio_client::start_server_process() {
return false; return false;
} }
// 创建子进程 // Create child process
process_id_ = fork(); process_id_ = fork();
if (process_id_ == -1) { if (process_id_ == -1) {
@ -353,9 +353,9 @@ bool stdio_client::start_server_process() {
} }
if (process_id_ == 0) { if (process_id_ == 0) {
// 子进程 // Child process
// 设置环境变量 // Set environment variables
if (!env_vars_.empty()) { if (!env_vars_.empty()) {
for (const auto& [key, value] : env_vars_.items()) { for (const auto& [key, value] : env_vars_.items()) {
std::string env_var = key + "=" + convert_to_string(value); std::string env_var = key + "=" + convert_to_string(value);
@ -365,11 +365,11 @@ bool stdio_client::start_server_process() {
} }
} }
// 关闭不需要的管道端 // Close unnecessary pipe ends
close(stdin_pipe_[1]); // 关闭写入端 close(stdin_pipe_[1]); // Close write end
close(stdout_pipe_[0]); // 关闭读取端 close(stdout_pipe_[0]); // Close read end
// 重定向标准输入/输出 // Redirect standard input/output
if (dup2(stdin_pipe_[0], STDIN_FILENO) == -1) { if (dup2(stdin_pipe_[0], STDIN_FILENO) == -1) {
LOG_ERROR("Failed to redirect stdin: ", strerror(errno)); LOG_ERROR("Failed to redirect stdin: ", strerror(errno));
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
@ -380,15 +380,15 @@ bool stdio_client::start_server_process() {
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
// 关闭已重定向的文件描述符 // Close already redirected file descriptors
close(stdin_pipe_[0]); close(stdin_pipe_[0]);
close(stdout_pipe_[1]); close(stdout_pipe_[1]);
// 设置非阻塞模式 // Set non-blocking mode
int flags = fcntl(STDIN_FILENO, F_GETFL, 0); int flags = fcntl(STDIN_FILENO, F_GETFL, 0);
fcntl(STDIN_FILENO, F_SETFL, flags | O_NONBLOCK); fcntl(STDIN_FILENO, F_SETFL, flags | O_NONBLOCK);
// 执行命令 // Execute command
std::vector<std::string> args; std::vector<std::string> args;
std::istringstream iss(command_); std::istringstream iss(command_);
std::string arg; std::string arg;
@ -405,22 +405,22 @@ bool stdio_client::start_server_process() {
execvp(c_args[0], c_args.data()); execvp(c_args[0], c_args.data());
// 如果execvp返回则表示出错 // If execvp returns, it means an error occurred
LOG_ERROR("Failed to execute command: ", strerror(errno)); LOG_ERROR("Failed to execute command: ", strerror(errno));
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
// 父进程 // Parent process
// 关闭不需要的管道端 // Close unnecessary pipe ends
close(stdin_pipe_[0]); // 关闭读取端 close(stdin_pipe_[0]); // Close read end
close(stdout_pipe_[1]); // 关闭写入端 close(stdout_pipe_[1]); // Close write end
// 设置非阻塞模式 // Set non-blocking mode
int flags = fcntl(stdout_pipe_[0], F_GETFL, 0); int flags = fcntl(stdout_pipe_[0], F_GETFL, 0);
fcntl(stdout_pipe_[0], F_SETFL, flags | O_NONBLOCK); fcntl(stdout_pipe_[0], F_SETFL, flags | O_NONBLOCK);
// 检查进程是否仍在运行 // Check if process is still running
int status; int status;
pid_t result = waitpid(process_id_, &status, WNOHANG); pid_t result = waitpid(process_id_, &status, WNOHANG);
@ -453,14 +453,14 @@ bool stdio_client::start_server_process() {
running_ = true; running_ = true;
// 启动读取线程 // Start read thread
read_thread_ = std::make_unique<std::thread>(&stdio_client::read_thread_func, this); read_thread_ = std::make_unique<std::thread>(&stdio_client::read_thread_func, this);
// 等待一段时间,确保进程启动 // Wait for a while to ensure process starts
std::this_thread::sleep_for(std::chrono::milliseconds(500)); std::this_thread::sleep_for(std::chrono::milliseconds(500));
#if defined(_WIN32) || defined(_WIN64) #if defined(_WIN32) || defined(_WIN64)
// 检查进程是否仍在运行 // Check if process is still running
DWORD exit_code; DWORD exit_code;
if (GetExitCodeProcess(process_handle_, &exit_code) && exit_code != STILL_ACTIVE) { if (GetExitCodeProcess(process_handle_, &exit_code) && exit_code != STILL_ACTIVE) {
LOG_ERROR("Server process exited immediately with status: ", exit_code); LOG_ERROR("Server process exited immediately with status: ", exit_code);
@ -492,8 +492,8 @@ void stdio_client::stop_server_process() {
running_ = false; running_ = false;
#if defined(_WIN32) || defined(_WIN64) #if defined(_WIN32) || defined(_WIN64)
// Windows实现 // Windows implementation
// 关闭管道 // Close pipes
if (stdin_pipe_[1] != NULL) { if (stdin_pipe_[1] != NULL) {
CloseHandle(stdin_pipe_[1]); CloseHandle(stdin_pipe_[1]);
stdin_pipe_[1] = NULL; stdin_pipe_[1] = NULL;
@ -504,22 +504,22 @@ void stdio_client::stop_server_process() {
stdout_pipe_[0] = NULL; stdout_pipe_[0] = NULL;
} }
// 等待读取线程结束 // Wait for read thread to finish
if (read_thread_ && read_thread_->joinable()) { if (read_thread_ && read_thread_->joinable()) {
read_thread_->join(); read_thread_->join();
} }
// 终止进程 // Terminate process
if (process_handle_ != NULL) { if (process_handle_ != NULL) {
LOG_INFO("Terminating process: ", process_id_); LOG_INFO("Terminating process: ", process_id_);
TerminateProcess(process_handle_, 0); TerminateProcess(process_handle_, 0);
// 等待进程结束 // Wait for process to finish
WaitForSingleObject(process_handle_, 2000); WaitForSingleObject(process_handle_, 2000);
DWORD exit_code; DWORD exit_code;
if (GetExitCodeProcess(process_handle_, &exit_code) && exit_code == STILL_ACTIVE) { if (GetExitCodeProcess(process_handle_, &exit_code) && exit_code == STILL_ACTIVE) {
// 进程仍在运行,强制终止 // Process is still running, force termination
LOG_WARNING("Process did not terminate, forcing termination"); LOG_WARNING("Process did not terminate, forcing termination");
TerminateProcess(process_handle_, 1); TerminateProcess(process_handle_, 1);
WaitForSingleObject(process_handle_, 1000); WaitForSingleObject(process_handle_, 1000);
@ -530,8 +530,8 @@ void stdio_client::stop_server_process() {
process_id_ = -1; process_id_ = -1;
} }
#else #else
// POSIX实现 // POSIX implementation
// 关闭管道 // Close pipes
if (stdin_pipe_[1] != -1) { if (stdin_pipe_[1] != -1) {
close(stdin_pipe_[1]); close(stdin_pipe_[1]);
stdin_pipe_[1] = -1; stdin_pipe_[1] = -1;
@ -542,28 +542,28 @@ void stdio_client::stop_server_process() {
stdout_pipe_[0] = -1; stdout_pipe_[0] = -1;
} }
// 等待读取线程结束 // Wait for read thread to finish
if (read_thread_ && read_thread_->joinable()) { if (read_thread_ && read_thread_->joinable()) {
read_thread_->join(); read_thread_->join();
} }
// 终止进程 // Terminate process
if (process_id_ > 0) { if (process_id_ > 0) {
LOG_INFO("Sending SIGTERM to process: ", process_id_); LOG_INFO("Sending SIGTERM to process: ", process_id_);
kill(process_id_, SIGTERM); kill(process_id_, SIGTERM);
// 等待进程结束 // Wait for process to finish
int status; int status;
pid_t result = waitpid(process_id_, &status, WNOHANG); pid_t result = waitpid(process_id_, &status, WNOHANG);
if (result == 0) { if (result == 0) {
// 进程仍在运行,等待一段时间 // Process is still running, wait for a while
std::this_thread::sleep_for(std::chrono::seconds(2)); std::this_thread::sleep_for(std::chrono::seconds(2));
result = waitpid(process_id_, &status, WNOHANG); result = waitpid(process_id_, &status, WNOHANG);
if (result == 0) { if (result == 0) {
// 进程仍在运行,强制终止 // Process is still running, force termination
LOG_WARNING("Process did not terminate, sending SIGKILL"); LOG_WARNING("Process did not terminate, sending SIGKILL");
kill(process_id_, SIGKILL); kill(process_id_, SIGKILL);
waitpid(process_id_, &status, 0); waitpid(process_id_, &status, 0);
@ -585,18 +585,18 @@ void stdio_client::read_thread_func() {
std::string data_buffer; std::string data_buffer;
#if defined(_WIN32) || defined(_WIN64) #if defined(_WIN32) || defined(_WIN64)
// Windows实现 // Windows implementation
DWORD bytes_read; DWORD bytes_read;
while (running_) { while (running_) {
// 读取数据 // Read data
BOOL success = ReadFile(stdout_pipe_[0], buffer, buffer_size - 1, &bytes_read, NULL); BOOL success = ReadFile(stdout_pipe_[0], buffer, buffer_size - 1, &bytes_read, NULL);
if (success && bytes_read > 0) { if (success && bytes_read > 0) {
buffer[bytes_read] = '\0'; buffer[bytes_read] = '\0';
data_buffer.append(buffer, bytes_read); data_buffer.append(buffer, bytes_read);
// 处理完整的JSON-RPC消息 // Process complete JSON-RPC message
size_t pos = 0; size_t pos = 0;
while ((pos = data_buffer.find('\n')) != std::string::npos) { while ((pos = data_buffer.find('\n')) != std::string::npos) {
std::string line = data_buffer.substr(0, pos); std::string line = data_buffer.substr(0, pos);
@ -608,7 +608,7 @@ void stdio_client::read_thread_func() {
if (message.contains("jsonrpc") && message["jsonrpc"] == "2.0") { if (message.contains("jsonrpc") && message["jsonrpc"] == "2.0") {
if (message.contains("id") && !message["id"].is_null()) { if (message.contains("id") && !message["id"].is_null()) {
// 这是一个响应 // This is a response
json id = message["id"]; json id = message["id"];
std::lock_guard<std::mutex> lock(response_mutex_); std::lock_guard<std::mutex> lock(response_mutex_);
@ -632,9 +632,9 @@ void stdio_client::read_thread_func() {
LOG_WARNING("Received response for unknown request ID: ", id); LOG_WARNING("Received response for unknown request ID: ", id);
} }
} else if (message.contains("method")) { } else if (message.contains("method")) {
// 这是一个请求或通知 // This is a request or notification
LOG_INFO("Received request/notification: ", message["method"]); LOG_INFO("Received request/notification: ", message["method"]);
// 目前不处理服务器发来的请求 // Currently not handling requests from the server
} }
} }
} catch (const json::exception& e) { } catch (const json::exception& e) {
@ -645,7 +645,7 @@ void stdio_client::read_thread_func() {
} else if (!success) { } else if (!success) {
DWORD error = GetLastError(); DWORD error = GetLastError();
if (error == ERROR_BROKEN_PIPE || error == ERROR_NO_DATA) { if (error == ERROR_BROKEN_PIPE || error == ERROR_NO_DATA) {
// 管道已关闭或没有数据 // Pipe is closed or no data available
LOG_WARNING("Pipe closed by server or no data available"); LOG_WARNING("Pipe closed by server or no data available");
break; break;
} else if (error != ERROR_IO_PENDING) { } else if (error != ERROR_IO_PENDING) {
@ -653,24 +653,24 @@ void stdio_client::read_thread_func() {
break; break;
} }
// 非阻塞模式下没有数据可读 // No data to read in non-blocking mode
std::this_thread::sleep_for(std::chrono::milliseconds(10)); std::this_thread::sleep_for(std::chrono::milliseconds(10));
} else { } else {
// 非阻塞模式下没有数据可读 // No data to read in non-blocking mode
std::this_thread::sleep_for(std::chrono::milliseconds(10)); std::this_thread::sleep_for(std::chrono::milliseconds(10));
} }
} }
#else #else
// POSIX实现 // POSIX implementation
while (running_) { while (running_) {
// 读取数据 // Read data
ssize_t bytes_read = read(stdout_pipe_[0], buffer, buffer_size - 1); ssize_t bytes_read = read(stdout_pipe_[0], buffer, buffer_size - 1);
if (bytes_read > 0) { if (bytes_read > 0) {
buffer[bytes_read] = '\0'; buffer[bytes_read] = '\0';
data_buffer.append(buffer, bytes_read); data_buffer.append(buffer, bytes_read);
// 处理完整的JSON-RPC消息 // Process complete JSON-RPC message
size_t pos = 0; size_t pos = 0;
while ((pos = data_buffer.find('\n')) != std::string::npos) { while ((pos = data_buffer.find('\n')) != std::string::npos) {
std::string line = data_buffer.substr(0, pos); std::string line = data_buffer.substr(0, pos);
@ -682,7 +682,7 @@ void stdio_client::read_thread_func() {
if (message.contains("jsonrpc") && message["jsonrpc"] == "2.0") { if (message.contains("jsonrpc") && message["jsonrpc"] == "2.0") {
if (message.contains("id") && !message["id"].is_null()) { if (message.contains("id") && !message["id"].is_null()) {
// 这是一个响应 // This is a response
json id = message["id"]; json id = message["id"];
std::lock_guard<std::mutex> lock(response_mutex_); std::lock_guard<std::mutex> lock(response_mutex_);
@ -706,9 +706,9 @@ void stdio_client::read_thread_func() {
LOG_WARNING("Received response for unknown request ID: ", id); LOG_WARNING("Received response for unknown request ID: ", id);
} }
} else if (message.contains("method")) { } else if (message.contains("method")) {
// 这是一个请求或通知 // This is a request or notification
LOG_INFO("Received request/notification: ", message["method"]); LOG_INFO("Received request/notification: ", message["method"]);
// 目前不处理服务器发来的请求 // Currently not handling requests from the server
} }
} }
} catch (const json::exception& e) { } catch (const json::exception& e) {
@ -717,12 +717,12 @@ void stdio_client::read_thread_func() {
} }
} }
} else if (bytes_read == 0) { } else if (bytes_read == 0) {
// 管道已关闭 // Pipe is closed
LOG_WARNING("Pipe closed by server"); LOG_WARNING("Pipe closed by server");
break; break;
} else if (bytes_read == -1) { } else if (bytes_read == -1) {
if (errno == EAGAIN || errno == EWOULDBLOCK) { if (errno == EAGAIN || errno == EWOULDBLOCK) {
// 非阻塞模式下没有数据可读 // No data to read in non-blocking mode
std::this_thread::sleep_for(std::chrono::milliseconds(10)); std::this_thread::sleep_for(std::chrono::milliseconds(10));
} else { } else {
LOG_ERROR("Error reading from pipe: ", strerror(errno)); LOG_ERROR("Error reading from pipe: ", strerror(errno));
@ -744,7 +744,7 @@ json stdio_client::send_jsonrpc(const request& req) {
std::string req_str = req_json.dump() + "\n"; std::string req_str = req_json.dump() + "\n";
#if defined(_WIN32) || defined(_WIN64) #if defined(_WIN32) || defined(_WIN64)
// Windows实现 // Windows implementation
DWORD bytes_written; DWORD bytes_written;
BOOL success = WriteFile(stdin_pipe_[1], req_str.c_str(), static_cast<DWORD>(req_str.size()), &bytes_written, NULL); BOOL success = WriteFile(stdin_pipe_[1], req_str.c_str(), static_cast<DWORD>(req_str.size()), &bytes_written, NULL);
@ -753,7 +753,7 @@ json stdio_client::send_jsonrpc(const request& req) {
throw mcp_exception(error_code::internal_error, "Failed to write to pipe"); throw mcp_exception(error_code::internal_error, "Failed to write to pipe");
} }
#else #else
// POSIX实现 // POSIX implementation
ssize_t bytes_written = write(stdin_pipe_[1], req_str.c_str(), req_str.size()); ssize_t bytes_written = write(stdin_pipe_[1], req_str.c_str(), req_str.size());
if (bytes_written != static_cast<ssize_t>(req_str.size())) { if (bytes_written != static_cast<ssize_t>(req_str.size())) {
@ -762,12 +762,12 @@ json stdio_client::send_jsonrpc(const request& req) {
} }
#endif #endif
// 如果是通知,不需要等待响应 // If this is a notification, no need to wait for a response
if (req.is_notification()) { if (req.is_notification()) {
return json::object(); return json::object();
} }
// 创建Promise和Future // Create Promise and Future
std::promise<json> response_promise; std::promise<json> response_promise;
std::future<json> response_future = response_promise.get_future(); std::future<json> response_future = response_promise.get_future();
@ -776,19 +776,22 @@ json stdio_client::send_jsonrpc(const request& req) {
pending_requests_[req.id] = std::move(response_promise); pending_requests_[req.id] = std::move(response_promise);
} }
// 等待响应,设置超时 // Wait for response, set timeout
const auto timeout = std::chrono::seconds(30); const auto timeout = std::chrono::seconds(30);
auto status = response_future.wait_for(timeout); auto status = response_future.wait_for(timeout);
if (status == std::future_status::ready) { if (status == std::future_status::ready) {
json response = response_future.get(); json response = response_future.get();
if (response.contains("isError") && response["isError"].get<bool>()) { if (response.contains("isError") && response["isError"].is_boolean() && response["isError"].get<bool>()) {
int code = response["error"]["code"]; if (response.contains("error") && response["error"].is_object()) {
std::string message = response["error"]["message"]; const auto& err_obj = response["error"];
int code = err_obj.contains("code") ? err_obj["code"].get<int>() : static_cast<int>(error_code::internal_error);
std::string message = err_obj.value("message", "");
// Handle error
throw mcp_exception(static_cast<error_code>(code), message); throw mcp_exception(static_cast<error_code>(code), message);
} }
}
return response; return response;
} else { } else {

View File

@ -1,69 +1,59 @@
# MCP 单元测试 # MCP Unit Tests
本目录包含 Model Context Protocol (MCP) 实现的单元测试,基于规范 2024-11-05。 This directory contains unit tests for the Model Context Protocol (MCP) implementation, based on the 2024-11-05 specification.
## 测试内容 ## Building and Running Tests
测试文件包括: ### Building Tests
- `test_mcp_message.cpp`: 测试消息相关功能
- `test_mcp_tool.cpp`: 测试工具相关功能
- `test_mcp_resource.cpp`: 测试资源相关功能
- `test_mcp_client.cpp`: 测试客户端相关功能
- `test_mcp_server.cpp`: 测试服务器相关功能
## 构建和运行测试
### 构建测试
```bash ```bash
# 在项目根目录创建构建目录 # Create a build directory in the project root
mkdir -p build && cd build mkdir -p build && cd build
# 配置项目 # Configure the project
cmake .. cmake ..
# 构建项目和测试 # Build the project and tests
make make
``` ```
### 运行测试 ### Running Tests
```bash ```bash
# 运行所有测试 # Run all tests
make run_tests make run_tests
# 或者直接运行测试可执行文件 # Or directly run the test executable
./test/mcp_tests ./test/mcp_tests
``` ```
### 运行特定测试 ### Running Specific Tests
要运行特定的测试,可以使用 Google Test 的过滤功能: To run specific tests, you can use Google Test's filtering capability:
```bash ```bash
# 运行所有消息相关测试 # Run all message-related tests
./test/mcp_tests --gtest_filter=McpMessageTest.* ./test/mcp_tests --gtest_filter=McpMessageTest.*
# 运行所有工具相关测试 # Run all tool-related tests
./test/mcp_tests --gtest_filter=McpToolTest.* ./test/mcp_tests --gtest_filter=McpToolTest.*
# 运行所有资源相关测试 # Run all resource-related tests
./test/mcp_tests --gtest_filter=McpResourceTest.* ./test/mcp_tests --gtest_filter=McpResourceTest.*
# 运行所有客户端相关测试 # Run all client-related tests
./test/mcp_tests --gtest_filter=ClientTest.* ./test/mcp_tests --gtest_filter=ClientTest.*
# 运行所有服务器相关测试 # Run all server-related tests
./test/mcp_tests --gtest_filter=ServerTest.* ./test/mcp_tests --gtest_filter=ServerTest.*
``` ```
## 测试依赖 ## Test Dependencies
测试使用 Google Test 框架,该框架会在构建时自动下载和配置。 Tests use the Google Test framework, which is automatically downloaded and configured during the build process.
## 注意事项 ## Notes
- 部分测试需要网络功能,确保本地端口(如 8090、8095未被占用 - Some tests require network functionality, ensure that local ports (such as 8090, 8095) are not in use
- 客户端和服务器测试会启动实际的服务器和客户端进行交互测试 - Client and server tests will start actual servers and clients for interaction testing
- 资源测试会在临时目录创建文件,测试完成后会自动清理 - Resource tests will create files in a temporary directory, which will be automatically cleaned up after testing