C++实现MCP协议:构建高效AI智能体工具调用层
在AI智能体越来越常见的今天,各种“智能体框架”都在解决同一个核心问题:模型怎么安全、规范、高效地调用外部工具和数据。我最近在一个AI智能体通讯项目里,负责用C++从零实现MCP(Model Context Protocol)协议的源码子项目,这里想把这套协议的落地思路、代码组织和踩过的坑完整梳理一遍。如果你正准备接入MCP,或者想在服务端用C++做一套稳定的工具调用协议层,这篇文章应该能帮你跳过不少弯路。
MCP本质上是一个基于JSON-RPC 2.0的通信协议,它把“模型需要上下文”和“模型需要调用工具”这两个需求标准化成了统一的消息格式和调用流程。社区里已经有TypeScript、Python、Java等语言的官方SDK,但C++的实现相对零散,要么依赖重型框架,要么只做了客户端没有服务端。我们之所以要在C++里重新造这套轮子,是因为智能体通讯项目本身是C++编写的底层服务,如果包一层Python或者Node进程来做协议转换,延迟和运维复杂度都会成倍增加。更关键的是,MCP的价值在于它定义了清晰的角色边界——客户端(AI应用)和服务端(工具宿主),但两边都跑在同一套进程模型里时,直接用C++原生的协程、线程池和内存管理,能把每一微秒都用在刀刃上。
这篇内容会从协议拆解开始,逐步穿插代码片段和工程决策,最后整理一份我自己遇到的几个典型问题排查记录。适合有一定C++基础、做过网络编程或协议解析的开发者阅读,也适合那些需要在现有C++项目中接入AI工具链的团队参考。即使你完全没接触过MCP,只要熟悉JSON-RPC和基本的socket编程,也能跟上节奏。
1. 先弄明白MCP到底在传输什么
1.1 MCP不是又一个RPC框架,而是一套有边界的消息契约
很多人第一次看到MCP,容易把它理解成gRPC或者Thrift这类通用RPC框架。实际上MCP的定位更接近“应用层活动组织规范”——它不像gRPC那样定义强类型的service接口,而是把对话中的上下文语义拆成三类对象: 资源(Resource) 、 工具(Tool) 和 提示词(Prompt) ,然后通过JSON-RPC 2.0的消息帧来传递操作指令。
资源是给模型“读”的数据,比如一个文件内容、一个数据库查询结果或一个网页抓取结果;工具是给模型“调用”的动作,比如“执行SQL”“发送邮件”“计算两数之和”;提示词则是预置的对话模板,帮助模型更好理解当前任务。这三类对象构成了MCP的语义核心,而协议本身只负责传输“如何列出它们、如何读取它们、如何调用它们”的请求和响应。
在C++实现里,我一开始最纠结的就是怎么建模这三类对象。如果用普通的struct硬编码,那每加一个新类型的工具就得改协议层代码,完全违背了MCP动态发现(dynamic discovery)的设计意图。最后我采用了 类型擦除+回调注册 的方案:协议层只维护一个注册表,每个工具都是一个回调函数加上元信息描述,元信息里包含参数schema(用了JSON Schema格式)。这样业务方往注册表里塞一个工具,协议层无需改动编码逻辑。
struct ToolMeta {
std::string name;
std::string description;
nlohmann::json inputSchema; // JSON Schema
};
using ToolExecutor = std::function<nlohmann::json(const nlohmann::json& params)>;
这个设计让我在后期扩展工具时,只需要关注业务逻辑,不用再打开协议源码改消息处理分支。
1.2 消息帧是“信封”,JSON-RPC是“信纸”
MCP的传输层支持stdin/stdout和SSE两种主流模式。stdin/stdout模式多用于本地子进程场景,比如IDE插件拉起一个MCP服务端进程;SSE模式多用于跨网络场景,比如远端工具服务器。但不管用哪种传输,消息本身的封装都是同一个要求: 无论是请求、响应还是通知,都必须被包在带有Content-Length头部的帧里,帧体是UTF-8编码的JSON-RPC文本 。
这个帧格式很容易让人联想到LSP(Language Server Protocol),实际上MCP在设计上确实参考了LSP的通信模式。帧头格式是:
Content-Length: <字节数>\r\n
\r\n
<JSON消息体>
很多初学MCP的朋友会直接按行读取来判断消息边界,但JSON消息本身可能包含换行符(比如字符串字段里的
\n
),按行读一定会拆错包。正确做法是严格解析头部,先拿到Content-Length,再精确读取对应字节数作为JSON体。这个道理虽然简单,但我在实践中看到过太多因为忘了处理“粘包”导致的诡异问题,后面会专门讲。
1.3 为什么官方SDK密集,还要用C++硬啃
官方MCP SDK目前最成熟的是TypeScript和Python版本,Java/Kotlin也有官方实现,但C++只有社区维护的早期库,而且大多停留在“能用”阶段,api设计也各成一派。我们的项目有几个刚需:
- 服务端核心是C++写的,工具执行需要直接操作内存数据结构,跨进程调Python会让热路径性能损失严重。
- 智能体需要高频发起工具调用,单次调用延迟上限要求低于5ms,C++的json解析和内存分配可控性最好。
- 部署环境需要静态链接单二进制,不能依赖Python运行环境或Node运行时。
在这些约束下,自研一套轻量级MCP内核是合理选择。但这不是说所有场景都该自研——如果你只是做AI原型验证,直接用Python的官方SDK绝对比自己写C++实现快得多。自研的代价在于协议细节和工具注册表的可扩展性都必须自己把控,后面每一次MCP版本升级都要同步更新代码。
2. 工程结构:拆成四层,不把协议和业务搅在一起
2.1 分层设计思路
项目命名为“AI智能体通讯项目的子项目”,实际代码仓库里我们也确实按独立组件来管理。整个子项目拆了四个目录:
mcp-core/ // 协议核心:帧编解码、JSON-RPC消息对象、消息分发器
mcp-transport/ // 传输层:stdio 和 TCP/SSE 两种Transport实现
mcp-server/ // 服务端实现:注册表、工具执行上下文、生命周期管理
mcp-client/ // 客户端实现:连接管理、请求发送、响应Future
examples/ // 示例服务端和客户端
这个拆分参考了SDK的常见做法,但也有C++特有的考量。比如
mcp-transport
层把传输细节隔离后,
mcp-core
可以做纯内存的单元测试,不依赖socket、pipe等系统资源;
mcp-core
只依赖JSON库和标准库,保证协议层的可移植性。
2.2 JSON库选型:nlohmann/json还是rapidjson
做MCP实现,JSON库是地基层。我最早用的是
nlohmann/json
,它api友好、错误信息好读,但在高频解析和序列化的压力下开销偏高。后来换成
rapidjson
,主要看中它的零拷贝解析(
SAX
模式)和精确控制内存分配的能力。不过
rapidjson
的api偏复杂,而且错误处理方式偏底层,实话说上手成本高了一截。
我的最终选择是:
core层用
nlohmann/json
做数据和schema解析,在传输层编解码帧体时先用
rapidjson
做合法性预检,然后转成
nlohmann::json
对象往下传
。这样两头的好处都占了——可以快速写业务逻辑,也能在高并发场景下避免字符串到对象之间的重复拷贝。
但这里有个经验教训:
不要在每一层都持有不同JSON表示
。我最初让传输层传
rapidjson::Document
、core层用
nlohmann::json
,结果数据一脚踩进两个库,序列化和反序列化各做一遍,数据流转极度痛苦。最后还是统一在core层用
nlohmann::json
,传输层只做字节流和字符串校验,不做结构化解析。这个调整让整个消息管道清晰了很多。
2.3 线程模型与并发边界
MCP服务端可能同时处理多个客户端连接,每个连接上可能并发多个请求,而工具执行往往又包含耗时操作(比如查数据库、调另一个HTTP服务)。为了不阻塞协议层,我做了一个线程模型:
- 每个Transport实例拥有一个reader线程,负责从socket/stdio读取字节流并按帧切分。
-
每个帧解析成消息对象后,投递到一个无锁队列(用
moodycamel::ConcurrentQueue,或者标准库std::mutex+deque也够用)。 - 一个dispatcher线程池(默认4个worker)从队列取消息,按方法名分发到对应的handler。
- 工具执行可能在handler里同步运行,也可能由handler自行投递到业务线程池;协议层不关心这个细节。
这个模型关键点在于请求和响应之间要有能力关联。每个JSON-RPC请求都有一个唯一的
id
,我们维护一个
std::unordered_map<RequestId, Promise<json>>
,dispatcher处理完结果后,由worker线程把响应塞回对应连接writer队列。用C++的
std::promise
/
std::future
让客户端api看起来像同步调用,但底层是异步流转,这个体验在测试的时候尤其舒服。
3. 核心模块逐个拆解:编解码、注册表、消息路由
3.1 帧编解码器的实现细节
帧解码器的输入是字节流,可能一次读入多个完整帧,也可能只读入半帧。所以我给它设计成了“状态机”模式:从
HeaderStart
、
ReadingLength
、
WaitForCRLF
、
ReadingBody
四个状态间迁移。头部解析完毕后记录
bodyLength
,然后进入“等待足够字节”的阻塞或异步状态。
class FrameDecoder {
public:
enum class State {
HeaderStart,
ReadingLength,
WaitNewline,
ReadingBody
};
// 每次push一段数据,可能产生0~n个完整帧
std::vector<std::string> push(const char* data, size_t len) {
buffer_.append(data, len);
std::vector<std::string> frames;
while (true) {
if (state_ == State::HeaderStart) {
// 查找 "Content-Length:" 前缀
}
// ... 各状态迁移逻辑
}
}
private:
State state_;
std::string buffer_;
size_t contentLength_ = 0;
};
状态机实现起来不复杂,但容易在边界条件上出毛病,尤其是头部字段和\r\n分隔符的匹配。我建议用
find("\r\n\r\n")
先找到头部结束位置,再从头部分离出各个字段,而不是一个字符一个字符去吞,那样代码又慢又难维护。
编码器则简单很多:查字节数、拼头部、拼JSON体,一次性输出。注意UTF-8字符串的字节数等于
str.size()
,因为MCP强制JSON消息体UTF-8编码,所以直接用
size()
没问题,不用考虑宽字符。
3.2 JSON-RPC消息模型的构建
MCP协议里,消息无非三种类型: 请求(带方法名和id) 、 响应(带id和结果或错误) 、 通知(带方法名但没有id) 。我在core层设计了三个类:
struct JsonRpcRequest {
std::string method;
nlohmann::json params;
int64_t id; // -1 表示通知
};
struct JsonRpcSuccess {
int64_t id;
nlohmann::json result;
};
struct JsonRpcError {
int64_t id;
int code;
std::string message;
nlohmann::json data;
};
编解码时,我按JSON对象里是否包含
id
、
method
、
result
、
error
字段组合来判别类型。为了严格校验,我用了JSON Schema去校验这些字段,但实际线上校验成本有点高,后来只保留了基本类型检查。
这里要特别提醒:MCP对
id
类型没有强行限定整数,也允许字符串。但为了和C++内部映射简单,我统一用整型做id。如果你在网络互通时需要兼容别的客户端,最好允许
id
字段是
std::variant<nullptr_t, int64_t, std::string>
,否则有些SDK会用字符串id发请求,解码就会报错。我在早期版本就吃过这个亏。
3.3 工具注册表与“动态发现”链路
MCP服务端最核心的能力就是让AI客户端了解到有哪些工具可用,然后按需调用。这个过程分为三步:
-
客户端发送
tools/list请求。 - 服务端返回注册表里所有工具的元信息(name、description、inputSchema)。
-
客户端根据模型决策选择某个工具,发送
tools/call请求,携带工具名和参数。
我的注册表实现核心是一个大
std::unordered_map<std::string, RegisteredTool>
,
RegisteredTool
同时持有
ToolMeta
和
ToolExecutor
。工具注册线程安全,用了
std::shared_mutex
保护,读取元信息用共享锁,注册新工具用独占锁。
工具调用时,我做了一步参数校验:根据工具声明的
inputSchema
,用
nlohmann::json
的schema校验库检查入参。虽然这套校验在性能测试里显得很重,但工具调用的频率通常远低于列表读取,所以可以接受。校验失败时返回
InvalidParams
错误,不会进入用户回调。
3.4 方法分发器:用“注册+回调”替代if-else地狱
MCP服务端需要处理的方法包括
initialize
、
ping
、
tools/list
、
tools/call
、
resources/list
、
resources/read
、
prompts/list
、
prompts/get
等等。如果全堆在一个大函数里用if-else判断,后面维护简直噩梦。我用了一个简单的分发器,本质上是一张方法名到处理函数的映射表:
using Handler = std::function<nlohmann::json(const nlohmann::json& params)>;
class Dispatcher {
public:
void registerHandler(std::string method, Handler h) {
handlers_[std::move(method)] = std::move(h);
}
nlohmann::json dispatch(const std::string& method, const nlohmann::json& params) {
auto it = handlers_.find(method);
if (it == handlers_.end()) {
throw MethodNotFound(method);
}
return it->second(params);
}
private:
std::unordered_map<std::string, Handler> handlers_;
};
这种设计让“新增一个协议方法”变成“注册一个回调”,不需要动调度逻辑。同时我在
initialize
这类握手方法里塞了服务端能力和版本信息,客户端需要先握手成功后才能继续发其他请求,这个状态机约束在分发器之上做了一层连接状态管理。
4. 关键实操:实现一个C++的MCP服务端(可直接抄作业)
4.1 transport层:先打通stdio
stdio传输是最简单的MCP传输方式,适合开发调试和后续嵌入到IDE或Agent子进程。实现思路:用
std::cin
读二进制流(注意要按字节读,不是按行读),用
std::cout
输出帧。
读取侧用
std::cin.read
配合
FrameDecoder
拼帧。输出侧需要注意:
std::cout
默认有缓冲,如果调用了
std::endl
还会额外刷新输出,性能不佳。正确做法是手动管理一个输出buffer,组装完一帧后一次性
std::cout.write
,然后
std::cout.flush()
。不少MCP客户端在握手时会等待服务端输出,如果不flush,客户端可能一直堵塞在读取阶段。
void sendJson(const nlohmann::json& message) {
std::string body = message.dump(); // UTF-8
std::ostringstream header;
header << "Content-Length: " << body.size() << "\r\n\r\n";
std::string frame = header.str() + body;
std::cout.write(frame.data(), frame.size());
std::cout.flush();
}
4.2 初始化握手:不仅是发一条消息
MCP的
initialize
请求携带协议版本、客户端能力(如工具是否支持)、客户端标识。服务端收到后,需要返回服务端支持的最高协议版本、服务端能力和服务端标识。如果客户端要求的版本高于服务端支持的版本,服务端可以返回
ProtocolVersion
错误,或者协商降级。
我实现了这样一个状态枚举:
enum class SessionState {
AwaitingInitialize,
Initialized,
Shutdown
};
握手成功后,把状态置为
Initialized
,之后才允许
tools/list
、
resources/list
等业务方法。
ping
和
notifications/initialized
是例外,
ping
任何状态均可响应,
notifications/initialized
是客户端在握手后主动通知服务端的,无需响应。
这里有件事印象很深:客户端SDK在握手后通常会立刻发
notifications/initialized
通知,然后再发
tools/list
。如果服务端状态机设计得不健壮,还没等通知到达就收到
tools/list
,就可能误判为“未初始化”。稳妥做法是:
一旦收到
initialize
响应完毕,就把状态置为
Initialized
,不再等待
notifications/initialized
。这个通知只作为参考,不把它当状态门禁。
4.3 工具调用全链路:从JSON参数到真实执行
以一个示例工具“打印问候语”为例,它接收一个
name
字符串,返回一个包含问候语的JSON对象。注册代码如下:
server.registerTool({
.name = "greet",
.description = "根据名字生成问候语",
.inputSchema = {
{"type", "object"},
{"properties", {
{"name", {{"type", "string"}, {"description", "用户名"}}}
}},
{"required", {"name"}}
}
}, [](const nlohmann::json& params) -> nlohmann::json {
std::string name = params["name"].get<std::string>();
return {{"greeting", "Hello, " + name + "!"}};
});
tools/call
请求到达分发器后,会走到一个特殊处理器。它把
params.name
映射到注册表里的工具,解析并校验
params.arguments
,然后执行回调,把执行结果包进
result
字段,同时携带
isError
标志。如果回调抛异常,则捕获并返回
InternalError
或工具错误信息,但
响应msg还是成功响应
,只是
result
里
isError=true
。这一点和传统RPC完全不同——MCP把工具执行失败也当作一个“成功返回的结果”,错误信息在内容里,而不是JSON-RPC错误。客户端需要明确判断
isError
字段才能感知工具执行失败。我第一次实现时直接把回调异常映射成了JSON-RPC错误,结果客户端SDK直接忽略了这个异常响应,排查了好久才发现协议语义理解错了。
4.4 资源与提示词的实现
MCP不只做工具调用,资源和提示词也是标准能力。资源列表实现方式和工具类似,但
resources/read
请求里携带的是一个
uri
,服务端根据这个uri去定位内容。我这里设计了一个
ResourceProvider
接口:
struct ResourceContent {
std::string uri;
std::string text; // 或二进制base64
};
using ResourceProvider = std::function<ResourceContent(const std::string& uri)>;
资源内容可以来自内存、文件、数据库或另一个上游服务,协议层不关心具体来源。只要注册时给了provider,就能被客户端发现和读取。这个设计在智能体场景里非常有用——模型可以先读取一个文件的资源内容,再调用对应工具去修改它,整个上下文链在MCP的语义下非常清晰。
提示词这块类似:
prompts/list
返回所有提示词模板的元信息,
prompts/get
返回具体提示词内容。提示词内容本质上是一个消息数组,里面包含role和content。我在实现时允许多个消息槽位,让调用时可以填充参数,这样同一个模板可以复用在不同上下文中。
4.5 客户端实现要点
有了服务端,客户端就是反过来的消息流。客户端要发起请求,需要有
sendRequest
方法,它分配一个id,并把一个
std::promise<json>
存入map,然后异步发送帧。当响应帧到达,解码后按id查找promise,set_value。通知和响应都不需要严格同步处理。
std::future<nlohmann::json> Client::sendRequest(const std::string& method,
const nlohmann::json& params) {
int64_t id = nextId_++;
JsonRpcRequest req{method, params, id};
auto promise = std::make_shared<std::promise<json>>();
{
std::lock_guard<std::mutex> lock(pendingMutex_);
pending_[id] = promise;
}
sendMessage(req);
return promise->get_future();
}
这里有个坑:如果服务端长时间不返回,这个future会一直挂着。我在客户端加了一个超时机制,每个请求在构造时记录时间戳,响应循环每次查询超时列表,超时的请求从pending表移除并set_exception。别小看这个机制,AI对话场景中工具执行经常慢,但网络抖动也会出现,没有超时控制,整个会话会被一个卡死的工具调用拖住。
5. 常见问题与排查技巧实录
5.1 粘包和半包:FrameDecoder的边界条件
用stdio通信时,最重要的问题就是帧边界。因为一次read可能读入两个半帧,也可能只读入半个帧。如果只按
\r\n\r\n
去找头部和体部,很容易出错。
我最常遇到的问题:客户端发送了多个通知(比如日志通知+状态通知)粘连在一个TCP包或stdin缓冲区里,如果解码器在解析完第一个帧后没有“继续解析剩余字节”的循环,第二个通知会被当作畸形数据,连接直接关闭。修复方式是在
push
方法里做一个死循环,直到缓冲区剩余字节不足以解析下一步为止。
排查这类问题的经验是:给解码器加一个调试模式,打印每次
push
后的剩余字节数、状态机位置。只看日志根本看不出来,但打上状态就一目了然。后来我把这个打印器做成了可开关的环境变量配置,生产环境关闭调试输出,测试时再打开。
5.2 JSON解析错误与中文编码
MCP的JSON消息体是UTF-8,而Windows下某些C++字符串常量的编码可能是GBK。如果工具返回的中文字符串不是UTF-8,
nlohmann::json::dump()
会输出无效UTF-8,而客户端SDK解析时直接抛异常。这个问题让我排查了很久,一开始以为是协议帧长度算错了,后来才发现是GBK和UTF-8打架。
解决思路很朴素:所有进入JSON对象的字符串,必须是经过UTF-8校验的
std::string
。如果上游业务代码返回了
std::wstring
或者宽字符字面量,需要显式转码。我们可以用具名工具
boost::locale
或
iconv
做转换,但不建议在协议层偷懒。最好在工具注册层就约定:
"All strings are UTF-8, always, no exceptions."
这一条约定写进团队规范,比任何代码防御都有效。
5.3 线程安全:响应帧写放行时别乱序
服务端并发处理请求时,不同线程可能会同时执行不同工具,执行完后各自向连接写响应。如果直接用多个线程写同一个
std::cout
或socket,帧会互相穿插,彻底破坏协议。我在服务端为每一个连接建了一个发送队列,任何线程要发响应时,只往队列里塞帧数据,由独立的写线程独占发送,严格保证帧顺序。
很多C++新手在实现这里时,会想到每发一帧就加锁。这确实能保证帧数据不穿插,但无法保证 响应顺序是有序的 。MCP对响应顺序没有严格要求——因为JSON-RPC用id关联请求响应,但对某些客户端来说,如果响应到达顺序和请求顺序不一致,可能影响它们的消息调度。稳妥做法是按请求id的完成时间自然排序即可,不需要强制和请求顺序一致。我一开始为了实现“保持顺序”用了复杂的有序map加条件变量,实际上一测试发现完全没必要,纯属给自己加戏。
5.4 内存增长与控制
C++实现的优势是可控,但如果工具执行时往
nlohmann::json
里塞大段文本,并且长期持有这些文本对象,内存增长会很明显。智能体场景中资源读取可能会反复获取同一份大文件,没有缓存策略就会导致每次请求都重新分配内存。我加了一个简单的LRU缓存,针对
resources/read
的相同uri,在生命周期内直接返回缓存内容。这个优化在工具读取配置文件、README、知识库片段时效果显著。
5.5 测试策略:模拟对端,逐层验证
单元测试层面,我直接用C++测试框架(如GoogleTest)对帧编解码、JSON-RPC消息类型判定、分发器路由做测试。集成测试层面,用自己的客户端连接自己实现的服务端,跑一遍“握手→列工具→调用工具”的完整流程,验证数据一致性和错误码。
更贴近真实环境的是用官方TypeScript SDK作为第三方对端来测试C++服务端——因为协议不互通的问题,往往不是自己写两端测不出来的。我在项目里加了一个
examples/mcp-ts-client
目录,专门放一个用TypeScript SDK写的迷你测试客户端,跑在Node里连接C++服务端,验证真实互通。这个做法强烈推荐,能发现很多“自我感觉良好但实际互不兼容”的细节问题,比如版本号字符串大小写、id类型、错误码范围等。
6. 踩坑后的几点硬核经验
这个子项目做下来,我最深的体会是: MCP协议本身看文档三天就能上手,但把协议做稳、做快、做可维护,拼的是工程细节 。如果你也在规划类似的子项目,有几个经验值得提前刻在脑门上:
第一,不要在core层图方便,把tools/list、resources/list的元信息直接构建成大JSON返回。线上工具数量多起来之后,这个列表可能会非常大,占几百KB甚至MB级别。MCP里有个分页参数,客户端会通过
cursor
来翻页,服务端必须实现分页。我第一次做的时候图省事全部返回,结果某些客户端直接内存爆炸。后来改成按固定条数分页,配合cursor游标,数据量再大也没事。
第二,响应帧的
Content-Length
千万不能算错。它必须等于JSON body的字节数,不能用字符数、更不能按UTF-8 code point个数算。如果你在body里包含了中文,
size()
是字节数,而如果用了某些会把宽字符转成short数组的库,长度就完全对不上了。写编码器的时候把这个地方做成一个独立函数,配单测,一劳永逸。
第三,协议版本要预留升级空间。MCP版本号从
2024-11-05
之类的日期格式逐步升版,你的服务端在
initialize
协商时,不能写死只支持某一个版本。我在握手逻辑里加了一个版本列表,选择客户端支持的最高版本,如果匹配不上就返回错误。这样以后MCP迭代时,只要在列表里增加新版本,旧客户端依然能正常工作。
第四,注意工具返回内容的体积控制。AI模型一次能接受的上下文token数量有限,如果工具返回一个几十MB的日志文件内容,模型根本消化不了,还会拖慢整个链路。我在工具层加了一个输出长度上限,超过阈值时返回截断内容和完整长度提示,让模型可以按需通过分页读取更多。这个设计在实践里极大提高了长文档工具调用的稳定性。
最后再说一点:尽管市面上已经有了不少MCP的SDK,但自己用C++实现一次,能让你对这个协议背后的设计意图有完全不同的理解。你会在写帧解码器的时候意识到,这个协议是如何为了流式、半双工、低延迟场景做出妥协;你会在实现工具注册表的时候明白,动态发现机制赋予AI应用的灵活度,远超传统的RPC接口。这套子项目做完之后,我们团队的智能体服务延迟下降了一个数量级,工具接入成本也大幅降低。对追求极致性能、需要深度定制协议行为的团队来说,用C++写MCP协议源码,绝对是对的选择。
更多推荐

所有评论(0)