最近在折腾Claude Code,发现十个配置问题里有八个都出在MCP.json上。要么是服务器没起来,要么是环境变量没传进去,要么是项目级配置和全局配置互相打架。这篇文章把我这段时间踩过的坑、验证过能跑通的写法、以及排查思路完整整理出来,希望能帮你少走弯路。
MCP(Model Context Protocol)这个词现在基本是AI编程工具链的标配了。无论你用Claude Code、Claude Desktop还是其他支持MCP协议的客户端,最终都要面对这个叫MCP.json的配置文件。搞懂了它,就等于掌握了给AI接电源插座的方法:你想让它读文件、操作数据库、调GitHub,都靠这个文件里的一条条服务器声明。
文章不会只贴一个配置样例就完事。我会从协议本身讲起,再逐字段拆解,然后给四个可以直接抄作业的实战示例,最后附上完整的排查链路。无论你是第一次接触MCP,还是已经在用但总被配置问题卡住,这篇都值得花十分钟仔细看完。
1. 先搞清楚MCP.json到底是干什么的
1.1 MCP协议在解决什么问题
传统上,让AI大模型访问外部数据或者执行操作,只有一条路:在提示词里粘贴文本让它"看"。比如你复制一段日志丢给模型,让它分析报错原因。但这种方式根本没法处理动态数据,更不可能让模型直接调API、查数据库、操作本地文件。
MCP协议想解决的就是这个割裂问题。它把AI应用和外部工具系统之间的连接标准化了。你可以把MCP理解成一个USB-C接口——原来每个工具都得配一根专属线缆,现在统一了接口标准,插上就能通。
具体到架构上,MCP有客户端(Client)和服务器(Server)两端。AI应用(比如Claude Code)是客户端,负责跟用户对话、理解意图。MCP服务器是执行方,负责提供具体能力,比如文件读写、GitHub操作、数据库查询。两端通过标准协议通信,而MCP.json就是客户端用来记录"有哪些服务器、怎么连接它们"的注册表。
1.2 MCP.json在Claude Code生态中的坐标
很多教程一上来就让你改配置,却不解释这个文件在整套工具链里处于什么位置。弄清楚坐标,后续排错会轻松很多。
在Claude Code里,MCP配置分三个层级,优先级从高到低分别是:
- 项目级配置文件
.mcp.json,放在项目根目录,只对当前项目生效,可以提交到Git仓库给团队共享。 - 用户级配置
~/.claude.json,属于当前系统用户,对所有项目生效,但不会提交到仓库。 - 命令行动态注册,通过
claude mcp add添加的服务器,同样存储在用户级配置里。
实际使用中,我个人的习惯是:跟项目强相关的工具(比如这个项目专用的数据库连接、本地服务路径)放项目级 .mcp.json;跟个人工作流强相关的工具(比如通用的文件系统读写、GitHub令牌)放用户级配置。
1.3 配置文件到底放在哪:三个常见位置
这是初学者最容易懵的地方。不同客户端、不同版本,配置文件位置不一样,网上教程又各说各话,搞得人一头雾水。
以Claude Code为例,常见的有这么几个位置:
- 项目级:项目根目录下的
.mcp.json - 用户级:
~/.claude.json里的mcpServers字段 - Claude Desktop:
~/Library/Application Support/Claude/claude_desktop_config.json(macOS)或%APPDATA%\Claude\claude_desktop_config.json(Windows)
我曾在项目根目录建了 .mcp.json,结果发现Claude Code根本没读取,后来才意识到我把文件命名成了 .mcp.json.example,就差一个后缀。如果你配置了半天发现完全不生效,第一件事就是确认文件名和位置对吗。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 配置文件逐字段拆解:每个键值背后的设计意图
2.1 command与args:如何正确拉起本地进程
本地MCP服务器最常见的传输方式是stdio,也就是客户端直接以子进程方式启动服务器程序。这个启动指令就由 command 和 args 两个字段决定。
json复制{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-filesystem",
"/Users/me/projects"
]
}
}
}
command 填的是可执行文件名或完整路径。填可执行文件名时,客户端会去系统PATH里找这个命令。这里有个隐藏要求:启动Claude Code的那个终端环境,必须能直接执行 npx 命令。如果你把Claude Code装在了某个有独立PATH的GUI环境里,可能会导致找不到npx。
args 是一个字符串数组,按顺序传给被启动的程序。这里最容易犯的错误是:把需要空格分隔的参数拆成了多个数组元素。比如 "-y @modelcontextprotocol/server-filesystem" 写成一个元素,这会导致程序把整串当成一个参数去解析。
npx -y 的组合要特别注意。npx 是npm自带的命令执行器,-y 表示自动确认安装缺失的包。首次运行某个MCP服务器时,npx会临时下载并执行对应的包,这个过程可能需要几十秒,如果你的网络状况不好,容易出现连接超时的假象。
2.2 env与cwd:环境变量和工作目录的坑
很多MCP服务器需要对接第三方服务,比如GitHub、Stripe、数据库。这时候就需要通过环境变量传入API密钥或连接信息。env 字段就是干这个的。
json复制{
"mcpServers": {
"github": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env": {
"GITHUB_PERSONAL_ACCESS_TOKEN": "ghp_xxx"
}
}
}
}
env 里的键值对会被注入到启动进程的环境变量中。这里有个非常关键的坑:如果你在 .mcp.json 里给某个键设置了值,它会覆盖系统环境变量里的同名值。这意味着你本机已经 export 过 GITHUB_TOKEN,但配置里写的是另一个值,那生效的是配置里的那一个。
cwd 字段指定服务器进程的工作目录。不是所有客户端都支持,但Claude Code和较新的MCP客户端都识别它。默认情况下,工作目录就是启动客户端的那个目录。如果你的服务器脚本里用了相对路径读取文件,一定要显式设置 cwd,否则脚本会因为找不到文件而报错,排查起来还特别隐蔽。
注意:项目级
.mcp.json里的env是明文存储的。不要把生产环境的密钥直接写进去,尤其是要提交到Git仓库共享的配置。后文会有专门讲敏感信息处理的部分。
2.3 type与url:本地stdio和远程HTTP怎么选
如果MCP服务器不在本机,而是跑在远程服务器上,那就不能靠 command 启动了,得用远程传输方式。这就是 type 和 url 字段的用武之地。
MCP协议演进过两代远程传输方案。早期是SSE(Server-Sent Events),现在主流客户端逐步迁移到Streamable HTTP。配置写法略有不同:
json复制{
"mcpServers": {
"remote-server": {
"type": "http",
"url": "http://localhost:3000/mcp"
}
}
}
如果是SSE格式:
json复制{
"mcpServers": {
"remote-server": {
"type": "sse",
"url": "http://localhost:3001/sse"
}
}
}
url 填的是服务器暴露的MCP端点。配置远程服务器时还有一个 headers 字段,可以携带鉴权信息:
json复制{
"mcpServers": {
"weather-api": {
"type": "http",
"url": "https://api.example.com/mcp",
"headers": {
"Authorization": "Bearer sk-xxx"
}
}
}
}
选择本地stdio还是远程HTTP,主要看两点:一是服务器程序跑在哪,二是你需不需要跨机器共享。本地脚本、需要访问本机文件的服务器,肯定用stdio;部署在云端的统一MCP网关,就必须用HTTP。
3. 实战示例:从文件系统到自定义脚本的完整配置
3.1 示例一:文件系统服务器(最基础)
文件系统服务器是官方提供的参考实现之一,作用是让AI能读取和操作指定目录下的文件。起个名字叫 filesystem,通过 npx 启动:
json复制{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-filesystem",
"/Users/me/projects",
"/Users/me/documents"
],
"env": {}
}
}
}
启动后,Claude Code里就能以 /mcp 命令查看这个服务器的连接状态。使用过程中,AI会调用文件读取、目录列举这些工具,你能看到它操作的路径范围被限制在 /Users/me/projects 和 /Users/me/documents 这两个目录里。
从安全角度讲,args里指定的路径就是文件系统服务器的访问边界。不要图省事直接传根目录 /,否则AI可以读取你机器上几乎所有文件,万一提示词注入攻击发生,后果会很严重。
3.2 示例二:GitHub集成(带环境变量)
在实际开发流程中,让AI直接读取仓库Issue、创建PR,能省掉大量反复复制粘贴的沟通成本。GitHub官方MCP服务器需要传一个Personal Access Token。
json复制{
"mcpServers": {
"github": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env": {
"GITHUB_PERSONAL_ACCESS_TOKEN": "ghp_你的token"
}
}
}
}
Token的获取方式:GitHub网站右上角头像 → Settings → Developer settings → Personal access tokens → Generate new token。权限范围至少勾选 repo(操作仓库)和 read:org(读取组织信息)。不要用workflow权限,除非你需要通过API修改工作流文件。
这里有个小细节:官方GitHub服务器的npm包要求环境变量名是 GITHUB_PERSONAL_ACCESS_TOKEN,但有些第三方fork却用 GITHUB_TOKEN。配之前先看一眼你用的包README,别把变量名写错了。
3.3 示例三:用Python脚本写一个极简自定义MCP服务器
官方服务器只能覆盖通用场景,真正好用的MCP配置,往往要对接你自己的工作台。这里用一个最小可运行的Python示例,展示如何把自己写的脚本接入MCP。
先安装MCP官方Python SDK:
bash复制uv add mcp
# 或者
pip install mcp
然后写一个简单的MCP服务器文件 hello_server.py:
python复制from mcp.server.fastmcp import FastMCP
mcp = FastMCP("hello")
@mcp.tool()
def add(a: int, b: int) -> int:
"""计算两个整数之和"""
return a + b
@mcp.tool()
def get_env_info() -> str:
"""返回当前环境的关键信息"""
import os
return os.getenv("MY_SERVER_INFO", "no info")
if __name__ == "__main__":
mcp.run()
然后通过 MCP.json 把这个脚本挂上去:
json复制{
"mcpServers": {
"hello": {
"command": "python",
"args": ["/absolute/path/to/hello_server.py"],
"env": {
"MY_SERVER_INFO": "configured via mcp.json"
}
}
}
}
启动之后,你在对话里让AI"用hello工具计算3和5的和",它就会自动调用 add 工具并返回结果。这就是自定义MCP服务器的基本套路:写一个Python/Node脚本,暴露 @mcp.tool() 装饰的函数,配置里把这个脚本作为启动命令即可。
关于Python环境的坑说一下。如果你的系统装了多个Python,command 用 python 还是 python3,可能对应不同解释器。确保你写的是安装了mcp包的那个解释器。更稳妥的做法是用虚拟环境里的完整路径:
json复制{
"mcpServers": {
"hello": {
"command": "/Users/me/.venvs/myenv/bin/python",
"args": ["/absolute/path/to/hello_server.py"]
}
}
}
3.4 示例四:远程HTTP服务器对接
假设你在内网部署了一个MCP网关,地址是 http://10.0.0.5:8080/mcp,并且需要token认证。配置如下:
json复制{
"mcpServers": {
"internal-gateway": {
"type": "http",
"url": "http://10.0.0.5:8080/mcp",
"headers": {
"Authorization": "Bearer abc123"
},
"env": {}
}
}
}
如果是新项目没有现成的远程MCP服务,你可以用官方SDK快速起一个测试服务。Python方面用FastMCP内置的HTTP能力,Node方面用官方Server类。这些细节展开讲会拉长篇幅,这里只强调配置端的一个要点:远程服务一定要验证 /mcp 这个路径能否直接响应POST请求。
我自己遇到过的情况是:服务端框架给MCP端点加了额外的路径前缀,结果配置里URL路径少写了一段,客户端一直在报握手失败。排查了半天才用curl试出来正确路径。所以接到远程服务器任务时,第一件事就是用curl把服务端点测通,再写配置。
4. 配置不生效?完整的排查链路
4.1 第一步:用命令验证配置是否被识别
配置完 MCP.json 后,先不要急着进交互界面试。在项目目录下直接运行:
bash复制claude mcp list
如果配置正确且服务器已连接,你会看到类似这样的输出:
text复制filesystem: connected
github: connected
hello: connected
如果某个服务器显示 disconnected,后面通常跟着错误信息。这个命令能帮你快速判断问题到底出在配置解析阶段,还是进程启动阶段。
有次我改了 .mcp.json 后,claude mcp list 死活不显示新增的服务器。后来发现在服务器名里用了空格和特殊字符,导致解析失败。MCP服务器名称建议只用小写字母、数字、连字符和下划线,别加空格。
4.2 第二步:区分是配置问题还是服务器本身问题
这一步是排查的核心思路。很多看似是配置问题的现象,根源其实是服务器进程本身跑不起来。
举个典型例子,文件系统服务器连接失败,报错信息是 ENOENT。这种情况下,先别急着改配置文件,单独在命令行里执行一次npx指令:
bash复制npx -y @modelcontextprotocol/server-filesystem /Users/me/projects
如果这条命令在你终端里能正常运行且不退出,说明服务器本身没毛病,问题大概率出在环境差异上——比如Claude Code启动时的PATH和终端不一致。如果同样的命令在终端里直接报错,那就是网络、node版本或包本身的问题,这时候再怎么折腾配置文件都没用。
用这个方法,我把"配置问题"和"环境问题"这两类错误在十分钟内做了初步隔离,效率比盲目试改高得多。
4.3 第三步:日志与常见错误速查
MCP客户端通常有调试日志。Claude Code里可以通过设置日志级别,或者直接观察启动时的输出。一些常见错误和原因,我整理了一个速查表:
| 错误信息 | 常见原因 | 解决办法 |
|---|---|---|
spawn npx ENOENT |
系统PATH里找不到npx | 把npx换成完整路径,或用node配合具体的cli入口 |
command not found |
命令拼写错误或依赖未安装 | 检查command字段,确认对应包已安装 |
Connection closed |
服务器进程启动后立即退出 | 单独在终端运行命令看具体报错 |
Unauthorized |
token无效或权限不足 | 重新生成token,检查scope |
ENOENT: no such file or directory |
args里路径不存在 | 确认路径存在且权限正确 |
Failed to connect to server |
远程URL不通 | 用curl验证端点,检查网络和防火墙 |
日志这块,Claude Code的 /mcp 命令也能看到每个服务器的连接状态。如果服务器列表里标红,说明进程没起来,点开details能看到退出码。退出码是个很有用的线索,比如退出码127表示命令找不到,退出码1通常是程序内部报错。
遇到退出码1的情况,可以看下客户端日志的stderr输出。有一次我发现某个Node写的服务器一直崩溃,stderr里报了一个模块找不到的错,后来定位到是因为没有安装某依赖。这种问题从配置层面怎么改都没用,必须先让服务器本身能独立跑起来。
5. 多人协作与安全实践:MCP.json进阶玩法
5.1 项目级共享配置的注意事项
项目级 .mcp.json 推荐提交到Git仓库,这样团队成员clone下来后,Claude Code会自动识别并连接项目需要的MCP服务器。但带来的问题是:不同成员本机环境不一样,同一个配置在不同机器上可能失效。
举个实际的例子。你在macOS上配好的文件系统服务器路径是 /Users/me/projects,Windows队友的路径是 C:\Users\me\projects。如果把这个路径写死到 .mcp.json 里,队友clone下来根本跑不通。
共享配置最稳妥的做法是:
- 只放跟仓库相关的远程服务,比如内网MCP网关、CI工具接口。
- 本地文件路径尽量用相对路径,或者交给用户级配置去覆盖。
- 在README里写清楚每个服务器需要哪些环境变量。
5.2 敏感信息怎么处理
env 字段明文写在JSON里,而JSON又可能被提交到Git仓库,这是MCP配置最大的安全隐患。GitHub标星的官方服务器配置示例里常出现 ghp_xxx 这种占位说明,但总有人直接把真实token贴进去。
我的处理方案分三层:
- 敏感值不写入
.mcp.json,只写环境变量占位符,然后在shell配置里export。 - 让MCP服务器程序自己去读系统的环境变量(大多数服务器都支持),配置里
env字段留空。 - 如果客户端确实不支持读取系统环境变量,至少把token放到用户级配置里,不要进项目仓库。
json复制{
"mcpServers": {
"github": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env": {}
}
}
}
这样写的效果是,服务器进程会读取GitHub官方服务器默认使用的环境变量名。如果本机已经export GITHUB_PERSONAL_ACCESS_TOKEN=...,配置空env反而能命中系统里的值。这么做的好处是,token只存在于你的shell profile里,不会进任何配置文件。
5.3 常用服务器组合推荐与性能考量
最后分享一套我目前用得比较顺的组合,供参考。
| 用途 | 服务器包 | 说明 |
|---|---|---|
| 本地文件读写 | @modelcontextprotocol/server-filesystem | 限定在工作目录 |
| GitHub操作 | @modelcontextprotocol/server-github | 需要token |
| 顺序思维推理 | @modelcontextprotocol/server-sequential-thinking | 让AI做多步推理 |
| 自定义脚本 | 自写Python/SDK脚本 | 对接内部平台 |
同时挂的服务器不是越多越好。每多一个服务器,初始化和上下文占用都会增加。Claude Code在处理复杂任务时,可能会因为上下文窗口被工具定义占满而变慢。我的经验是,忙项目时挂三到四个服务器足够,日常轻量开发只挂文件系统一个就行。
另一个性能优化小技巧:本地stdio服务器用 npx -y 启动时,每次运行都会把包下载到npm缓存,然后创建临时执行环境。如果你发现某个服务器连接特别慢,可以考虑把包全局安装:
bash复制npm install -g @modelcontextprotocol/server-filesystem
然后配置里直接写:
json复制{
"mcpServers": {
"filesystem": {
"command": "mcp-server-filesystem",
"args": ["/Users/me/projects"]
}
}
}
这样跳过了npx的解析和下载环节,启动速度会明显提升。缺点是全局包的版本是固定的,升级时需要手动 npm update -g。
配置MCP.json这事,本质上就是"让AI知道去哪里找工具、用什么凭证访问、边界在哪"。文件格式本身不复杂,真正花时间的都在环境差异和权限细节上。我个人最深的体会是:写配置之前先手动跑一遍命令,确认服务器本身能跑通,再去纠结JSON的格式问题。顺序对了,排错效率能翻一倍。
