1. 新版 Cursor 的 MCP 配置到底变了什么
如果你最近升级了 Cursor,打开设置想加一个自定义 MCP 服务器,可能会发现老教程里的路径和字段对不上了。新版本把 MCP 配置从原来的界面式操作,改成了以mcp.json为核心的 JSON 文件配置方式,字段结构、生效逻辑、调试入口都有调整。这篇就围绕「新版本 Cursor 中配置自定义 MCP 服务器」这件事,从配置文件定位、参数填写、连通性验证,到最小可运行的工具源码,走一遍完整闭环。
先说清楚 MCP 是什么。Model Context Protocol 是一套让 AI 编辑器连接外部工具和服务的协议,通信走标准输入输出(stdio),消息格式是 JSON-RPC 2.0。你可以把它理解成给 Cursor 装「插件插座」:Cursor 负责发请求,你的 MCP 服务器负责执行具体动作,比如读 git diff、查数据库、调内部接口,然后把结果按协议格式吐回去。它适合谁?适合那些不满足于编辑器内置能力、想把公司内部工具或私有服务接进 AI 工作流的开发者。
新版配置的核心变化有三点。第一,配置入口统一到用户级mcp.json,不再依赖图形界面逐项点选。第二,服务器定义里command、args、cwd、env这几个字段的语义更严格,路径写错会直接导致服务器起不来。第三,工具列表和调用结果都要求严格的 JSON-RPC 结构,initialize没走完就调tools/list会直接报错。
我试过在 Windows 和 macOS 上各配一遍,最容易踩的坑不是协议本身,而是路径分隔符和 Python 解释器指向。下面按顺序把每一步拆开,你可以直接跟着做。
2. TaoToken 统一 Key 接入 MCP 的前置准备
在写 MCP 服务器之前,得先解决「模型从哪来」的问题。很多自定义 MCP 工具本身不生成内容,而是把提示词或数据整理好,再交给模型处理。这时候如果每个工具都单独配一套 Key,维护成本会很高。用 TaoToken 的统一 API 通道,可以把模型调用收敛到一个 Base URL 和一把 Key 上,MCP 服务器里只读环境变量就行。
TaoToken 在这里扮演的是统一接入层:你拿到一个 API 地址和 Key,MCP 工具内部通过它去请求模型,不用在代码里硬编码多家厂商的地址。官网入口是 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,API 根地址是 https://taotoken.net/api ,注意 API 地址后面不加 UTM 参数。
具体要准备三样东西:
第一,API Key。去控制台创建,地址是 https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,创建完复制出来,后面写进env字段。Key 的管理页面在 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,可以随时吊销重发。
第二,Base URL。统一用https://taotoken.net/api,MCP 服务器里拼请求路径时基于它。
第三,Model ID。这个要和你实际调用的模型对上,写进配置或代码里。如果你不确定用哪个,可以先去模型对话页面试一下,地址是 https://taotoken.net/models?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,确认模型能正常返回再写进 MCP 工具。
如果你打算长期做编码类 Agent,或者要跑多个 MCP 工具共享额度,可以看下 Coding Plan,地址是 https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= 。接入文档在 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,遇到字段不清楚可以对照查。
这里强调一个原则:MCP 服务器里不要明文写 Key,统一走env注入。这样配置文件可以进版本库,Key 留在本地环境变量或 Cursor 的env块里,泄露风险小很多。
3. 可复制的 mcp.json 配置与最小工具源码
这一节是全文的核心,给你一份能直接抄的配置片段,加一份最小可运行的 MCP 服务器源码。先看配置文件。
新版 Cursor 的 MCP 配置在用户设置目录下。Windows 一般是%APPDATA%\.Cursor\mcp.json,macOS 在~/.cursor/mcp.json。如果文件不存在就新建一个。结构如下:
{ "mcpServers": { "taotoken-demo-mcp": { "command": "python", "args": ["server.py"], "cwd": "D:/mcp-demo/taotoken-demo-mcp", "env": { "TAOTOKEN_API_KEY": "sk-你的Key", "TAOTOKEN_BASE_URL": "https://taotoken.net/api", "TAOTOKEN_MODEL_ID": "你的模型ID" } } } }字段逐个说。taotoken-demo-mcp是服务器名,随便起,但后面在 Cursor 里看到的工具会挂在这个名字下。command是启动命令,Python 就写python,Node 就写node。args是参数数组,指向你的入口脚本。cwd是工作目录,必须写绝对路径,Windows 上用正斜杠D:/...比反斜杠稳,避免转义问题。env里放三个变量:Key、Base URL、Model ID,这就是前面说的三件套,缺一个工具内部就调不通模型。
注意cwd和args的关系:args里的server.py是相对cwd的,所以脚本要放在cwd指向的目录里。路径里尽量别带中文和空格,否则某些环境下启动会失败。
接下来是最小工具源码。新建server.py,内容如下:
#!/usr/bin/env python3 import json import sys import os API_KEY = os.environ.get("TAOTOKEN_API_KEY", "") BASE_URL = os.environ.get("TAOTOKEN_BASE_URL", "https://taotoken.net/api") MODEL_ID = os.environ.get("TAOTOKEN_MODEL_ID", "") request_id = 0 initialized = False def send(result=None, error=None): resp = {"jsonrpc": "2.0", "id": request_id} if error: resp["error"] = error else: resp["result"] = result print(json.dumps(resp), flush=True) def handle_initialize(params): global initialized initialized = True send({ "protocolVersion": "2024-11-05", "capabilities": {"tools": {}}, "serverInfo": {"name": "taotoken-demo-mcp", "version": "1.0.0"} }) def handle_tools_list(params): if not initialized: send(error={"code": -32002, "message": "Server not initialized"}) return send({ "tools": [ { "name": "echo_env", "description": "Return current TaoToken config for connectivity check", "inputSchema": { "type": "object", "properties": {} } } ] }) def handle_tools_call(params): if not initialized: send(error={"code": -32002, "message": "Server not initialized"}) return name = params.get("name") if name == "echo_env": masked = API_KEY[:6] + "***" if API_KEY else "(empty)" send({ "content": [ { "type": "text", "text": f"base_url={BASE_URL}\nmodel_id={MODEL_ID}\napi_key={masked}" } ] }) else: send(error={"code": -32601, "message": f"Tool not found: {name}"}) def handle_request(req): global request_id request_id = req.get("id", 0) method = req.get("method") params = req.get("params", {}) if method == "initialize": handle_initialize(params) elif method == "tools/list": handle_tools_list(params) elif method == "tools/call": handle_tools_call(params) else: send(error={"code": -32601, "message": f"Method not found: {method}"}) def main(): print("taotoken-demo-mcp started", file=sys.stderr) for line in sys.stdin: line = line.strip() if not line: continue try: handle_request(json.loads(line)) except json.JSONDecodeError as e: print(f"invalid json: {e}", file=sys.stderr) except Exception as e: print(f"error: {e}", file=sys.stderr) if __name__ == "__main__": main()这份源码只做一件事:把env里的三个变量回显出来,用来验证配置是否被正确注入。它实现了initialize、tools/list、tools/call三个必需方法,协议版本写2024-11-05,和 Cursor 新版对齐。echo_env工具把 Key 做了掩码,只显示前六位,避免日志泄露。
把server.py放进D:/mcp-demo/taotoken-demo-mcp,确认mcp.json的cwd指向同一目录,然后重启 Cursor。重启是必须的,新版不会热加载 MCP 配置。
4. 验证请求与成功结果
重启 Cursor 后,打开设置里的 MCP 面板,应该能看到taotoken-demo-mcp这个服务器,状态是绿色或已连接。如果显示 0 个工具,先别急,按下一节的排查走。
验证分两步。第一步,确认工具被识别。在 MCP 面板里展开服务器,应该看到echo_env。第二步,在 Cursor 的 AI 聊天里直接调用它,输入类似「调用 echo_env 工具,把当前配置打印出来」。正常情况下,AI 会发起tools/call,服务器返回一段文本,内容形如:
base_url=https://taotoken.net/api model_id=your-model-id api_key=sk-abc***看到这段就说明整条链路通了:Cursor 启动进程、注入env、走完initialize、列出工具、调用工具、拿到结果。如果api_key显示(empty),说明env没注入成功,回去检查mcp.json的env块拼写。
想更直接地验证协议层,可以手动喂一条 JSON-RPC 消息。在终端里跑:
cd D:/mcp-demo/taotoken-demo-mcp echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}' | python server.py你应该看到一行 JSON 返回,包含protocolVersion和serverInfo。再喂一条tools/list:
echo '{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}' | python server.py注意这里每次都是新进程,initialized是 False,所以第二条会返回Server not initialized。这是正常的,因为 stdio 模式下 Cursor 会保持同一个进程连续发消息。要模拟连续调用,可以写个脚本一次喂两行:
printf '%s\n%s\n' '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}}' '{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}' | python server.py这样就能看到先初始化、再列出工具的完整返回。这一步能过,说明你的 JSON-RPC 实现没问题,剩下的就是 Cursor 侧的配置对齐。
成功结果的标准很简单:聊天里能拿到echo_env的返回,且base_url和model_id和你写的一致。到这一步,一个可运行的 MCP 调用闭环就完成了。
5. 本篇常见报错排查
配置 MCP 最容易卡在几个固定报错上,逐个对照。
报错一:Server not initialized(code -32002)。这是tools/list或tools/call在initialize之前被调用。原因通常是 Cursor 发了消息但你的服务器没正确响应initialize,或者响应格式不对。检查handle_initialize里send的result是否包含protocolVersion、capabilities、serverInfo三个字段,缺一个 Cursor 可能认为初始化失败。另外确认request_id是从请求里取的,不是写死的。
报错二:local proxy failed或进程起不来。这类多半是command或cwd的问题。先确认python在系统 PATH 里,终端能直接跑python --version。如果用的是虚拟环境,command要写虚拟环境里的解释器绝对路径,比如D:/venv/Scripts/python.exe。cwd必须是绝对路径且目录存在,路径带空格要小心。Windows 上如果python指向 Microsoft Store 的占位程序,也会起不来,换成真实安装路径。
报错三:401或鉴权失败。如果工具内部要调模型,返回 401 说明 Key 不对或没注入。先在终端echo $TAOTOKEN_API_KEY(Windows 用echo %TAOTOKEN_API_KEY%)确认环境变量,再确认mcp.json的env块里 Key 没有多余空格。Key 失效就去 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= 重新生成。
报错四:reading choices相关解析错误。这通常出现在工具内部调模型后解析响应时,说明返回结构和你代码里取字段的路径不一致。先打印原始响应体,确认返回的是标准结构,再调整取值逻辑。别直接假设字段一定存在,加个判空。
报错五:OAuth 或授权跳转异常。如果你接的是需要 OAuth 的远程 MCP 服务,回调地址和客户端配置要对齐。本地 stdio 模式一般不涉及 OAuth,遇到这类报错先确认你用的是不是远程模式,以及授权信息是否过期。
报错六:工具列表为空。服务器连上了但工具是 0 个,检查tools/list返回的tools数组是否为空,以及每个工具是否有name、description、inputSchema。inputSchema必须是合法 JSON Schema,type写object,properties即使为空也要给。
排查通用手法:看 Cursor 的输出面板,MCP 相关日志会打在那里;同时在服务器脚本里往stderr打日志,stdout只留给 JSON-RPC,混用会导致解析失败。这一点很关键,print调试信息一定要带file=sys.stderr。
6. 把 MCP 工具接进日常编码流
跑通最小闭环之后,就可以往真实场景扩了。比如把echo_env换成读 git diff 的工具,或者接内部接口查询。核心结构不变:initialize声明能力,tools/list暴露工具,tools/call执行逻辑,模型调用统一走 TaoToken 的 Base URL 和 Key。
如果你要接 Claude Code 这类编码 Agent,配置思路类似,Base URL 和 Key 还是那套,Model ID 按实际填。文档在 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,Claude Code 相关说明可以对照看。想先验证模型通不通,去 https://taotoken.net/models?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= 试一条请求最快。
最后给个实用建议:MCP 服务器脚本单独建目录,用版本控制管理,mcp.json里的 Key 走env不落盘。每次改完配置记得重启 Cursor,改完源码也要重启,因为 stdio 进程是启动时拉起的。调试阶段把日志打到stderr,稳定后再精简。这样一套下来,你的自定义 MCP 工具就能稳定挂在 Cursor 里,随用随调。