
1. 本地调试 MCP Stdio 服务到底在解决什么问题MCPModel Context Protocol是让 AI 客户端调用外部工具的一套协议而 Stdio 是它最朴素也最常用的传输方式客户端把本地脚本当成子进程拉起来通过标准输入输出收发 JSON-RPC 消息。你在 Cursor 里写代码时想让 AI 顺手查个天气、读个本地文件、跑个内部脚本走的就是这条路。问题在于很多人第一次配 Cursor 的 MCP 时配置写完了却不知道参数到底有没有传进去。表现是Cursor 里工具列表能看到但一调用就报错或者返回的永远是默认值。根因通常有三个——command和args的路径写法不对、env里的环境变量没被服务读到、以及 Stdio 的 stdout 被日志污染导致 JSON-RPC 解析失败。这篇就聚焦这个场景在 Cursor 里用 MCP Stdio 接入一个本地 Python 服务传入自定义参数并且用可复现的方式验证参数确实生效。适合已经在用 Cursor、想把自己写的脚本接进 AI 工作流的人。我会给出可直接复制的mcp.json配置片段、TaoToken 的settings.json骨架以及一套「先手动管道测、再进 Cursor 测」的排查顺序。实测下来按这个顺序走90% 的参数不生效问题能在五分钟内定位。2. TaoToken 前置统一 Key 与 API 通道的准备在把本地服务接进 Cursor 之前先把模型侧的通道理顺。TaoToken 的作用是给你一个统一的 Key 和 API 入口Cursor、Claude Code、各种 Agent 都指向同一个地址省得每个工具单独配一遍。你需要先拿到 Key。打开控制台创建 API Keyhttps://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite创建完在 API Keys 页面可以随时查看和轮换https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteAPI 的基础地址是https://taotoken.net/api这个地址在 Cursor 的模型配置和后面的settings.json里都会用到。注意它不带任何查询参数直接填就行。注意Key 只存在本地配置文件里不要提交到 Git也不要在截图里露出完整字符串。轮换成本很低怀疑泄露就直接重建。如果你只是想先验证模型通道通不通可以用模型对话页面直接发一条消息https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite这一步和 MCP 是两条独立的链路模型通道负责「AI 怎么想」MCP Stdio 负责「AI 能调什么工具」。两条都通了Cursor 里的体验才完整。长期跑编码任务、想让 Agent 连续调用工具的话可以了解下 Coding Planhttps://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite3. 可复制配置Cursor mcp.json 与 settings.json 骨架3.1 先写一个能接收参数的 Stdio 服务服务端用 FastMCP关键是两点把 stdin/stdout 强制成 UTF-8以及工具函数要能读到传入的参数。下面这个weather-stdio.py放在D:\Mcp\test\下import sys import urllib.parse from mcp.server.fastmcp import FastMCP server FastMCP(weather-service) sys.stdout.reconfigure(encodingutf-8) sys.stdin.reconfigure(encodingutf-8) server.tool() def get_weather(city: str Beijing) - str: 获取城市天气信息 city_decoded urllib.parse.unquote(city) print(f接收到请求获取{city_decoded}的天气, filesys.stderr) weather_info { temperature: 27, condition: 晴, humidity: 65, wind: 东北风3-4级, } return ( f{city_decoded}的天气温度 {weather_info[temperature]}°C f{weather_info[condition]}湿度{weather_info[humidity]}% f{weather_info[wind]} ) if __name__ __main__: print(天气服务已启动..., filesys.stderr) server.run(transportstdio)这里有个容易踩的坑调试用的print必须写到sys.stderr。Stdio 传输下 stdout 是 JSON-RPC 的专用通道任何一行非 JSON 的输出都会让客户端解析失败表现就是「服务启动了但工具调不动」。3.2 Cursor 的 MCP 配置片段Cursor 的 MCP 配置一般放在项目级.cursor/mcp.json或全局配置里。核心是command、args、env三块{ mcpServers: { weather-stdio: { command: python, args: [ D:\\Mcp\\test\\weather-stdio.py ], env: { PYTHONIOENCODING: utf-8, PYTHONUNBUFFERED: 1, MCP_DEFAULT_CITY: Guangdong, TAOTOKEN_API_BASE: https://taotoken.net/api, TAOTOKEN_API_KEY: sk-你的Key } } } }参数说明对照字段作用常见错误command启动进程的可执行文件写相对路径导致找不到args传给脚本的参数数组Windows 反斜杠没转义env注入子进程的环境变量服务端没读白配env.PYTHONUNBUFFERED关闭输出缓冲不设会看到日志延迟env里的值服务端要主动读才生效比如在脚本里加os.environ.get(MCP_DEFAULT_CITY)。很多人配了env却发现没用就是因为服务端根本没读这个变量。3.3 TaoToken settings.json 骨架如果你同时用 Claude Code 或其它支持settings.json的客户端可以放一份统一骨架让模型通道指向 TaoToken{ env: { ANTHROPIC_BASE_URL: https://taotoken.net/api, ANTHROPIC_API_KEY: sk-你的Key, ANTHROPIC_MODEL: claude-sonnet-4-5 }, permissions: { allow: [] } }ANTHROPIC_BASE_URL填https://taotoken.net/apiKey 用上一步创建的。这样模型请求和 MCP 工具调用各走各的配置互不干扰。Claude Code 的接入细节可以看文档https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite4. 验证请求参数到底有没有传进去4.1 先用管道手动测绕开 Cursor在进 Cursor 之前先在终端确认服务本身能正确收发。写一个test_weather.py当客户端模拟初始化、发通知、再调工具import sys import urllib.parse sys.stdout.reconfigure(encodingutf-8) sys.stdin.reconfigure(encodingutf-8) def main(): print({jsonrpc: 2.0, id: 1, method: initialize, params: {protocolVersion: 0.1.0, clientInfo: {name: test-client, version: 1.0.0}, capabilities: {}}}) sys.stdout.flush() print({jsonrpc: 2.0, method: notifications/initialized, params: {}}) sys.stdout.flush() city_encoded urllib.parse.quote(广东) print(f{{jsonrpc: 2.0, id: 2, method: tools/call, params: {{name: get_weather, arguments: {{city: {city_encoded}}}}}}}) sys.stdout.flush() if __name__ __main__: main()然后管道串起来跑cd D:\Mcp\test python test_weather.py | python weather-stdio.py预期能看到服务端返回的 JSON-RPC 响应里面result字段包含「广东的天气温度 27°C晴……」。如果这里就失败问题在服务端跟 Cursor 无关先修脚本。4.2 进 Cursor 后确认工具注册把mcp.json配好后重启 Cursor在 MCP 面板里应该能看到weather-stdio这个 server展开能看到get_weather工具。如果 server 显示红色或工具列表为空先看 Cursor 的 MCP 日志通常是command路径不对或 Python 环境不对。4.3 触发一次带参数的调用在 Cursor 对话里直接说「用 get_weather 查一下广东的天气」。观察两处一是 Cursor 弹出的工具调用卡片里arguments是否带上了city二是服务端 stderr 里是否打印了「接收到请求获取广东的天气」。两处都对上说明参数链路完整。5. 本篇常见错排查报错一Unexpected token或 JSON 解析失败。服务端往 stdout 打了非 JSON 内容。检查所有print是否都加了filesys.stderr第三方库的日志也要重定向。报错二工具列表为空。command找不到。Windows 上建议写python的全路径或者确认python在系统 PATH 里。用where python查一下。报错三参数永远是默认值。三种可能客户端没传arguments服务端函数签名参数名和调用时不一致参数被 URL 编码后没解码。对照get_weather(city: str)和调用里的city是否完全一致。报错四中文乱码。三处都要设 UTF-8脚本里的reconfigure、mcp.json的env.PYTHONIOENCODING、以及终端本身的编码。缺一处就可能出现「广东」变问号。报错五改了配置没生效。Cursor 需要重启才重新加载 MCP 配置。改完mcp.json记得完全退出再打开。报错六env 里的 Key 读不到。服务端要用os.environ.get(TAOTOKEN_API_KEY)主动读env不会自动变成变量。读不到时先打印os.environ确认注入成功。6. 把这条链路固定成你的调试习惯我现在调 MCP 服务的固定顺序是先管道手动测通服务端再进 Cursor 测工具注册最后测带参数调用。三步里任何一步失败问题范围立刻缩小到一层不用在 Cursor 和服务端之间来回猜。参数验证有个小技巧在服务端把收到的原始参数和解析后的参数都打到 stderr比如print(fraw{city} decoded{city_decoded}, filesys.stderr)。这样一眼就能看出是编码问题还是传参问题。模型通道这边Key 和地址统一走 TaoToken接入文档在https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite需要新建或轮换 Key 时去 API Keys 页面https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewrite把本地 Stdio 服务和统一模型通道都配好之后Cursor 里的 Agent 才真正具备「能想也能做」的完整能力。下一步可以试着把更多本地脚本按同样的骨架接进来参数命名保持一致维护成本会低很多。