PythonでMCPサーバーを作る|FastMCP入門

LLM

MCPサーバーを自作すると、社内データ検索、独自API、計算処理などを、MCP対応のAIエージェントから共通の方法で利用できます。

ただし、MCPの通信仕様やJSON Schemaを最初から実装する必要はありません。Python向けフレームワーク「FastMCP」を使えば、普通のPython関数へデコレーターを付けるだけでツールとして公開できます。

この記事では、FastMCPで最小のMCPサーバーを作り、ツール・リソース・プロンプトを登録し、ローカルテストとHTTP起動まで進めます。

  • FastMCPのインストール
  • 最小MCPサーバーの作成
  • ツール、リソース、プロンプトの違い
  • STDIOとHTTPでの起動
  • Pythonクライアントからの動作確認
  • OpenAI Responses APIへ接続するときの注意点

FastMCPサーバーの構成

スポンサーリンク

FastMCPとは

FastMCPは、PythonでMCPサーバー・クライアントを構築するための高水準フレームワークです。関数名、型ヒント、docstringから、ツール名・説明・入力スキーマを自動生成します。

つまり開発者は、MCPのJSON-RPCやスキーマ生成よりも「ツールが何をするか」に集中できます。

FastMCPと公式MCP Python SDKの違い

現在、Pythonでは次の2系統があるため、import文を混同しないことが重要です。

選択肢 パッケージ 主なimport
FastMCP単体 fastmcp from fastmcp import FastMCP
公式MCP Python SDK v2 mcp from mcp.server import MCPServer

この記事では、独立して開発されているfastmcpパッケージを使います。2026年8月時点ではFastMCP 3.xが安定版で、4.xはプレリリースです。本番環境では動作確認したバージョンを固定してください。

スポンサーリンク

FastMCPをインストールする

uvを使う場合

新しいプロジェクトを作成し、FastMCPを追加します。

mkdir fastmcp-sample
cd fastmcp-sample
uv init
uv add fastmcp

pipを使う場合

仮想環境を有効にしてからインストールします。

pip install fastmcp

インストール後は、次のコマンドでバージョンを確認できます。

fastmcp version

最小のMCPサーバーを作る

server.pyを作り、2つの数値を加算するツールを登録します。

from fastmcp import FastMCP

mcp = FastMCP("Calculation Server")

@mcp.tool
def add(a: int, b: int) -> int:
    """2つの整数を加算する。"""
    return a + b

if __name__ == "__main__":
    mcp.run()

これだけでaddはMCPツールになります。FastMCPは次の情報をPythonコードから取得します。

  • ツール名:関数名add
  • 説明:docstring
  • 引数:ab
  • 引数の型:int
  • 戻り値の型:int

型ヒントは単なる補足ではなく、クライアントから渡される値を検証するための契約です。説明と型を省略すると、モデルが誤った使い方をしやすくなります。

ツールを実用的にする

デフォルト値を設定する

引数にデフォルト値を付けると、その引数は任意項目になります。

from fastmcp import FastMCP

mcp = FastMCP("Search Server")

@mcp.tool
def search_documents(query: str, limit: int = 5) -> dict:
    """キーワードに一致する文書を検索する。"""
    sample_documents = [
        {"id": "doc-001", "title": "MCP導入ガイド"},
        {"id": "doc-002", "title": "AIエージェント運用ルール"},
    ]
    return {
        "query": query,
        "count": min(limit, len(sample_documents)),
        "items": sample_documents[:limit],
    }

辞書などのPythonオブジェクトを返すと、クライアントが扱いやすい構造化結果になります。実際の検索処理では、データベースや外部APIから取得した結果を必要最小限に整形して返します。

非同期処理を使う

外部APIやデータベースを待つ処理はasync defにできます。

import httpx

from fastmcp import FastMCP

mcp = FastMCP("Status Server")

@mcp.tool
async def check_status(url: str) -> dict:
    """指定したHTTPS URLの応答状態を確認する。"""
    if not url.startswith("https://"):
        raise ValueError("HTTPS URLだけを指定してください")

    async with httpx.AsyncClient(timeout=10) as client:
        response = await client.get(url)

    return {"url": url, "status_code": response.status_code}

この例を本番で使う場合は、アクセス可能なドメインを許可リストで限定してください。ユーザー入力のURLへ無制限にアクセスすると、SSRFの原因になります。

リソースとプロンプトを追加する

MCPサーバーが公開する要素はツールだけではありません。

種類 主に選ぶ主体 用途
ツール モデル 計算、検索、更新などの処理
リソース アプリケーション 読み取り用データ
プロンプト ユーザー 再利用する指示テンプレート

リソースを登録する

@mcp.resourceで、設定値などの読み取り専用データを公開できます。

from fastmcp import FastMCP

mcp = FastMCP("Project Server")

@mcp.resource("project://config")
def get_project_config() -> dict:
    """プロジェクトの公開設定を返す。"""
    return {
        "project_name": "BUNSHIN",
        "language": "ja",
        "version": "1.0",
    }

URIに{name}のような変数を入れると、パラメーター付きのリソーステンプレートも作れます。

プロンプトを登録する

