MCP协议:Agent工具的“USB标准“
MCP协议Agent工具的USB标准上一篇文章我们设计了Skill系统外部工具可以通过JSON格式定义、HTTP请求接入。但这仍然是我们自己定义的格式——换个Agent框架就得换种写法。MCP协议要做的就是统一这一切。一、问题回顾没有标准的日子Day2中我们用一个JSON来定义外部工具{name:get_weather,description:查询天气,executor:{type:http,url:https://...}}这能跑但有个大问题这个格式是我们自己定义的。如果别人写了一个Brave搜索工具它用的是完全不同的格式。如果想把那个工具接入我们的Agent得先理解它的格式再写适配代码。类比一下这就像每家手机厂商有自己的充电口——iPhone用Lightning安卓用Micro-USB/Type-C。你有100根不同的充电线每根只能充一种设备。太荒谬了。MCP要解决的问题就是定义一个所有Agent和所有工具都遵循的标准协议。实现一次处处可用。二、MCP的三要素Client、Server、Transport2.1 Client工具消费者Client就是Agent这边。它不关心工具的具体实现只关心三件事listTools()告诉我你能做什么callTool()帮我做一件事listResources()你有什么数据可以给我读Client的责任是消费工具而不是定义工具。传统模式下每接入一个新工具Agent代码就得改。MCP模式下Agent只需要知道如何发现工具不需要知道有哪些工具。2.2 Server工具提供者Server是工具那一边。任何程序只要实现了MCP协议就可以被任何Agent消费。Server内部可以做什么可以是读写文件filesystem Server、操作数据库postgres Server、搜索网页fetch Server、控制浏览器playwright Server。对Client来说这些都是工具调用方式完全相同。2.3 Transport通信方式MCP支持两种通信方式stdio模式Agent启动一个子进程作为MCP Server。Agent通过标准输入输出和它通信。好处是进程隔离——Server崩溃不影响Agent且不需要网络配置。我们的项目用的就是这个模式。Streamable HTTP模式Server作为独立的HTTP服务运行。适合远程工具场景——工具部署在内网服务器上Agent通过网络远程调用。重点是两种Transport用的是同一套协议。工具开发者只需要实现一次逻辑stdio和HTTP两种部署方式都可以支持。三、工具发现pull vs push3.1 MCP选择的是Pull模式Agent连接Server后主动调用listTools()获取工具列表。为什么不让Server推送工具给Agent这是协议设计中的一个关键选择。Pull模式的优势Client掌控节奏什么时候连接、什么时候发现由Agent决定容错性更好Server临时挂了Agent可以下次连接时重新发现简单可靠不需要注册中心不需要状态同步Push模式的问题是Server需要知道有哪些Client在监听。如果有新的Client上线怎么通知如果推送中心挂了怎么办引入了太多额外复杂性。MCP的选择是保持简单——Pull模式不需要任何中心化组件Agent和Server直接通信。3.2 实际的发现过程1. Agent启动 2. 查询数据库 → 获取用户安装的MCP Skill 3. 对每个MCP Skill a. 启动子进程如 npx modelcontextprotocol/server-filesystem /workspace b. 通过stdio发送初始化请求握手 c. 调用 listTools() → 拿到工具列表 d. 将每个工具包装为 FunctionCallback适配Spring AI格式 4. 所有工具注册到ChatClient → LLM看到完整工具列表四、工程实践超时、缓存、失败处理MCP Server可能出各种问题——npx没装、网络不通、配置错误。如果Agent启动时卡在一个失败的连接上整个应用都起不来。4.1 超时保护连接和工具发现必须在10秒内完成。超时了就不等了——这个Server标记为失败Agent带着其他正常工具启动。4.2 缓存策略三个缓存Map同时工作connections: skillId → MCP连接对象复用连接 callbacks: skillId → 已发现的工具列表复用工具定义 failures: skillId → 已知失败的Server不再重试成功过一次的Server工具列表被缓存后续直接复用不再重复发现。失败过的Server被标记在失败缓存中后续跳过。只有用户主动刷新时才清除缓存重新尝试。4.3 失败不阻塞这是最重要的工程原则。引入MCP是为了扩展能力但不能让扩展成为瓶颈。一个MCP Server连接失败不应该影响Agent回答今天天气怎么样这种不需要MCP工具的问题。所以我们的策略是能连上就加上连不上就跳过。Agent带着它能获得的所有工具工作。五、MCP工具的命名策略为什么MCP工具名字前都加mcp_前缀内置计算器: calculate 外部HTTP工具: ext_get_weather MCP文件读取: mcp_read_file MCP网页搜索: mcp_search_web两个原因避免冲突有多个来源的工具名字可能重复。前缀天然的命名空间。LLM判断辅助LLM看到mcp_前缀就知道这是MCP协议的工具可以信任其输入输出格式。看到ext_前缀就知道这是用户自定义的格式可能不标准。六、MCP的长远意义最后聊一个更大的视角。回想计算机外设的历史USB之前每种设备专用接口键盘PS/2、鼠标串口、打印机并口USB之后一个接口连接所有设备MCP在做同样的事情。现在的Agent工具生态是碎片化的——每家的工具格式都不一样。MCP一旦普及所有工具遵循同一个标准任何Agent都能消费。对Java开发者尤其重要。传统上AI工具几乎全是Python写的。但MCP是语言无关的——一个Python实现的MCP ServerJava Agent通过stdio就能调用。MCP用协议标准化打破了语言壁垒。当然MCP还很年轻。它目前缺少认证机制如何验证一个MCP Server的可信度、缺少工具编排多个工具如何协作。协议在进化但方向是对的。项目链接项目代码day6-agent