这段时间手头一直在推进一个代号 AgentEarth 的 AI 服务中台项目,核心目标很直接:把散落在不同系统里的模型能力、业务工具、数据源全部收拢成统一的服务层,向上对各类 Agent 应用暴露一套标准接口。在技术选型阶段,我花了大量时间啃 MCP 协议,也踩了不少坑。写这篇文章,主要是想把 MCP 协议的核心机制、AgentEarth 的整体架构设计,以及我们在落地过程中的真实经验完整串一遍。无论你是后端开发、AI 应用工程师,还是准备做 Agent 平台化的技术负责人,这篇文章应该都能给你一些可落地的参考。
1. 项目概述与核心需求解析
1.1 MCP 是什么,为什么突然大家都在聊
MCP 的全称是 Model Context Protocol,中文一般叫模型上下文协议。它做的事情本质上就是给 AI 应用和外部工具、数据源之间定义一个标准化的“插头”。以前每个 Agent 要调用一个工具,往往需要单独写一套集成代码,工具越多,集成成本越高。MCP 出现之后,工具方只需要实现一次 MCP Server,所有支持 MCP 的 Client 都能直接接入,就像 USB 接口统一了各种外设连接一样。
我在 AgentEarth 项目里最直观的感受是:MCP 解决的并不是“某个具体功能怎么做”,而是“能力之间如何标准化连接”的问题。它定义了三大类原语——Resources(资源)、Tools(工具)、Prompts(提示模板),再配合 JSON-RPC 2.0 的消息格式,让 AI 应用可以与外部能力进行结构化交互。
网上讨论 MCP 的文章不少,但大多数停留在概念介绍层面。实际做项目的时候,你会遇到很多细节问题:连接生命周期怎么管理、工具调用超时怎么处理、上下文内容太大怎么办、鉴权信息如何传递、stdio 和 HTTP 传输怎么选。这篇文章会重点拆这些实操层面的东西。
1.2 AgentEarth 的定位:从能力堆砌到协议化服务中台
AgentEarth 不是一个简单的前端 ChatBot,也不是一个单体 Agent 框架。我们一开始就没有把它设计成“又一个 Agent 应用”,而是定位成一个面向多智能体、多应用场景的 AI 服务中台。
所谓服务中台,核心价值在于“复用”。假设公司里有订单系统、库存系统、CRM、知识库、数据分析平台,每一个系统都有可能被多个 Agent 调用。如果每个 Agent 项目都自己写一套对接代码,维护成本会指数级上升。AgentEarth 做的事情是把这些系统的能力通过 MCP Server 封装成标准化服务,注册到中台,再由中台统一对外提供接口。
这个定位带来几个明确需求:
- 能力需要协议化封装,不能各自为政。
- 服务需要有统一的注册、发现、路由能力。
- 对外暴露的接口需要有安全、限流、审计能力。
- 上层应用需要有一致的调用体验,无论底层是 HTTP API、数据库、还是内部 RPC 服务。
基于这些需求,MCP 协议几乎是为我们量身定做的底座。
1.3 选型决策:为什么选 MCP 而不自研协议
项目启动之初,团队内部也讨论过是不是自研一套 Agent 工具调用协议,或者干脆用传统 API 网关那一套。最终选择 MCP,我总结了四点核心原因:
第一,生态兼容性。MCP 发布之后,主流 AI 开发框架、大模型厂商和开源社区都在往这个方向靠拢。使用 MCP,意味着未来接入新的模型、新的客户端工具时,不需要重新适配协议。
第二,语义贴合 AI 场景。传统 REST API 是面向请求响应设计的,但 AI 应用需要的不仅是“调用一个接口”,还包括模型上下文管理、流式响应、工具能力发现、提示模板复用等。MCP 的原语设计从一开始就是为 Agent 场景设计的。
第三,集成成本低。MCP SDK 已经提供成熟的 Client 和 Server 骨架,开发人员只需要实现具体的工具逻辑,不用从零搭建消息层、连接层、状态机。
第四,便于横向扩展。中台本身需要支持多种传输方式(本地进程、内网 HTTP、公网暴露),MCP 在这方面的抽象做得比较清晰。
当然,MCP 并不是银弹。它目前还在快速演进中,规范版本更新频繁,有些高级特性(如分布式授权、流式传输标准)还在完善。这些问题我在后面章节会展开讲。
2. MCP 协议核心机制拆解
2.1 协议分层与消息模型:JSON-RPC 2.0 是地基
MCP 的底层消息模型是 JSON-RPC 2.0,这是一套非常成熟、轻量的远程调用协议。它定义了三种消息类型:Request、Response、Notification。Request 由调用方发出,需要接收方返回 Response;Notification 则不需要回复,适合推送事件、日志等场景。
理解这一点很重要,因为很多初学者会把 MCP 想象成某种神奇的 AI 协议,但扒开看,它就是一个结构化消息交换协议,只是消息的内容和语义是为 AI 上下文设计的。
MCP 协议层大致可以做这样的分层:
- 传输层(Transport):负责字节流传输,常见实现为 stdio、HTTP+SSE、Streamable HTTP。
- 会话层(Session):管理连接的建立、初始化、关闭、心跳。
- 原语层(Primitives):提供 Resources、Tools、Prompts 三种能力模型。
- 应用层(Application):在原语之上构建 AI 应用逻辑,比如 Agent 编排、上下文组装。
我打一个生活化的比方:JSON-RPC 2.0 相当于信封和信封的填写规范,传输层是邮局用汽车还是用火车送信,会话层是邮局怎么登记、签收、回收信封,原语层是信纸上可以写三种固定格式的内容。这种分层抽象让每一层可以独立演进和替换。
2.2 三种原语:Resources、Tools、Prompts 怎么用
MCP 定义的原语很容易被误解,我这里逐一说明,并附上 AgentEarth 里的实际用法。
Resources 用于向模型提供“可读取的数据”。它可以是数据库内容、文件的片段、API 返回的数据快照。在协议中,Client 可以列出所有 Resources,读取某个 Resource 的内容。AgentEarth 中我们把知识库文档、业务表格元数据、系统运行状态指标都暴露成了 Resource。这样做的好处是模型可以根据需要主动获取上下文,而不是靠开发者把所有信息硬编码到 Prompt 里。
Tools 则是最常用的原语,它代表“可执行的操作”。Tool 通常包含 name、description、inputSchema 三个关键字段。name 是调用标识,description 用于给大模型描述这个工具是干什么的、什么时候应该用它,inputSchema 则是 JSON Schema 格式的参数定义。
这里有一个经验:description 写得越准确,模型对工具的调用准确率越高。我之前见过一个团队在写工具描述时非常敷衍,结果模型反复尝试错误工具,输出质量直线下降。工具描述本质上是在做“语义路由”,不能省。
Prompts 是预定义的提示模板,它可以让 Client 在特定场景下快速组装交互上下文。例如“订单退款处理流程”可以预置成一个 Prompt,把最初的一长串指令和约束条件都封装好,Agent 调用时只需要传入订单号。这在一定程度上避免了 Prompt 在多个客户端之间重复维护的问题。
2.3 能力协商与生命周期管理
MCP 并不是简单的“连接后随便调用”,它有一套能力协商机制。连接建立后,Client 会发送 initialize 请求,其中包含 protocolVersion、clientInfo、capabilities。Server 收到后返回自己的协议版本和支持的能力集合。双方要协商出一个双方都支持的版本和功能组合,后续通信才正式开始。
初始化之后还要发送 initialized 通知,服务端收到后才会认为会话完全可用。这个“通知”环节很容易被开发者忽略,我见过有人直接用工具发送请求,结果服务端一直返回“session not initialized”之类的错误。
连接关闭也有讲究,参数叫 shutdown。如果 Client 直接断开连接而不发 shutdown,Server 端就可能留下未清理的资源。在 AgentEarth 中,我们对生命周期做了严格的封装:初始化失败自动重试;一定时间无消息时由 Client 发起 ping;重试无响应时触发关闭流程,并在日志中记录原因。
2.4 Transport 层怎么选:stdio、HTTP+SSE、Streamable HTTP
MCP 的 Transport 是很多人容易迷糊的地方,我花了最多时间研究的就是这一块。目前主流有三种:
stdio 传输适合本地启动子进程的场景。例如一个 Python 脚本作为 MCP Server,由 Claude Desktop 或 IDE 插件拉起,二者通过标准输入输出交换 JSON-RPC 消息。这种方式的优点是零端口占用、生命周期好控制,缺点是只能在本机使用,不适合跨网络部署。
HTTP+SSE 适合远程服务。Client 通过 HTTP POST 发送请求,服务端通过 SSE(Server-Sent Events)通道向客户端推送响应和事件。这种方式能解决跨网络通信问题,但需要维护 SSE 连接,网关、负载均衡的配置也要额外注意。
Streamable HTTP 是 MCP 后续规范演进中的一个更灵活的方案,它把请求响应和流式消息都统一到 HTTP 通道,服务端可以用 Streaming Response 返回结果。相比传统 SSE,它的连接管理更简单。
AgentEarth 内部是这样决策的:本地开发调试工具用 stdio;内网服务之间使用 HTTP+SSE;对外面向第三方应用的能力开放,后期平滑迁移到 Streamable HTTP。三者的消息语义完全一致,只是传输方式不同,所以切换的成本主要在基础设施层,业务代码几乎不用动。
3. AgentEarth 整体架构设计
3.1 分层的服务中台架构
AgentEarth 的架构可以粗略分成四层:接入层、协议网关层、编排调度层、能力提供层。
接入层负责对接各种 Client,包括 Web 应用、移动端、IDE 插件、定时任务。这层不考虑具体业务逻辑,只负责建立连接、维护会话、处理传输协议差异。
协议网关层是核心,它负责 MCP 协议语义的转换、路由和策略控制。一个 Client 请求进来,网关先做协议版本协商,再做身份认证、权限校验、限流判断,然后根据目标能力将请求路由到对应的后端服务。
编排调度层负责复杂的多步任务。单一工具调用可以直接走网关,但如果 Agent 需要完成“查库存—算价格—生成订单—通知仓管”这一系列操作,就需要编排层来管理状态和调用顺序。我们在这层引入了任务 ID 和步骤状态机,方便追踪一个复杂任务每一步的执行情况。
能力提供层是真正执行业务逻辑的部分,可以是自研微服务、旧系统 API、第三方 SaaS 接口、内部数据库等。MCP Server 在这里做能力适配,把这些系统包装成统一的能力模型。
3.2 为什么把能力沉淀在“中台”而不是“单体 Agent”里
有一种常见做法是直接用 Agent 框架内置的 Tool Calling 机制,每个 Agent 自己注册工具、自己调 API。这样做在 Demo 阶段没问题,一旦到了生产环境,就会遇到资源争抢、权限混乱、工具重复维护、日志格式不统一等一系列问题。
中台化之后,同样的能力被封装成单一服务,多个 Agent 共享调用。能力的上下线、版本迭代、参数调整都只在中台操作一次,所有接入方自动受益。对我个人而言,这种“一次接入、处处复用”的收益在工作中非常明显,尤其在团队协作场景下,后端只需要维护能力本身,AI 应用只需要关注业务编排。
当然,中台的缺点也很现实:它会引入额外的网络延迟,架构设计复杂度变高。AgentEarth 的应对办法是把高频调用的能力尽量靠近业务侧部署,并用连接池、多路复用等手段降低延迟影响。
3.3 多租户、权限与限流熔断
中台一旦对外开放,多租户问题马上就会浮现。不同业务线、不同外部合作伙伴接入同一个 MCP Server,最怕的是互相影响和越权访问。
AgentEarth 在权限模型上做了两件事:第一,每一层会话都绑定一个 tenantId,由网关负责解析并注入到上下文;第二,能力提供层会依据 tenantId 做行级数据过滤,确保租户 A 根本拿不到租户 B 的数据。
限流熔断方面,我们参考了普通 API 网关的成熟经验:按租户、按工具、按并发数三级限流。比如同一个订单服务,普通租户每秒最多调用 50 次,VIP 租户放宽到 500 次。熔断条件设定为错误率达到 30% 时打开熔断开关,快速失败并返回可读错误信息,避免服务雪崩。
这里多提一嘴:MCP 协议本身没有定义复杂的授权体系,它把鉴权交给传输层,这一点是设计取舍。自建中台时,千万不要认为服务暴露成 MCP 就天然安全,还是要在网关层把安全策略做厚。
4. 核心实现与实操步骤
4.1 环境准备与最小骨架搭建
我以 Python 生态为例,因为 AgentEarth 的主力语言就是 Python。先安装 MCP SDK:
pip install mcp注意不同版本的 SDK API 有一定差异,我写的示例所用的方法名属于常见封装方式,如果你用的版本 API 不一致,以官方 SDK 文档为准。
最小骨架的服务端代码大概长这样:
import logging from mcp.server import Server from mcp.server.models import InitializationOptions import mcp.server.stdio logging.basicConfig(level=logging.INFO) server = Server("agentearth-order") @server.list_tools() async def list_all_tools(): return [ { "name": "query_order_status", "description": "根据订单ID查询订单的当前状态", "inputSchema": { "type": "object", "properties": { "order_id": {"type": "string", "description": "订单编号"} }, "required": ["order_id"], }, } ] @server.call_tool() async def execute_tool(name: str, arguments: dict): if name == "query_order_status": order_id = arguments.get("order_id", "") # 此处替换为真实业务逻辑 status = "pending" return {"content": [{"type": "text", "text": status}]} raise ValueError(f"Unknown tool: {name}") async def main(): async with mcp.server.stdio.stdio_server() as (read_stream, write_stream): await server.run( read_stream, write_stream, InitializationOptions( server_name="agentearth-order", server_version="0.1.0", capabilities={"tools": {}}, ), ) if __name__ == "__main__": import asyncio asyncio.run(main())这段代码虽然短,但完整覆盖了一个 MCP Server 最核心的部分:能力声明、工具列表、工具执行、初始化协议。先把这三件事跑通,再往里面加业务逻辑就会顺手很多。
4.2 定义服务端:注册 Tool、Resource、Prompt 的实际姿势
实际项目里的工具肯定不止一个。AgentEarth 中我们按业务域拆分工具命名空间,避免工具名冲突。比如 order 域下的工具叫 query_order_status、create_refund;crm 域下的工具叫 get_customer_profile、update_customer_tag。命名看起来简单,但在模型自动调用时却非常重要,清晰的命名可以显著减少误选。
Resource 的注册逻辑类似,只是侧重点在于数据读取而不是操作执行。我们在实现订单查询资源时,将“订单详情”“订单追踪事件流”分别暴露成不同 URI:
- agentearth://orders/{order_id}/detail
- agentearth://orders/{order_id}/events
这样模型可以按需读取,不必把全量数据塞给上下文。做资源 URI 设计时有一个建议:遵循“域/实体/属性”的层次,方便后面对接关系和访问控制策略。
Prompts 的注册在早期很容易被忽略。我们后来发现,如果每次都在 Client 侧维护 Prompt 文本,版本一多就会非常痛苦。于是把“客服回复风格指令”“数据分析步骤模板”“订单异常处理流程”全部下沉为 Prompts 原语。业务侧只需要知道有哪些模板可用,具体的提示词内容由中台的 Prompt 管理服务统一维护。
4.3 构建客户端并打通第一个调用链
服务端写好了,客户端怎么连?一个普通 Python Client 通过 stdio 启动 Server 的示例:
import asyncio from mcp import ClientSession, StdioServerParameters from mcp.client.stdio import stdio_client async def main(): server_params = StdioServerParameters( command="python", args=["server.py"], env=None, ) async with stdio_client(server_params) as (read_stream, write_stream): async with ClientSession(read_stream, write_stream) as session: await session.initialize() tools = await session.list_tools() print("Available tools:", tools) result = await session.call_tool( "query_order_status", {"order_id": "A101"} ) print("Tool result:", result) if __name__ == "__main__": asyncio.run(main())这里有几个容易踩的坑。第一个坑是 stdio 子进程的工作目录问题,如果 server.py 依赖相对路径的配置文件,必须在 StdioServerParameters 中显式指定 cwd。第二个坑是日志不能直接打到标准输出,因为 stdio 通道要传 JSON-RPC 消息,日志一旦也输出到 stdout,Client 端解析就会出问题。
我在开发时统一把日志写到文件或 stderr。SDK 的日志默认行为可能需要调整,实测环境中你去检查 stderr 会方便很多。
4.4 加上中间件:鉴权、日志、超时控制
MCP SDK 本身没有“中间件”这个概念,但在实际工程里,我们非常需要在请求进入工具执行之前做一些通用处理。AgentEarth 的做法是在 Client 侧和 Server 侧都包一层装饰器或者拦截器,效果等同中间件。
超时控制实现逻辑如下:
- 所有 Tool 调用统一设置操作超时时间,默认 10 秒,查询类工具 3 秒,写入类工具 30 秒。
- 超时后立即向调用方返回错误,但不终止后台线程,后台线程继续执行但结果直接丢弃。
- 对可重试的幂等操作,超时后自动重试一次,但重试仅限查询和幂等写入,非幂等操作严禁自动重试。
鉴权的常见实现是在 initialize 阶段拿到 Client 传过来的自定义鉴权字段。一种思路是在 MCP 的 clientInfo 中携带 token,但更推荐的做法是放在 HTTP Header 中。如果你的传输方式是 stdio,那就必须在启动 Server 时通过环境变量把身份信息传进去。
下面是一个“日志装饰器”的简单示例:
import functools import logging logger = logging.getLogger("mcp.log") def log_call(func): @functools.wraps(func) async def wrapper(name: str, arguments: dict): logger.info("tool called: %s, args=%s", name, arguments) try: result = await func(name, arguments) logger.info("tool success: %s", name) return result except Exception as e: logger.exception("tool failed: %s, error=%s", name, e) raise return wrapper5. 性能、可靠性与安全加固
5.1 性能瓶颈与优化手段
MCP 协议本身是 JSON 消息,性能瓶颈通常不在协议层,而在序列化成本和网络往返次数。JSON-RPC 消息里的参数和结果可能会很大,尤其是文档类 Resource,动辄几 MB。
实践中我能给到的优化方案有几个:
一是对大型 Resource 做分页或按需切片。MCP 里可以在 Resource 的 URI 中带 query 参数,或者由 Server 返回部分数据并标注 total size,Client 再决定是否继续读取。
二是对高频 Tool 调用使用连接池。HTTP 传输时,保持长连接比每次新建连接性能好非常多。我们在中台网关层维护了每个上游服务的连接池,复用空闲连接,实测减少了 60% 以上的握手开销。
三是对读取类数据做缓存。一些业务字典数据、用户基础信息,可以在 MCP Server 侧做短期本地缓存,避免每次调用都穿透到数据库。缓存策略要考虑幂等性和数据一致性,不能一概而论。
四是不要滥用 Notification 推送。SSE 通道适合低频事件广播,如果业务事件非常频繁(每秒上百次),SSE 会成为瓶颈,这时可以考虑由 Client 定期拉取状态而不是实时推送。
5.2 超时、重试与幂等设计
可靠性设计就像保险,绝对不能等到事故发生了才动手。AgentEarth 里有一套统一的调用策略参数:
| 参数 | 默认值 | 说明 |
|---|---|---|
| connect_timeout | 5s | TCP/stdio 连接建立超时 |
| init_timeout | 10s | initialize 握手超时 |
| tool_timeout | 30s | 工具执行结束超时 |
| read_timeout | 60s | 读取流式响应最大等待时间 |
| max_retries | 3 | 单次会话内最大重试次数 |
| retry_backoff | 50ms | 初始重试退避时间 |
重试时用指数退避,但是注意所有重试都必须做幂等校验。比如“创建订单”这个工具,如果由于网络超时导致客户端不确定是否创建成功,服务端需要通过幂等键去查之前是否已经受理过这个请求。实践中常见的方案是让 Client 在调用时传一个 request_id 作为幂等键,服务端用这个字段做去重。
幂等设计是很多人容易忽略的。超时重试如果处理不好,用户就可能收到两条重复的退款指令。我在中台建设中坚持一条原则:只要工具名称暗示会产生副作用,就必须支持幂等或者显式拒绝自动重试。
5.3 安全加固清单
MCP 传输层支持不同的安全机制,但协议本身不含强制加密。安全加固方面,我整理了一张清单:
- 网络层:公网访问必须启用 TLS,拒绝明文 HTTP。
- 身份认证:使用 Bearer Token 或 mTLS,避免把密钥写在 URI 里。
- 授权模型:每次工具调用都要校验 action 权限,做到按租户、按工具、按按钮粒度授权。
- 输入校验:不能只依赖 MCP 的 inputSchema 做基础类型校验,业务层还需要做参数合法性校验、长度限制、危险字符过滤。
- 审计日志:记录调用者身份、调用的工具、传入参数、返回结果摘要、耗时和异常信息。涉及敏感数据的参数可以做脱敏。
- 依赖安全:定期扫描 SDK 和依赖包漏洞,升级 MCP SDK 至稳定版本。
很多团队只在网关层做认证,却没有在业务工具层做二次校验,这种做法属于典型的“外紧内松”,一旦内部网络被穿破,后面就是一马平川。安全的关键在于纵深防御。
6. 常见问题与排查技巧实录
6.1 连接类问题速查
| 现象 | 可能原因 | 排查建议 |
|---|---|---|
| Client 连不上 stdio Server | 子进程启动失败,或命令路径错误 | 手动在终端执行 server 启动命令,观察是否报错 |
| stdio 通信乱码 | 服务端有非 JSON 输出混入 stdout | 把所有 print、日志输出全部改到 stderr |
| initialize 超时 | 协议版本不匹配或 Server 端未正确声明 capabilities | 抓包看 initialize 请求与响应是否完整 |
| HTTP 连接返回 404 | MCP endpoint 路径配置错误 | 确认请求路径与 Server 注册路径一致 |
| SSE 连接反复断开 | 网关空闲连接超时时间太短 | 在负载均衡器上调整空闲超时参数 |
我在 AgentEarth 的早期开发阶段,最频繁遇到的问题就是 stdio 通信乱码。当时 SDK 日志默认输出到 stdout,JSON-RPC 消息被日志污染,客户端解析一直报错。解决的方案很简单,把所有日志重定向到 stderr,但仍然建议你在编排脚本里直接设置环境变量把日志关掉。
6.2 调用与数据类问题
| 现象 | 可能原因 | 排查建议 |
|---|---|---|
| 工具找不到 | Server 端未注册或运行时未加载模块 | 先调用 session.list_tools() 确认工具列表 |
| 工具入参报错 | 模型生成的参数不符合 inputSchema | 检查 Schema 描述是否明确,增加 enum、格式示例 |
| 返回内容太大 | Resource 未做分页或限制 | 检查客户端拼接逻辑,对长文本做摘要式读取 |
| 上下文内容超限 | 模型上下文窗口不够,或消息历史太长 | 合理规划工具返回内容,只返回关键字段 |
| 数据不一致 | 多个中台实例共享同一个数据库但未做缓存同步 | 在缓存层增加版本号或引入分布式缓存 |
工具入参报错是一个很有意思的问题。大模型根据自然语言生成 JSON 参数,它没有咱们手写代码那么谨慎,类型错误、缺字段都经常发生。除了加强 Schema 描述,还可以在工具执行器里做一层参数规范化,例如把数字字符串隐式转成 int、非必填字段自动补默认值。
6.3 排障方法论与日志埋点建议
遇到排查难题时,我有一个固定流程:先复现,再缩小范围,最后加日志。复现是指在隔离环境里构造最小请求,确认问题是否稳定出现;缩小范围是逐步跳过网关、编排层,直接调用目标工具,看问题出在哪一层;最后再在疑点处增加详细日志。
MCP 里的每个请求都对应一个 request id,强烈建议在日志中带上这个 id。这样当 Client 报出一堆错误时,我们可以快速从网关日志中按 request id 找到完整链路。另外,给每个 Tool 增加生命周期埋点:进入执行、参数解析完成、业务逻辑完成、结果序列化完成、响应已发送。这样在性能排查时,可以直接看出耗时分布,是网络开销还是业务逻辑开销。
还有一个非常容易被忽视的问题:时钟漂移。日志链路中的时间戳如果来自多台机器,而机器时间不同步,排查时长会很痛苦。因此在生产环境配置统一的 NTP 同步是基本的运维习惯。
7. 从单体 Agent 到服务中台的演进路径
7.1 分阶段演进路线
有朋友问过我,AgentEarth 这种中台是不是一开始就要把架构设计得这么复杂。我的回答是:不要一步到位,分阶段演进更容易控制风险。
第一阶段,先在现有的单体 Agent 里引入 MCP 协议做内部工具解耦。这时候不需要独立部署中台,只要让 Agent 通过 MCP Client 调本地或本网的几个 MCP Server,就能清晰看到协议带来的标准化收益。
第二阶段,增加一个轻量网关层,负责统一鉴权、限流、日志。此时 MCP Server 可以部署在多台机器,网关做路由转发。这是从“单机协议使用”迈向“平台化服务”的关键一步。
第三阶段,引入服务注册与发现、弹性伸缩、多租户隔离、配置中心,形成完整的服务中台。AgentEarth 目前正处于第二阶段向第三阶段过渡的状态。
第一阶段遇到什么问题、第二阶段如何解决,每一个阶段踩的坑都会让你对架构的理解更深入。
7.2 存量系统兼容与团队协作分工
存量系统往往不支持 MCP,这没有关系,MCP Server 本质上是适配器。我们接一个旧订单系统时,只需要写一个薄薄的 MCP Server,内部调用旧系统的 HTTP API 或读数据库,对外暴露 MCP 协议即可。真正难的不是技术,而是确定“哪些能力适合暴露给 Agent”。我的经验是先暴露高频、低风险、弱依赖的查询类能力,再逐步开放写入类能力,敏感操作还要引入人工审批。
团队协作分工方面,AgentEarth 目前分成了三组:负责协议基础设施的平台组、负责各业务域能力适配的服务组、负责上层 Agent 应用编排的应用组。平台组维护 SDK 版本和网关,服务组专注于工具质量和数据安全,应用组关注编排效果。这样各司其职,职责边界清楚很多。
7.3 下一步扩展方向
MCP 规范还在快速演进,AgentEarth 后续会重点跟进以下几个方向:分布式授权模型、流式响应规范、跨 Server 的任务编排、以及更完善的可观测性标准。
我个人比较期待协议层能标准化“任务执行状态查询”能力。现在做一个耗时较长的工具任务,客户端只能干等或者自己轮询,如果协议层能统一支持任务提交、状态查询、结果拉取,中台的复杂任务编排会轻松很多。
另外,多模态数据通过 MCP 传递也是一个趋势。图像、音频、视频这类二进制内容在 JSON-RPC 消息里传输效率不高,未来可能会看到 base64 编码的标准化封装或者二进制的流式传输方案被纳入规范。
写在最后的一些体会
我在 AgentEarth 项目里踩过最深的一个坑,是刚开始过度关注协议传输层的细节,反而把精力放在了能直接产生价值的业务场景上。MCP 是一种工具,不是目的。它最核心的价值是统一了 AI 应用与外部能力的交互标准,但你的中台能不能真正为业务带来价值,还是取决于你封装出来的工具质量、权限边界、运维体系以及上层编排的设计。
如果让我给正在做类似项目的你一条建议,我会说:先用一个小范围、高价值的业务场景做试点,彻底跑通 MCP Server 的封装、客户端的接入、权限和日志,再横向扩展。不要一上来就追求所谓的通用中台架构,业务没有验证,平台就是空中楼阁。AgentEarth 走到现在,我最开始设计的那张架构图已经被推翻重画了三次,但我并不觉得这有什么可惜。架构是为当前的真实问题服务的,保持演进的心态,比一步到位的完美设计重要得多。