金曜日, 8月 1, 2025
金曜日, 8月 1, 2025
- Advertisment -
ホームニューステックニュースFastMCPとOpen API Specification を使った天気予報Remote MCP Serverの実装

FastMCPとOpen API Specification を使った天気予報Remote MCP Serverの実装


まとめ

  • ウェザーニューズの気象データを提供する Remote MCP Server を構築しました
  • FastMCP と Open API Specification を使うことで、短期間で MCP Server を実装できました
  • 本記事で紹介した MCP Server は Beta 版として公開しています

はじめに

こんにちは、ウェザーニューズ モバイル・インターネット事業部の Hoka です。直近では API リファレンスページ、コーポレートブログの構築を担当しています。

ウェザーニューズでは気象データを主に REST API 経由で社内外に提供・配信しています。LLM・AI Agent を組み込んだサービス開発が社内で進む中で、それらのアプリケーションが標準化された仕組みで自社の気象データを取得できることの重要性が高まってきました。このような背景から、FastMCP と Open API Specification (OAS) を使い、自社の気象データ API を Remote MCP (Model Context Protocol) Server として実装しました。

MCP Server の実装

MCP Server の構築にはFastMCPを利用しました。FastMCP には、Open API Specification を使って REST API から MCP Server を構築する機能が実装されています。
ウェザーニューズでは、気象 API リファレンスを構築した際に Open API Specification を作成していたため、MCP Server 用に description などを新たに記述する必要なく短期間、低開発コストで MCP Server を実装することができました。

FastMCP Instance の定義

Open API Specification を FastMCP Instance 定義時の引数として渡すことで、specification をもとに tools が自動で定義されます。

main.py

from fastmcp import FastMCP
import json

...


wxtech_api_spec_path = BASE_DIR / "spec" / "openapi.json"

with open(wxtech_api_spec_path) as f:
    wxtech_api_spec = json.load(f)


mcp = FastMCP.from_openapi(
    openapi_spec=wxtech_api_spec,
    client=client,
    stateless_http=True,
    json_response=True,
    route_maps=custom_maps,
)

...

認証

MCP Server 自体は Amazon API Gateway の認証機能を使い、API キーを使って認証しています。MCP Server が内部的に呼び出す REST API に対しても同様の API キーを使って認証する必要があるため、httpxライブラリのAsyncClientを継承したクライアントを実装し、API キーをヘッダーに付与してリクエストを送信しています。

httpx.AsyncClient を継承したクライアントの実装

custom_httpx.py

import contextvars
from typing import Any
import httpx
from httpx import URL, Headers
from starlette.middleware.base import BaseHTTPMiddleware, RequestResponseEndpoint
from starlette.requests import Request as StarletteRequest
from starlette.responses import Response as StarletteResponse

current_request_headers_var: contextvars.ContextVar[Headers | None] = contextvars.ContextVar("current_request_headers", default=None)


class ContextAwareAsyncClient(httpx.AsyncClient):
    async def request(self, method: str, url: URL | str, **kwargs: Any) -> httpx.Response:
        incoming_headers: Headers | None = current_request_headers_var.get()

        merged_headers: Headers = Headers(dict(self.headers))

        request_specific_headers_input = kwargs.pop("headers", None)
        request_specific_headers = Headers(request_specific_headers_input) if request_specific_headers_input else Headers()

        if incoming_headers:
            api_key = incoming_headers.get("X-API-Key")
            if api_key:
                merged_headers["X-API-Key"] = api_key

        for name, value in request_specific_headers.items():
            merged_headers[name] = value

        kwargs["headers"] = merged_headers
        return await super().request(method, url, **kwargs)



class RequestHeaderContextMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, request: StarletteRequest, call_next: RequestResponseEndpoint) -> StarletteResponse:
        token = current_request_headers_var.set(Headers(headers=request.headers))
        try:
            response = await call_next(request)
        finally:
            current_request_headers_var.reset(token)
        return response

インフラ構成

MCP Server は AWS Lambda 上で動作し、API Gateway, CloudFront を介してアクセスできるようにしています。

Tools

任意の緯度経度の天気予報を取得可能なツールを実装しました。内部的に利用する API の仕様に合わせて、日本国内の場合は 1km メッシュ、日本国外のエリアは 5km メッシュの天気予報をレスポンスとして返します

Requestの例

{
  "lat": 35.651377,
  "lon": 140.041161
}

Responseの例

