← MCP

Agent 怎样通过 MCP Client 使用外部工具?

中等 Agent 接入 MCP 工具 · 第 1 / 2 问 更新于 2026/09/27
MCPAI AgentMCP Client工具调用系统设计
学习 AI 实战项目

简化版

Agent 所在的应用是 MCP 的 Host:它为每个要接入的 MCP Server 建一个客户端,启动时完成初始化和能力协商,再用 tools/list 拉取各 Server 的工具,转换成模型的工具定义交给 Agent 循环。模型选中某个工具后,Host 根据工具名找到它属于哪个 Server,通过对应的客户端发送 tools/call,把返回的内容作为工具结果放回对话,执行失败(isError)也原样交给模型让它换路。除了这条主线,Host 还要负责几件协议不管的事:保证交给模型的工具名唯一并能路由回 Server;按 Agent 的职责只启用需要的 Server 和工具;为调用设置超时;在 Server 通知工具列表变化时刷新;对有副作用的操作要求用户确认;把每次调用记下来(哪个 Server、哪个工具、入参、结果、耗时)。这样换一个工具来源只是多连一个 Server,Agent 的循环逻辑不用改。

详细版

启动:
  for 每个配置的 Server:
      建客户端 → initialize → notifications/initialized
      tools/list → 登记「工具名 → Server」映射(重名则加前缀或拒绝)
  按 Agent 的角色筛出可用工具 → 转成模型工具定义

运行(Agent 循环的每一轮):
  模型返回工具调用 name + arguments
  → 查映射找到 Server → 对应客户端 tools/call(带超时)
  → 结果内容转成工具消息;isError 也交回模型
  → 记录调用日志 → 进入下一轮

变化:
  收到 tools/list_changed 通知 → 重新拉取列表 → 更新映射
职责在哪一层
连接、协议、消息收发MCP 客户端
工具名唯一、路由、按角色筛选Host 的工具适配层
决定调哪个工具模型(通过 Function Calling)
超时、确认、审计Host

完整版教学

一、Agent 不直接和 Server 说话

一个常见的误解是「Agent 连上 MCP Server 就能用工具了」。实际上 Agent 里的模型只认识工具定义,它和 Server 之间隔着两层:

模型 ──工具调用意图──▶ Agent 循环 ──工具名──▶ 工具适配层 ──tools/call──▶ MCP 客户端 ──▶ Server
模型 ◀──工具结果───── Agent 循环 ◀──内容──── 工具适配层 ◀──result────── MCP 客户端 ◀── Server

MCP 客户端负责协议:初始化、收发 JSON-RPC、处理通知。工具适配层负责把 MCP 世界翻译成 Agent 世界:工具定义的转换、名字到 Server 的路由、结果内容到工具消息的转换。三类原语见「MCP 的 Tools、Resources、Prompts 分别是什么?由谁决定使用?」,传输与初始化见「MCP 的 stdio 和 Streamable HTTP 怎么选?一次连接的生命周期是怎样的?」。Agent 循环本身和不用 MCP 时完全一样。主流 Agent 框架大多提供了现成的 MCP 客户端和适配层,能把远端工具直接变成框架里的工具对象。

二、启动时:连接、发现、登记

启动阶段要为每个 Server 完成三件事:

步骤做什么要注意
连接本地 Server 以子进程启动(stdio),远程 Server 连 HTTP 端点远程 Server 要配置认证
初始化协商协议版本和能力Server 不支持工具能力就不要调用 tools/list
发现tools/list 拉取工具,登记映射不同 Server 可能有同名工具

同名工具的处理方式要提前定:给工具名加上 Server 前缀(比如 github__search),或者发现重名时拒绝加载并报警。交给模型的名字必须唯一,否则模型选中了 search,应用不知道该发给谁。

三、只给 Agent 需要的工具

连上的 Server 越多,拿到的工具越多,但不是所有工具都应该交给每个 Agent:

客服 Agent:订单查询、物流查询、知识库检索        ← 只读
运营 Agent:数据报表、优惠券发放                  ← 有写操作,需要确认

