FastMCPでREST APIをMCP化|OpenAPIから自動生成

FastMCPとOpenAPIでREST APIをMCP化する実践ガイド LLM

既存のREST APIをAIエージェントから使いたい場合、エンドポイントごとにMCPツールを手書きする必要はありません。APIにOpenAPI仕様書があれば、FastMCPのFastMCP.from_openapi()でMCPサーバーの土台を自動生成できます。

ただし、API全体をそのままMCP化するのは危険です。削除・更新・管理用エンドポイントまでモデルへ公開され、ツール数が増えすぎて選択精度も下がる可能性があります。

この記事では、Swagger Petstoreを例に、PythonとFastMCPでREST APIをMCP化します。最小構成だけでなく、読み取り系エンドポイントだけを許可するRouteMap、APIキー、ツール名の整理、Pythonクライアントからのテストまで解説します。

  • OpenAPIからMCPツールを自動生成する
  • REST APIの一部だけを許可リストで公開する
  • 外部APIの認証情報を安全に渡す
  • 自動生成されたツールをPythonからテストする
  • OpenAI Responses APIへ接続するときの構成を理解する

OpenAPIからFastMCPサーバーを生成する構成

スポンサーリンク

REST APIのMCP化とは

REST APIのMCP化とは、既存APIのエンドポイントをMCPのツールやリソースとしてAIクライアントへ提供することです。

通常、AIモデルがREST APIを直接利用するには、エンドポイント、HTTPメソッド、パラメーター、レスポンス形式を個別に教える必要があります。OpenAPI仕様書にはこれらの情報が構造化されているため、FastMCPは仕様書を読み取り、MCPコンポーネントへ変換できます。

REST API側 MCP側での役割
operationId ツール名の基礎
summarydescription モデルが読むツール説明
path・query・request body 入力スキーマ
HTTPレスポンス ツールの実行結果
HTTPメソッド・path・tag 公開範囲を決める条件

FastMCP 3.xでは、内部的にOpenAPIProviderがOpenAPI仕様からコンポーネントを供給します。最初はFastMCP.from_openapi()を使えば、Providerを直接組み立てなくても始められます。

スポンサーリンク

自動変換が向いているケース

OpenAPIからの自動生成は、次の用途に向いています。

  • 既存APIを短時間でAIから試したい
  • 社内APIの検索・参照機能をMCP対応にしたい
  • 手書き実装へ進む前にプロトタイプを作りたい
  • OpenAPIの変更をMCP側へ追従させたい

一方、数百のエンドポイントを持つAPIを丸ごと公開する用途には向きません。モデルへ見せるツールが増えるほど、似た名前や複雑な引数が判断を難しくします。

公式ドキュメントも、自動変換は導入や試作には便利ですが、複雑なAPIでは目的に合わせて設計したMCPサーバーの方が高い性能を得やすいと説明しています。

事前準備

OpenAPI仕様書のURLを確認する

APIによって異なりますが、OpenAPI仕様書は次のようなURLで提供されます。

https://api.example.com/openapi.json
https://api.example.com/openapi.yaml
https://api.example.com/swagger.json

ブラウザまたはHTTPクライアントで開き、openapiinfopathsが含まれているか確認してください。この記事ではSwagger公式サンプルの次のURLを使用します。

https://petstore3.swagger.io/api/v3/openapi.json

FastMCPとhttpxをインストールする

uvを使う場合は、プロジェクトを作って依存関係を追加します。

mkdir fastmcp-openapi
cd fastmcp-openapi
uv init
uv add "fastmcp>=3,<4" httpx

pipの場合は、仮想環境を有効にしてから次を実行します。

pip install "fastmcp>=3,<4" httpx

この記事は単体パッケージのFastMCP 3.xを対象とします。importはfrom fastmcp import FastMCPです。

最小構成でREST APIをMCP化する

まずはOpenAPI仕様書を取得し、すべてのエンドポイントをMCPツールへ変換する最小例です。

import httpx

from fastmcp import FastMCP

OPENAPI_URL = "https://petstore3.swagger.io/api/v3/openapi.json"
BASE_URL = "https://petstore3.swagger.io/api/v3"

response = httpx.get(OPENAPI_URL, timeout=20.0)
response.raise_for_status()
openapi_spec = response.json()

api_client = httpx.AsyncClient(
    base_url=BASE_URL,
    timeout=20.0,
)

mcp = FastMCP.from_openapi(
    openapi_spec=openapi_spec,
    client=api_client,
    name="Petstore MCP Server",
)

if __name__ == "__main__":
    mcp.run(transport="http", host="127.0.0.1", port=8000)

server.pyとして保存し、次のコマンドで起動します。

python server.py

接続先はhttp://127.0.0.1:8000/mcpです。この最小例では、OpenAPI仕様書に含まれるエンドポイントが原則としてすべてツールになります。

理解用としては便利ですが、実運用ではこのまま公開しないでください。Petstoreにも追加・更新・削除エンドポイントが含まれます。

RouteMapで公開範囲を制限する

