mcp-cpp
一个现代的C++20 SDK 模型上下文协议 (MCP),目标方案修订 2025-11-25.
状态: 贝塔。线格式锁定在官方MCP规范中;C++ API在1.0之前可能仍有改进。
 
为什么
LLM应用程序越来越需要一种统一的方式来与工具、资源和 及时的供应商。MCP就是那个协议;该SDK旨在成为一个干净、快速、, 符合规范的C++实现, 代理、本地应用程序和高吞吐量服务器。
设计目标:
- 规格忠实。 字段名、方法名和行为与
官方TypeScript模式 逐字逐句。
- C++20的习语。 RAII,价值语义,
std::variant对于总和类型,
异步结果的未来,没有继承层次结构,组合将 同上
- 表面小,杠杆大。 有重点的公共API;大部分工作
发生在几个名字很好的类型后面。
- 生产准备就绪。 消毒剂清洁,必须放在安全的地方,没有
热路径上的隐藏分配。
0.1中有什么
| 功能 | 服务器 | 客户端 | 备注 |
|---|---|---|---|
| 初始化/能力协商 | ✅ | ✅ | 规格2025-11-25 |
| 工具(列表、调用) | ✅ | ✅ | 文本/图像/音频内容块 |
| 资源(列表、阅读、模板、订阅/取消订阅) | ✅ | ✅ | 文本+base64 blob内容 |
| 提示(列表,获取) | ✅ | ✅ | 带有内容块的键入邮件 |
取样(sampling/createMessage) | ✅ 发起人 | ✅ 响应者 | 服务器发起LLM调用 |
根(roots/list) | ✅ 发起人 | ✅ 响应者 | 客户端文件系统作用域 |
竣工(completion/complete) | ✅ | ✅ | 自动补全建议 |
取消(notifications/cancelled) | ✅ | ✅ | 钢丝水平支撑 |
进展(notifications/progress) | ✅ | ✅ | 类型化令牌+处理程序 |
日志记录(notifications/message, logging/setLevel) | ✅ | ✅ | RFC-5424级别 |
Ping(ping) | ✅ | ✅ | 双向活性 |
| 分页 | ✅ | ✅ | 可配置页面大小 |
stdio 运输 | ✅ | ✅ | 规范正确的框架 |
| 可流式HTTP传输 | ✅ | ✅ | POST+SSE GET流+会话ID |
任务(tasks/get/result/list/cancel,状态通知) | ✅ | ✅ | 增强 tools/call 通过 task 场;规格2025-11-25 |
激励(elicitation/create,表单+url模式) | ✅ 发起人 | ✅ 应答器 | 规范2025-11-25 |
| OAuth 2.1授权(HTTP) | ✅ | ✅ | 承载+RFC 9728受保护的资源元数据 |
快速启动
最小服务器
#include
int main() {
mcp::Server server{
mcp::Implementation{.name = "calc", .version = "1.0.0"},
};
server.tool("add",
nlohmann::json{
{"type", "object"},
{"properties", {
{"a", {{"type", "number"}}},
{"b", {{"type", "number"}}},
}},
{"required", nlohmann::json::array({"a", "b"})},
},
[](const nlohmann::json& args) -> mcp::CallToolResult {
const double a = args.at("a").get();
const double b = args.at("b").get();
return {
.content = { mcp::TextContent{.text = std::to_string(a + b)} },
};
});
server.run(std::make_unique());
}从支持MCP服务器的主机上运行(Claude Desktop、Cursor、官方 通过指向得到的二进制文件。Stderr是你的 诊断;stdout是为JSON-RPC流保留的。
最小客户
#include
int main() {
auto pair = /* spawn the server, wire its stdio to a transport */;
mcp::Client client{
mcp::Implementation{.name = "my-app", .version = "0.1.0"},
};
client.connect(std::move(pair.transport));
auto info = client.initialize().get();
std::cout (&out.content[0])) {
std::cout text (topts));
auto info = client.initialize().get();
auto out = client.call_tool("add", {{"a", 1}, {"b", 2}}).get();两者 examples/http_calculator_server (绑定127.0.0.1:8080通过 默认值)和stdio examples/calculator_server 船与 建造。
一个不平凡的演示:助手的持久笔记
examples/mcp_notes_server 是一个SQLite+FTS5草稿行,它提供 Claude(或任何MCP客户端)持久、全文可搜索内存 跨届会议。将其插入克劳德桌面/Claude Code并询问 “将我的Postgres-prod连接字符串保存在db.prod下”--它是 下次你打开聊天时。搜索“postgres”,你会得到一个 BM25排名片段命中率。Glob删除通过规范 启发式原语,因此人类之前会得到一个是/否提示 任何东西都被抹去了。
建造它(libsqlite3-dev 在Linux上,随macOS附带):
cmake -S . -B build -DMCP_BUILD_EXAMPLES=ON
cmake --build build --target mcp_notes_server看 examples/mcp_notes_server/README.md 为了电线。
服务器发起的LLM调用(采样)
一种常见的代理模式是让工具要求宿主应用程序进行 代表其发出法学硕士电话。
server.tool("ask", schema,
[&server](const nlohmann::json& args) -> mcp::CallToolResult {
auto resp = server.sample(mcp::CreateMessageRequestParams{
.messages = {
mcp::SamplingMessage{
.role = mcp::Role::user,
.content = mcp::TextContent{.text = args["q"]},
},
},
.max_tokens = 512,
}).get();
return {
.content = { mcp::TextContent{
.text = std::get(resp.content).text,
}},
};
});客户端插入一个执行实际LLM调用的采样处理程序:
client.set_sampling_handler(
[](const mcp::CreateMessageRequestParams& req) -> mcp::CreateMessageResult {
// ...invoke your LLM provider with `req.messages` etc...
return {
.role = mcp::Role::assistant,
.content = mcp::TextContent{.text = "..."},
.model = "claude-3-5-sonnet-20241022",
};
});Session在工作线程上分派入站请求,因此调用 server.sample(...).get() 从工具处理程序内部来看是安全的——它不是 僵局。
建筑
cmake -B build -G Ninja
cmake --build build
ctest --test-dir build --output-on-failure构建选项
| 选项 | 默认值 | 效果 |
|---|---|---|
MCP_BUILD_TESTS | ON | 构建GTest测试套件。 |
MCP_BUILD_EXAMPLES | ON | 构建示例服务器/客户端。 |
MCP_WARNINGS_AS_ERRORS | ON\* | 将警告升级为错误(仅限顶级)。 |
MCP_USE_SYSTEM_DEPS | 关闭 | find_package 而不是 FetchContent. |
MCP_ENABLE_HTTP | ON | 构建可流式HTTP传输(cpp-httplib)。 |
MCP_ENABLE_ASAN | 关闭 | -fsanitize=address,undefined. |
MCP_ENABLE_TSAN | 关闭 | -fsanitize=thread. |
MCP_ENABLE_COVERAGE | 关闭 | --coverage 对于gcov/llvm-cov。 |
\*当这是顶级项目时,默认为ON;通过以下方式消费时关闭 add_subdirectory.
消耗图书馆
CMake find_package
之后 cmake --install build,下游项目可以做到:
find_package(mcp REQUIRED)
target_link_libraries(my_app PRIVATE mcp::mcp)add_subdirectory /获取内容
include(FetchContent)
FetchContent_Declare(mcp
GIT_REPOSITORY https://github.com/Neumann-Labs/mcp-cpp.git
GIT_TAG main)
FetchContent_MakeAvailable(mcp)
target_link_libraries(my_app PRIVATE mcp::mcp)60秒内完成架构
+-----------------+
application -> | Server / Client | <- public façade
+-----------------+
|
+-----------------+
| Session | <- JSON-RPC dispatch +
+-----------------+ request/response correlation
|
+-----------------+
| Transport | <- byte shovel (StdioTransport,
+-----------------+ Streamable HTTP, in-memory pair)
|
v
wire- 运输 是一个小型抽象界面:它铲出框架;会议
解析它们。
- 会话 拥有请求id生成器、待定请求映射、
请求/通知调度表和超时扫描。都一样 线路两侧的类——服务器和客户端只是注册不同 处理程序和调用不同的便利方法。
- 服务器/客户端 是薄立面:它们将键入的C++值转换为
JSON-RPC框架和背面,暴露 tool() / resource() / prompt() / sample() / list_tools() /并且各自拥有一个会话。
线程
- 传输在自己的线程上读取并调用会话的
在消息回调时。
- 入站 *请求:* 被分派到一个分离的工作线程,因此用户
处理程序本身可以在同一会话上发出进一步的请求 (例如。 server.sample(...).get() 从工具处理器内部)。 Session::close() 等那些工人做完再撕 成员下降。
- 入站 *通知* 和 *回应* 直接在read线程上运行。
send_request/send_notification从任何线程调用都是安全的。
许可证
Apache 2.0——请参阅 许可证.