按 Agent 的职责筛选工具有两个好处:一是工具定义少了,占用的上下文少、模型选错的概率低;二是权限边界清楚,客服 Agent 根本看不到发券工具,也就不会被诱导去调用它。筛选规则应该写在 Host 的配置里,而不是靠提示词告诉模型「不要用某某工具」。

记忆钩子:连上的是全部工具,交给模型的只是这个 Agent 该用的那几个。

四、运行时:路由、超时、错误

每次模型要调用工具,适配层要做的事:

1. 按工具名查映射 → 找到 Server 和原始工具名(去掉前缀)
2. 校验参数是否符合工具的 inputSchema(可选,但能提前挡住格式错误)
3. 通过客户端发送 tools/call,设置超时
4. 处理结果:
     正常结果      → 内容转成工具消息
     isError=true  → 错误文本同样转成工具消息,让模型看到失败原因
     超时 / 断线   → 发取消通知,把「工具超时」作为结果交回模型,或按策略终止
5. 记录日志:Server、工具名、入参、结果摘要、成败、耗时

超时尤其重要:远程 Server 可能卡住,没有超时的话整个 Agent 运行会一直挂着。用一组示意数字:一次 Agent 运行调 10 次工具,某个 Server 偶尔响应要 2 分钟;设 20 秒超时后,最坏情况这次调用多等 20 秒就能继续,而不是整次运行卡死。

五、工具列表会变

MCP Server 的工具不是固定的。Server 声明支持列表变更通知后,工具增减时会发送 notifications/tools/list_changed。Host 收到后重新拉取列表、更新映射。要注意两点:一是正在运行的 Agent 手里是旧的工具清单,可能调用一个刚被下线的工具,适配层要能给出清楚的「工具不存在」结果;二是工具说明变了要留痕,因为说明就是提示词的一部分,被改动的说明可能改变模型的行为,甚至被用来注入恶意指令。

六、确认与审计

工具注解里可能写着「只读」或「有破坏性」,但这些只是 Server 的自我声明。Host 应该用自己的配置决定哪些工具调用前需要用户确认:

工具类型建议
只读查询(按 Host 自己的判定)直接执行,记录日志
写操作、发消息、花钱的操作执行前展示工具名和参数,用户确认后再调用
来自第三方、未经审核的 Server默认需要确认,或只在隔离环境里使用

审计日志要能回答:这次运行调了哪个 Server 的哪个工具、参数是什么、返回了什么、谁确认的。出了问题才能追到是模型选错了、参数传错了,还是 Server 返回了不可信的内容。

七、常见误区与追问

  • 误区:Agent 直接连 MCP Server。 模型只认识工具定义,协议交互由 MCP 客户端完成,路由和转换由 Host 的适配层完成。
  • 误区:连上的工具都交给模型。 按 Agent 职责筛选,工具越少选得越准、权限越清楚。
  • 误区:不同 Server 的工具名不会冲突。 同名很常见,要加前缀或拒绝加载,保证交给模型的名字唯一。
  • 误区:工具执行失败就中断 Agent。 isError 的结果应交回模型让它换路,只有超出策略的情况才终止。
  • 误区:工具注解标了只读就可以免确认。 注解是自我声明,确认规则应由 Host 配置决定。
  • 追问:Agent 运行中 Server 下线了一个工具怎么办? 适配层对找不到的工具返回清楚的错误结果,收到列表变更通知后刷新映射。
  • 追问:换一个工具来源要改 Agent 代码吗? 不用,新增一个 Server 配置,启动时自动发现和登记,Agent 循环不变。

八、加强记忆

Agent 接入 MCP 记「客户端管协议、适配层管翻译、Host 管规矩」:启动时每个 Server 一个客户端,初始化、协商能力、tools/list 发现工具,登记「工具名 → Server」映射并保证名字唯一;按 Agent 职责只把需要的工具转成模型的工具定义。运行时模型选工具,适配层按名字路由、带超时发 tools/call,结果和 isError 都转成工具消息交回模型,超时就取消并告诉模型。工具列表变化时按通知刷新,说明变更要留痕;是否需要用户确认由 Host 配置决定,不信注解;每次调用记下 Server、工具、入参、结果和耗时。