从 4 个工具到无限扩展:给 AI Agent 装上 MCP 协议

我的 Agent 只会 4 件事:读文件、写文件、列目录、跑命令。直到我发现了 MCP 协议------它让 Agent 的工具箱从一个固定大小的工具箱,变成了一个可以无限插拔的插座。


一、4 个工具的极限

上篇文章里,我搭好了一个能自主编程的 AI Agent。它有一个 ReAct 循环的大脑,还有 4 个工具作为四肢:

复制代码
read_file        → 读取文件内容
write_file       → 写入文件(自动创建目录)
list_directory   → 列出目录
exec_command     → 执行 shell 命令

一开始觉得很够用。创建项目?exec_commandpnpm create vite。写组件?write_file 覆盖 App.tsx。装依赖?exec_commandpnpm install

直到我想让它做这些事:

  • "帮我搜一下项目里所有用到 useState 的文件"
  • "把这次改动提交到 git,写一条规范的 commit message"
  • "看看这段代码最后一次是谁改的"

Agent 的回答是:

makefile 复制代码
我无法搜索文件内容。可用工具: read_file, write_file, list_directory, exec_command

问题暴露了:Agent 的能力天花板就是工具的数量。每想加一个能力,就得写一个新工具文件、定义 Zod schema、注册到 index.js。这跟装软件只能从源代码编译有什么区别?

更难受的是:隔壁 Cursor、Claude Code 里的那些工具------Git 操作、GitHub Issue、数据库查询------人家早就写好了,但我得自己重新实现一遍。

有没有一种方式,能让 Agent 直接接入已有的工具生态


二、MCP 协议:工具的"USB 接口"

在翻 LangChain 文档时,我看到了一个词:MCP(Model Context Protocol)

简单说,MCP 定义了一套标准------就像 USB 协议让任何 U 盘都能插进任何电脑一样,MCP 让任何 MCP Server 提供的工具都能被任何 MCP Client(也就是我的 Agent)使用

架构长这样:

arduino 复制代码
┌─────────────────────┐          ┌──────────────────────┐
│   我的 Agent         │          │  Filesystem Server    │
│   (MCP Client)      │          │  (MCP Server)         │
│                     │          │                      │
│  "帮我搜所有 .tsx"   │          │  - search_files       │
│        │            │          │  - directory_tree      │
│        ▼            │          │  - edit_file           │
│   tools/list ───────┼─────────►│  - get_file_info       │
│                      │          │  - move_file           │
│   tools/call ───────┼─────────►│  ... 14 个工具         │
│                      │          │                      │
└─────────────────────┘          └──────────────────────┘

最妙的是:我不用写一行工具代码search_filesdirectory_treeedit_file 这些功能,Filesystem Server 已经实现好了。我只需要告诉 Agent 去哪找这个 Server。


三、集成:两行配置,14 个工具到手

LangChain 官方有 @langchain/mcp-adapters 这个包,专门做 MCP → LangChain 的桥接。核心 API 就两个:

javascript 复制代码
import { MultiServerMCPClient } from '@langchain/mcp-adapters';

// 1. 创建客户端,传入 Server 配置
const client = new MultiServerMCPClient({
  mcpServers: {
    filesystem: {
      transport: 'stdio',
      command: 'node',
      args: ['./node_modules/@modelcontextprotocol/server-filesystem/dist/index.js', '.']
    }
  }
});

// 2. 初始化连接 → 获取工具
const toolsByServer = await client.initializeConnections();
const mcpTools = await client.getTools();
// → [DynamicStructuredTool × 14]

getTools() 返回的是什么?是 LangChain 原生的 DynamicStructuredTool 对象------跟我的 read_filewrite_file 完全是同一种东西。直接 model.bindTools([...localTools, ...mcpTools]) 就完事了,Agent 根本感觉不到区别。

运行效果:

csharp 复制代码
[MCP] 连接 1 个服务器: filesystem
[MCP]   ✓ filesystem: 14 个工具
[MCP] 共加载 14 个 MCP 工具
🔧 可用工具 (15): read_file, write_file, list_directory, exec_command,
                  search_files, directory_tree, edit_file, move_file,
                  read_multiple_files, get_file_info, create_directory,
                  read_text_file, read_media_file,
                  list_directory_with_sizes, list_allowed_directories

从 4 个到 15 个,我只写了两样东西:一个 JSON 配置文件,一个不到 130 行的 McpClientManager 封装类。


四、三个踩过的坑

坑 1:npx 罢工了

最初配置长这样:

json 复制代码
{
  "command": "npx",
  "args": ["-y", "@modelcontextprotocol/server-filesystem", "."]
}

一运行就报错:

lua 复制代码
EBADDEVENGINES Invalid name "pnpm" does not match "npm" for "packageManager"

原因是我项目的 package.json 里写了 "devEngines": { "packageManager": "pnpm" }。npx(npm 家的)检测到这个约束,直接拒绝执行------即使它只是用来跑一个跟项目无关的 MCP Server。

解决方案 :先 pnpm add @modelcontextprotocol/server-filesystem 安装到项目,再用 node ./node_modules/.../dist/index.js 直接启动 Server 进程。绕过了包管理器的纠纷。

坑 2:工具名打架

