学完 Tool Use,我已经能解释模型怎样提出工具调用。但接入更多服务时,问题又变了:每个系统都有自己的接口、参数和鉴权方式;如果对方本身也是一个 Agent,我还需要知道它是否接受了任务、什么时候需要补充信息,以及最终交付了什么。

既然已经有 Function Calling 和 HTTP API,为什么还需要 MCP 与 A2A?我的理解是:前者让外部能力有共同的接入方式,后者让独立智能体之间的任务协作有共同的表达方式。

协议内容更新于 2026 年 9 月 14 日:本文以 MCP 2026-07-28、A2A 1.0.1 为基线。页面归档日期与本次技术更新日期分开记录。

MCP 与 A2A 在系统中的位置

图 1:Host 可以接入 MCP 服务,也可以通过 A2A 委托远端智能体。两种协议不是依次经过的上下级关系。

1. 先把三个问题分开

假设我要做一个技术资料助手,帮助我搜索文档、核对不同版本的说法,并生成报告。

我遇到的问题对应的机制
模型怎样表达“搜索这段内容”?Function Calling/Tool Calling
搜索、文档、数据库怎样通过共同接口供程序使用?MCP
如何把“整理一份带证据的研究报告”委托给独立 Agent?A2A

Function Calling 是模型接口中的调用表达机制。MCP 是 Host 与能力提供方之间的协议。A2A 面向独立智能体之间的通信与任务协作。执行策略、权限、预算和结果验收,仍然需要运行程序负责。

这里容易出现一个误会:把 MCP 理解成“更高级的推理方法”。实际上,即使程序完全不调用 LLM,也可以作为 MCP Client 使用服务;模型也可以通过直接注册的本地函数完成 Tool Use,而不使用 MCP。

ReAct 讨论如何在行动与反馈之间继续决策;协议讨论参与方如何互相理解消息。这些能力可以组合,但解决的问题不同。A2A 与 MCP 的官方定位

2. MCP 统一的是哪一段接口?

MCP 的全称是 Model Context Protocol,模型上下文协议。它经常被比作 USB-C:不同应用通过共同接口接入外部能力。这个比喻适合入门,但不能进一步推导成“接上就自动理解业务”。

2.1 Host、Client、Server 各自做什么?

Host 是用户使用的应用或 Agent 运行环境。Client 是其中负责 MCP 通信的组件。Server 暴露工具、数据或模板,并把协议请求接到真正的业务实现上。

文档 MCP Server 后面仍然可能调用搜索引擎、数据库和文件服务。它把业务适配封装在能力提供方一侧,方便多个 Host 复用;底层 API 的差异没有凭空消失。

统一接入前后

图 2:统一的是跨应用的接入契约;服务内部仍然需要业务适配、认证和错误处理。

2.2 Tools、Resources、Prompts

能力技术资料助手中的例子理解重点
Toolssearch_documents、export_report调用一个操作,操作也可以只是查询
Resourcesdocument://spec/v2通过 URI 获取内容
Promptscompare_spec_versions获取带参数的提示模板

同一份文档可以通过读取工具获取,也可以作为 Resource 暴露。设计时应考虑调用者需要“执行查询”,还是“定位并读取已有内容”。不能简单把 Tools 等同于写入,把 Resources 当作数据库权限系统。

Prompt 模板也不是自动生效的系统指令。Host 决定如何展示或使用模板,外部模板的来源不会让它天然获得更高权限。具体能力说明见 MCP 服务端概念

2.3 “发现服务”有两个层次

首先,Host 需要知道服务器地址或本地启动命令,这可能来自配置或目录。然后,Client 才能向已知服务器发现能力。

新版 server/discover 用于查询服务器支持的版本、能力与身份:服务端必须实现,客户端可以选择是否先调用。它不是让客户端自动扫描互联网寻找所有服务。MCP Discovery

3. 一次 MCP 工具调用怎样接到模型上?

