weather-learning-server
Weather MCP 学习项目
一个渐进式学习项目,展示从普通 LLM 应用到通过模型上下文协议 (MCP) 暴露天气能力的完整路径。
学习进度
普通 LLM 应用 — 通过 Ollama 与本地开源模型聊天
传统天气 API 应用 — Open-Meteo 客户端(阶段 2A)+ 直接 LLM 编排(阶段 2B)
Weather MCP 服务器 — 通过 stdio 将天气暴露为 MCP 工具(阶段 3)
MCP 客户端/代理 — 显式工具客户端(阶段 4A)+ 模型选择工具(阶段 4B)
本仓库当前实现了 阶段 1 至 4B。
Related MCP server: MCP Weather Data Fetcher
环境要求
Python 3.12 或更高版本
Ollama(或任何兼容 OpenAI 的本地服务器)
一个支持工具调用的本地开源模型(默认:
qwen2.5:7b)
不需要 OpenAI 或 Gemini 账号。
设置
1. 安装并启动 Ollama
从 https://ollama.com 安装,然后拉取一个模型:
ollama pull qwen2.5:7b或者使用你已有的、支持工具调用的任何模型(ollama list 查看),然后在 .env 中将 LLM_MODEL 设置为该名称。
确认 Ollama 正在运行(macOS 安装后通常自动启动):
ollama list2. 创建虚拟环境
python3 -m venv .venv
source .venv/bin/activate在 Windows 上:
python -m venv .venv
.venv\Scripts\activate3. 安装依赖
pip install -e ".[dev]"4. 配置环境变量
cp .env.example .env.env 中的默认值指向本地 Ollama:
LLM_BASE_URL=http://localhost:11434/v1
LLM_API_KEY=ollama
LLM_MODEL=qwen2.5:7bLLM_BASE_URL— 兼容 OpenAI 的 API URL(上面显示的是 Ollama 的默认值;用于 Chat Completions 和 Responses)LLM_API_KEY— 客户端库需要;Ollama 会忽略它(任何非空值均可)LLM_MODEL— 来自ollama list的本地模型名称(阶段 4B 的工具调用功能与qwen2.5:7b配合良好)
其他选项:LM Studio、vLLM 或任何支持 OpenAI 聊天 API 的服务器——只需更改 LLM_BASE_URL 和 LLM_MODEL。
阶段 2A:Open-Meteo 天气客户端
app/weather_client.py 分两步与 Open-Meteo 通信(无 LLM,无 MCP):
地理编码 —
GET https://geocoding-api.open-meteo.com/v1/search将城市名称(以及可选的州/地区和国家)转换为纬度、经度、规范名称、行政区域、国家和时区。预报 —
GET https://api.open-meteo.com/v1/forecast使用这些坐标获取当前天气(温度、湿度、风速、WMO 天气代码)。
调用方收到的是类型化模型(Location、CurrentWeather、WeatherResult),而不是原始提供商 JSON。WMO 天气代码到文本的转换集中在一处(WMO_WEATHER_CODES / weather_condition_from_code)。
示例(异步):
from app.weather_client import get_current_weather
result = await get_current_weather("Berlin")
print(result.location.name, result.current.temperature, result.current.condition)阶段 2B:直接天气 + LLM 应用
app/direct_weather_app.py 是一个传统的 LLM 应用:你的代码决定何时调用天气 API,然后将结果传递给 LLM 以生成友好摘要。
User
→ direct_weather_app
→ Open-Meteo (application-controlled)
→ LLM (summarize only the supplied payload)
→ Response如何运行
在虚拟环境激活、Ollama 运行且网络可访问 Open-Meteo 的情况下:
python -m app.direct_weather_app "San Francisco"可选的消歧义参数:
python -m app.direct_weather_app "Springfield" --state Illinois --country US或使用控制台脚本:
direct-weather "San Francisco"在 stderr 上你会看到编排步骤:
应用接收到城市名
应用调用天气提供者
应用接收到结构化天气数据
应用将天气上下文发送给 LLM
Stdout 显示结构化天气块,然后是 LLM 摘要。
与普通 LLM 应用的区别
阶段 1 | 阶段 2B | |
天气数据 | 无 — 模型没有实时天气 | 首先从 Open-Meteo 获取 |
谁调用天气? | 无人 | 应用代码(显式) |
LLM 角色 | 回答自由形式提示 | 总结权威数据 |
MCP / 工具 | 无 | 无 |
重要学习点:LLM 不会发现或调用天气工具。应用编排 Open-Meteo,然后要求 LLM 组织结果。提示告诉模型数据是权威的,不要编造缺失的事实。
阶段 3:Weather MCP 服务器
app/mcp_server.py 将现有的 weather_client 暴露为 MCP 工具。该服务器仅提供能力——它不与 LLM 对话或管理对话。
官方 SDK 版本及使用的 API
在本项目环境中检查:
项目 | 值 |
包名 | PyPI 上的官方 |
安装版本 | 2.0.0 |
服务器类 | 来自 |
未使用 | 第三方 |
from mcp.server import MCPServer
mcp = MCPServer("weather-learning-server")服务器职责
向 MCP 客户端通告工具(工具发现)
接受
get_current_weather工具调用委托给
app.weather_client(不重复 Open-Meteo 代码)返回结构化天气数据(或安全的工具错误)
通过 stdio 进行 MCP 通信(针对此本地学习 POC)
暴露的工具契约:get_current_weather
参数
名称 | 类型 | 必需 | 描述 |
| string | 是 | 城市或地点名称 |
| string | 否 | 用于消歧义的州/行政区域 |
| string | 否 | 国家名称或 ISO-3166-1 alpha-2 代码 |
结构化结果字段
resolved_location、region、country、latitude、longitude、temperature、apparent_temperature(如可用)、condition、wind_speed、observation_time、timezone、units
如何启动服务器
python -m app.mcp_server或者:
weather-mcp-server对于 stdio,进程会等待 MCP 主机在 stdin/stdout 上。单独在终端中运行它会看起来“卡住”——这是预期的。
stdio 传输的工作原理(概念上)
MCP host / Inspector
├── spawns: python -m app.mcp_server
├── writes JSON-RPC MCP messages → server stdin
└── reads JSON-RPC MCP messages ← server stdout此 POC 不使用端口和 HTTP
stdout 是协议线路(不要在那里
print()普通应用输出)日志应放在 stderr 上
使用官方 MCP Inspector 独立测试
已验证:
官方
mcp2.0.0(MCPServer)官方 Inspector 包
@modelcontextprotocol/inspectorNode.js 22.19+(当前 Inspector 文档需要)
可访问 Open-Meteo 的网络
前提条件
cd weather-mcp-learning
python3 -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]" # includes mcp[cli]确认 Node/npx:
node --version # need 22.19.0 or newer
npx --version如果你的系统 node/npx 损坏或太旧,请使用 nvm(或等效工具)安装当前 Node,然后确保 npx 在 PATH 的最前面。
选项 A — 通过 mcp dev 的 Web UI(官方 SDK 辅助工具)
从项目根目录,在虚拟环境激活状态下(还需要 uv,因为 mcp dev 通过 uv run 启动服务器):
mcp dev app/mcp_server.py --with-editable .预期:
终端输出类似
MCP Inspector Web is up and running at: http://localhost:6274?MCP_INSPECTOR_API_TOKEN=...浏览器打开 Inspector
Inspector 启动/连接到本地 stdio 服务器(
weather-learning-server)会话初始化(显示服务器名称/说明)
打开 Tools → 列表显示
get_current_weather选择工具 → UI 显示来自模式的文档字符串/描述和输入字段(
city必需;state_or_region/country可选)设置
city=San Francisco→ Run Tool结果面板显示结构化内容,如
resolved_location、region、temperature、condition、units等
--with-editable . 将此项目安装到 mcp dev 构建的临时环境中,以便 import app... 正常工作。
选项 B — 通过 Inspector + 项目配置的 Web UI
仓库根目录中的 mcp-inspector.json 将 Inspector 指向本地 stdio 服务器:
npx -y @modelcontextprotocol/inspector --config ./mcp-inspector.json --server weather-learning-server打开打印的 http://localhost:6274?... URL,确认会话已连接,然后像选项 A 一样使用 Tools 选项卡。
选项 C — 可脚本化的 CLI 检查(无浏览器)
这些用于从终端证明相同的协议步骤。从项目根目录运行,虚拟环境激活,且 PATH 上有可用的 Node 22.19+ npx:
# 1–2. Start/connect over stdio + initialize session
npx -y @modelcontextprotocol/inspector --cli \
--config ./mcp-inspector.json \
--server weather-learning-server \
--method initialize \
--format json预期的 JSON 包含 "name": "weather-learning-server" 在 result.serverInfo 下。
# 3–4. List tools; confirm description + input schema
npx -y @modelcontextprotocol/inspector --cli \
--config ./mcp-inspector.json \
--server weather-learning-server \
--method tools/list \
--format json预期:一个名为 get_current_weather 的工具,其 inputSchema.required 包含 city,以及一个实时/当前天气描述。
# 5–6. Invoke with city = San Francisco; display structured result
npx -y @modelcontextprotocol/inspector --cli \
--config ./mcp-inspector.json \
--server weather-learning-server \
--method tools/call \
--tool-name get_current_weather \
--tool-arg 'city=San Francisco' \
--format json预期:"isError": false 和包含以下字段的 structuredContent:
{
"resolved_location": "San Francisco",
"region": "California",
"country": "United States",
"latitude": 37.77493,
"longitude": -122.41942,
"temperature": 13.8,
"apparent_temperature": 12.1,
"condition": "Fog",
"wind_speed": 19.1,
"observation_time": "2026-08-12T22:45",
"timezone": "America/Los_Angeles",
"units": {
"temperature": "°C",
"wind_speed": "km/h",
"apparent_temperature": "°C"
}
}数值天气值随时间变化;字段名和 "isError": false 才是关键。
官方 Inspector 文档:MCP Inspector · SDK 运行文档:Running your server
阶段 4A:基础 MCP 客户端(显式工具调用)
app/basic_mcp_client.py 是一个非 LLM 的 MCP 客户端。它通过 stdio 启动本地天气 MCP 服务器,发现工具,然后显式地调用 get_current_weather。
basic_mcp_client
→ list_tools
→ get_current_weather (hardcoded by this app — not chosen by an LLM)
→ MCP server (app.mcp_server via stdio)
→ Open-Meteo重要:此客户端仍然显式地调用天气工具。LLM 尚未选择工具。这将在后续阶段实现。
如何运行
在虚拟环境激活状态下(无需自己启动 MCP 服务器——此客户端会生成它):
python -m app.basic_mcp_client "San Francisco"可选过滤器:
python -m app.basic_mcp_client "Springfield" --state Illinois --country US或者:
basic-mcp-client "San Francisco"你应该看到:
weather-learning-server的连接/协议信息每个已发现工具的名称、描述和输入模式
对
get_current_weather的显式调用结构化的 MCP 工具结果 JSON
退出进程会清理 MCP 会话和子服务器进程。
阶段 4B:OpenAI Responses 代理(模型选择的 MCP 工具)
app/mcp_agent.py 连接到天气 MCP 服务器,在运行时发现工具,通过官方 OpenAI Responses API 将这些工具定义提供给模型,执行模型请求的任何工具调用(通过 MCP),将工具结果返回给模型,并打印最终答案。
user question
→ mcp_agent
→ MCP list_tools (discovery)
→ OpenAI Responses API (question + tool schemas)
→ model may request tool(s)
→ MCP tools/call (only discovered names)
→ Responses function_call_output
→ final natural-language answer没有 if "weather" in question,没有城市正则表达式,也没有硬编码的 get_current_weather 调用。模型选择是否使用工具。
代理循环(详细)
启动 MCP 会话 — 通过 stdio 生成
python -m app.mcp_server;初始化客户端工具发现 —
list_tools;记录每个工具的名称/描述模式转换 — MCP 工具 → Responses
type: "function"工具模型轮次 —
client.responses.create(..., tools=..., tool_choice="auto")检查输出 — 如果存在
function_call项:验证工具名称是否在已发现集合中
解析/验证 JSON 参数
调用 MCP;保留结构化结果
使用
previous_response_id提交function_call_output
重复直到模型返回最终文本消息(或达到最大迭代次数)
打印最终答案并关闭 MCP 会话/子进程
如何运行
ollama pull qwen2.5:7b # once, if needed
source .venv/bin/activate
python -m app.mcp_agent "What is the current weather in San Francisco?"
python -m app.mcp_agent "Explain what dependency injection is."预期:
天气问题 → 日志显示
model_requested_tools/get_current_weather的tool_call,然后是天气答案依赖注入问题 → 日志显示最终响应,没有工具调用
观察 stderr 上的 [mcp-agent] 行:发现、模型输出类型、工具名称/参数/持续时间/结果。API 密钥从不记录。
运行普通应用
在虚拟环境激活且 Ollama 运行的情况下:
python -m app.plain_llm_app或使用自定义提示:
python -m app.plain_llm_app "What is the Model Context Protocol in one sentence?"你也可以使用已安装的控制台脚本:
plain-llm "Hello!"运行测试
pytest项目布局
weather-mcp-learning/
README.md
.env.example
.gitignore
pyproject.toml
mcp-inspector.json
app/
__init__.py
config.py
llm_client.py
plain_llm_app.py
weather_client.py
direct_weather_app.py
mcp_server.py
basic_mcp_client.py
mcp_agent.py
tests/备注
官方
openaiPython 包被用作兼容 OpenAI 的客户端(早期使用 Chat Completions;在 Stage 4B 中使用 Responses API)。请求会发送到你配置的LLM_BASE_URL(默认为 Ollama)。天气查询通过
httpx使用 Open-Meteo(app/weather_client.py)。Stage 2B(
direct_weather_app.py)显式编排天气 → LLM 的流程;不涉及 MCP 和工具调用。Stage 3 使用官方
mcp2.0.0 SDK(mcp.server中的MCPServer)通过 stdio 运行。请勿使用第三方fastmcp包。Stage 4A(
basic_mcp_client.py)仍然显式调用天气工具(不涉及 LLM 工具选择)。Stage 4B(
mcp_agent.py)在通过 Responses API 完成 MCP 发现后,让模型自行选择工具。
Available Tools
1 toolget_current_weatherGet current weatherA
Get live/current weather for a city.
This tool returns real-time current conditions from the Open-Meteo weather provider (not a forecast summary and not model-invented weather). Provide a city name; optionally add state_or_region and/or country when the city name is ambiguous. The response includes the resolved location, coordinates, temperature, condition, wind, observation time, timezone, and units.
| Name | Required | Description | Default |
|---|---|---|---|
| city | Yes | City or place name to look up (required). | |
| country | No | Optional country name or ISO-3166-1 alpha-2 code (for example 'United States' or 'US'). | |
| state_or_region | No | Optional state or first-level administrative region used to disambiguate the city (for example 'California' or 'Illinois'). |
Output Schema
| Name | Required | Description |
|---|---|---|
| units | Yes | |
| region | No | Administrative region / state when available |
| country | Yes | Country name |
| latitude | Yes | |
| timezone | Yes | |
| condition | Yes | Human-readable weather condition |
| longitude | Yes | |
| wind_speed | No | |
| temperature | Yes | |
| observation_time | Yes | Observation timestamp from the provider |
| resolved_location | Yes | Canonical place name from geocoding |
| apparent_temperature | No | Apparent (feels-like) temperature when the provider returns it |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are provided, so the description carries the full burden. It discloses that data comes from Open-Meteo, that it's real-time (not forecast or invented), and lists the response fields. It does not mention rate limits or availability constraints, which would be helpful.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is very concise: two sentences. The first sentence states the core purpose, and the second adds key details in a natural flow. No wasted words.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the tool has an output schema, the description doesn't need to explain return values. It also covers the important semantic clarifications (real-time, provider, disambiguation hints). For a simple 3-param weather tool, this is complete.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so baseline is 3. The description restates that city is required and that state_or_region and country help disambiguate, but does not add new format or usage details beyond the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description clearly states 'Get live/current weather for a city' with specific verb and resource. It explicitly distinguishes itself from non-forecast and non-invented weather, and mentions the provider (Open-Meteo).
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explains when to use (for current real-time conditions) and notes when to add optional parameters for disambiguation. It doesn't explicitly state when NOT to use or list alternative tools, but context is clear enough.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
1 tool update
v0.1.0- First observed
get_current_weather
TDQS
Scored across 1 tool
With only one tool, there is no risk of confusion between tools. The tool's purpose is clearly defined and distinct by default.
The single tool uses a clear verb_noun pattern (get_current_weather), but with only one tool, consistency cannot be meaningfully evaluated across a set.
A server named 'weather-learning-server' with only one weather tool feels incomplete for learning purposes. A 'learning' server typically benefits from multiple tools (e.g., forecast, history, alerts) to cover educational use cases.
The server only provides current weather data, missing obvious complementary tools like forecasts, historical data, or weather alerts. This severely limits its usefulness for weather-related learning or applications.
Maintenance
Related MCP Connectors
Weather MCP — wraps Open-Meteo API (free, no auth)
OpenWeather MCP — wraps the OpenWeatherMap API (openweathermap.org)
Open-Meteo MCP — weather forecast + historical reanalysis + sister APIs
Weather, code search, currency & Solana trust scoring as MCP tools. Free, no API key needed.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceFetches current weather information for any city using the Open-Meteo API through a simple MCP tool interface.1,264 npmMIT
- FlicenseNot gradedqualityCmaintenanceEnables AI agents to retrieve live weather updates for any city via OpenWeatherMap, wrapped in MCP format.1-
- AlicenseAqualityDmaintenanceMCP server that provides current weather for any city using Open-Meteo APIs. It exposes a single tool 'get_weather' returning temperature, humidity, wind, and other weather data.19 npmMIT
- FlicenseAqualityCmaintenanceProvides weather information through MCP tools, including city search, current weather, forecasts, comparisons, and warmest day identification, using the Open-Meteo API.7-