Skip to main content
Glama
TSchwierz

weather-packing-bot

by TSchwierz

weather-packing-bot

スケジュールに従って実行されるボットで、繰り返し行われる3都市間の移動ルートについて、複数日分の天気予報とパッキングの推奨事項を含むSlackメッセージを投稿します。また、MCP(Model Context Protocol)サーバーとクライアントのペアの小さなリファレンス実装でもあります。予報データは、ボットスクリプトが直接取得するのではなく、スタンドアロンのMCPサーバーによって提供され、MCPクライアントによって消費されます。

概要

このボットは毎週のスケジュール(金曜日 15:00 CEST)で実行され、当日から翌週の月曜日の夕方までの天気をカバーするSlackメッセージを投稿します。予報とパッキングの推奨事項は旅程を考慮します。つまり、その期間の各日に旅行者が3都市のうちどの都市にいることが予想されるかを考慮し、すべての場所の天気を無差別に表示するのではありません。

GitHub Actions (scheduled, Fridays)
        │
        ▼
bot/packing_advisor.py            MCP client
        │  spawns as subprocess, communicates over stdio
        ▼
mcp_server/weather_server.py      MCP server, queries Open-Meteo
        │
        ▼
   Slack incoming webhook

Related MCP server: Weather Prediction MCP Server

MCPサーバーとクライアントを使用する理由

サーバーは、誰がどのような目的で呼び出すかを知ることなく天気ツールを公開します。呼び出し元は、このボット、IDEアシスタント、その他のMCP対応クライアントのいずれでもかまいません。クライアントはサーバーのPython関数を直接インポートしません。実行時に利用可能なツールを検出し(session.list_tools())、プロトコルを介して名前でツールを呼び出します(session.call_tool(...))。この分離こそが、同じプロセス内で関数を単に呼び出すのとは異なる設計上の特徴です。

コンポーネント

mcp_server/weather_server.py

Open-Meteo APIをラップし、4つのツールを公開するMCPサーバー(mcpパッケージのMCPServerクラスを使用):

ツール

説明

list_cities()

設定された都市名を返します。

get_forecast(city, days=3)

1つの都市の日次天気サマリー。

get_forecast_all(days=3)

設定されたすべての都市の日次天気サマリー。

render_temperature_chart(days=4)

Slackのコードブロック用にフォーマットされた、都市別・日別のコンパクトなテキストチャート(気温バー、天気アイコン、降水量)。

都市はCOORDINATESディクショナリで定義されており、自由に拡張できます。

bot/packing_advisor.py

以下のことを行うMCPクライアント:

  1. forecast_window() を使用して予報期間(当日から翌週の月曜日までを含む)を計算します。

  2. weather_server.py をサブプロセスとして起動し、MCPハンドシェイクを実行し、その期間に対して get_forecast_all と render_temperature_chart を呼び出します。

  3. 期間内の各日付を WEEKLY_ITINERARY(曜日をその日に旅行者がいることが予想される都市(複数可)にマッピングするディクショナリ)と照合し、関連する場所のみが出力に反映されるようにします。

  4. 一致した日を recommend_packing() に渡します。これは気温と降水量のしきい値を使用するルールベースの関数です(外部AI呼び出しはありません)。

  5. Slack Block Kitメッセージ(日別の内訳、パッキングリスト、チャート)をフォーマットし、受信ウェブフックを介して投稿します。

旅行の旅程

bot/packing_advisor.py の WEEKLY_ITINERARY は、毎週繰り返されるルートを定義しています:

WEEKLY_ITINERARY = {
    4: [("Nijmegen", "arrival")],       # Friday
    5: [("Nijmegen", "")],              # Saturday
    6: [("Nijmegen", "")],              # Sunday
    0: [("Den Bosch", "day"), ("Kerpen", "evening, back home")],  # Monday
}