技术资料助手发现一个文档服务后,可以获取工具列表,把合适的工具定义提供给模型。模型提出调用,Host 验证,再经 MCP Client 转发;结果经适配后回到模型对话。

从模型调用到 MCP 请求

图 3:模型接口与 MCP 是两套消息边界。Host 维护工具路由以及两侧调用标识的关联。

这里有两个很实际的细节。

第一,多个 Server 都可能提供同名工具。Host 应维护可信路由,例如把模型可见名称 docs_search 映射到指定服务器的 search_documents,而不是只凭名称猜目的地。

第二,MCP 的 Schema 表达范围与具体模型 API 的支持范围未必相同。适配时要检查兼容性;如果需要简化模型可见描述,执行前仍必须按完整契约验证,不能悄悄放宽业务约束。

3.1 JSON-RPC 是信封,MCP 定义具体方法

下面是 MCP 2026-07-28 的一个普通工具调用示例。假设双方使用该版本,且本轮不需要客户端补充输入:

{
  "jsonrpc": "2.0",
  "id": 21,
  "method": "tools/call",
  "params": {
    "name": "search_documents",
    "arguments": {"query": "MCP 工具结果", "limit": 3},
    "_meta": {
      "io.modelcontextprotocol/protocolVersion": "2026-07-28",
      "io.modelcontextprotocol/clientCapabilities": {},
      "io.modelcontextprotocol/clientInfo": {
        "name": "study-assistant", "version": "1.0"
      }
    }
  }
}

对应结果可以是:

{
  "jsonrpc": "2.0",
  "id": 21,
  "result": {
    "resultType": "complete",
    "content": [
      {"type": "text", "text": "找到文档 doc-17:MCP 工具结果规范。"}
    ],
    "isError": false
  }
}

id 关联请求与响应;method 指定协议操作;arguments 是工具自己的参数。模型接口里的 tool call ID 与 JSON-RPC ID 可以不同,由 Host 维护对应关系。

resultType: complete 表示这次协议结果完成,不代表整项用户任务完成;还要检查工具是否报告错误以及返回内容是否满足需要。MCP 工具也支持结构化结果和输出 Schema。MCP Tools

3.2 stdio 与 Streamable HTTP

stdio 常用于 Host 启动本地 Server,通过标准输入输出交换消息;调试日志不能随意混进协议输出。Streamable HTTP 常用于远端服务,也能用于本机 HTTP 服务。

HTTP 调用除了 JSON-RPC 正文,还需要按协议带版本和方法等头部;只复制上面的 JSON,并不等于完整 HTTP 请求。方法/名称头便于网关识别请求,但服务端仍需验证头与正文一致,不能只信任调用者填入的头部。

Streamable HTTP 可以返回 JSON 或使用 SSE 流。它与早期被弃用的 HTTP+SSE transport 不是同一个版本的传输设计。Streamable HTTP 规范

4. 新版为什么转向无状态?

4.1 从连接握手,到每个请求自带信息

2025-11-25 及更早版本使用 initialize → initialized 握手。2026-07-28 移除了这套握手与协议级会话,版本和客户端能力随请求携带;客户端身份信息推荐随请求提供。

这减少了处理请求时对先前连接状态的依赖。但“旧版一定需要粘性会话”“新版随便换副本就万事大吉”都太绝对:部署仍然要处理业务数据库、身份、任务存储和缓存。

协议状态与 MRTR

图 4:新协议不依赖先前的初始化握手。需要补充信息时,通过 MRTR 返回要求,再由客户端发起后续请求。

例如一次资料分析返回 analysis_id,下一次查询显式传这个 ID。状态依然存在,只是其归属和引用方式更清楚了。对象 ID 也不是访问凭证,读取它仍要校验身份与权限。

4.2 MRTR:缺信息时,不必把请求一直挂在那里

MRTR 是 Multi Round-Trip Requests,多次往返请求。假设导出报告时缺少输出语言,Server 可以返回下面这样的中间结果。

