引子
在2024年的年初,我们使用Cursor的时候,我们可能会感觉Cursor写后端比写前端厉害,即使后端的逻辑比前端复杂多了。
因为,那时候Cursor可以读取后端的输入、输出、甚至访问数据库(我们把数据库连接给它,它自己安装Shell工具去读)。
但是,那时候Cursor访问不了前端。如果页面效果不是我们想要的,我们只能截图给他(但是他图片理解能力有不强),或者只能我们口述页面效果哪里哪里不对(又讲不清)。
这个问题的根源,不是模型不够聪明,也不是上下文被污染了。是那时候Cursor调用工具的能力,无法调用浏览器看网页效果。
那么,内置工具够不着的外部系统,怎样安全地接进工具调用循环?
外部工具和Cursor约定一个标准协议,外部系统按协议把能力暴露出来,ClaudeCode按协议接入,两边各实现一次。这就是MCP。
- 官方文档:https://code.claude.com/docs/en/mcp
- 快速开始:https://code.claude.com/docs/en/mcp-quickstart
- 协议规范:https://modelcontextprotocol.io/
原理
什么是MCP
MCP(Model Context Protocol,模型上下文协议)是一个开源标准协议。外部系统实现一个MCP服务,把自己的能力以标准形式暴露出来;ClaudeCode作为MCP客户端接入外部系统,循环里就多了一批可调用的能力。数据库、Jira、GitHub、浏览器,接法完全一致。
MCP服务的三类能力
MCP服务有三类能力,在ClaudeCode中各有对应的入口:
| 能力 | 是什么 | 在ClaudeCode中的入口 |
|---|---|---|
| Tools(工具) | 供Claude调用的可执行函数,如查询数据、调用API | 进入工具调用循环,与内置工具一样被自动选用,规范名为mcp__<server>__<tool> |
| Resources(资源) | 可按需读取的数据实体,如一条Issue、一个文件 | 会话中用@server:scheme://id引用,如@github:issue://123 |
| Prompts(提示) | MCP服务预置的提示模板 | 成为斜杠命令/mcp__server__name,如/mcp__github__list_prs,提示名与MCP服务名中的空格会转换为下划线 |
其中:
- Tools覆盖绝大多数场景。
- Resources适合把一份外部数据精确拉进上下文。
- Prompts则把MCP服务作者写好的任务模板变成一条斜杠命令。
示例
需求
一个Web项目团队,要让ClaudeCode接入GitHub、项目数据库和公司内部API。
第一步:接GitHub
用HTTP传输加Bearer token。示例代码:
1 | claude mcp add --transport http github https://api.githubcopilot.com/mcp/ \ |
添加后三类能力全部就位:让Claude"把刚才的报错整理成issue提交"走的是工具调用;@github:issue://123把一条issue精确拉进上下文;/mcp__github__list_prs直接执行MCP服务预置的提示。
第二步:接项目数据库
用项目范围让全组共享。示例代码:
1 | claude mcp add --scope project \ |
这条命令有三个关键点:
--scope project:把配置写进项目根目录的.mcp.json,提交后同事克隆仓库即用。--:之后是stdio MCP服务的启动命令。- 单引号:防止Shell在
add时抢先展开${DB_URL:-...},让它原样写进.mcp.json、留到运行时才按后文的环境变量展开规则取值,真实凭证不进仓库。
第三步:接公司内部API
手写.mcp.json配headersHelper。示例代码:
1 | { |
三个字段各管一件事:
headersHelper
指向的脚本在标准输出打印一个JSON对象作为附加请求头(如{"X-Token": "..."}),token的获取与刷新逻辑都在脚本里,配置文件中不出现任何凭证,见后文认证一节。timeout
按内网延迟收紧到30秒。alwaysLoad
让这台高频MCP服务的工具始终加载,见后文性能一节。
改完重启会话,claude mcp list确认三台MCP服务都在线。
实践
claude mcp 命令族
MCP服务用claude mcp命令族管理:
| 命令 | 说明 |
|---|---|
claude mcp add |
添加MCP服务;远程形式为claude mcp add <name> <url>,本地stdio形式把启动命令放在--之后 |
claude mcp list |
列出所有已配置MCP服务及连接状态 |
claude mcp get <name> |
查看单台MCP服务的详细配置与错误详情 |
claude mcp remove <name> |
删除MCP服务,可用--scope指定从哪个范围删 |
claude mcp add-json <name> '<json>' |
用一段JSON直接配置MCP服务 |
claude mcp add-from-claude-desktop |
从Claude Desktop导入已配置的MCP服务 |
claude mcp login <name> / claude mcp logout <name> |
OAuth登录与清除凭证,见下文认证一节 |
claude mcp serve |
把ClaudeCode自身作为一台MCP服务启动,供其他MCP客户端调用 |
claude mcp reset-project-choices |
重置项目级MCP服务的批准状态 |
claude mcp add 常用参数
| 参数 | 说明 |
|---|---|
--transport {http\|sse\|stdio\|ws} |
传输方式,默认stdio,远程推荐http |
--scope {local\|project\|user} |
配置存储范围,默认local |
--header "Key: Value" |
附加请求头,常用于传Bearer token |
--env KEY=value |
传递给MCP服务的环境变量 |
--client-id、--client-secret、--callback-port <PORT> |
预配置OAuth凭证,见下文认证一节 |
传输方式
| 传输 | 用途 | 特点 | 认证方式 |
|---|---|---|---|
| HTTP | 远程托管MCP服务(推荐) | 标准请求响应,可靠性高 | OAuth、Bearer token、headersHelper |
| SSE | 远程托管(已废弃) | Server-Sent Events单向推送,仅为兼容旧配置保留 | 静态header |
| stdio | 本地进程 | 以子进程方式启动本地程序,可访问本地文件与数据库socket | 环境变量、本地权限 |
| WebSocket | 持久连接、事件推送 | 双向通信,不支持OAuth | 静态header、headersHelper |
选型准则:
- 本地进程选stdio
- 远程一律HTTP(SSE只在迁移旧配置时会遇到)
- WebSocket留给需要持久双向连接的场景。
配置范围
同一台MCP服务登记在哪个范围,决定了它对谁生效、存在哪个文件里:
| 范围 | 存储位置 | 生效范围 | 进版本库 |
|---|---|---|---|
| local | ~/.claude.json中当前项目的条目 |
仅当前项目、当前用户 | 否 |
| project | 项目根目录的.mcp.json |
克隆该仓库的所有人 | 是 |
| user | ~/.claude.json顶层的mcpServers |
当前用户的所有项目 | 否 |
| plugin | 插件内打包的.mcp.json |
插件启用时自动加载,见《7.Plugin》 | 随插件分发 |
| claude.ai | 云端(claude.ai的connectors配置页) | 登录该账户的ClaudeCode | 否(云端) |
同名MCP服务同时存在于多个范围时,按local>project>user>plugin>claude.ai取高优先级的那个。
不想加载claude.ai连接器时,可在settings.json中设disableClaudeAiConnectors: true,或以环境变量ENABLE_CLAUDEAI_MCP_SERVERS=false启动。
范围的选择有个简单次序:个人通用工具放user,团队共享放project,单项目试验用默认的local。
.mcp.json
例子
示例代码:
1 | { |
- 只留
${GITHUB_TOKEN}这样的引用,真实凭证在每个人自己的环境变量里
主要字段
| 字段 | 适用传输 | 说明 |
|---|---|---|
type |
全部 | http、sse、stdio、ws之一 |
url |
HTTP/SSE/WS | MCP服务端点 |
headers |
HTTP/SSE/WS | 静态请求头 |
headersHelper |
HTTP/WS | 指向一个脚本,运行时用其输出作为请求头,见下文认证一节 |
oauth |
HTTP | 预配置OAuth:clientId、callbackPort、scopes、authServerMetadataUrl |
command、args、env |
stdio | 本地进程的启动命令、参数与环境变量 |
timeout |
全部 | 超时,毫秒 |
alwaysLoad |
全部 | 跳过工具延迟加载,见下文性能一节 |
注意事项
- 配置值支持环境变量展开:
${VAR}在运行时替换为环境变量值,${VAR:-default}在变量未设置时使用默认值。这是把.mcp.json安全提交进仓库的关键。 .mcp.json在会话启动时一次性读取,改动配置后必须重启会话才生效。
会话内管理
会话内运行/mcp打开交互面板,可以查看各MCP服务的连接状态与工具数量、发起认证、手动重连或切断连接。
认证
标准OAuth流程
会话内操作
远程MCP服务的标准认证是OAuth,流程是:
- 添加MCP服务,如
claude mcp add --transport http <name> <url>; - 会话内运行
/mcp,选中该MCP服务,执行Authenticate; - 浏览器完成登录后,token由ClaudeCode保存并自动刷新。
命令行操作
也可以直接在命令行认证:claude mcp login <name>。
在SSH或没有图形界面的机器上加--no-browser,会改为给出URL让我们在别处完成登录。
这个认证方式,很像我们在ClaudeCode上的"Claude account with subscription"认证方式。
退出登录
claude mcp logout <name>清除已存储的凭证。
预配置
有些MCP服务不支持OAuth动态注册,需要预先配置凭证。
示例代码:
1 | claude mcp add --transport http \ |
注意:--client-secret不直接跟值,命令会提示输入,避免密钥留在shell历史里。
自定义认证
用headersHelper字段指向一个脚本,由脚本动态生成请求头。
上文的《第三步:接公司内部API》就是这种方式。
权限与命名
工具规范名
每台MCP服务的每个工具,在ClaudeCode里都有一个规范名,模式为mcp__<server_name>__<tool_name>。
例如GitHub MCP服务的create_issue工具就是mcp__github__create_issue。
MCP服务名与工具名中不属于A-Z、a-z、0-9、_、-的字符会被替换为_。
这个命名空间可以直接用在权限规则里:写完整工具名精确控制单个工具,写mcp__<server>__*通配整台MCP服务。示例代码:
1 | { |
权限规则的通用语法、六种模式与deny>ask>allow的优先级见《1.Start》,本节只是MCP命名空间下的增量。
项目级MCP服务的批准
项目级.mcp.json来自仓库、可能由别人提交,所以其中的MCP服务首次使用前需要我们显式批准,未批准的会显示为"Pending approval"。
在交互式claude会话里完成确认即可。
需要重新决定时,用claude mcp reset-project-choices整体重置批准状态。