1. 从零跑通和风天气 MCP:为什么值得折腾
和风天气 API 本身不复杂,一个 GET 请求就能拿到实时天气。但当你把它接进 Claude Code、Cline 或者自己写的 Agent 时,问题就来了:AI 工具不认识你的 Python 函数,它只认 MCP 协议暴露出来的工具。FastMCP 就是干这个的——把普通函数包装成 MCP 工具,让模型能像调用内置能力一样调用你的天气查询。
这篇要解决的核心检索词是「和风天气 MCP 服务 FastMCP 封装」。简单说,就是让你本地跑一个 MCP Server,AI 客户端连上之后,直接问「北京现在天气怎么样」就能拿到结构化结果。适合谁?适合正在做本地开发、个人项目、想给 AI 助手加实时数据能力的同学。不需要你懂 MCP 协议细节,FastMCP 把装饰器一贴,工具就注册好了。
我试过的场景是这样的:在 Claude Code 里配置好 MCP Server 地址,然后让它查天气,它会自动调用get_weather工具,返回城市、天气现象、温度、湿度。整个过程你只负责写一个几十行的 Python 文件,剩下的交给 FastMCP 和 MCP 客户端。
这里有个关键点容易被忽略:和风天气的 API 需要 Key,而 Key 的管理如果散落在各个项目里,后期维护很痛苦。所以我会把鉴权通道统一走 TaoToken,这样 Base URL、Key、Model ID 三件套集中管理,换项目时不用到处改配置。下面从环境准备开始,一步步把服务跑起来。
2. TaoToken 前置准备:统一 Key 与 API 通道
在写代码之前,先把鉴权通道理清楚。和风天气的 Key 你可以直接在官网申请,但如果你同时还在用其他模型 API,建议把通道统一到 TaoToken 管理。它的作用是给你一个统一的 API 入口,Key 和 Base URL 集中配置,MCP 服务里读环境变量就行,不用硬编码。
你需要准备三样东西:Base URL、API Key、Model ID。这三件套在 TaoToken 控制台都能拿到。具体路径是:登录后进控制台,在 API Keys 页面创建 Key,然后复制 Base URL。Model ID 根据你实际调用的模型填,比如做天气查询这种工具调用场景,选一个支持 function calling 的模型即可。
配置方式我推荐用环境变量,避免把 Key 写进代码提交到仓库。你可以在项目根目录建一个.env文件:
TAOTOKEN_BASE_URL=https://taotoken.net/api TAOTOKEN_API_KEY=你的Key TAOTOKEN_MODEL_ID=你的模型ID HEFENG_API_KEY=你的和风天气Key HEFENG_HOST=你的和风天气API Host注意和风天气的 Host 不是固定的api.heweather.net,新版控制台会给你分配专属 Host,这个必须从控制台复制,用错了会返回 401 或者 403。我踩过的坑就是直接用了文档里的示例域名,结果一直报鉴权失败,换成控制台里的专属 Host 才通。
如果你还没创建 Key,可以先去控制台把 Key 建好,顺手把接入文档看一遍,里面写了不同语言的调用示例。对于 MCP 服务来说,你只需要保证 Python 进程能读到这几个环境变量就行。用python-dotenv加载.env是最省事的做法:
from dotenv import load_dotenv import os load_dotenv() HEFENG_API_KEY = os.getenv("HEFENG_API_KEY") HEFENG_HOST = os.getenv("HEFENG_HOST")这样代码里不出现明文 Key,换环境时只改.env。TaoToken 的通道管理也是同理,Base URL 和 Key 都从环境变量读,MCP 服务本身不关心你用的是哪个模型,它只负责把工具暴露出去。
3. 可复制配置:FastMCP 服务端与工具函数注册
现在进入核心部分。先装依赖:
pip install fastmcp requests python-dotenv然后建一个weather_mcp.py,完整代码如下。这段代码可以直接复制运行,只需要改.env里的值。
import csv import os import requests from fastmcp import FastMCP from dotenv import load_dotenv load_dotenv() HEFENG_API_KEY = os.getenv("HEFENG_API_KEY") HEFENG_HOST = os.getenv("HEFENG_HOST") weather = FastMCP("search_weather") CITY_CSV_PATH = "China-City-List-latest.csv" def load_city_map(): city_map = {} with open(CITY_CSV_PATH, "rt", encoding="utf-8") as f: reader = csv.reader(f) for row in reader: if len(row) >= 2: city_map[row[1].strip()] = row[0].strip() return city_map CITY_MAP = load_city_map() @weather.tool() def get_weather(city: str) -> str: """查询指定城市的实时天气。city 传中文城市名,例如 北京、上海。""" city = city.strip() location_id = CITY_MAP.get(city) if not location_id: return f"城市未找到:{city}" url = f"https://{HEFENG_HOST}/v7/weather/now" params = { "location": location_id, "key": HEFENG_API_KEY, } try: resp = requests.get(url, params=params, timeout=10) data = resp.json() now = data["now"] return ( f"{city} 天气:{now['text']} " f"温度:{now['temp']}℃ " f"湿度:{now['humidity']}%" ) except Exception as e: return f"获取失败:{e}" if __name__ == "__main__": weather.run(transport="sse", host="0.0.0.0", port=9080)几个关键点说明。第一,城市 ID 映射我改成了启动时加载一次,而不是每次查询都读 CSV,这样并发请求时不会反复开文件。第二,@weather.tool()装饰器把get_weather注册成 MCP 工具,函数签名和 docstring 会被 MCP 客户端读取,所以 docstring 要写清楚参数含义,模型靠这个决定怎么传参。第三,传输方式用 SSE,端口 9080,这样 MCP 客户端可以通过 HTTP 连过来。
城市列表 CSV 从 GitHub 上的 LocationList 仓库下载,放到项目根目录,文件名保持China-City-List-latest.csv。这个文件里第一列是 Location ID,第二列是城市名,我的load_city_map就是按这个结构解析的。
如果你用的是 Claude Code,MCP 配置写在settings.json里,路径和原文一致:
{ "mcpServers": { "weather": { "url": "http://127.0.0.1:9080/sse" } } }Cline 的 MCP 配置类似,在 MCP Servers 面板里填 URL 就行。Codex 的话,auth.json里配好 Base URL 和 Key,MCP 部分单独在配置文件里指向 SSE 地址。三件套(Base URL、Key、Model ID)在 TaoToken 控制台拿,MCP 服务本身不直接调模型,所以 Model ID 是给客户端用的。
4. 验证请求:从启动服务到拿到天气结果
服务写好了,先本地启动:
python weather_mcp.py看到类似Uvicorn running on http://0.0.0.0:9080的输出就说明 SSE 服务起来了。这时候别急着连客户端,先用 curl 验证一下 MCP 端点是否可达:
curl -N http://127.0.0.1:9080/sse正常会返回一串 SSE 事件流,包含event: endpoint之类的信息。如果连接被拒绝,检查端口是否被占用,或者防火墙是否拦了 9080。
接下来在 MCP 客户端里测试。以 Claude Code 为例,配置好settings.json后重启,然后输入「查一下北京现在的天气」。客户端会调用get_weather工具,传参{"city": "北京"},服务端返回类似:
北京 天气:晴 温度:26℃ 湿度:45%如果返回「城市未找到」,说明 CSV 里没有匹配到该城市名。你可以打开 CSV 确认一下,有些城市在列表里是「北京市」而不是「北京」,这时候要么改传参,要么在load_city_map里做一次别名映射。
验证模型通道是否正常,可以单独发一个请求到 TaoToken 的模型对话接口,确认 Base URL 和 Key 没问题。这一步和 MCP 服务是独立的,但建议做一次,避免后面排查时分不清是 MCP 的问题还是鉴权的问题。
实测下来,整个链路跑通后,查询延迟在 300ms 到 800ms 之间,主要取决于和风天气 API 的响应速度。SSE 长连接保持住之后,后续调用不需要重新建连,体验比较顺。
5. 常见报错排查:401、local proxy failed 与 choices 读取失败
这一节列几个真实遇到过的报错和排查思路。
401 Unauthorized:最常见。先确认HEFENG_API_KEY是否正确,然后确认HEFENG_HOST是不是控制台里的专属 Host。很多人直接用api.heweather.net,这个在新版控制台里已经不对了。另外检查 Key 是否过期,和风天气的 Key 有有效期,过期后需要重新生成。
local proxy failed:这个通常出现在 MCP 客户端连接 SSE 端点时。原因可能是服务没启动,或者端口不对。先curl一下http://127.0.0.1:9080/sse,如果本地能通但客户端报这个错,检查客户端配置里的 URL 是不是写成了localhost而服务绑定的是0.0.0.0,有些环境里localhost解析有问题,换成127.0.0.1试试。
reading choices 失败:这个报错一般出现在模型调用环节,不是 MCP 服务本身。说明客户端在解析模型返回的choices字段时出错。排查方向是检查 TaoToken 的 Base URL 是否带上了正确的路径,以及 Model ID 是否填对。如果 Base URL 少了/v1或者多了斜杠,都可能导致返回结构异常。
OAuth 相关报错:如果你在客户端里配了 OAuth 鉴权,但 MCP 服务本身没做 OAuth,会报这个。MCP 的 SSE 端点默认不需要 OAuth,把客户端里的 OAuth 配置去掉,直接用 URL 连接即可。
城市 ID 匹配失败:CSV 文件路径不对,或者编码不是 UTF-8。确认文件在项目根目录,且用encoding="utf-8"打开。如果 CSV 里有 BOM 头,第一列会带\ufeff,可以在读取时用utf-8-sig编码。
排查顺序建议:先确认服务启动 → curl 验证 SSE → 客户端连上 → 调用工具 → 检查返回。每一步单独验证,不要跳步。
6. 把天气 MCP 接进你的工作流
服务跑通之后,你可以把它常驻在本地,作为 AI 助手的实时数据源。启动命令可以写成 systemd 服务或者用nohup挂后台:
nohup python weather_mcp.py > weather_mcp.log 2>&1 &这样关掉终端也不会断。日志里能看到每次工具调用的记录,方便排查。
如果你想让 MCP 服务更稳,可以在get_weather里加一层缓存,比如用functools.lru_cache或者简单的字典缓存,避免短时间内重复请求和风天气 API。和风天气的免费额度有限,缓存能省不少调用量。
另外,TaoToken 的 Coding Plan 适合长期跑 Agent 的场景,如果你打算把天气查询和其他工具串起来做自动化,可以考虑把通道统一到 Coding Plan 管理。模型对话接口可以用来单独测试模型是否正常响应,接入文档里有各语言的示例,API Keys 页面管理你的 Key。
最后一步,把.env加入.gitignore,别把 Key 提交上去。服务端代码可以开源,配置自己留着。这样一套下来,你就有个能随时问天气的 MCP 服务了。