{
  "jsonrpc": "2.0",
  "id": 22,
  "result": {
    "resultType": "input_required",
    "inputRequests": {
      "report_language": {
        "method": "elicitation/create",
        "params": {
          "mode": "form",
          "message": "报告使用哪种语言?",
          "requestedSchema": {
            "type": "object",
            "properties": {
              "language": {"type": "string", "enum": ["zh", "en"]}
            },
            "required": ["language"]
          }
        }
      }
    },
    "requestState": "opaque-state-issued-by-server"
  }
}

这个例子要求客户端预先声明支持相应 elicitation 能力。客户端获取回答,将对应的 inputResponses 加到原请求的后续请求中;若收到了 requestState,原样回传,使用新的 JSON-RPC ID。

requestState 对客户端是不透明的。服务端若用它影响权限或业务逻辑,需保护并验证完整性;需要单次消费的场景还得自行防止重复使用。补信息的往返机制不自动保证业务只执行一次。MRTR 规范

4.3 新旧版本知识怎样保留?

下面这些变化适合建立索引,遇到实现问题再查细节:

主题2026-07-28 的学习重点
初始化请求携带版本与能力;版本不兼容时处理错误
服务端变化通知使用 subscriptions/listen;无状态不排斥流式订阅
长任务原实验性任务能力移到官方扩展
列表和资源缓存根据 ttlMs、cacheScope 等信息处理新鲜度和缓存范围
断流不沿用旧的 SSE 自动续传假设,需按新版规则处理重发

这些属于协议演进,不能据此认为所有 SDK 和服务器已经同步升级。MCP 变更说明

Roots、Sampling、Logging 已被标记为弃用,但不等于立即删除。Roots 曾用于告知工作范围,真正的隔离仍要靠权限或沙箱;Sampling 曾让服务端请求客户端使用模型。新实现应参考各自迁移路径。动态客户端注册也已进入弃用登记,授权接入应核对当前方案,而不是沿用所有旧示例。弃用功能登记

5. A2A:从“调用能力”到“交付任务”

A2A 是 Agent2Agent 协议。假如文档助手只想读取一页内容,工具接口已经很自然;如果它要委托研究 Agent“核对多个版本,找出矛盾,交付带来源的报告”,调用方还需要任务状态、补充输入和产物等共同约定。

对方内部可以使用自己的模型、工具和工作流。调用方不需要知道全部实现,但要知道对方承诺接受什么、交付什么。

5.1 Agent Card 是服务说明,不是能力证明

Agent Card 描述智能体身份、能力、可用接口和认证要求。1.0 的 supportedInterfaces 可以声明不同协议绑定;不要把 A2A 写成只能使用 JSON-RPC。

正文学习时,先关注“谁、会什么、怎么连、怎么认证”四件事。JSON-RPC、HTTP+JSON、gRPC 是公共语义的不同绑定;代码示例则应固定其中一种。

Agent Card 中的 skill 是对外能力描述,不等同于本地 SKILL.md 技能包。签名可帮助校验卡片完整性与来源,但还需要可信密钥来源,也不能证明任务答案正确。扩展卡片提供认证后可见的信息,后端仍须执行真实授权。A2A 规范

5.2 Message、Task、Artifact 不要混为一谈

对象放到资料助手里理解
Message发出需求、补充语言、交换回答
Part消息或产物中的文字、文件、结构化内容等部分
Task需要跟踪的一项报告整理工作
Artifact任务生成的报告、分析结果,可逐步产生
contextId关联相关交互,不自动共享两侧完整记忆

远端可以直接返回 Message,也可以返回 Task。它不完全由耗时决定,而取决于服务的交互契约。报告生成中的一句解释可以是 Message;交付的报告适合成为 Artifact,产物也不必等到最后一刻才全部出现。

6. 当任务没有立即结束

6.1 状态说明“下一步由谁推进”

A2A 典型任务生命周期

图 5:典型路径示意,不要求每个任务经历全部状态。等待补充输入与进入终态,是不同的处理分支。

