AI Agent 接入MCP:不用重复造轮子,外部工具一键接入

文章目录

    • 前言
    • 一、先把结论甩出来
    • [二、MCP 的基本模型:三步,多一步算我输](#二、MCP 的基本模型:三步,多一步算我输)
      • [1. 什么是工具服务器](#1. 什么是工具服务器)
      • [2. 通信流程就三步](#2. 通信流程就三步)
    • 三、配置文件:mcp_servers.json
    • [四、连接与发现:register_mcp_servers 干了什么](#四、连接与发现:register_mcp_servers 干了什么)
    • 五、MCPToolAdapter:把远端工具包装成本地工具
    • 六、完整链路:从配置文件到模型调用
    • [七、两种传输方式:本地亲儿子 vs 云端干儿子](#七、两种传输方式:本地亲儿子 vs 云端干儿子)
      • [1. stdio 模式(本地子进程)](#1. stdio 模式(本地子进程))
      • [2. http 模式(远端 HTTP 服务)](#2. http 模式(远端 HTTP 服务))
    • [八、单个服务器挂了,agent 不能跟着躺](#八、单个服务器挂了,agent 不能跟着躺)
    • 九、设计亮点,抄作业时间
      • [1. 适配器模式屏蔽了"远端"](#1. 适配器模式屏蔽了"远端")
      • [2. namespace 避免命名冲突](#2. namespace 避免命名冲突)
      • [3. 配置格式兼容 Claude 桌面端](#3. 配置格式兼容 Claude 桌面端)
    • 十、小结


P.S. 目前国内还是很缺AI人才的,希望更多人能真正加入到AI行业,共同促进行业进步,增强我国的AI竞争力。想要系统学习AI知识的朋友可以看看我精心打磨的教程 http://blog.csdn.net/jiangjunshow,教程通俗易懂,高中生都能看懂,还有各种段子风趣幽默,从深度学习基础原理到各领域实战应用都有讲解,我22年的AI积累全在里面了。注意,教程仅限真正想入门AI的朋友,否则看看零散的博文就够了。

前言

Tavily 能联网搜索,Context7 能查最新文档,GitHub CLI 能管仓库------现成的服务堆了一车库,非要自己撸一套轮子。这跟你家里有洗衣机、偏要手搓衣服有什么区别?区别是洗衣机不会笑话你,评论区会。

所以,MCP 登场。全称 Model Context Protocol,翻译成大白话就是"工具界的 USB-C 接口"。以前每家工具服务一个接口,想让 agent 调谁,就得给谁单独写适配器,数据线多到能绕地球一圈。现在协议统一,一根线走天下。

一、先把结论甩出来

接入一个 MCP 工具服务,只需要两步:

步骤 做什么
1 配置 mcp_servers.json:告诉框架 MCP 服务在哪里、怎么启动
2 启用 MCP:--enable-mcp 启动参数,或 ENABLE_MCP=true 环境变量

就这两步,指令长度还不如"下楼买瓶酱油"------而且人家买酱油至少还得问一句"要生抽还是老抽"。

剩下的框架全包:自动连服务、自动发现工具列表、自动把远端工具注册成本地工具,之后的调用链和内置工具完全一致。亲儿子待遇,一个字都不用多说。

二、MCP 的基本模型:三步,多一步算我输

1. 什么是工具服务器

一个独立的进程(或者远端 HTTP 服务),里面注册了一堆工具,每个工具都有自己的名字、描述和参数 schema。

说人话:一个"工具超市"。每个货架上都贴好了标签,写清楚这是什么、怎么用、要什么参数,你推着购物车逛就行。

2. 通信流程就三步

复制代码
握手:连接服务器
↓
发现:list_tools() → 拿到工具列表(名字 + 描述 + inputSchema)
↓
调用:call_tool(name, params) → 拿到结果

就这三步,没了。协议的价值全在"标准化"三个字------任何 MCP 服务器都支持这三步,任何 MCP 客户端都能和任何 MCP 服务器通信。这感觉就像全世界终于统一了插座标准,以前去朋友家总得带转换头,现在一根线搞定。

MyCodeAgent 实现了 MCP 的客户端侧,一个 MCPClient 类把这三个操作全封装了。

三、配置文件:mcp_servers.json

在项目根目录创建(或修改)一个 mcp_servers.json,一个服务器一段配置:

json 复制代码
{
  "mcpServers": {
    "tavily": {
      "command": "uvx",
      "args": ["mcp-server-tavily"],
      "env": {
        "TAVILY_API_KEY": "tvly-xxxxx"
      }
    },
    "context7": {
      "command": "uvx",
      "args": ["--from", "context7-mcp", "context7-mcp"],
      "env": {
        "CTX7_API_KEY": "your-key"
      }
    }
  }
}

三个关键字段,拿小本本记好:

  • command:启动工具服务器的命令(uvx 是 Python 的"免安装直接跑"神器)
  • args:传给 command 的参数
  • env:工具服务器需要的环境变量,比如 API key

说到 API key,我多说一句:这玩意儿你填错一次、报错一次,就会记得比对象生日还牢。

除了本地进程,还支持远程 HTTP 服务器:

json 复制代码
{
  "mcpServers": {
    "remote-tool": {
      "transport": "http",
      "url": "https://your-mcp-server.com/v1"
    }
  }
}

读这个文件的是 extensions/mcp/config.py。它还兼容 Claude 桌面端的写法,同一个配置文件两边通用,不用你翻译第二遍。就像同一份简历,字节腾讯通吃,连排版都不用改。

四、连接与发现:register_mcp_servers 干了什么

agent 启动时(带 --enable-mcp),register_mcp_servers() 被调用:

python 复制代码
# extensions/mcp/bootstrap.py
def register_mcp_servers(tool_registry, project_root):
    # 1. 读取 mcp_servers.json
    servers = load_mcp_servers(project_root)

    # 2. 为每个 server 创建 MCPClient(还没连接)
    for server_name, spec in servers.items():
        config = _build_client_config(project_root, spec, MCPClientConfig)
        client = MCPClient(config)

        # 3. 连接 server,发现工具列表,注册到 ToolRegistry
        tools_meta = register_mcp_tools(tool_registry, client, namespace=server_name)

注意 namespace=server_name 这个参数,它解决了一个非常真实的痛点:两家不同的 MCP 服务器可能都有一个叫 search 的工具。就像两个小区都叫"幸福家园",你不加前缀,外卖小哥只能靠猜------猜错一次,你就收获一份凉透的麻辣烫。

加了 namespace 之后,一个变成 tavily_search,一个变成 context7_search(冒号会被清理成下划线),谁是谁清清楚楚,模型想调错都难。

五、MCPToolAdapter:把远端工具包装成本地工具

register_mcp_tools() 调完 list_tools_sync() 之后,会为每个远端工具创建一个 MCPToolAdapter:

python 复制代码
# extensions/mcp/adapter.py
class MCPToolAdapter(Tool):
    """把 MCP 远端工具伪装成本地 Tool。"""

    def get_parameters(self) -> list[ToolParameter]:
        # 从 MCP 工具的 inputSchema 生成参数定义
        schema = self._schema  # 从 list_tools 拿到的 JSON Schema
        properties = schema.get("properties", {})
        required = set(schema.get("required", []))
        return [
            ToolParameter(name=name, type=spec.get("type", "any"), ...)
            for name, spec in properties.items()
        ]

    def run(self, parameters: dict) -> ToolResult:
        # 1. 校验参数
        invalid = self._validate_params(parameters)
        if invalid:
            return to_protocol_invalid_param(invalid, ...)

        # 2. 调用远端工具
        result = self._mcp_client.call_tool_sync(self._remote_name, parameters)

        # 3. 把结果转成标准信封
        return to_protocol_result(result, ...)

这个 Adapter 就干三件事:

  1. 参数接口对齐:把 MCP 工具的 JSON Schema 翻译成 ToolParameter 列表,get_openai_tools() 才能生成正确的 Function Calling schema
  2. 执行转发:调 call_tool_sync() 把请求发给远端
  3. 结果归一化:把 MCP 格式的响应转成 ToolResult,下游完全不用关心这工具是本地还是远端

注册完之后,tavily_search 和 Read、Bash 在框架眼里没有任何区别:都在 ToolRegistry 里,都会出现在 get_openai_tools() 的列表里,都走同一个 Executor 管道。这就像外包和正式员工,活儿干完结算方式一模一样------唯一的区别是工牌颜色,哦不对,工牌颜色也一样。

六、完整链路:从配置文件到模型调用

把上面几步串起来,一个 MCP 工具从配置到被模型调用,完整链路长这样:

复制代码
写 mcp_servers.json,填入 server 配置
│
▼
agent 启动(--enable-mcp)
│
▼
bootstrap.py 调用 register_mcp_servers()
├── 读 mcp_servers.json
├── 为每个 server 创建 MCPClient
└── 连接 server,调 list_tools_sync(),拿到工具列表
│
▼
为每个工具创建 MCPToolAdapter
MCPToolAdapter 注册到 ToolRegistry
│
▼
tool_registry.get_openai_tools() 返回的工具列表里包含了 tavily_search
│
▼
模型看到工具列表,决定调用 tavily_search
│
▼
ToolRegistry.execute_tool("tavily_search", params)
→ ToolExecutor 走四关卡(权限/锁/熔断/run)
→ MCPToolAdapter.run()
→ MCPClient.call_tool_sync("search", params)
→ 结果转 ToolResult → 返回给模型

整条链路里,MCP 的特殊性只在 MCPToolAdapter.run() 这一层。权限检查、熔断器、字节预算、结果写入历史------其他部分全部复用内置工具的代码。

说白了,你只是点了个外卖,锅碗瓢盆还是家里那套,菜是外面送的。你不会因为点一次外卖就把厨房拆了重建,对吧?

七、两种传输方式:本地亲儿子 vs 云端干儿子

_build_client_config() 会根据配置决定用哪种传输方式。

1. stdio 模式(本地子进程)

json 复制代码
{
  "command": "uvx",
  "args": ["mcp-server-tavily"]
}

框架 fork 一个子进程运行工具服务器,通过 stdin/stdout 管道通信,子进程和 agent 同生命周期------同生共死,相当感人,像极了创业老板对员工许下的承诺。

2. http 模式(远端 HTTP 服务)

json 复制代码
{
  "transport": "http",
  "url": "https://your-mcp-server.com/v1"
}

框架通过 HTTP 和远端服务通信,不用启动子进程,适合托管在服务器上的 MCP 工具,比如付费 SaaS API。对 SaaS 来说,你的信用卡才是它真正的"传输层"。

两种模式对上层代码完全透明,MCPToolAdapter.run() 根本不关心底层是子进程还是 HTTP,只管调 call_tool_sync()。

八、单个服务器挂了,agent 不能跟着躺

register_mcp_servers() 里有一个很关键的容错处理:

python 复制代码
for server_name, spec in servers.items():
    try:
        tools_meta = register_mcp_tools(tool_registry, client, namespace=server_name)
        registered_tools.extend(tools_meta)
    except Exception as exc:
        logger.warning("MCP tool registration failed for %s: %s", server_name, exc)
        continue  # 单个失败,继续处理下一个

如果 Tavily 启动失败(比如密钥忘填了),不会影响 Context7 的注册,更不会让整个 agent 启动失败,顶多在日志里留一条 warning。

这个设计思路和 CompositeRuntimeEventSink 一脉相承:可观测性基础设施或外部服务的故障,不能让你 agent 的主路径停下来。翻译成生活语言:不能因为楼下保安昨晚没睡好,你就把整个物业都辞了。

九、设计亮点,抄作业时间

1. 适配器模式屏蔽了"远端"

MCPToolAdapter 让远端工具在框架内部和本地工具无法区分。权限检查、熔断器、字节预算------内置工具享有的保护,MCP 工具自动获得,一行逻辑都不用重写。

2. namespace 避免命名冲突

两个服务器都有 search?加上 namespace 就变成 tavily_search 和 context7_search,模型看得清清楚楚,不会误调。

3. 配置格式兼容 Claude 桌面端

{"mcpServers": {...}} 是 Claude 桌面端的标准写法。MyCodeAgent 兼容它,意味着你给 Claude 配好的文件直接拿过来就能用,不用二次翻译。

十、小结

设计选择 方案 工程价值
接入方式 JSON 配置文件 + --enable-mcp 零代码接入,改配置即可
工具注册 MCPToolAdapter 实现 Tool 接口 远端工具和本地工具走同一管道
命名空间 namespace:remote_name 前缀 多 server 不冲突
故障隔离 单 server 失败 continue 不影响其他 server 和 agent 启动
传输方式 stdio(本地进程)/ http(远端服务) 覆盖本地和云端两种部署场景

至此,Part 6「从零扩展」系列全部收官。回顾这四篇:

  • 20:添加新工具------Tool 基类、协议信封、沙箱检查、注册与测试
  • 21:接入新 LLM------表驱动的 provider 路由,加一行 profile 就搞定
  • 22:写 Skill------Markdown 定义专家行为,热加载,$ARGUMENTS 参数注入
  • 23:接入 MCP------配置文件 + 适配器,外部工具生态一键接入

这四个扩展维度加起来,覆盖了你几乎能想到的所有"让 agent 做新事情"的需求:

  • 需要 agent 执行新的具体动作 → 工具
  • 需要 agent 用新的 LLM → provider
  • 需要 agent 学一种新的处理方式 → Skill
  • 需要 agent 调用现成的外部服务 → MCP

最后说句掏心窝的话:干这行久了你会发现,大部分"创新"其实就是"把现成的东西接好"。MCP 干的就是这件事,而你只需要写一个 JSON 文件。所以下次别人问你最近在忙啥,你可以挺起胸膛说:"在做生态对接。"------翻译过来就是改配置文件。

本系列的分析都基于开源项目 MyCodeAgent,源码里按文章顺序在关键位置加了注释,可以对照着看,也可以直接克隆下来跑:

行了,本篇到此为止。如果这篇帮到了你,点个赞再走。下次别再问我"MCP 是不是哪款新出的奶茶"------那叫 Meco。

P.S. 目前国内还是很缺AI人才的,希望更多人能真正加入到AI行业,共同促进行业进步,增强我国的AI竞争力。想要系统学习AI知识的朋友可以看看我精心打磨的教程 http://blog.csdn.net/jiangjunshow,教程通俗易懂,高中生都能看懂,还有各种段子风趣幽默,从深度学习基础原理到各领域实战应用都有讲解,我22年的AI积累全在里面了。注意,教程仅限真正想入门AI的朋友,否则看看零散的博文就够了。

相关推荐
词却1 小时前
OpenCV学习:dlib 人脸检测
人工智能·opencv·学习
锋行天下1 小时前
LangGraph 基础打字效果
人工智能
QXWZ_IA1 小时前
电力作业安全管控如何从人防走向技防智防?智能工器具体系解析
人工智能·科技·安全
Rocky Ding*1 小时前
【三年面试五年模拟】2026-09-08 DeepSeek AI Agent开发岗社招一面:20道问题与系统设计题全解析
论文阅读·人工智能·深度学习·机器学习·aigc·ai-native·deepseek
猎嘤一号2 小时前
学术训练(一)文献检索与阅读
人工智能·算法·机器学习·科研入门
AI让世界更懂你2 小时前
计算机专业研究生核心能力培养(5)——论文写作的结构与方法
人工智能·深度学习·机器学习
代码方舟2 小时前
零信任架构实战:基于天远全能消金报告构建自动化信用合规网关
运维·人工智能·架构·自动化
小华同学ai2 小时前
这个开源项目,有点东西!2.9 万 Star DeepTutor
人工智能·开源·github