c++开发大模型mcp服务(二)MCP 架构说明

image

 电脑硬件接口为类比的MCP架构示意图

参考自:MCP 核心架构解析(TYPESCRIPT实现)

1.整体架构设计

MCP采用经典的客户端-服务器架构模型,包含三个主要角色:

  • 主机(Host):通常是启动连接的LLM应用程序,如Claude Desktop或各类IDE环境。主机负责初始化和维护整个通信流程。
  • 客户端(Client):在主机应用内部运行,与服务器保持1:1的连接关系,负责发送请求和接收响应。
  • 服务器(Server):向客户端提供上下文信息、工具支持和提示内容,是整个协议的服务提供方。
    image

这种分层架构设计使得MCP能够灵活适应不同的应用场景,同时保持高效的通信性能。

 2.数据流程设计

1) 初始化阶段

  1. 客户端发送初始化请求,包含协议版本和能力信息
  2. 服务器响应其协议版本和能力
  3. 客户端发送初始化通知作为确认
  4. 正常消息交换开始

image

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,功能全面,性能高。
  • 线程库: 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. 初始化与生命周期

遵循原文描述的初始化流程:

  1. 启动: MCP Server 进程启动,初始化 MCPServer 对象并绑定 StdioTransport
  2. 初始化请求: Host (Client) 发送 initialize 请求,包含其能力(capabilities)。
  3. 初始化响应: Server 解析 initialize 请求,配置自身,然后发送包含自身能力的 initialize 响应。
  4. 初始化确认: Host 发送 initialized 通知,表示初始化完成。
  5. 正常通信: 开始处理 request 和 notification 消息。
  6. 终止: 当 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 协议,关键在于:

  1. 理解其客户端-服务器和基于 JSON-RPC 的核心架构。
  2. 选择合适的JSON和网络/IO库。
  3. 实现一个健壮的传输层(如 Stdio 或 HTTP)。
  4. 构建一个协议层来解析消息、路由请求/通知并生成响应。
  5. 遵循标准的初始化和生命周期流程。

通过这种方式,您可以创建高性能、可集成的 C++ MCP 服务器,将本地工具、系统功能或专用库安全地暴露给 LLM 应用。

posted @ 2025-08-28 14:52  南水之源  阅读(69)  评论(1)    收藏  举报