MCP(Model Context Protocol)サーバー実装ガイド 2026 ─ Python SDK・FastMCPで作るClaude/Cursor/ChatGPT対応ツール

MCP(Model Context Protocol)サーバーをPython SDK・FastMCPで実装する2026年版ガイド。stdio/Streamable HTTP、OAuth 2.1、Claude/Cursor/ChatGPT接続、そして本番運用の勘所を動くコードで解説します。

MCPサーバー実装ガイド 2026 (Python SDK)

最終更新: 2026年9月8日

MCP(Model Context Protocol)サーバーとは、LLMクライアント(Claude Desktop、Cursor、Zed、OpenAI Responses APIなど)に対して、ツール・リソース・プロンプトを標準化されたJSON-RPCインターフェースで公開するプロセスのことです。Python SDK mcp(またはFastMCP)を使えば、10〜20行程度のコードで既存のAPIやデータベースを1つのMCPサーバーとして公開し、複数のLLMクライアントから同じツールを呼び出せるようになります。この記事では、2026年時点で本番運用に耐えるMCPサーバーの作り方を、stdioとStreamable HTTP両トランスポートで実装しながら解説します。

  • MCPは2024年11月にAnthropicが公開した仕様で、2025年6月18日版(spec 2025-06-18)が2026年時点の推奨バージョン。OpenAI、Google、Microsoftが公式クライアントを提供している。
  • プリミティブはTools・Resources・Promptsの3種類。Toolsは副作用ありの関数呼び出し、Resourcesは読み取り専用データ、Promptsはユーザー起動のテンプレート。
  • ローカル用途はstdio、リモート配布はStreamable HTTP(旧HTTP+SSEはdeprecated)。
  • Python SDKには公式の低レベルmcpと、Pythonic APIを提供するFastMCP 2.xがあり、本番用途ではFastMCPが実質デファクト。
  • OAuth 2.1認可はspec 2025-03-26で標準化。リモートサーバーを公開する場合はほぼ必須。
  • MCPは関数呼び出し(Function Calling)を置き換えるものではなく、ツール定義の配布層として上に載る。1つのMCPサーバーを書けば、Claude・Cursor・ChatGPTから同時に使える。

MCP(Model Context Protocol)とは何か

MCPは、LLMアプリケーション(ホスト)と外部データソース・ツールとの間の通信を標準化するオープンプロトコルです。公式仕様ではJSON-RPC 2.0をベースにしたリクエスト・レスポンス・通知の3種類のメッセージを定義しており、トランスポート層に依存しない設計になっています。私が2025年に受けた仕事の半分は「既存のn8nカスタムノードやLangChain Toolを、MCPサーバーとして書き直してほしい」というもので、これは1回書けばClaude・Cursor・ChatGPT・Zedから使えるという配布効率の高さが理由でした。

正直、初めてこの話を聞いたときは「また新しい標準か」と身構えたのですが、実装して2〜3週間触ってみて考えが変わりました。MCPが解決する問題を1文で言えば、「N個のLLMアプリケーションとM個のツールの間にN×Mの実装を書かなくて済む」ことです。従来はClaude用にAPI、ChatGPT用にプラグイン、Cursor用に別のツール定義、といったN×Mの組み合わせが必要でした。MCPが挟まることでN+Mに落ち、ツール開発者は仕様に従って1回書くだけ、クライアント開発者は仕様に従って1回パースするだけになります。この構造はUSB-Cが充電・データ・映像を標準化したのと同じ発想で、公式ドキュメントでも "USB-C for AI applications" という比喩が使われています。

2026年時点で対応済みのクライアントは Claude Desktop / Claude Code / Cursor / Zed / Continue / Cline / Windsurf / VS Code(組み込み)/ Sourcegraph Cody / ChatGPT Desktop / OpenAI Responses API / Google Gemini CLI など20以上あり、公式サーバーレジストリにはreference implementationsとして filesystem・github・postgres・puppeteer・slack など30以上が並んでいます。

2026年時点のMCP最新動向 ─ Streamable HTTPと認可

MCP仕様は年3〜4回改訂されており、2026年9月時点の最新はspec 2025-06-18です。過去1年で本番運用に影響する変更が3つ入っているので、古いチュートリアルを見ながら実装すると詰みます。

1. HTTP+SSEの廃止、Streamable HTTPへの一本化