月曜日は移動日であるため、エントリが2つあります。旅程では、旅行者は日中はデン・ボスにいて、夕方にはケルペンに戻るとされています。このディクショナリは、ルートが変更された場合に編集する唯一の場所です。

チャート

render_temperature_chart は画像ではなく、プレーンテキストの等幅チャートを生成します。これは意図的な制約です。Slackの受信ウェブフックはテキストまたはBlock Kit JSONのみを投稿でき、バイナリファイルをアップロードできないためです。実際の画像を投稿するには、Slackボットトークン(files:writeスコープを持つインストール済みのSlackアプリ)と、ウェブフックの代わりにSlackのfiles.upload Web APIの呼び出しが必要になります。これは実行可能な拡張ですが、追加の認証情報の管理が必要です。現在の実装では、その要件を完全に回避しています。

セットアップ

前提条件

  • 受信ウェブフックURLを持つSlackアプリ。

  • コードをホストし、スケジュールされたワークフローを実行するためのGitHubリポジトリ。

ローカル実行

python -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
cp .env.example .env   # fill in SLACK_WEBHOOK_URL
export $(grep -v '^#' .env | xargs)
python bot/packing_advisor.py

SIMULATE_DATE(.env.example を参照)をISO日付に設定すると、実際の金曜日を待たずに旅程/予報期間のロジックをテストできます。

MCPサーバーは単独でも検査できます:

pip install "mcp[cli]"
mcp dev mcp_server/weather_server.py

これにより、ブラウザでMCP Inspectorが開き、ツールスキーマの表示や、get_forecast や render_temperature_chart などのツールを直接呼び出すのに便利です。

GitHubリポジトリの設定

  • SLACK_WEBHOOK_URL をリポジトリシークレットとして追加します(設定 → シークレットと変数 → アクション → シークレット)。

  • 他のリポジトリ設定は不要です。旅程はリポジトリ変数ではなくコードで定義されています。

スケジュール

.github/workflows/weekly-forecast.yml は、cron: "0 13 * * 5" により毎週金曜日の13:00 UTC(15:00 CEST)に実行されます。GitHub Actionsのcronスケジュールは夏時間を考慮しないため、CET(冬)期間中は15:00ではなく現地時間の14:00に実行されます。そのオフセットが重要であれば、cron式を季節に応じて調整できます。ワークフローは手動トリガー(workflow_dispatch)もサポートしており、オプションの simulate_date 入力があり、スケジュールに依存する前のテストに便利です。

既知の制限事項と可能な拡張

  • Slackメッセージには、上記の理由により、画像ではなくテキストベースのチャートが含まれます。matplotlibによるPNGチャートと、Slackボットトークンおよび files.upload を組み合わせることは、可能なフォローアップです。

  • recommend_packing() はルールベースです。これをLLM(例:Anthropic API)への呼び出しに置き換えると、より自然言語的な出力が可能になります。MCPツールの結果はプレーンなディクショナリなので、そのような呼び出しに直接渡すことができます。

  • 旅程は固定された毎週のパターンです。カレンダー統合ソース(例:カレンダーから実際の旅行日を読み取る)により、繰り返しの毎週のルートを超えて一般化できるでしょう。

ソース資料に関する注記

元のドラフトサーバースクリプトには、この実装の開発中に修正されたいくつかの問題がありました:

  • 未使用で存在しない httpx2 インポート。

  • get_params() はパラメータディクショナリを構築したが、それを返さなかった。

  • Open-Meteoクライアントは if __name__ == "__main__" 内でのみ構築され、モジュールスコープでそれを参照するツール関数からは利用できなかった。

  • process_response は dict_keys オブジェクトをインデックスとして list に代入しようとしたが、これは無効である。

  • ツール関数はpandasの DataFrame オブジェクトとnumpyスカラー型を返したが、これらはJSONシリアライズ可能ではない。MCPツールの結果はJSONとして送信されるため、シリアライズ可能でなければならない。

Related MCP Connectors

Related MCP Servers