weather-learning-server
Weather MCP 学習
段階的に学べるプロジェクトで、プレーンな LLM アプリケーションから Model Context Protocol (MCP) を通じて天気機能を公開するまでの道筋を示します。
学習の段階
プレーンな LLM アプリケーション — Ollama 経由でローカルのオープンソースモデルとチャット
従来の天気 API アプリ — Open-Meteo クライアント(ステージ 2A)+ 直接 LLM オーケストレーション(ステージ 2B)
Weather MCP サーバー — 天気を MCP ツールとして stdio 経由で公開(ステージ 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/activateWindows の場合:
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 は 2 段階で Open-Meteo と通信します(LLM も MCP もなし):
ジオコーディング —
GET https://geocoding-api.open-meteo.com/v1/searchで都市名(オプションで州/地域と国)を緯度、経度、正式名称、行政地域、国、タイムゾーンに変換します。予報 —
GET https://api.open-meteo.com/v1/forecastでその座標を使用して 現在の 天気(気温、湿度、風、WMO 天気コード)を取得します。
呼び出し元は、生のプロバイダ JSON ではなく、型付けされたモデル(Location、CurrentWeather、WeatherResult)を受け取ります。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
このプロジェクト環境で確認:
項目 | 値 |
パッケージ | 公式 |
インストール版 | 2.0.0 |
サーバークラス |
|
未使用 | サードパーティの |
from mcp.server import MCPServer
mcp = MCPServer("weather-learning-server")サーバーの責務
MCP クライアントにツールを通知する(ツール発見)
get_current_weatherツール呼び出しを受け付けるapp.weather_clientに委譲する(Open-Meteo コードの重複なし)構造化された天気ペイロード(または安全なツールエラー)を返す
このローカル学習 POC では stdio 経由で MCP を話す
公開ツール契約: 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-serverstdio を使用する場合、プロセスは stdin/stdout で MCP ホストを待機します。単独でターミナルで実行すると「ハング」しているように見えますが、それは想定内です。
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 ヘルパー)
venv がアクティブなプロジェクトルートから(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 に docstring/説明とスキーマからの入力フィールド(
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 チェック(ブラウザ不要)
これらは、ターミナルから同じプロトコル手順を証明するのに便利です。venv がアクティブで、動作する Node 22.19+ の npx が PATH にあるプロジェクトルートから実行:
# 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 という名前のツールが 1 つ、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 クライアントです。ローカルの天気 MCP サーバーを stdio 経由で起動し、ツールを発見し、明示的に 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 answerif "weather" in question も、都市の正規表現も、ハードコードされた get_current_weather 呼び出しも ありません。ツールを使用するかどうかは モデル が選択します。
エージェントループ(詳細)
MCP セッション開始 —
python -m app.mcp_serverを stdio 経由で起動; クライアントを初期化ツール発見 —
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/tool_callforget_current_weatherが表示され、天気の回答が得られる依存性注入に関する質問 → ログにツール呼び出し なし の最終応答が表示される
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,107 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-