spec 2024-11-05では2種類のトランスポートが定義されていました:

  • stdio:ホストがサーバーを子プロセスとして起動し、標準入出力でJSON-RPCをやり取り。ローカル用途向け。
  • HTTP+SSE(旧):GETでSSEチャネルを開き、POSTでメッセージ送信する双方向ストリーム。

2025-03-26でHTTP+SSEがdeprecatedとなり、Streamable HTTPに置き換わりました。単一のPOSTエンドポイントでリクエストを送り、レスポンスは通常のJSONか、必要ならSSEストリームとして返す方式です。1つのURLで済むためリバースプロキシ・認可・ロードバランサの設定が大幅に単純化されました。既存のHTTP+SSEサーバーは2026年内にStreamable HTTPへ移行しておくのが安全です。

2. OAuth 2.1認可の標準化

spec 2025-03-26で認可仕様が追加され、リモートMCPサーバーはOAuth 2.1(PKCE必須、Implicit Flow禁止)に準拠することになりました。/.well-known/oauth-authorization-serverでメタデータを公開し、クライアントは動的クライアント登録(RFC 7591)で登録します。私が最近ヘルスケア系のSaaSに書いたMCPサーバーでは、既存のAuth0テナントをそのままAuthorization Serverとして使い、MCPサーバー側はリソースサーバーとしてBearerトークンを検証するだけで済みました。

3. 構造化ツール出力とエラーモデル

spec 2025-06-18では、ツールの返り値にJSON Schemaで型付けされた structuredContent フィールドが追加されました。従来はテキストブロックしか返せず、LLMが再パースする必要がありましたが、これでツール→ツールのパイプラインを組みやすくなっています。エラーも isError: true フラグとJSON-RPCエラーコードの2レイヤーで区別できるようになり、リトライ判断が明確になりました。

Python SDKとFastMCPで最小サーバーを実装する

Pythonでは2種類のSDKが実質標準です。公式 mcp パッケージは仕様を直訳した低レベルAPIを提供し、fastmcp(jlowin氏開発、後に公式取り込み)はデコレータベースのPythonic APIを提供します。私は基本的にFastMCPを推奨します ─ 型ヒントからJSON Schemaを自動生成してくれるので、Pydanticモデルさえ書けばツール定義は完成します。

まずインストールと最小の"Hello Tools"サーバーを見てみましょう。以下の例は在庫管理APIをMCPサーバーとして公開する想定です。

# pyproject.toml or pip
# uv add "fastmcp>=2.3" httpx pydantic

# server.py
from typing import Annotated
from pydantic import BaseModel, Field
from fastmcp import FastMCP
import httpx

mcp = FastMCP(
    name="inventory-mcp",
    version="0.1.0",
    instructions=(
        "在庫SKUの検索と発注をサポートします。"
        "search_skuでSKUを検索し、create_purchase_orderで発注してください。"
    ),
)

class SKU(BaseModel):
    sku_id: str
    name: str
    on_hand: int = Field(description="現在の在庫数")
    reorder_point: int

@mcp.tool()
async def search_sku(
    query: Annotated[str, Field(description="SKU名の部分一致(日本語可)")],
    limit: Annotated[int, Field(ge=1, le=50)] = 10,
) -> list[SKU]:
    """在庫SKUを名前で検索する。上位limit件を返す。"""
    async with httpx.AsyncClient(timeout=10) as client:
        r = await client.get(
            "https://internal-erp.example.com/api/skus",
            params={"q": query, "limit": limit},
        )
        r.raise_for_status()
        return [SKU(**row) for row in r.json()["items"]]

@mcp.tool()
async def create_purchase_order(
    sku_id: str,
    quantity: Annotated[int, Field(ge=1, le=10_000)],
    supplier_id: str,
) -> dict:
    """指定SKUの発注を作成する。返り値は発注番号を含むdict。"""
    async with httpx.AsyncClient(timeout=30) as client:
        r = await client.post(
            "https://internal-erp.example.com/api/purchase_orders",
            json={"sku_id": sku_id, "qty": quantity, "supplier": supplier_id},
        )
        r.raise_for_status()
        return r.json()

if __name__ == "__main__":
    # ローカル(Claude Desktop / Cursor / Zed 用)
    mcp.run(transport="stdio")

これだけで、2つのツール(search_sku、create_purchase_order)を公開する完全なMCPサーバーが動きます。PydanticのFieldで書いたdescriptionやバウンドは、そのままJSON Schemaに翻訳されてクライアントに送られ、LLMがツールをいつ・どう呼ぶかの判断材料になります。私の経験上、このツール説明文とパラメータ説明の質が、エージェントの成功率を決める最大の要因です。「searchする」と1語で書くか「SKU名の部分一致(日本語可)」と書くかで、Claude Sonnetの正答率が体感で30%変わります。