客户端看到执行中状态,可以等待更新;看到需要输入,应获取缺失信息;看到需要授权,应进入可信授权流程。完成后读取并验收产物,失败或拒绝时保留原因。

终态任务不能简单当作还能继续工作的对象。若要修改已完成的报告,应按服务契约发起新的工作,并在合适的上下文中关联之前结果。A2A 任务生命周期

6.2 怎样拿到结果?

方式优点需要处理
直接响应交互简单请求等待时间与超时
轮询易于接入和恢复查询轮询间隔、截止时间、请求负载
流式更新及时呈现状态和产物变化断流、事件归并、当前状态核对
推送通知客户端无需持续等待回调认证、重复通知、地址安全

推送或流式能力需要对方声明支持,不能默认每个 Agent 都实现。一次连接断开,也不等于远端任务取消;请求取消更不等于撤销已经发生的业务操作。A2A 异步操作

6.3 客户端如何组织处理?

下面是使用自定义适配层的 Python 伪代码。sendget_taskdecode_result 等名称不是某个官方 SDK 的固定接口;适配层负责认证、序列化和协议版本。示例使用轮询,保留超时后查询所需的任务标识。

import asyncio
from uuid import uuid4

async def delegate(api, card_url, brief, resolve_input,
                   timeout=120, poll_interval=2):
    card = await api.fetch_card(card_url)
    api.check_interface_and_trust(card)
    api.prepare_auth(card)  # 使用可信凭证,不让模型生成凭证
    loop = asyncio.get_running_loop()
    deadline = loop.time() + timeout

    async def bounded(operation, *args, **kwargs):
        remaining = deadline - loop.time()
        if remaining <= 0:
            raise TimeoutError("deadline_reached")
        return await asyncio.wait_for(
            operation(*args, **kwargs), timeout=remaining
        )

    reply = await bounded(api.send, text=brief,
                          message_id=str(uuid4()))
    kind, value = api.decode_result(reply)
    if kind == "message":
        return {"status": "answered", "message": value}
    task = value
    while True:
        if task.state == "TASK_STATE_COMPLETED":
            return {"status": "completed", "artifacts": task.artifacts}
        if task.state in {"TASK_STATE_FAILED", "TASK_STATE_REJECTED",
                          "TASK_STATE_CANCELED"}:
            return {"status": task.state, "task_id": task.id}
        if task.state == "TASK_STATE_AUTH_REQUIRED":
            return {"status": "needs_auth", "task_id": task.id}
        try:
            if task.state == "TASK_STATE_INPUT_REQUIRED":
                answer = await bounded(resolve_input, task)
                reply = await bounded(
                    api.send, text=answer, task_id=task.id,
                    context_id=task.context_id, message_id=str(uuid4())
                )
                # 先消费返回消息/产物,再读取权威任务快照。
                api.record_reply(reply)
            else:
                await bounded(asyncio.sleep, poll_interval)
            task = await bounded(api.get_task, task.id)
        except TimeoutError:
            return {"status": "local_timeout", "task_id": task.id,
                    "context_id": task.context_id}

这段流程没有自动重发首次委托。首次发送超时可能意味着“对方已经接收,但响应丢失”,需要按服务提供的去重和查询机制恢复,不能直接假定任务未创建。有了 messageId 也不应默认所有服务都保证发送幂等。任务完成后,调用方还要检查报告覆盖范围、来源和格式。

7. 放到同一个案例里看

用户要求:“比较两个版本的技术规范,整理差异,并说明哪些变化影响现有实现。”

资料助手先通过 MCP 搜索和读取规范,保存来源与版本标识;再通过 A2A 委托研究 Agent 分析。委托内容除了文件,还应说明目标、比较范围和验收条件。

研究 Agent 自行选择内部工具,必要时通过自己的 MCP 服务读取补充资料。它发现目标系统版本不明确,返回需要输入;资料助手获取真实版本后继续任务。最终报告作为产物返回,资料助手检查引用和结论,再交给用户。

一次完整的资料协作