Filesystem Server 提供了 read_filewrite_filelist_directory 三个工具------跟我本地写的完全同名

如果不处理,bindTools 会绑进去两个 read_file。模型调用时到底走哪个?不知道,行为不确定。

解决方案 :在 McpClientManager.getTools() 里加一层去重:

javascript 复制代码
const localNames = new Set(localTools.map(t => t.name));
const newMcpTools = mcpTools.filter(t => !localNames.has(t.name));
// 本地工具优先 --- 它们没有路径白名单限制,权限更高

结果:3 个重名 MCP 工具被自动跳过,最终 15 个工具(4 本地 + 11 MCP 新增),无冲突。

坑 3:连接失败不能阻塞 Agent

MCP Server 是一个独立进程。它可能因为各种原因挂掉------网络问题、依赖缺失、进程崩溃。

如果 Server 连不上就直接 throw,那 Agent 就完全没法启动了------即使 4 个本地工具完全正常。

解决方案:降级策略。

javascript 复制代码
try {
  await client.initializeConnections();
} catch (e) {
  logger.warn(`[MCP] 连接失败: ${e.message}`);
  this.#connected = false;  // 仅用本地工具,Agent 照常启动
}

MCP 是锦上添花,不是雪中送炭。它不应该成为 Agent 的单点故障。


五、封装:McpClientManager 的 4 个职责

我把所有 MCP 逻辑收敛到一个 McpClientManager 类里(完整代码),它只做 4 件事:

职责 实现
初始化连接 init({ servers })new MultiServerMCPClient()initializeConnections()
工具合并去重 getTools(localTools) → 本地优先,过滤 MCP 重名工具
错误降级 try/catch 所有 MCP 操作,失败时退化为仅本地工具
生命周期清理 close() 关闭所有 MCP 连接,main.jsfinally 块确保执行

用法:

javascript 复制代码
const mcp = await initMcpClient({ servers: config.mcp.servers });
const tools = await mcp.getTools(localTools);

const modelWithTools = model.bindTools(tools);
const agent = new ReactAgent({ model: modelWithTools, tools, ... });

try {
  await agent.run(task);
} finally {
  await mcp.close();
}

六、现在我的 Agent 能做什么

加上 Filesystem MCP Server 后,Agent 多了这些能力:

perl 复制代码
🔍 search_files           → 按 glob 模式搜索文件(再也不用 exec_command + grep 了)
📂 directory_tree         → 可视化目录树("帮我看看项目结构")
✏️  edit_file             → 基于 diff 精确编辑(而不是整个文件覆盖)
📦 read_multiple_files    → 批量读取("把 src/ 下所有 .ts 文件读出来")
📁 move_file              → 移动/重命名文件
ℹ️  get_file_info          → 获取文件大小、修改时间等元信息

而且这只是开始。mcp-servers.json 里加一行配置就能接更多 Server:

json 复制代码
{
  "mcpServers": {
    "filesystem": { "command": "node", "args": [...] },
    "github": { "command": "node", "args": [...] },    // ← 加这个就有 GitHub Issue/PR 能力
    "postgres": { "transport": "streamableHttp", "url": "..." }  // ← 加这个就能查数据库
  }
}

七、总结:工具系统的思维转变

做这个集成的过程中,我最大的收获不是技术本身,而是一个思维转变:

传统思维 :Agent 需要什么工具 → 我写什么工具 MCP 思维:Agent 需要什么能力 → 找现成的 MCP Server → 配置一行 JSON

前者是个工具箱,你自己买工具往里塞。后者是个插座,你能插上整个世界的电器。

对于个人开发者来说,这意味着你不需要重新实现 Git 操作、GitHub API、数据库查询------社区已经写好了 MCP Server,你的 Agent 直接用就行。

对于团队来说,这意味着你们的工具可以跨项目复用。写一个内部 API 的 MCP Server,所有项目的 Agent 都能调用------不管你用的是 LangChain、Claude Code 还是 Cursor。

这就是 MCP 协议真正的价值:让 AI Agent 的工具系统,从"手工业"进入"工业化"


下一篇:MCP 协议入门:从零理解 Model Context Protocol

相关推荐
不好听6132 小时前
MCP 协议入门:从零理解 Model Context Protocol
agent
老梁agent2 小时前
为什么你的 Agent 答错问题你查不出来?
物联网·agent
leeyi2 小时前
Eino 的“你好世界“背后发生了什么(第48篇-E34)
aigc·agent·ai编程
志凌海纳SmartX3 小时前
构建 Agent Ready 的企业云基础设施(白皮书下载)
人工智能·ai·agent·smartx·企业云·榫卯企业云
葫芦和十三13 小时前
图解 MongoDB 30|查询路由与广播查询:带片键定向,不带就广播
后端·mongodb·agent
葫芦和十三13 小时前
图解 MongoDB 29|分片键陷阱:jumbo chunk、孤儿文档和写入热点
后端·mongodb·agent
花椒技术15 小时前
告别狂野式 AI Coding:Harness Engineering 在 H5 项目里的最小闭环实践
agent·ai编程·前端工程化
米小虾17 小时前
从RAG到GraphRAG:2026年知识增强生成技术的进化之路与实战指南
人工智能·agent