Streamable HTTPで公開する

リモートに配布する場合はトランスポートを切り替えます。FastMCPはASGIアプリを返せるので、UvicornやHypercornで直接動かせます。

# server_http.py
from server import mcp  # 上のmcpインスタンスを再利用
import uvicorn

if __name__ == "__main__":
    # Streamable HTTP を /mcp にマウント
    app = mcp.streamable_http_app(path="/mcp")
    uvicorn.run(app, host="0.0.0.0", port=8080, log_level="info")

これで https://mcp.example.com/mcp がMCPエンドポイントになります。認可を挟む場合は、UvicornではなくFastAPI/StarletteアプリでこのASGIをマウントし、ミドルウェアでBearerトークン検証をすればOKです。

Tools・Resources・Promptsの使い分け

MCPのプリミティブは3種類あり、それぞれ役割が明確に分かれています。ここを混同してTools一択で作ると、UXが劣化するので注意してください。

プリミティブ制御者典型的な用途副作用キャッシュ
Toolsモデル(自動呼び出し)API呼び出し、DB書き込み、計算あり基本なし
Resourcesアプリ/ユーザー(明示添付)ファイル、DB行、ログ、ドキュメントなし(読み取り専用)あり(URI単位)
Promptsユーザー(スラッシュコマンド)定型ワークフローの起動テンプレートなし該当なし

Resourcesを正しく使う

Resourcesはfile://postgres://jira://のような独自URIスキームで識別されるコンテキストデータです。Claude Desktopでは@メンションでユーザーが明示的に会話に添付し、Cursorでは自動的にコンテキストに注入されます。「ユーザーがどのデータを渡すか選ぶ」性質があるため、社内ドキュメント検索やソースコード参照はToolsではなくResourcesで公開したほうがコスト効率が良くなります(不要なコンテキストがトークンに乗らない)。

@mcp.resource("jira://issue/{issue_key}")
async def get_jira_issue(issue_key: str) -> str:
    """Jira issue の Markdown 化された本文を返す。"""
    async with httpx.AsyncClient() as client:
        r = await client.get(f"https://internal-jira.example.com/api/issues/{issue_key}")
        r.raise_for_status()
        data = r.json()
        return f"# {data['summary']}\n\n**Status:** {data['status']}\n\n{data['description']}"

Promptsでワークフローを配布する

Promptsは「/incident-review」のようなスラッシュコマンドとしてクライアントUIに現れます。私が書いた顧客サポート向けサーバーでは、"問い合わせトリアージ"というPromptを1つ用意し、それがResourcesで顧客履歴を読み、Toolsで社内KBを検索し、下書き返信を生成する、という一連の流れを1コマンドに閉じ込めています。エンジニアでないオペレーターが再現性のあるワークフローを起動する手段として非常に有効です。

Claude Desktop・Cursor・Zed・ChatGPTでの接続設定

MCPサーバーが書けても、クライアント側の設定ファイルの形式は微妙に違います。よく使う4クライアントの設定例をまとめます。

Claude Desktop(stdio)

~/Library/Application Support/Claude/claude_desktop_config.json(macOS)を編集します。

{
  "mcpServers": {
    "inventory": {
      "command": "uv",
      "args": ["run", "--project", "/Users/priya/mcp/inventory", "python", "server.py"],
      "env": {
        "ERP_API_KEY": "sk-xxxxxxxx"
      }
    }
  }
}

Cursor(stdio または HTTP)

Cursorはプロジェクトルートの.cursor/mcp.jsonとグローバル設定の両方をサポートします。Streamable HTTPを使う場合はurlを指定します。

{
  "mcpServers": {
    "inventory": {
      "url": "https://mcp.example.com/mcp",
      "headers": { "Authorization": "Bearer ${INV_TOKEN}" }
    }
  }
}

ChatGPT / OpenAI Responses API

2025年3月にOpenAIが公式にMCPをサポートし、Responses APIで直接指定できるようになりました。認可付きリモートサーバーが前提です。

from openai import OpenAI
client = OpenAI()

resp = client.responses.create(
    model="gpt-5.1",
    input="在庫が10個以下のSKUを一覧して",
    tools=[{
        "type": "mcp",
        "server_label": "inventory",
        "server_url": "https://mcp.example.com/mcp",
        "authorization": "Bearer xxxxxxxx",
        "require_approval": "never",  # 本番は "always" または フィルタ関数
    }],
)
print(resp.output_text)