FastMCPのRouteMapを使うと、HTTPメソッド、path、OpenAPIタグを条件に、各ルートをツール・リソース・除外へ振り分けられます。

読み取り系だけを許可する

次の例では、/pet/以下のGETエンドポイントだけをツールにし、それ以外をすべて除外します。

import httpx

from fastmcp import FastMCP
from fastmcp.server.providers.openapi import MCPType, RouteMap

OPENAPI_URL = "https://petstore3.swagger.io/api/v3/openapi.json"
BASE_URL = "https://petstore3.swagger.io/api/v3"

response = httpx.get(OPENAPI_URL, timeout=20.0)
response.raise_for_status()
openapi_spec = response.json()

api_client = httpx.AsyncClient(
    base_url=BASE_URL,
    timeout=20.0,
    follow_redirects=True,
)

mcp = FastMCP.from_openapi(
    openapi_spec=openapi_spec,
    client=api_client,
    name="Petstore Read Only MCP",
    route_maps=[
        RouteMap(
            methods=["GET"],
            pattern=r"^/pet/.*",
            mcp_type=MCPType.TOOL,
        ),
        RouteMap(mcp_type=MCPType.EXCLUDE),
    ],
    mcp_names={
        "findPetsByStatus": "find_pets_by_status",
        "getPetById": "get_pet_by_id",
    },
)

if __name__ == "__main__":
    mcp.run(transport="http", host="127.0.0.1", port=8000)

RouteMapは上から順番に評価され、最初に一致したルールが使われます。

  1. GETかつ/pet/以下ならツールにする
  2. それ以外はMCPType.EXCLUDEで除外する

最後の全件除外ルールが重要です。これがないと、独自ルールに一致しなかったルートへFastMCPのデフォルトルールが適用され、ツールとして公開されます。

tagで管理用ルートを除外する

OpenAPI側でinternaladminタグを付けている場合は、タグでも除外できます。

from fastmcp.server.providers.openapi import MCPType, RouteMap

route_maps = [
    RouteMap(tags={"internal"}, mcp_type=MCPType.EXCLUDE),
    RouteMap(tags={"admin"}, mcp_type=MCPType.EXCLUDE),
    RouteMap(methods=["GET"], mcp_type=MCPType.TOOL),
    RouteMap(mcp_type=MCPType.EXCLUDE),
]

ただし、タグの付け忘れだけで管理APIが公開されないよう、HTTPメソッドやpathも組み合わせる方が安全です。

OpenAPIルートを許可・除外するフロー

自動生成されたツールを確認する

FastMCPのPythonクライアントからツール一覧を取得し、実際に1件呼び出します。

import asyncio

from fastmcp import Client

async def main() -> None:
    async with Client("http://127.0.0.1:8000/mcp") as client:
        tools = await client.list_tools()
        print("公開中のツール:")
        for tool in tools:
            print(f"- {tool.name}")

        result = await client.call_tool(
            "find_pets_by_status",
            {"status": "available"},
        )
        print(result.data)

asyncio.run(main())

別のターミナルでpython client.pyを実行します。確認すべき点は、ツールが動くことだけではありません。

  • 許可したツールだけが一覧へ表示される
  • addPetdeletePetなどの更新系ツールが存在しない
  • 引数名と型がOpenAPIの定義どおりになっている
  • 不正な値で適切にエラーになる
  • 外部APIのタイムアウトがMCP側へ伝わる

APIキーが必要なREST APIへ接続する

接続先APIがBearer tokenを必要とする場合は、httpx.AsyncClientへAuthorizationヘッダーを設定します。

import os

import httpx
from fastmcp import FastMCP

api_token = os.environ["TARGET_API_TOKEN"]

api_client = httpx.AsyncClient(
    base_url="https://api.example.com",
    headers={"Authorization": f"Bearer {api_token}"},
    timeout=20.0,
)

mcp = FastMCP.from_openapi(
    openapi_spec=openapi_spec,
    client=api_client,
    name="Authenticated API MCP",
)

APIキーをソースコード、OpenAPI仕様書、WordPress記事、エラーログへ直接書かないでください。本番ではSecret Managerやデプロイ先のシークレット機能を使います。

2種類の認証を混同しない

この構成には、認証が2箇所あります。

認証 守る区間 設定場所
REST APIの認証 FastMCP → 接続先API httpx.AsyncClient
MCPサーバーの認証 AIクライアント → FastMCP FastMCPのAuth Provider

httpxへAPIキーを設定しても、MCPサーバー自体が保護されるわけではありません。HTTPで外部公開する場合は、MCP側にもOAuthなどの認証を設定してください。

ツール名と説明をAI向けに整える

自動生成ツールの品質は、OpenAPI仕様書の品質に大きく依存します。

operationIdを安定させる

FastMCPはOpenAPIのoperationIdをツール名の基礎にします。各エンドポイントへ、短く一意で意味の分かるoperationIdを付けます。

operationId: search_customer_orders
summary: 顧客IDを指定して注文履歴を検索する

