1. 从 weather.py 到 Claude Code:MCP 落地的真实卡点
如果你已经看过 Claude Code 官方最佳实践里关于 MCP 的那一节,大概会有一种“道理都懂,但手就是动不起来”的感觉。官方文档告诉你 MCP 是模型上下文协议,能让 Claude Code 调用外部工具;也告诉你用 FastMCP 可以快速写一个服务端。但真正落到键盘上,问题就来了:weather.py 到底怎么写才不会被 Claude Code 拒收?settings.json 和 config.toml 里那些字段哪个是必须的?启动之后怎么确认工具真的被注册进去了?为什么控制台一片安静,日志去哪了?
这篇就是来解决这些“最后一公里”的问题。我会用一个最小可跑的 weather.py 作为例子,把 FastMCP 服务端骨架、Claude Code 的接入配置、TaoToken 统一 Key 通道的接法,以及验证工具调用链路的完整动作串起来。适合已经装好 Claude Code、想跑通第一个自定义 MCP 工具的人。读完你手里会有一个能查天气的 MCP 服务,并且知道每一步为什么这么配。
需要先说明一点:MCP 的 stdio 模式对输出极其敏感。服务端往 stdout 里多打一行字,Claude Code 那边就可能直接报 JSON 解析错误。这个坑我在后面会专门拆开讲,因为它几乎是新手必踩的第一个雷。
2. TaoToken 前置:把 Key 和 API 通道先理顺
在写 weather.py 之前,建议先把模型侧的通道配好。原因很简单:Claude Code 本身要通过一个 API 端点来调用模型,而 MCP 工具调用是挂在这条链路之上的。如果模型通道本身没通,你后面验证 MCP 的时候会分不清是工具没注册,还是模型根本没响应。
TaoToken 在这里的角色是提供一个统一的 Key 和 API 入口。你可以在官网 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= 了解它的定位,实际接入时用的是 API 地址 https://taotoken.net/api(这个不加 UTM)。拿到 Key 之后,Claude Code 的模型请求就走这条通道。
具体操作上,先去控制台创建一个 API Key。打开 https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_content=console&utm_campaign=rewrite ,在 API Keys 页面生成一个 Key,复制保存。这个 Key 后面会写进 Claude Code 的配置里。
如果你还没决定用哪种接入方式,可以先在模型对话页面 https://taotoken.net/chat?utm_source=taotoken_aicg_blog_end&utm_content=model_chat&utm_campaign=rewrite 试一下模型是否正常响应。确认通道没问题,再往下走 MCP 的部分,排障会轻松很多。
对于长期在 Claude Code 里做编码和 Agent 任务的场景,Coding Plan 会更合适,入口在 https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding_plan&utm_campaign=rewrite 。它的额度模型对高频调用更友好,MCP 工具反复触发也不会心疼。
3. 可复制配置:weather.py 骨架与 Claude Code 接入
3.1 写一个最小可用的 weather.py
先建目录,再建文件。假设你的项目目录叫 weather-mcp:
mkdir weather-mcp cd weather-mcp然后创建 weather.py。这里我用一个公开的天气接口做示例,你只需要替换成自己的 Key 即可。注意代码里没有任何 print 语句,这是刻意的。
import os import httpx from mcp.server.fastmcp import FastMCP # 替换为你自己的天气 API Key WEATHER_KEY = os.environ.get("WEATHER_KEY", "你的_API_KEY") mcp = FastMCP("WeatherServer") @mcp.tool() async def get_weather(city: str) -> str: """ 查询指定城市的实时天气。 Args: city: 城市名称,例如 宁波、北京 """ url = "https://restapi.amap.com/v3/weather/weatherInfo" params = { "key": WEATHER_KEY, "city": city, "extensions": "base", } async with httpx.AsyncClient() as client: resp = await client.get(url, params=params) data = resp.json() if data.get("status") == "1" and data.get("lives"): live = data["lives"][0] return ( f"城市:{live['province']}{live['city']}\n" f"天气:{live['weather']}\n" f"温度:{live['temperature']}°C\n" f"风向:{live['winddirection']}风 {live['windpower']}级\n" f"湿度:{live['humidity']}%\n" f"发布时间:{live['reporttime']}" ) return f"查询失败:{data.get('info', '请检查城市名或 Key')}" if __name__ == "__main__": mcp.run()依赖安装建议用虚拟环境,避免污染全局 Python:
python3 -m venv .venv source .venv/bin/activate pip install httpx "fastmcp[all]"装完之后,先单独跑一下服务端,确认它能启动:
python weather.py如果它安静地停在那里不报错,说明 stdio 服务端已经就绪。这时候按 Ctrl+C 退出,准备接入 Claude Code。
3.2 配置 Claude Code 的 MCP 接入
Claude Code 读取 MCP 配置的位置通常在项目根目录的.mcp.json,或者用户级的 settings 里。推荐用项目级.mcp.json,隔离性好,换项目不会互相干扰。
在项目根目录创建.mcp.json:
{ "mcpServers": { "weather": { "command": "/绝对路径/weather-mcp/.venv/bin/python", "args": [ "/绝对路径/weather-mcp/weather.py" ], "env": { "WEATHER_KEY": "你的_API_KEY" } } } }这里有两个关键点。第一,command 必须指向虚拟环境里的 python,而不是系统 python,否则依赖找不到。第二,路径必须是绝对路径,相对路径在 Claude Code 启动时的工作目录下容易解析失败。
如果你用的是 config.toml 形式的配置(部分版本支持),骨架类似:
[mcp_servers.weather] command = "/绝对路径/weather-mcp/.venv/bin/python" args = ["/绝对路径/weather-mcp/weather.py"] [mcp_servers.weather.env] WEATHER_KEY = "你的_API_KEY"配置写完后,重启 Claude Code,或者新开一个对话,让它重新加载 MCP 配置。
3.3 把模型通道指向 TaoToken
MCP 工具要能被调用,前提是 Claude Code 的模型请求是通的。在 Claude Code 的配置里,把 API 端点指向 TaoToken:
export ANTHROPIC_BASE_URL="https://taotoken.net/api" export ANTHROPIC_API_KEY="你的_TaoToken_Key"如果你用的是 settings.json 形式,可以写成:
{ "env": { "ANTHROPIC_BASE_URL": "https://taotoken.net/api", "ANTHROPIC_API_KEY": "你的_TaoToken_Key" } }这样模型请求走 TaoToken 通道,MCP 工具调用挂在同一条链路上,排障时只需要看一个方向。
4. 验证请求:确认工具真的被注册和调用
4.1 检查 MCP 是否加载成功
重启 Claude Code 后,先问它一句:
当前有哪些可用的 MCP 工具?如果配置正确,它应该会列出 weather 相关的工具,比如get_weather。如果没列出来,说明配置没被读取,回到上一步检查.mcp.json的路径和 JSON 格式。
4.2 触发一次真实调用
直接问:
宁波现在的天气怎么样?Claude Code 会识别到这是一个需要调用get_weather的请求,然后通过 MCP 协议把city="宁波"传给你的 weather.py。服务端请求天气接口,把结果返回给模型,模型再组织成自然语言回复你。
成功的话,你会看到类似这样的输出:
城市:浙江宁波 天气:晴 温度:28°C 风向:东南风 3级 湿度:65% 发布时间:2024-xx-xx xx:xx:xx这一步跑通,说明从 FastMCP 服务端到 Claude Code 工具调用的整条链路是通的。
4.3 看日志的正确姿势
很多人会问:为什么 VSCode 控制台没有打印日志?因为 MCP 的 stdio 模式要求服务端只能输出标准 JSON 通信数据。你在代码里加一个print("debug"),Claude Code 那边就会读到非 JSON 内容,直接报Invalid JSON: expected value at line 1 column 2。
正确的日志做法是写到文件,或者用 stderr:
import sys print("debug info", file=sys.stderr)stderr 不会被 MCP 协议解析,所以安全。stdout 留给协议本身。
5. 本篇常见错排查
5.1 Invalid JSON 报错
报错长这样:
Invalid JSON: expected value at line 1 column 2 input_value='source ...'原因通常是启动命令里混入了 shell 输出。比如你在.mcp.json的 command 里写了source .venv/bin/activate && python weather.py,这个source命令的输出会被 MCP 当成协议数据读进去,直接崩。
解决方法是不要在 command 里做激活动作,直接把 command 指向虚拟环境的 python 绝对路径:
"command": "/绝对路径/weather-mcp/.venv/bin/python"5.2 pip 安装报 externally-managed-environment
在 macOS 上用 Homebrew 装的 Python 3.11+,直接pip install会报 PEP 668 保护错误。不要用--break-system-packages硬装,会污染全局环境。
正确做法是建虚拟环境:
python3 -m venv .venv source .venv/bin/activate pip install httpx "fastmcp[all]"每次新开终端都要重新source .venv/bin/activate。VSCode 里按Cmd + Shift + P,选Python: Select Interpreter,挑带.venv的那个,代码红线就会消失。
5.3 工具列不出来
如果 Claude Code 说没有可用工具,按顺序检查:.mcp.json是不是在项目根目录;JSON 格式有没有多余逗号;command 路径是不是绝对路径且文件存在;python 能不能手动跑起来python weather.py。这四步过一遍,基本能定位。
5.4 调用返回查询失败
如果工具被调用了但返回“查询失败”,多半是 API Key 没传进去。检查.mcp.json的env字段有没有写对,或者代码里os.environ.get("WEATHER_KEY")的变量名和配置里是否一致。也可以先在终端里export WEATHER_KEY=xxx再手动跑一次服务端验证。
6. 继续往下走
MCP 跑通之后,你可以把 weather.py 当成模板,复制出更多工具:查快递、读数据库、调内部接口。FastMCP 的装饰器模式让新增工具的成本很低,只要注意 stdout 干净、依赖装在虚拟环境里、路径用绝对路径,基本不会翻车。
如果你在接入过程中遇到 Key 或通道相关的问题,可以直接去 API Keys 页面 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api_keys&utm_campaign=rewrite 检查 Key 状态,接入文档在 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite 有更细的字段说明。Claude Code 相关的配置细节,可以参考 ClaudeCodeAnthropic 页面 https://taotoken.net/claude-code-anthropic?utm_source=taotoken_aicg_blog_end&utm_content=claudecode&utm_campaign=rewrite 。
最后留一个实用习惯:每次改完.mcp.json,先手动跑一遍python weather.py,确认服务端能安静启动,再重启 Claude Code。这一步能帮你过滤掉八成配置问题。