MCP 怎样接入工具并让 Agent 协作

MCP 怎样接入工具并让 Agent 协作
Asaakii一、同一个能力,两个人要用
我的项目里有个需求,当时把我卡住了。
系统要检索政策文件。这个能力有两个消费者:
- 模型。用户问「十四五规划里怎么说数字经济」,模型得自己判断该查哪个库、加什么过滤条件,然后调用。这是自主的。
- 图节点。产业诊断链跑到某一步,固定要去捞该县的产业材料。这是流程写死的,模型不参与决策。
一个是「模型想查就查」,一个是「流程到这儿必须查」。两种调用方式,控制强度完全相反。
我最初的念头是写两套:MCP 工具给模型用,图节点里再写一份直连的检索逻辑。写到一半就发现不对:元数据过滤条件怎么构造、rerank 参数怎么配、多库结果怎么合并去重,这些逻辑要在两个地方各维护一遍。改一处忘一处是迟早的事。
这个问题最后的解法,让我对「通信协议到底解决了什么」的理解和教科书不太一样。但要讲清楚这个解法,得先把 MCP 是什么、怎么运作说明白,否则「暴露层」「两个出口」这些词都是悬空的。所以这篇的顺序是:先讲机制,再讲我的取舍。
二、先分清:三种协议不是同一道选择题
初学者最容易混淆的是:看到多个 Agent,就以为一定要上 A2A;看到 MCP,又以为它会自动替你实现所有服务接入。实际上,它们解决的是不同边界上的问题:
| 协议 | 提出方 | 解决的边界 | 一句话理解 |
|---|---|---|---|
| MCP | Anthropic 发起的开放协议 | 应用/Agent ↔ 工具、资源、提示能力 | 给应用接工具和上下文能力 |
| A2A | Google 发起的开放协议 | 独立部署的 Agent ↔ Agent | 给远程 Agent 交接任务 |
| ANP | 开源 Agent Network Protocol 社区 | 开放网络中的 Agent 之间 | 给去中心化 Agent 网络铺基础设施 |
三者的关系,可以按「距离」记:MCP 管的是一个 Agent 伸手够外部工具,距离最近;A2A 管的是两个独立 Agent 隔着网络交接任务,距离中等;ANP 管的是成百上千个陌生 Agent 在开放网络里互相发现,距离最远。
这篇的主角是 MCP,也是三者里目前最该先搞懂的一个。A2A 和 ANP 我会在后面给出真实的机制对照,哪怕结论是「我没用」,你也得先知道自己在拒绝什么。
三、为什么需要一层协议
先说清楚 MCP 要解决的问题,不然容易把它当成万能药。
一个能推理、能调工具的 Agent,看起来已经很完整了,但它有三个反复出现的痛点:
- 工具集成成本高。每接一个新服务,无论 GitHub、数据库还是文件系统,都得写一个专门的适配器:处理它的 HTTP 请求、认证、错误、返回格式。API 一变,相关代码全得改。更麻烦的是别人写的适配器你用不了,因为大家的接口约定各不相同。
- 能力扩展被锁死。Agent 的能力被钉在预定义的工具集里,没有一套统一办法在运行时「发现」新工具、问它「你能干什么、参数怎么传」。
- 跨系统协作只能手糊。任务复杂到需要多个独立部署的 Agent 配合时,没有标准的任务交接格式,只能各写各的胶水。
MCP 要解决的是第一、第二个痛点:给「应用怎么够到外部能力」定一套标准化的接入契约。客户端不需要预先知道某个服务的私有接口,只要对方讲 MCP,它就能问出「你有哪些工具、每个工具要什么参数」,然后按统一格式调用。
但要先纠正一个常见误解。很多人把 MCP 类比成「Agent 世界的 TCP/IP」,然后顺势以为它会替你把所有服务都实现好。更准确的读法是:它统一的是通信规则,不是业务实现。就像 TCP/IP 让任意两台机器能通信,但不会替你写好 HTTP 服务器、也不会替你实现网站的业务逻辑。MCP 同理,它能消掉「每个客户端重复对接同一个服务」的成本,但服务本身特有的业务适配、认证、治理,一样都不会少。这一点后面第九节会用我的真实经历再说一遍。
四、MCP 到底长什么样
这是原来那版最缺的一节。光讲定位不讲机制,等于没讲协议。
4.1 三个角色:Host、Client、Server
MCP 里有三个角色,务必先分清:
- Host(宿主):你实际在用的那个 AI 应用,比如 Claude Desktop、某个 IDE 插件、你自己写的 Agent 程序。它是「决策者」,决定连哪些 Server、把哪些工具给模型看、允不允许某次调用。
- Client(客户端):Host 内部为每一个 Server 各起一个的连接器,一对一。它负责按 MCP 协议和 Server 通信。你通常不直接写它,用 SDK 就有。
- Server(服务端):真正提供能力的那一方,暴露工具、资源、提示给外界。GitHub 的官方 MCP Server、文件系统 Server,以及后面我自己写的检索 Server,都属于这一类。
一句话记:Host 决定给不给,Server 决定有什么,Client 是中间那根管子。「模型自动拿到全部上下文」是误解,给哪些能力、何时给模型看、是否允许调用,全由 Host 说了算。
4.2 一次完整的对话:从握手到调用
MCP 底层是 JSON-RPC 2.0。一次典型的交互分四步,我把真实报文贴出来(省略了部分字段),你会发现它并不神秘。
第一步,握手(initialize)。Client 连上 Server,先互相通报协议版本和能力:
1 | // Client → Server |
1 | // Server → Client:我讲这个版本,我提供 tools 能力 |
第二步,问「你有哪些工具」(tools/list)。这一步就是前面说的「运行时发现能力」:
1 | // Client → Server |
1 | // Server → Client:返回工具清单,每个工具自带 JSON Schema |
注意那个 inputSchema,它是模型能「看懂」这个工具的关键。Host 会把它转成模型的工具定义,模型据此决定要不要调、怎么填参数。
第三步,调用(tools/call)。模型决定要查天气,Host 通过 Client 发出调用:
1 | // Client → Server |
1 | // Server → Client:返回结果内容 |
四步走完,你就理解了 MCP 的骨架:握手 → 发现 → 调用 → 拿结果。所谓「标准化接入」,标准化的就是这四步的格式。任何讲 MCP 的 Server,Client 都能用同一套流程问出它有什么、并调用它,这才是「适配器不用每家各写一遍」那句话的真正含义。
4.3 三个原语:tools、resources、prompts
上面只演示了 tools。MCP Server 实际能暴露三类东西:
- tools(工具):模型可以调用的动作,会产生副作用或返回计算结果,比如「查天气」「写文件」「检索政策库」。这是用得最多的一类。
- resources(资源):可供读取的上下文数据,用 URI 标识,比如一个文件、一段数据库记录、一份代码。它更像「只读的、可被引用的内容」,由应用决定要不要塞进上下文。
- prompts(提示):预置的提示模板,通常由用户显式触发(比如 IDE 里的一个斜杠命令),用来把某种常见操作固化成一句话。
初学阶段,先牢牢记住 tools 就够了;resources 和 prompts 知道「还有这两类、分别是只读上下文和提示模板」即可,用到再深入。
4.4 两种传输:stdio 与 HTTP
MCP 的报文可以走两种通道,这会直接影响后面第九节的信任问题:
- stdio:Server 作为本地子进程运行,Client 通过标准输入输出和它通信。桌面应用装的本地 MCP Server 多是这种。它跑在你机器上,可能读取本机环境变量里的凭证。
- Streamable HTTP:Server 是一个远程 HTTP 服务,Client 通过 HTTP 请求(可配合流式返回)与之通信。适合团队共享、集中部署的场景。
记住这个区别:stdio 是本机第三方进程,HTTP 是远程服务。它俩在「谁在执行代码、凭证放在哪」上完全不同,治理方式也就不同。
五、动手:十行定义一个 MCP 工具
概念讲完,看代码你会更踏实。用官方 Python SDK 的 FastMCP,定义一个工具几乎就是给普通函数加个装饰器:
1 | from mcp.server.fastmcp import FastMCP |
就这些。有几个点值得说:
@mcp.tool()会自动根据类型标注(city: str)和文档字符串,生成 4.2 里那份inputSchema和description。你不用手写 JSON Schema,这是 SDK 帮你省的活。- 函数体就是你的业务实现。MCP 不关心你在里面调 API 还是查数据库,它只负责把这个函数「标准地端出去」。
- 最后
mcp.run(transport="stdio")决定走哪种传输。
把这段和第四节的四步握手对上:你写的这个函数,会出现在 tools/list 的返回里;模型调它,就是一次 tools/call。你负责实现,MCP 负责暴露,这句话现在有代码撑着了。它也正好引出我项目里真正想讲的那个结构。
六、我的项目:一个业务层,两个出口
回到开头那个「要不要写两套」的问题。最后的结构是四层:
flowchart TB Client["DifyClient(API 层)<br/>封装 HTTP、检索参数、rerank 配置"] Service["DifyToolService(业务层)<br/>检索逻辑 · 元数据过滤构造 · 多库合并去重"] MCP["MCP Server(工具暴露层)<br/>retrieve_policy_docs / retrieve_county_materials …"] Node["LangGraph Node(图节点)<br/>dify_retrieve"] Model["🤖 模型自主调用"] Graph["⚙️ 流程确定性调用"] Client --> Service Service --> MCP Service --> Node MCP --> Model Node --> Graph
关键在中间那层。业务逻辑只写在 DifyToolService 里一遍,上面长出两个出口。先看业务层本身(简化版):
1 | class DifyToolService: |
选库、构造过滤、rerank、去重,这些是我这套知识库特有的编排,都收在这一层。
出口一是 MCP Server,暴露给模型自主调用。就是第五节那个装饰器,套在业务方法上:
1 | mcp = FastMCP("dify-retrieval") |
注意签名里那几个可选参数 domain、data_period,这是我特意暴露给模型的。模型问「十四五规划怎么说数字经济」,会自己填上 data_period="十四五"、domain="数字经济"。查不查、怎么查,模型说了算。
出口二是 LangGraph 图节点,流程确定性调用。同一个 service,直接调,参数由代码写死:
1 | def dify_retrieve(state: DiagnosisState) -> DiagnosisState: |
把两个出口并排看,结论就很直白了:同一份 service.retrieve_*,一个被 @mcp.tool() 包起来交给模型自由裁量,一个在图节点里带着写死的参数直接调。控制强度完全相反,实现只有一份。
这才是 MCP 在我这儿真正的价值:不是「少写代码」,而是写一次,两种消费方式都能用。想清楚「把业务逻辑沉到协议下面、协议只负责暴露」这一层,两个出口就是自然的结果,根本不用写两套。
七、A2A:当 Agent 要和 Agent 说话
标题里的「Agent 协作」指的就是 A2A。这一节给它一个真实的机制介绍,因为只有先看懂它,我后面「为什么没用」才站得住。
A2A 解决的是两个独立部署的 Agent 隔着网络交接任务。它有两个核心概念。
先看 Agent Card,一张「能力名片」。每个 A2A Agent 在一个约定的 well-known 路径下(如 /.well-known/agent.json)放一份 JSON,声明自己是谁、能干什么:
1 | { |
别的 Agent(或编排器)拉到这张名片,就知道能把什么任务派给它。这和 MCP 的 tools/list 是同一种思路:先发现,再调用,只是发现的对象从「工具」变成了「另一个 Agent」。
再看任务生命周期,一个状态机。派任务不是一问一答,而是一个可以长时间运行、带进度的任务,状态在几个值之间流转:
stateDiagram-v2 [*] --> submitted: 提交任务 submitted --> working: 开始处理 working --> input_required: 需要补充信息 input_required --> working: 收到补充 working --> completed: 产出结果 working --> failed: 出错 working --> canceled: 被取消 completed --> [*] failed --> [*] canceled --> [*]
发起方可以订阅进度、在 input-required 时补料、最后收走产物。这套东西是为跨边界、异步、可能要来回几轮的协作设计的。
但我没用。我的系统里确实有「多个专业角色」,首席经济学家、政策策划专家、规划专家、报告编辑,听起来正是 A2A 的场景。可是角色之间不协商。谁在什么条件下干什么、结果交给谁,全部写死在图里:监测 → 诊断 → 归因 → 规划 → 质量门禁 → 专家终审。这是一条流水线,不是一个对等网络。
A2A 的哲学是「对等通信、尽量少依赖中心协调器」。而我的系统恰恰需要那个中心协调器,它就是 LangGraph 的图。对一个要出正式分析报告的系统来说,「谁在什么时候说了什么」必须可预测、可复现、可追责。让角色之间自由协商,我拿什么保证这次和上次的产出口径一致?而且这些角色都在同一个进程里,用 A2A 那套 Agent Card 发现 + HTTP 任务交接,纯属给自己加一层没必要的网络和序列化开销。
协议解决的问题,你得真的有才行。A2A 是把角色拆成独立部署、需要跨网络异步协作时才划算;我的场景不是。
八、ANP:更远的那一层
ANP(Agent Network Protocol)讨论的比 A2A 还底层:在一个开放的、去中心化的 Agent 网络里,成百上千个互不认识的 Agent,怎么拥有身份、怎么被发现、怎么建立可信通信。它关心的议题包括:
- 身份:用去中心化标识(DID)给每个 Agent 一个可验证、不依赖某个中心机构的身份;
- 发现:通过 Agent 的描述文档,让陌生 Agent 能被检索、被理解;
- 协商与通信:在没有预先约定的情况下,两个 Agent 怎么协商用什么协议往下沟通。
可以把它理解成「Agent 版的互联网基础设施」,DNS、身份、路由那一层的事。它目前还很早期。
我也没用,而且理由更简单:我没有那个规模的问题。我的 Agent 总共不到二十个,全在同一个进程里,「注册表」就是一个 Python 列表。ANP 要解决的是「陌生 Agent 在开放网络里互相发现」,而我的 Agent 彼此之间根本不陌生,也不在开放网络里。没有那个规模,就不需要那个规模的方案。
九、几点落差
机制和取舍讲完,说三个我踩过、书上没太强调的落差。
9.1 MCP 并没有让我少写适配器
书里的许诺是:有了 MCP,就不用为每个服务手写适配器了。
但我照样写了 DifyClient。
因为通用 MCP Server 给不了我要的东西:Dify 的检索 API 要构造 retrieval_model(检索方式、rerank 模型、top_k)、要构造 metadata_filtering_conditions、要处理多库并行检索后的合并去重。这些是我这套知识库体系特有的编排,不是「调个 HTTP 接口」那么简单。
对照第六节的代码就清楚了:MCP 帮我省掉的是最外面那层装饰器和 Schema,省不掉里面 DifyToolService 和 DifyClient 的业务实现。所以 MCP 的定位要说准:它是一个「暴露层」,不是「实现层」。它标准化的是模型怎么够到你的能力,而不是你的能力怎么实现。
那句「不用手写适配器」的许诺,正确的读法是:如果你要的服务已经有成熟的官方 MCP Server(GitHub、文件系统这些),确实不用写;但凡你的需求带一点自己的业务编排,适配器还是得你写,MCP 只帮你把它标准地端出去。
9.2 MCP 的信任问题比想象中重要
用 MCP 生态时我才意识到一件事:协议统一了接入方式,却不会替你完成信任判断。
回到第 4.4 节那两种传输。本地 stdio Server 往往意味着要在你机器上跑一个第三方进程,并可能从环境变量里读凭证;远程 HTTP Server 则未必在本机执行。无论哪种,是否调用、携带什么凭证、结果是否可接受,都应该由 Host/Client 的权限策略控制,而不是交给模型自行决定。
MCP 对 HTTP 传输定义了授权框架,但授权并不是所有实现自动具备的能力;对 stdio 传输,规范建议从环境获取凭证。因此,安装来源、凭证最小化、工具 allowlist、升级机制、调用审计,都是运行时和团队自己要补上的治理层。
我用的运行时对这件事处理得挺克制:内置 MCP 目录里的条目需要经过 PR 合并;升级要显式触发;安装时的密钥统一放进凭证文件。需要强调的是:这是这个运行时的治理策略,不是 MCP 协议自动提供的信任等级。
一开始我觉得这有点保守,后来觉得这是对的。「更推荐选大公司背书的 MCP 工具」,说的是同一件事:MCP 把「接入外部能力」的成本降到了很低,低到你会不假思索地接,而这恰恰是风险所在。
9.3 协议不解决「该不该调」
最后一点。MCP 解决的是「怎么调」,没解决「该不该调、该调哪个」。
第六节里模型看得见的工具,是业务链在注册表里声明的,模型分析链只看得见它那三个工具。这个决定和 MCP 无关:MCP 让你能接一百个工具,但一百个工具摆在模型面前,「选哪个」本身就成了新的失败模式。工具越多,模型选错的概率越高,上下文也越挤。
有句话我很认同:如果人类工程师都说不准该用哪个工具,别指望 Agent 做得更好。MCP 负责让门开着,决定哪扇门对这个任务开着的,还是你自己。
十、小结
先把机制收一下:MCP 底层是 JSON-RPC,一次交互就是握手 → 发现 → 调用 → 拿结果四步;三个角色是 Host 决定给不给、Server 决定有什么、Client 是中间的管子;三个原语里 tools 最常用;两种传输 stdio 和 HTTP 在信任上截然不同。搞懂这些,你就能自己写一个 MCP Server 了。
再把取舍收一下,落到我的项目:
- MCP 用了,但它给我的不是「少写适配器」,
DifyClient该写还得写,而是同一份业务逻辑,模型走 MCP 自主调用,流程走图节点确定性调用,实现只有一份; - A2A 没用,因为我的角色之间不协商,流水线要的是可复现,不是对等;
- ANP 没用,因为我没有那个规模的问题。
回到开头那个「写两套」的纠结。答案其实不在协议本身,而在分层:把业务逻辑沉到协议下面去,协议只负责暴露。想清楚这一层,两个出口就是自然的结果。
MCP 是一个很好的暴露层。但它暴露的东西,还得你自己造。
参考资料
- Hello-Agents 第十章:智能体通信协议 - Datawhale
- Model Context Protocol 规范 - MCP 项目主页
- MCP Python SDK -
FastMCP出处 - MCP Authorization specification
- A2A Protocol - Agent Card 与任务生命周期
- Agent Network Protocol - 去中心化身份与发现










