Skip to main content
Glama
HumairaShaista

weather-learning-server

Weather MCP 学習

段階的に学べるプロジェクトで、プレーンな LLM アプリケーションから Model Context Protocol (MCP) を通じて天気機能を公開するまでの道筋を示します。

学習の段階

  1. プレーンな LLM アプリケーション — Ollama 経由でローカルのオープンソースモデルとチャット

  2. 従来の天気 API アプリ — Open-Meteo クライアント(ステージ 2A)+ 直接 LLM オーケストレーション(ステージ 2B)

  3. Weather MCP サーバー — 天気を MCP ツールとして stdio 経由で公開(ステージ 3)

  4. 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 list

2. 仮想環境の作成

python3 -m venv .venv
source .venv/bin/activate

Windows の場合:

python -m venv .venv
.venv\Scripts\activate

3. 依存関係のインストール

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:7b
  • LLM_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 もなし):

  1. ジオコーディング — GET https://geocoding-api.open-meteo.com/v1/search で都市名(オプションで州/地域と国)を緯度、経度、正式名称、行政地域、国、タイムゾーンに変換します。

  2. 予報 — 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 にオーケストレーションの手順が表示されます:

  1. アプリケーションが都市を受け取った

  2. アプリケーションが天気プロバイダを呼び出した

  3. アプリケーションが構造化された天気データを受け取った

  4. アプリケーションが天気コンテキストを LLM に送信した

stdout には構造化された天気ブロック、次に LLM の要約が表示されます。

プレーンな LLM アプリケーションとの違い

ステージ 1 plain_llm_app

ステージ 2B direct_weather_app

天気データ

なし — モデルはライブ天気を持たない

最初に Open-Meteo から取得

誰が天気を呼ぶか?

誰も呼ばない

アプリケーションコード(明示的)

LLM の役割

自由形式のプロンプトに答える

信頼できるペイロードを要約する

MCP / ツール

なし

なし

重要な学習ポイント: LLM は天気ツールを 発見したり呼び出したりしません。アプリケーションが Open-Meteo をオーケストレーションし、LLM に結果を言い換えるよう依頼します。プロンプトは、ペイロードが信頼できるものであり、不足している事実を捏造しないようにモデルに指示します。

ステージ 3: Weather MCP サーバー

app/mcp_server.py は既存の weather_client を MCP ツール として公開します。サーバーは機能のみを提供し、LLM と通信したり会話を管理したりしません。

使用している公式 SDK バージョンと API

このプロジェクト環境で確認:

項目

値

パッケージ

公式 mcp on PyPI (modelcontextprotocol/python-sdk)

インストール版

2.0.0

サーバークラス

mcp.server の MCPServer

未使用

サードパーティの fastmcp パッケージ; 古い v1 の FastMCP インポートパス

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

引数

名前

型

必須

説明

city

string

はい

都市または場所の名前

state_or_region

string

いいえ

曖昧性解消のための州/行政地域

country

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 を使用する場合、プロセスは 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 を使用した独立テスト

以下に対して検証済み:

  • 公式 mcp 2.0.0 (MCPServer)

  • 公式 Inspector パッケージ @modelcontextprotocol/inspector

  • Node.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 .

期待される動作:

  1. ターミナルに MCP Inspector Web is up and running at: http://localhost:6274?MCP_INSPECTOR_API_TOKEN=... のようなメッセージが表示される

  2. ブラウザで Inspector が開く

  3. Inspector がローカルの stdio サーバー(weather-learning-server)に接続する

  4. セッションが初期化される(サーバー名/説明が表示される)

  5. Tools を開く → リストに get_current_weather が表示される

  6. ツールを選択 → UI に docstring/説明とスキーマからの入力フィールド(city 必須; state_or_region / country オプション)が表示される

  7. city = San Francisco に設定 → Run Tool

  8. 結果ペインに 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"

次のものが表示されるはずです:

  1. weather-learning-server の接続/プロトコル情報

  2. 発見された各ツールの名前、説明、入力スキーマ

  3. get_current_weather への明示的な呼び出し

  4. 構造化された 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 呼び出しも ありません。ツールを使用するかどうかは モデル が選択します。

エージェントループ(詳細)

  1. MCP セッション開始 — python -m app.mcp_server を stdio 経由で起動; クライアントを初期化

  2. ツール発見 — list_tools; 各ツールの名前/説明をログ出力

  3. スキーマ変換 — MCP ツール → Responses の type: "function" ツール

  4. モデルターン — client.responses.create(..., tools=..., tool_choice="auto")

  5. 出力検査 — function_call アイテムが存在する場合:

    • ツール名を発見済みセットに対して検証

    • JSON 引数をパース/検証

    • MCP を呼び出し; 構造化結果を保持

    • previous_response_id を指定して function_call_output を送信

  6. 繰り返し — モデルが最終テキストメッセージを返すまで(または最大反復回数に達するまで)

  7. 最終回答を出力 し、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_call for get_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/

注意事項

  • 公式のopenai Pythonパッケージは、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では、公式のmcp 2.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 tool
get_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.

ParametersJSON Schema
NameRequiredDescriptionDefault
cityYesCity or place name to look up (required).
countryNoOptional country name or ISO-3166-1 alpha-2 code (for example 'United States' or 'US').
state_or_regionNoOptional state or first-level administrative region used to disambiguate the city (for example 'California' or 'Illinois').

Output Schema

ParametersJSON Schema
NameRequiredDescription
unitsYes
regionNoAdministrative region / state when available
countryYesCountry name
latitudeYes
timezoneYes
conditionYesHuman-readable weather condition
longitudeYes
wind_speedNo
temperatureYes
observation_timeYesObservation timestamp from the provider
resolved_locationYesCanonical place name from geocoding
apparent_temperatureNoApparent (feels-like) temperature when the provider returns it

TDQS

A4.3/5.0
Behavior4/5

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.

Conciseness5/5

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.

Completeness5/5

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.

Parameters3/5

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.

Purpose5/5

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.

Usage Guidelines4/5

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. 1 tool updatev0.1.0
    • First observedget_current_weather

TDQS

A3.9/5.0

Scored across 1 tool

Disambiguation5/5

With only one tool, there is no risk of confusion between tools. The tool's purpose is clearly defined and distinct by default.

Naming Consistency3/5

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.

Tool Count2/5

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.

Completeness2/5

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

ActivitySlowing
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers