c++开发大模型mcp服务(二)MCP 架构说明
电脑硬件接口为类比的MCP架构示意图
参考自:MCP 核心架构解析(TYPESCRIPT实现)
1.整体架构设计
MCP采用经典的客户端-服务器架构模型,包含三个主要角色:
- 主机(Host):通常是启动连接的LLM应用程序,如Claude Desktop或各类IDE环境。主机负责初始化和维护整个通信流程。
- 客户端(Client):在主机应用内部运行,与服务器保持1:1的连接关系,负责发送请求和接收响应。
- 服务器(Server):向客户端提供上下文信息、工具支持和提示内容,是整个协议的服务提供方。
这种分层架构设计使得MCP能够灵活适应不同的应用场景,同时保持高效的通信性能。
2.数据流程设计
1) 初始化阶段
- 客户端发送初始化请求,包含协议版本和能力信息
- 服务器响应其协议版本和能力
- 客户端发送初始化通知作为确认
- 正常消息交换开始
2 ) 消息交换阶段
支持两种基本模式:
- 请求-响应模式:客户端或服务器发送请求,对方返回响应
- 通知模式:任一方发送不需要响应的单向消息
3 ) 终止阶段
连接可通过以下方式终止:
- 调用close()方法进行优雅关闭
- 传输层断开
- 出现错误条件
3. 技术栈与依赖
实现一个 C++ MCP 服务器需要以下关键技术:
- JSON 库: 用于序列化和反序列化 JSON-RPC 消息。推荐使用成熟的库:
nlohmann/json
: 功能强大,易用,头文件仅依赖。rapidjson
: 高性能,内存占用低。
- 网络库: 用于实现传输层。根据需求选择:
- 标准输入/输出 (Stdio): 适用于与本地进程(如桌面 AI 应用)通信。实现最简单,使用
std::cin
和std::cout
。 - HTTP/HTTPS 库: 适用于远程通信。推荐:
cpp-httplib
: 轻量级,头文件仅依赖的 HTTP 服务器/客户端库。Boost.Beast
: 基于 Boost.Asio,功能全面,性能高。
- 标准输入/输出 (Stdio): 适用于与本地进程(如桌面 AI 应用)通信。实现最简单,使用
- 线程库: MCP 需要处理异步消息。使用 C++11 标准库的
<thread>
,<mutex>
,<future>
或std::async
。 - 构建系统:
CMake
是管理 C++ 项目依赖和构建过程的行业标准。
4. 核心消息类型(C++ 数据结构)
MCP 消息基于 JSON-RPC 2.0 格式。在 C++ 中,可以使用 nlohmann::json
对象作为所有消息的通用容器,或定义结构体/类。
cpp
#include <nlohmann/json.hpp>
using json = nlohmann::json;
// --- 1. 请求 (Request) ---
struct Request {
std::string method; // 方法名,如 "listResources"
json params; // 可选参数对象
std::string id; // 请求ID,用于匹配响应 (JSON-RPC 2.0 规范)
// 构造函数、序列化/反序列化方法 (to_json, from_json) 可省略,直接用 json 对象操作
};
// --- 2. 结果/响应 (Result) ---
// Result 是对成功请求的响应,其结构由具体 method 决定。
// 例如,listResources 的响应可能如下:
struct ListResourcesResult {
std::vector<Resource> resources; // Resource 包含 uri, name 等字段
};
// --- 3. 错误 (Error) ---
struct Error {
int code; // 错误码 (参考 ErrorCode 枚举)
std::string message; // 错误描述
json data; // 可选的附加数据
};
// --- 4. 通知 (Notification) ---
// 与 Request 结构相同,但没有 'id' 字段。
struct Notification {
std::string method;
json params;
};
// --- 标准错误码 (参考原文) ---
enum class ErrorCode {
ParseError = -32700,
InvalidRequest = -32600,
MethodNotFound = -32601,
InvalidParams = -32602,
InternalError = -32603,
// 自定义错误码范围: -32000 到 -32768
};
5. 传输层实现 (以 Stdio 为例)
Stdio 传输简单高效,适合本地集成。
cpp
class StdioTransport {
public:
// 从标准输入读取一行 JSON 消息
bool readMessage(json& message) {
std::string line;
if (std::getline(std::cin, line)) {
try {
message = json::parse(line);
return true;
} catch (const json::parse_error& e) {
// 发送 ParseError
sendError(ErrorCode::ParseError, "Invalid JSON", e.what());
return false;
}
}
return false; // EOF or error
}
// 将 JSON 消息写入标准输出
void sendMessage(const json& message) {
std::cout << message.dump() << std::endl; // dump() 转为字符串,endl 发送
std::cout.flush(); // 确保立即输出
}
private:
void sendError(ErrorCode code, const std::string& message, const std::string& data = "") {
json errorJson = {
{"jsonrpc", "2.0"},
{"error", {
{"code", static_cast<int>(code)},
{"message", message},
{"data", data}
}}
// 注意:如果是对请求的错误响应,需要包含 'id'
};
sendMessage(errorJson);
}
};
6. 协议层与服务器实现
实现一个 MCP Server
类,管理连接、消息路由和请求处理。
cpp
class MCPServer {
private:
std::unique_ptr<Transport> transport; // 可以是 StdioTransport 或 HTTPTransport
std::map<std::string, std::function<json(const json&)>> requestHandlers;
std::map<std::string, std::function<void(const json&)>> notificationHandlers;
bool initialized = false;
public:
MCPServer(std::unique_ptr<Transport> trans) : transport(std::move(trans)) {}
// 注册请求处理函数
template<typename Handler>
void setRequestHandler(const std::string& method, Handler&& handler) {
requestHandlers[method] = std::function<json(const json&)>(handler);
}
// 注册通知处理函数
template<typename Handler>
void setNotificationHandler(const std::string& method, Handler&& handler) {
notificationHandlers[method] = std::function<void(const json&)>(handler);
}
// 启动服务器主循环
void run() {
json message;
while (transport->readMessage(message)) {
try {
handleMessage(message);
} catch (const std::exception& e) {
// 记录日志,避免因单个消息导致服务器崩溃
std::cerr << "Error handling message: " << e.what() << std::endl;
}
}
// 读取结束 (EOF),正常退出
}
private:
void handleMessage(const json& message) {
// 检查是否为通知 (无 'id' 字段)
if (message.find("id") == message.end()) {
handleNotification(message);
return;
}
// 处理请求
handleRequest(message);
}
void handleNotification(const json& notification) {
std::string method = notification.value("method", "");
auto it = notificationHandlers.find(method);
if (it != notificationHandlers.end()) {
it->second(notification.value("params", json{}));
} else {
// MethodNotFound 通常只对请求返回,通知可忽略或记录日志
std::cerr << "Notification method not found: " << method << std::endl;
}
}
void handleRequest(const json& request) {
std::string method = request.value("method", "");
json params = request.value("params", json{});
std::string id = request.value("id", ""); // 必须存在
auto it = requestHandlers.find(method);
if (it != requestHandlers.end()) {
try {
json result = it->second(params);
// 构造成功响应
json response = {
{"jsonrpc", "2.0"},
{"id", id},
{"result", result}
};
transport->sendMessage(response);
} catch (const std::exception& e) {
// 构造错误响应
json response = {
{"jsonrpc", "2.0"},
{"id", id},
{"error", {
{"code", static_cast<int>(ErrorCode::InternalError)},
{"message", "Request handler error"},
{"data", e.what()}
}}
};
transport->sendMessage(response);
}
} else {
// 方法未找到
json response = {
{"jsonrpc", "2.0"},
{"id", id},
{"error", {
{"code", static_cast<int>(ErrorCode::MethodNotFound)},
{"message", "Method not found: " + method}
}}
};
transport->sendMessage(response);
}
}
};
7. 初始化与生命周期
遵循原文描述的初始化流程:
- 启动: MCP Server 进程启动,初始化
MCPServer
对象并绑定StdioTransport
。 - 初始化请求: Host (Client) 发送
initialize
请求,包含其能力(capabilities)。 - 初始化响应: Server 解析
initialize
请求,配置自身,然后发送包含自身能力的initialize
响应。 - 初始化确认: Host 发送
initialized
通知,表示初始化完成。 - 正常通信: 开始处理
request
和notification
消息。 - 终止: 当
transport->readMessage()
返回false
(EOF) 或收到特定关闭通知时,服务器优雅退出。
8. 示例:实现一个简单的资源列表服务器
cpp
int main() {
// 1. 创建传输层
auto transport = std::make_unique<StdioTransport>();
MCPServer server(std::move(transport));
// 2. 注册处理函数 (示例)
server.setRequestHandler("listResources", [](const json& params) -> json {
// 模拟返回资源列表
return json::array({
{{"uri", "file:///home/user/docs"}, {"name", "Documents"}},
{{"uri", "file:///home/user/code"}, {"name", "Code"}}
});
});
server.setNotificationHandler("exit", [](const json& params) {
// 收到 exit 通知,可以执行清理并退出
std::exit(0);
});
// ... 注册其他处理函数 (如 initialize)
// 3. 启动主循环
server.run();
return 0;
}
9. 最佳实践与注意事项
- 错误处理: 严格遵循 JSON-RPC 2.0 错误码,避免泄露敏感信息。
- 输入验证: 仔细验证
params
的结构和类型,防止注入攻击。 - 线程安全: 如果处理函数可能阻塞(如网络IO),考虑在独立线程中执行,并确保
transport->sendMessage()
是线程安全的。 - 资源管理: 正确管理内存、文件句柄、网络连接等资源,避免泄漏。
- 日志记录: 添加适当的日志,便于调试和监控。
- 安全: 对于 HTTP 传输,务必使用 TLS (HTTPS)。验证连接来源(如通过 API Key)。
10. 总结
使用 C++ 实现 MCP 协议,关键在于:
- 理解其客户端-服务器和基于 JSON-RPC 的核心架构。
- 选择合适的JSON和网络/IO库。
- 实现一个健壮的传输层(如 Stdio 或 HTTP)。
- 构建一个协议层来解析消息、路由请求/通知并生成响应。
- 遵循标准的初始化和生命周期流程。
通过这种方式,您可以创建高性能、可集成的 C++ MCP 服务器,将本地工具、系统功能或专用库安全地暴露给 LLM 应用。