MCP vs Function Calling ─ どちらを使うべきか

「MCPは既存のFunction Callingを置き換えるのか?」とよく聞かれますが、答えはNo、MCPはFunction Callingの上に載る配布層です。LLMプロバイダのAPIレベルで見ると、モデルが返すのは今もtool_useブロック(Anthropic)やtool_calls(OpenAI)であり、実行はホスト側の責務です。MCPはその「ホストが持つツール一覧をどこから取ってくるか」を標準化したにすぎません。

使い分けの目安は以下です。

  • 単一アプリケーション、単一LLMプロバイダ、内製ツールのみ → 素のFunction Calling(またはPydanticAIのような型安全ラッパ)で十分。MCPの分散システム的複雑性を持ち込む価値はない。
  • ツールを複数クライアント(Claude Desktop、IDE、Web UIなど)から使いたい → MCPが最適。1回書けば全クライアントで動く。
  • サードパーティにツールを配布したい → MCP一択。プロバイダ固有のツール定義を配ってもエコシステムが育たない。
  • エージェントフレームワーク内部の内部ツール → LangGraphやOpenAI Agents SDKの@toolデコレータの方が軽い。

実際に本番で運用してみると、MCPを採用した価値は「新しいLLMプロバイダに移行するコストが下がる」ことでも実感できます。私が2025年後半に担当した案件では、GPT-5.1からClaude Opus 5に切り替える判断が下ったのが金曜午後、翌月曜には切替完了 ─ ツール定義をMCP経由で提供していたため、変更はプロバイダSDKの呼び出し部分だけでした。Claude Agent SDKの本番運用ガイドで紹介したHooksパターンとの併用も相性が良く、MCPで公開したツールの前後にHookを差し込んでレート制限や監査ログを追加する運用が定着しています。

本番運用チェックリスト ─ ログ・エラー・認可

ここまでは開発の話です。実際に本番運用に載せるとき、私がクライアントに必ず確認するチェックリストが以下の7項目です。SaaSにMCPサーバーを埋め込む場合、これを飛ばすと確実に事故ります。

1. 認可(Authorization)

Streamable HTTPで公開する時点で、認可なしはあり得ません。spec 2025-03-26に準拠したOAuth 2.1を実装するか、既存のBearer JWT検証を挟みます。Auth0/Okta/Keycloakを持っている組織なら、それをAuthorization Serverとして使うのが最短経路です。/.well-known/oauth-authorization-serverのメタデータURLをMCPクライアントに教えるだけで、動的クライアント登録が動きます。

2. Rate Limiting とサーキットブレーカ

LLMエージェントは「同じツールを100回連続で呼ぶ」ような失敗モードを持ちます。ツール実装側で必ずレート制限を入れてください。私はslowapi(FastAPI用)かaiolimiterで、ユーザーIDごとに10 req/秒、500 req/時を最低ラインにしています。上流APIが落ちた時のサーキットブレーカ(purgatorycircuitbreaker)も入れておくと、深夜のRate Limit連鎖崩壊を防げます。

3. 構造化ログとトレース

MCPツール呼び出しは分散トレースの絶好の対象です。OpenTelemetry GenAI規約(2026時点でBeta)に従って、gen_ai.tool.namegen_ai.tool.call.id、入出力トークン数を記録しておけば、後からLangfuseやArize Phoenixで分析できます。詳細はLLMオブザーバビリティの記事でまとめました。

4. エラーの分類

ツールが失敗したとき、モデルにリトライ可能なエラーを伝えるか、ユーザーに戻すべきエラーを伝えるかを区別する必要があります。FastMCPではToolErrorと通常のPython例外で扱いが変わります。

from fastmcp.exceptions import ToolError

@mcp.tool()
async def transfer_funds(from_acct: str, to_acct: str, amount: int) -> dict:
    if amount <= 0:
        # モデルに戻す(モデルが再試行/修正可能)
        raise ToolError("amountは正の整数である必要があります")
    try:
        return await banking_api.transfer(from_acct, to_acct, amount)
    except InsufficientFunds as e:
        # ビジネスエラー ─ モデルに戻して代替案を提示させる
        raise ToolError(f"残高不足: {e.balance}円")
    except httpx.HTTPStatusError as e:
        if e.response.status_code >= 500:
            # 内部エラー ─ MCPレイヤでリトライすべき
            raise  # そのままpropagate、isError=true + code=-32603
        raise ToolError(f"銀行API拒否: {e.response.text}")