繰り返し使う指示は@mcp.promptで登録します。

from fastmcp import FastMCP

mcp = FastMCP("Writing Server")

@mcp.prompt
def summarize_document(text: str, language: str = "Japanese") -> str:
    """文書要約用のプロンプトを生成する。"""
    return (
        f"次の文章を{language}で3つの要点にまとめてください。"
        f"\n\n{text}"
    )

FastMCPの開発・テスト・接続フロー

MCPサーバーを起動する

STDIOで起動する

mcp.run()の標準設定はSTDIOです。ローカルのAIクライアントが、子プロセスとしてサーバーを起動する用途に向いています。

python server.py

サーバーが標準入出力を通信に使うため、print()でデバッグ文字列を標準出力へ混ぜないようにします。ログは標準エラー出力またはロギング機構へ送ります。

HTTPで起動する

リモート接続用にHTTPで起動する場合は、FastMCP CLIからトランスポートを指定できます。

fastmcp run server.py:mcp --transport http --port 8000

ローカルの接続先は通常http://localhost:8000/mcpです。外部サービスから利用するには、HTTPS化、認証、到達可能なホストへのデプロイが必要です。

MCP Inspectorで確認する

開発中は、ブラウザからツール一覧や引数を確認できるMCP Inspectorが便利です。

fastmcp dev server.py:mcp

表示されたURLを開き、ツール名・説明・引数スキーマ・実行結果を確認します。

Pythonクライアントからテストする

HTTPで起動したサーバーへFastMCPクライアントから接続します。

import asyncio

from fastmcp import Client

async def main() -> None:
    async with Client("http://localhost:8000/mcp") as client:
        result = await client.call_tool(
            "add",
            {"a": 12, "b": 30},
        )
        print(result)

asyncio.run(main())

ツールを追加したら、正常系だけでなく次のケースも確認します。

  • 必須引数がない
  • 文字列へ数値以外が渡される
  • 上限を超える値が渡される
  • 外部APIがタイムアウトする
  • 認証情報が無効になっている

OpenAI Responses APIから使うには

前回の「Responses APIでMCP連携するPython実装」で解説したように、OpenAI Responses APIからはtype: "mcp"server_urlを指定して接続できます。

ただし、localhostはOpenAI側から到達できません。FastMCPサーバーを外部公開する場合は、少なくとも次を準備します。

  1. Streamable HTTPで起動する
  2. 公開ホストへデプロイする
  3. HTTPSを有効にする
  4. OAuthなどの認証を設定する
  5. 公開するツールを最小限にする
  6. 更新系ツールに承認フローを入れる

設計上の違いは「MCPとFunction Callingの違い」も参照してください。

本番運用で注意すること

ツールの説明を具体的にする

docstringはモデルがツールを選ぶ判断材料です。「検索する」だけではなく、検索対象、返す情報、使うべき場面を具体的に書きます。

入力をサーバー側でも検証する

型が正しくても、値が安全とは限りません。IDの存在確認、文字数、数値範囲、アクセス可能なパスやURLを検証します。

副作用のある処理を分離する

読み取りと更新を同じツールへ詰め込まず、目的ごとに分けます。購入・削除・公開・外部送信には、クライアント側の承認に加えてサーバー側の権限確認も必要です。

バージョンを固定する

FastMCPは進化が速く、公式ドキュメントも開発版の機能を含む場合があります。本番環境では、検証済みバージョンをロックファイルまたは完全一致で固定します。

よくあるエラー

ModuleNotFoundError: fastmcp

FastMCPをインストールしたPython環境と、server.pyを実行している環境が同じか確認します。which pythonpython -m pip show fastmcpなどで切り分けます。

ツールが一覧に表示されない

  • @mcp.toolを付け忘れていないか
  • 起動しているファイルやインスタンス名が正しいか
  • fastmcp run server.py:mcpの指定が合っているか
  • 編集後にサーバーを再起動したか

HTTP接続で404になる

クライアントの接続先に/mcpが含まれているか確認します。また、リバースプロキシがパスを書き換えていないかも確認します。

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

  • ✅ FastMCPのバージョンを固定した
  • ✅ ツール名・docstring・型ヒントを確認した
  • ✅ 入力値をサーバー側で検証した
  • ✅ 読み取りと更新ツールを分離した
  • ✅ 外部APIへタイムアウトを設定した
  • ✅ APIキーを環境変数またはSecret Managerで管理した
  • ✅ HTTPSと認証を設定した
  • ✅ 更新・削除・送信には承認と権限確認を入れた
  • ✅ エラーログから機密情報を除外した
  • ✅ 正常系・異常系の両方をテストした

まとめ

FastMCPを使うと、通常のPython関数からMCPツールを短いコードで作れます。

  1. FastMCPインスタンスを作る
  2. @mcp.toolで関数を登録する
  3. 型ヒントとdocstringを正確に書く
  4. InspectorとPythonクライアントでテストする
  5. リモート公開時はHTTPS・認証・権限制御を追加する

まずは副作用のない計算・検索ツールを1個だけ作り、ローカルで動作を確認してから外部APIやデータベース連携へ広げるのがおすすめです。

参考資料

コメント

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