既存のoperationIdを変えられない場合は、mcp_namesでMCP側の名前を上書きできます。

descriptionへ利用条件を書く

モデルがツールを正しく選べるように、summarydescriptionへ次を含めます。

  • 何を取得・実行するツールか
  • どの場面で使うか
  • 必須IDの形式
  • 返すデータの範囲
  • 更新や課金などの副作用

「データを取得する」だけでは、似たツールとの違いを判断できません。

ローカルのOpenAPIファイルを使う

仕様書を外部URLから取得せず、リポジトリ内で固定したい場合はJSONファイルを読み込みます。

import json
from pathlib import Path

from fastmcp import FastMCP

openapi_spec = json.loads(
    Path("openapi.json").read_text(encoding="utf-8")
)

mcp = FastMCP.from_openapi(
    openapi_spec=openapi_spec,
    client=api_client,
    name="Versioned API MCP",
)

仕様書をコードと一緒にバージョン管理すると、API側の突然の変更でMCPツールの一覧や入力スキーマが変わる事故を減らせます。更新時はOpenAPI差分とツール一覧のスナップショットを確認します。

OpenAI Responses APIから使う構成

今回作ったFastMCPサーバーをOpenAI Responses APIから使う場合、全体は次の流れになります。

Responses API
    ↓ MCP(HTTPS・認証)
FastMCPサーバー
    ↓ REST API(APIキー)
既存の業務API

Responses API側からlocalhostへは接続できないため、外部から到達可能なHTTPS URLが必要です。具体的なPython実装は「Responses APIでMCP連携する方法」を参照してください。

FastMCP自体の作成方法を先に確認したい場合は「PythonでMCPサーバーを作る|FastMCP入門」、設計の使い分けは「MCPとFunction Callingの違い」で整理しています。

本番運用の設計ポイント

API全体ではなくユースケース単位で公開する

「顧客情報API全部」ではなく、「顧客を検索する」「注文状況を確認する」のように目的を絞ります。モデルが必要としない管理・監査・一括操作エンドポイントは除外します。

読み取りと更新を別サーバーに分ける

参照専用MCPと更新可能MCPを分けると、権限・承認・監査ログを設計しやすくなります。削除・送信・購入・公開などは、人間の承認なしに実行させないでください。

レスポンスを必要最小限にする

REST APIの巨大なレスポンスをそのまま返すと、トークン消費と誤判断が増えます。自動変換で試した後、よく使う処理は手書きツールへ置き換え、必要なフィールドだけ返す設計も検討します。

タイムアウトと再試行を制御する

外部APIへのタイムアウトを必ず設定します。更新系リクエストを無条件に再試行すると二重登録になるため、冪等性キーやAPI仕様を確認してください。

よくあるエラー

ツールが多すぎる

RouteMapの最後に全件除外ルールがあるか確認します。許可するルートを先に並べ、最後にRouteMap(mcp_type=MCPType.EXCLUDE)を置きます。

ツール名が分かりにくい

OpenAPIのoperationIdを修正するか、mcp_namesで上書きします。変更後はMCPクライアントからlist_tools()を再取得します。

REST APIで401になる

httpx.AsyncClientへ必要なAuthorizationヘッダーが設定されているか、トークンの期限と権限を確認します。MCP側の認証エラーと、接続先REST APIの401を区別してください。

/mcpへ接続できない

FastMCPサーバーがHTTP transportで起動しているか、ポート番号と/mcpパスが正しいか確認します。外部公開時はリバースプロキシのパス書き換えも確認します。

実運用前のチェックリスト

  • ✅ OpenAPI仕様書の取得元とバージョンを固定した
  • ✅ RouteMapを許可リスト方式で設定した
  • ✅ 更新・削除・管理ルートを除外した
  • ✅ 公開ツールの名前と説明を人間が確認した
  • ✅ REST APIとMCPサーバーの認証を別々に設定した
  • ✅ APIキーをシークレットとして管理した
  • ✅ 外部APIへタイムアウトを設定した
  • ✅ 正常系・権限不足・404・429・タイムアウトをテストした
  • ✅ レスポンスから個人情報と不要フィールドを除いた
  • ✅ 副作用のある操作に承認と監査ログを入れた

まとめ

FastMCPのFastMCP.from_openapi()を使うと、既存のREST APIからMCPサーバーの土台を短いPythonコードで作れます。

  1. OpenAPI仕様書とAPIのbase URLを確認する
  2. httpx.AsyncClientを設定する
  3. FastMCP.from_openapi()でMCPサーバーを作る
  4. RouteMapで公開範囲を許可リストにする
  5. Pythonクライアントでツール一覧と実行結果を検証する
  6. 外部公開時はMCP側にも認証を追加する

最初は読み取り専用の2〜3エンドポイントだけをMCP化するのがおすすめです。自動生成で動作を確かめ、利用頻度の高い処理からAI向けの手書きツールへ改善していくと、安全性と使いやすさを両立できます。

参考資料

コメント

タイトルとURLをコピーしました