5. Idempotency

書き込み系ツールは冪等キーを取ることを強く推奨します。エージェントは平気で同じ発注を2回投げます。Idempotency-Keyヘッダを上流APIに渡すか、自前でRedisに{tool_name}:{args_hash}:{user_id}を24時間キャッシュしましょう。

6. MCP Inspectorでの手動テスト

公式のMCP Inspector(npx @modelcontextprotocol/inspector)は、ツール・リソース・プロンプトを対話的に叩けるデバッガです。CI/CDに組み込みにくいですが、リグレッションチェックには自動化されたeval harnessを別途組みます。エージェント評価の詳細はAIエージェントのEvals実践入門で解説しています。

7. バージョニングと後方互換

ツール名やパラメータを変更するとき、既存クライアントのプロンプト・エージェント履歴が壊れます。「削除しない・リネームしない」を原則にして、新機能は新ツールとして追加し、旧ツールは deprecated: true(FastMCPの@mcp.tool(annotations={"deprecated": True}))でマークしましょう。3〜6ヶ月の移行期間を置いてから削除するのが安全です。

よくある質問

MCPサーバーはPython以外の言語でも書けますか?

はい。公式SDKはTypeScript、Python、Kotlin、Java、C#、Swift、Ruby、Rustが提供されています(2026年9月時点)。仕様はJSON-RPC 2.0ベースなので、SDKが無い言語でも自前で実装できます。ただしFastMCP相当の高レベルAPIを持つのは現状Python(FastMCP)とTypeScript(公式SDKのServer class)だけで、他言語では低レベルAPIでの実装が中心になります。

MCPサーバーはどこにホストするのが一般的ですか?

ローカルツール(ファイル操作、ローカルDB)はstdio + ユーザーPC上で動かすのが標準です。SaaS的にチーム全体に配布する場合はStreamable HTTPで公開し、Cloudflare Workers、Fly.io、AWS Lambda(Function URL経由)、Google Cloud Runなどが定番のホスティング先です。CloudflareとMicrosoftはマネージドMCPホスティングを2025年に発表しており、認可・ログ・スケーリングを肩代わりしてくれます。

既存のOpenAPI仕様からMCPサーバーを自動生成できますか?

できます。FastMCPにはFastMCP.from_openapi()という関数があり、OpenAPI 3.x仕様を渡すと各エンドポイントをToolsまたはResourcesとして自動公開します。ただし自動生成されたツール名・説明文はLLMにとって不親切なことが多いので、本番運用では手動で説明文を上書きするか、AsyncAPI/OpenAPIのdescriptionを改善するのがおすすめです。

MCPサーバーからLLMを呼び出すことはできますか?

Sampling という仕組みで可能です。サーバーがクライアントに「このプロンプトをあなたのLLMで実行して結果を返してほしい」と依頼できます。これによりサーバー側でAPIキーを持たずに、ユーザーの契約しているLLMを間接的に使えます。ただし2026年9月時点でSamplingを完全サポートしているクライアントはClaude DesktopとClaude Codeのみで、Cursor・ChatGPT側は対応途中です。

MCPとLangChain Tools、どちらでツールを書くべきですか?

目的次第です。LangChain/LangGraph内部でのみ使うならLangChain Toolsが軽量です。同じツールを複数のクライアント(Claude Desktop、Cursor、社内Web UIなど)に配布したいならMCPが有利です。両者は排他ではなく、LangChainにはlangchain-mcp-adaptersというライブラリがあり、任意のMCPサーバーをLangChain Toolとしてマウントできます。私は「本番配布はMCP、社内エージェントの内部ツールはLangChain」のハイブリッドで運用しています。

著者について Priya Ramaswamy

Priya spent four years at Zapier building the Tables product before leaving in 2023 to consult on agent infrastructure for Series A startups. She's shipped custom n8n nodes for two YC-backed companies (a clinical-trial logistics platform and a freight broker), and her PR adding streaming-token support to LangChain's Bedrock chat wrapper was merged in early 2024. Most of her current work is unglamorous: helping ops teams replace 40-step Make.com scenarios with a single LangGraph state machine, then arguing with their CFO about token budgets. She writes here about the parts of agent work that vendor blogs skip - eval harnesses that don't lie, retry logic that survives a rate-limited Anthropic endpoint at 2am, and why 'just add a vector DB' is almost always the wrong answer. Based in Toronto. Eight years total in workflow tooling.