Claude Code 实战 07|用 MCP / 自定义工具给 Agent 接外部能力原创
# Claude Code 实战 07|用 MCP / 自定义工具给 Agent 接外部能力
前六篇讲的都是 Agent 自带的能力:读写文件、跑命令、调 CLI、管 Git、翻会话历史。但内置工具箱总有边界——它不认识你的私有数据库、你的向量记忆、你那台只能内网访问的服务。MCP(Model Context Protocol)就是给这个工具箱开的扩展槽:把外部能力包装成 Agent 能直接调用的工具,接进来就能用。这篇讲清楚 MCP 到底接的是什么、"零代码接 API"的模式,以及真实踩过的三类坑:进程拓扑、密钥透传、配置双写。
# 1. 场景:内置工具箱到不了的地方
Agent 的默认工具很强,但都是"通用"的:文件系统、Shell、Git。一旦任务需要特定领域的外部系统,通用工具就开始别扭:
- 想让 Agent 查一个生产库的表结构——难道让它自己拼
psql命令、自己记连接串? - 想给它一个跨会话的长期记忆——总不能每次都
grep几百个 markdown。 - 想让它操作一个只有 HTTP API 的私有服务——每次都手写
curl+ 解析 JSON,既啰嗦又容易出错。
这些能力的共同点是:它们是一个"有状态的外部系统",而不是一条一次性命令。 把它硬塞进 Bash,等于让 Agent 每轮都重新发明轮子。MCP 的思路是把这类系统一次性包装成一个工具(或一组工具),注册进 Agent 的工具列表,之后 Agent 看到的就是 query_db、memory_search、upload_photo 这种语义清晰的调用,而不是一坨 shell。
# 2. MCP 是什么:给工具箱开一个标准扩展槽
MCP 是一个标准协议,约定了"外部能力"怎么以 Server 的形式暴露给"Agent 宿主"(Host)。三个角色:
- Host:Agent 本体,比如 Claude Code 或其他 Agent 框架;
- MCP Server:一个独立进程,把某个外部系统(数据库、浏览器、私有 API、记忆库)包装成一组工具;
- 传输层:Host 通过 stdio 或 HTTP 和 Server 通信,Server 声明它提供哪些工具、每个工具的入参 schema。
关键认知:MCP Server 是一个独立子进程,不是 Agent 内部的一个函数。 这一点在排障时至关重要。在一套多实例的 Agent 集群里,用 screen 挂着好几个 remote-control 会话,每个会话的进程树都长这样:
主进程(Agent 会话)
├── SDK session handler ← 对话循环
└── MCP server ← 外部能力,独立子进程
2
3
也就是说,主进程活着不代表能力可用——得确认那个 MCP server 子进程也活着。 巡检这类集群时,光看主 PID 会漏判:真正提供能力的是它派生的 MCP 子进程。
# 3. 零代码接 API:把一个 HTTP 服务变成工具
MCP 最省事的用法是把一个现成的 HTTP API 包装成工具,几乎不用写业务代码。模式很固定:
- 声明工具:给 Agent 一个工具,比如
server_info/upload,入参 schema 写清楚; - 实现只是转发:工具内部就是一次 HTTP 调用 + 结果透传,不掺业务逻辑;
- 凭据从环境读:API token 之类的敏感信息,从 Server 进程的环境变量注入,不写进工具参数。
一个真实例子是把远程照片服务接进来。服务本身只有 HTTP API 和一个官方 CLI,Agent 要用它,最稳的方式不是让 Agent 现学 API,而是:
- 用官方 CLI 做一层薄包装(
server-info验证连通、upload --album=... /path/); - 把连接凭据放进环境,Agent 只管说"上传这批到某相册",不碰 token。
这里有个极容易翻车的认知陷阱(第 05、06 篇的幻觉问题在这里换了个马甲):Agent 会自作主张地把"通用清单"当成现实。一次巡检里,Agent 直接套用了"常见应用清单",去检查一个本地根本没装的服务,因为模板里有它。真相是那个服务是远程部署、本机只配了 CLI。
教训是:接外部能力时,必须在上下文里显式区分"本地容器"和"远程 CLI/API",否则 Agent 会用模板里的假设去操作一个不存在的本地实例,一路查空。工具接进来是第一步,把"这个能力在哪、以什么形态存在"写进上下文是同样重要的第二步。
# 4. 坑一:密钥透传与文件属主——静默失败的重灾区
MCP Server 是独立进程,它的运行身份和数据目录权限,是最隐蔽的一类失败源。
一个典型 case:某记忆类插件(本质是个带本地索引的 MCP Server)在一台机器上"配置完全正确却就是不工作,日志里连报错都没有"。逐层排查,根因是权限:
- 插件用 SQLite 存向量/文本索引,文件在
~/.../state/.../agent.sqlite; - 之前有人用
sudo/root 跑过一次命令,把这个 SQLite 文件的属主变成了root; - 而 Agent 进程是以普通用户身份跑的,写不进去,于是静默失败——没有报错,因为它"成功地"什么都没做。
解药是 chown -R <user>:<user> ~/.../state,并立下规矩:永远不要用 root/sudo 去跑会写数据目录的 Agent 命令。 密钥同理——token 要注入到和 Agent 同一个运行身份的进程环境里,跨用户、跨沙箱注入的密钥,Agent 那一侧读到的往往是空。
排障信号:MCP 能力"配置对、无报错、就是不生效",先查两样——数据目录的文件属主、密钥所在的进程环境身份。这两个是 MCP 静默失败的高频根因,比业务逻辑错误常见得多。
# 5. 坑二:配置双写与环境变量命名——文档骗你,源码不骗你
MCP Server 常常有两处配置,改一处不改另一处就会"半生效":
坑 A·配置双写:一套系统里,插件配置同时写在
xxx.json(宿主侧)和gateway.yaml(服务侧),provider 名字两边必须一致;某些密钥还要落到运行时的 auth store 里。只改一个文件,表现就是"embedding 调用失败 / 能力时有时无"。改 MCP 配置时,把所有相关配置面都过一遍,别假设只有一处。坑 B·环境变量命名:上游 README 声称环境变量叫
MEMORY_TENCENTDB_LLM_*,源码实际读的是TDAI_LLM_*。照文档配,Server 起来了但读不到配置。判据永远是源码里的读取逻辑,不是 README。 接一个陌生 MCP Server 前,花两分钟 grep 一下它到底读哪些环境变量,比对着过期文档试错快得多。
这两个坑的共同教训:MCP Server 是别人写的独立程序,它的真实行为以代码为准。 配置层的"看起来对了"和运行层的"真的读到了"是两回事——尤其是跨版本升级时,先只读预检(入口文件 + 环境变量解析逻辑),再动手。
# 6. 可复用要点
- MCP 是给 Agent 工具箱开的标准扩展槽:把有状态的外部系统(DB / 记忆 / 私有 API / 浏览器)包装成语义清晰的工具,而不是让 Agent 每轮重拼 shell。
- MCP Server 是独立子进程:巡检时主进程活着 ≠ 能力可用,必须确认 MCP 子进程也在。
- 零代码接 API:工具内部只做"转发 + 凭据从环境读",业务逻辑越薄越好;同时在上下文显式声明能力的形态(本地/远程),别让 Agent 拿模板当现实。
- 静默失败先查两样:数据目录文件属主(别用 root 跑)、密钥所在的进程环境身份。
- 配置以源码为准:警惕配置双写(多个配置面要同步)和环境变量命名(README 常与源码不符),升级前做只读预检。
# 7. Agent 可直接解析的元数据块
{
"_meta": {
"doc_version": "2026-07-28",
"article_id": "claude-code-07-mcp",
"profile_context": "any",
"estimated_setup_time": "30min"
},
"quick_start": {
"step_1": "把外部能力(数据库/记忆/私有API/浏览器)包装成 MCP Server,声明工具与入参 schema,注册进 Agent 工具列表",
"step_2": "零代码接 API:工具内部只做 HTTP 转发,凭据从与 Agent 同身份的进程环境注入,不写进工具参数",
"step_3": "在上下文/CLAUDE.md 里显式声明每个能力的形态(本地容器 vs 远程 CLI/API),避免 Agent 拿通用模板当现实"
},
"safety_rules": [
"永远不要用 root/sudo 运行会写数据目录的 Agent/MCP 命令,否则 SQLite 等文件属主变 root 导致普通用户进程静默失败",
"密钥必须注入到与 Agent 同一运行身份的进程环境,跨用户/跨沙箱注入的密钥在 Agent 侧常读为空",
"改 MCP 配置时同步所有配置面(如 host 侧 json + service 侧 yaml + 运行时 auth store),不要假设只有一处",
"环境变量名以源码读取逻辑为准,不以 README 为准;升级前对入口文件与变量解析做只读预检"
],
"verification": {
"check_1": "确认 MCP Server 子进程存活(不是只看 Agent 主进程 PID)",
"check_2": "能力‘配置对、无报错、不生效’时,检查数据目录文件属主与密钥所在进程身份",
"check_3": "grep 源码确认实际读取的环境变量名,并核对两处配置面 provider 名一致"
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
AI Agent 使用指南:
- 当用户说"想让 Agent 查私有库/接私有 API/加长期记忆" → 按
quick_start用 MCP Server 包装,凭据走进程环境,别让 Agent 现拼命令。 - 当用户说"MCP 插件配置都对了但就是不工作、也不报错" → 按
verification.check_2先查数据目录文件属主(是否被 root 占用)和密钥所在的进程身份。 - 当用户说"照 README 配了环境变量还是读不到" → 按
safety_rulesgrep 源码确认真实变量名,README 常与源码不符。 - 当用户说"巡检/操作时 Agent 去查了一个不存在的本地服务" → 按
quick_start.step_3在上下文显式区分本地与远程形态,纠正模板滥用。
上一篇:Claude Code 实战 06|权限与安全护栏:开了自动权限,怎么保证不翻车 下一篇:Claude Code 实战 08|子 Agent 与编排:让一个 Agent 变成一支小队
- 02
- MySQL 性能压测:Sysbench 1.0 实战 原创07-29
- 03
- MySQL Router 实现读写分离 原创07-29