图 6:两侧可以各自管理工具与上下文,通过明确的输入、任务状态和产物完成协作。

这里的 MCP URI、远端任务 ID、产物下载地址都属于具体系统的命名与权限范围。把一个本机路径发给远端 Agent,并不意味着对方能读取;需要提供实际可访问且权限受控的内容或引用。

7.1 哪些情况下不需要增加协议?

系统需求我会优先考虑
单个程序内稳定、明确的操作本地函数或现有 API
多个 Host 复用同一组外部能力MCP
独立服务之间需要任务委托、补输入和产物交付A2A
同一进程里切换角色、调用子 Agent先判断内部接口是否足够

远端 Agent 可以被包装成 MCP Tool。这样做时,对外暴露的是工具调用契约;采用 A2A,则强调任务协作契约。不能仅按“对面有没有 LLM”或者“是不是长任务”选协议,MCP 自身也可以通过扩展处理任务。

8. 我的思考:统一之后,难题去了哪里?

8.1 协议让接口可对接,业务含义仍然需要澄清

两个 Server 都提供 search,不代表它们搜索相同范围、保证相同时效或返回相同质量。我的关注点从“字段能不能接上”,转向“调用者和提供方是否对结果有一致预期”。

如果需要做选择,我会先定义一组真实任务,再比较直接 API、MCP 接入和远端委托的效果。成功率、恢复成本、延迟和维护负担,都比是否采用热门协议更有说服力。

8.2 无状态,让我重新思考状态归属

我最初把无状态理解成不保存数据。现在更在意:任务属于谁,哪一方提供权威状态,下一次请求怎样明确引用它。

协议级会话减少之后,业务状态不会自动变简单。明确 ID 有利于跟踪,但也必须处理有效期、权限、版本和重复操作。把所有状态塞进一个模型看不见的会话,或者全部塞进对话文本,都未必是好设计。

8.3 委托执行以后,验收责任仍然存在

A2A 让远端 Agent 能自主完成内部步骤,但调用方仍需要描述清楚交付标准。一个 completed 状态只说明对方报告任务完成;它没有替我证明引用准确、计算正确或用户目标已经满足。

我会把“任务完成”和“产物验收”设计成两个判断。需要证据的报告,就检查来源;需要实际文件,就确认文件存在、可读取、版本正确。

8.4 接入越容易,越需要清楚的信任边界

Agent Card、工具描述、文档内容和返回结果提供信息,但不自动授予权限。协议复用认证机制,运行系统仍要决定谁能访问哪份数据、哪些操作需要确认,以及失败后怎样恢复。

这与上一篇 Tool Use 的认识是一致的:结构化接口提供了可以检查的入口,真正的可靠性来自对这些入口实施明确规则。

9. 面试时怎样回答?

MCP 与 Function Calling 有什么区别? Function Calling 表达模型提出的调用;MCP 约定能力提供方与 Host 之间的发现、访问和通信。Host 可以把 MCP 工具适配到模型接口,它们不是替代关系。

A2A 会取代 MCP 吗? 两者主要面向不同交互契约:能力访问与智能体任务协作。可以同时使用,也不必为了多 Agent 就全部引入。

无状态以后,任务状态存在哪里? 业务系统仍可保存状态,通过明确的任务或对象 ID 引用。无状态讨论的是协议请求对隐含会话的依赖。

任务请求超时怎么办? 先区分本地等待结束与远端执行状态。已有任务 ID 时查询状态;首次发送结果不确定时使用约定的去重/恢复机制,不盲目重新创建任务。

怎样判断是否值得引入协议? 看复用需求、独立部署边界、协作生命周期和维护成本,再用实际任务验证收益。协议名本身不是架构合理性的证据。

版本参考:MCP 版本说明A2A 发布记录。文中伪代码用于解释调用方控制流程,接入时需按选定 SDK 和协议绑定实现适配层。

最后修改:2026 年 09 月 14 日
如果觉得我的文章对你有用,请随意赞赏