{
  "requestId": "xxxx-xxxx-xxxx-xxxx-xxxx",
  "wxdata": [
    {
      "lat": 35.651377,
      "lon": 140.041161,
      "srf": [
        {
          "arpress": 1002,
          "date": "2025-07-28T13:00:00+09:00",
          "feelidx": 8,
          "feeltmp": 35,
          "prec": 0,
          "rhum": 68,
          "temp": 32.9,
          "wnddir": 12,
          "wndspd": 4,
          "wx": 100
        },
        ...

Resources

MCP Server が内部的に呼び出す API では、「晴れ時々くもり」、「くもり一時雨」などの天気表現を 3 桁のコードでレスポンスしています。コードと天気表現を対応付けたテーブルを Resources で定義しました。

Resourcesの抜粋

[
    {
        "code": "100",
        "value": "晴れ"
    },
    {
        "code": "101",
        "value": "晴れ時々くもり"
    },
    {
        "code": "102",
        "value": "晴れ一時雨"
    },
    ...

MCP クライアントでの動作

Claude Desktop

まず、claude_desktop_config.json に設定を記述します。

claude_desktop_config.json の例

claude_desktop_config.json

{
  "mcpServers": {
    "WxTech1kmMeshPinpointWeatherForecast": {
      "command": "npx",
      "args": [
        "mcp-remote",
        "https://wxtech.weathernews.com/api/beta/ss1wx/mcp/",
        "--header",
        "X-API-Key: [X_API_KEY]"
      ]
    },
    "WxTech5kmMeshGlobalWeatherForecast": {
      "command": "npx",
      "args": [
        "mcp-remote",
        "https://wxtech.weathernews.com/api/beta/global/wx/mcp/",
        "--header",
        "X-API-Key: [X_API_KEY]"
      ]
    }
  }
}

「海浜幕張の明日の天気を教えてください」などのプロンプトを入力すると、MCP Server から取得した気象データで回答が生成されることを確認できます。

Claude Desktop Demo 1

Claude Artifacts を使ってグラフ化するといった使い方もできます。

Claude Desktop Demo 2

VSCode

VSCode に設定を記述し GitHub Copilot Agent Mode から利用することで、Jupyter Notebook で可視化する、CSV ファイルに書き出して解析に利用するなどの使い方ができます。

mcp.json の例

mcp.json

{
 "servers": {
  "WxTech1kmMeshPinpointWeatherForecast": {
      "type": "http",
      "url": "https://wxtech.weathernews.com/api/beta/ss1wx/mcp/",
      "headers": {
        "X-API-Key": "[X_API_KEY]"
      }
    },
  "WxTech5kmMeshGlobalWeatherForecast": {
      "type": "http",
      "url": "https://wxtech.weathernews.com/api/beta/global/wx/mcp/",
      "headers": {
        "X-API-Key": "[X_API_KEY]"
      }
    }
 }
}

MCP Serverから取得したデータをJupyter Notebookで可視化した例
MCP Server から取得したデータを Jupyter Notebook で可視化した例

LangChain, LangGraph を使ったクライアント

Python で LangChain, LangGraph を使ってシンプルなクライアントを実装した例を示します。

main.py

import asyncio
import os

from langchain_mcp_adapters.client import MultiServerMCPClient
from langgraph.prebuilt import create_react_agent

x_api_key = os.getenv("WXTECH_API_KEY")

async def main() -> None:
    
    client = MultiServerMCPClient(
        {
            "WxTech1kmMeshPinpointWeatherForecast": {
                "transport": "streamable_http",
                "url": "https://wxtech.weathernews.com/api/beta/ss1wx/mcp/",
                "headers": {"X-API-Key": x_api_key},
            },
        },
    )

    
    tools = await client.get_tools()

    
    agent = create_react_agent("openai:gpt-4.1-nano", tools)

    
    response = await agent.ainvoke({"messages": [{"role": "user", "content": "今日の海浜幕張の天気は?"}]})
    messages = response["messages"]
    for message in messages:
        print(message.content)

if __name__ == "__main__":
    asyncio.run(main())

レスポンス例

uv run main.py
今日の海浜幕張の天気は?
{
  "requestId": "xxxx-xxxx-xxxx-xxxx-xxxx",
  "wxdata": [
    {
      "lat": 35.6564,
      "lon": 140.0363,
      "srf": [
        {
          "arpress": 1001,
          "date": "2025-07-29T16:00:00+09:00",

今日は海浜幕張の天気は晴れで、最高気温は約37度、最低気温は約25度です。風は南西からやや弱めで、湿度は比較的高いです。海に行くなら暑さ対策と水分補給を忘れずにしてくださいね。

最後に

今後は、MCP Server 経由での天気アイコンの提供や、より柔軟な形式でのデータ取得を可能にするための機能追加なども検討中です。また、本記事で紹介した Remote MCP Server は Beta 版として公開しています。各種 MCP クライアントからご利用いただき、フィードバックをいただけると幸いです。

WxTech Data API for AI Agents (MCP Server) – WxTech Data API リファレンス



Source link

Views: 0

RELATED ARTICLES

返事を書く

あなたのコメントを入力してください。
ここにあなたの名前を入力してください

- Advertisment -