MCP Server をゼロから構築する:
AI ツール呼び出しの完全開発ガイド

Python または TypeScript 開発者の方で、Claude・GPT・Cursor 上の AI にデータベースアクセス、API 呼び出し、ファイル読み書きをさせたい一方、大規模言語モデル単体では外部操作の「手足」がないという制約に直面している方へ向けた記事です。学習データにはカットオフがあり、リアルタイム操作はできません。本稿では空白プロジェクトから本番投入可能な MCP Serverを書き上げます。Tools / Resources / Prompts 三大機能、stdio と HTTP+SSE 二種のトランスポート、デバッグから Docker デプロイまでを一通りカバーし、読了後に Cursor または Claude Desktop へ自前ツールチェーンを接続できる状態を目指します。ノードとプランは NOVAKVM 料金ページをご確認ください。

AI ツール統合は三段階で進化してきました。Function Calling(OpenAI 2023)がモデル出力を構造化 JSON 呼び出しに変換し、Plugins / GPT Actions が HTTP エンドポイントを会話プラグイン化し、MCP(Model Context Protocol) は Anthropic が 2024 年 11 月にオープンソース化したプロトコルとして「モデルがツールをどう発見・記述・呼び出すか」を標準化しました。一度実装すれば Claude Desktop、Cursor、VS Code Continue など複数のホストで再利用できます。

  • LLM の能力境界:リアルタイムデータへ直接アクセスできず、ファイルシステムやデータベースも操作できません。外部ツールが「感覚器官と手足」を補います。
  • フォーマットの分断:OpenAI Function Calling、Claude Tool Use、LangChain Agent はそれぞれ異なるツール schema を持ち、モデルや IDE を変えるたびにアダプタ層の書き直しが発生します。
  • 発見メカニズムの欠如:従来の REST API は AI に「何ができるか」を能動的に伝えません。MCP ランタイムは tools/list で能力一覧を動的に公開します。
  • セッションとコンテキスト:MCP は永続接続を維持し、多段 Agent ワークフローに適します。REST の無状態リクエストでは複雑タスクの連鎖が困難です。
  • セキュリティと権限:Server 側で書き込み可能ツールと読み取り専用リソースを細かく制御し、モデルによる本番データへの越権操作を防ぎます。

Client-Server JSON-RPC アーキテクチャは次の流れに整理できます。

  • Host(ホスト):Claude Desktop、Cursor、VS Code などユーザーインターフェースを提供します。
  • MCP Client:Host 内蔵で、各 Server と 1:1 セッションを維持します。
  • MCP Server(自前実装):Tools(実行可能操作)、Resources(読み取り専用データ)、Prompts(再利用プロンプトテンプレート)の三大原語を公開します。
  • トランスポート層:ローカル開発は stdio(子プロセス stdin/stdout)、リモートデプロイは HTTP + SSE(Server-Sent Events プッシュ)を使います。
  • プロトコル層:全メッセージは JSON-RPC 2.0 に従い、initializetools/listtools/callnotifications/cancelled という流れになります。
  • ライフサイクル:ハンドシェイク → 能力交渉 → 通常稼働 → グレースフルシャットダウン。Client は ping でキープアライブできます。
MCP vs OpenAI Function Calling vs LangChain ツール統合
次元 MCP OpenAI FC LangChain Tools
標準化 オープンプロトコル、ベンダー横断 OpenAI API に依存 フレームワーク内部形式
ツール発見 ランタイム tools/list リクエスト時 schema 埋め込み コード登録、静的
Resources / Prompts ネイティブ対応 非対応 独自ラッパーが必要
トランスポート stdio / HTTP+SSE HTTPS API プロセス内呼び出し
IDE ネイティブ統合 Cursor、Claude Desktop 等 ChatGPT プラグイン 追加ブリッジ層が必要

MCP が解くのは「API を呼べるか」ではなく「AI がツールを統一的に発見・選択・正しく呼び出す方法」です。これが Agent 時代の中核課題です。

公式は Pythonmcp パッケージ(FastMCP 高レベル API 含む)と TypeScript@modelcontextprotocol/sdk の二系統を提供しています。Python はプロトタイプとデータ/ML 向き、TypeScript は Node.js エコシステムとフロントエンドチーム向きです。以降は Python FastMCP を主軸に解説しますが、TypeScript 側の構造は対称です。

setup.sh
mkdir my-mcp-server && cd my-mcp-server
python3 -m venv .venv
source .venv/bin/activate
pip install "mcp[cli]" httpx pydantic python-dotenv
npm install -g @modelcontextprotocol/inspector

推奨プロジェクト構成は次のとおりです。

project-tree
my-mcp-server/
├── server.py
├── tools/
├── resources/
├── prompts/
├── tests/
├── Dockerfile
├── requirements.txt
└── .env

デバッグと接続に必要な三要素は次のとおりです。

  • MCP Inspector:公式ビジュアルデバッガー。tools/resources/prompts を一覧し、手動呼び出しができます。
  • Claude Desktop:macOS では ~/Library/Application Support/Claude/claude_desktop_config.json を編集し stdio Server を登録します。
  • Cursor:Settings → MCP から Server 設定を追加します。stdio とリモート HTTP エンドポイントの両方に対応しています。

FastMCP で最小構成の Server を作り、say_hello ツールを公開します。

server.py
from mcp.server.fastmcp import FastMCP

mcp = FastMCP("hello-server")

@mcp.tool()
def say_hello(name: str) -> str:
    return f"Hello, {name}! MCP Server is running."

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

Inspector で起動検証します。

terminal
npx @modelcontextprotocol/inspector python server.py

ブラウザで Inspector UI を開き、Tools パネルから say_hello{"name": "NOVAKVM"} を渡すと、挨拶メッセージが返ることを確認できます。

Claude Desktop 設定claude_desktop_config.json):

claude_desktop_config.json
{
  "mcpServers": {
    "hello-server": {
      "command": "/path/to/my-mcp-server/.venv/bin/python",
      "args": ["/path/to/my-mcp-server/server.py"]
    }
  }
}

Cursor MCP 設定:プロジェクトルートに .cursor/mcp.json を作成するか、Cursor Settings → MCP で同等の JSON を追加します。Cursor を再起動すると Agent モードで say_hello ツールが表示されます。

MCP ツールの関数シグネチャがそのままドキュメントになります。パラメータ名、型アノテーション、docstring が JSON Schema に変換され、モデルが理解します。複雑な入力は Pydantic モデルで制約します。

tools/search.py
from pydantic import BaseModel, Field

class SearchInput(BaseModel):
    query: str = Field(..., description="検索キーワード")
    limit: int = Field(10, ge=1, le=100, description="返却件数上限")

@mcp.tool()
async def search_docs(input: SearchInput) -> list[dict]:
    ...

本番級 Server では次の五種類のツールを実装することが多いです。

  • calculator:安全な数式評価。eval の裸実行は禁止し、ast または専用ライブラリで解析します。
  • file_read / file_write:ルートディレクトリを限定(chroot 思想)し、パストラバーサルを防ぎます。書き込みは二次確認または拡張子ホワイトリストを設けます。
  • fetch_url:非同期 HTTP リクエスト。実装例は下記のとおりです。
  • db_query:読み取り専用 SQL(SELECT)のみ。パラメータ化クエリでインジェクションを防ぎます。書き込みは別ツールに分離し監査ログを残します。
  • get_current_time:ISO 8601 タイムスタンプを返し、モデルが「現在時刻を知らない」問題を解消します。
tools/http.py
import httpx

@mcp.tool()
async def fetch_url(url: str, timeout: int = 30) -> str:
    async with httpx.AsyncClient(timeout=timeout) as client:
        resp = await client.get(url)
        resp.raise_for_status()
        return resp.text[:50000]

エラーハンドリングのベストプラクティスは次のとおりです。

  • ツール内で例外を捕捉し、構造化エラーテキストを返します。プロセスをクラッシュさせず、Client がモデルへ結果を渡して retry や戦略変更を促します。
  • 外部 API にはタイムアウトとリトライ上限を設定し、モデルの無限ループ呼び出しを防ぎます。
  • API Key や接続文字列などの機密情報は環境変数から読み取り、tool 戻り値や Resources へ書き込みません。
  • 書き込み操作ツールは変更内容と影響行数のサマリーを返し、モデルがユーザーへ確認しやすくします。

Resource と Tool の本質的差異:Resource は読み取り専用のコンテキストデータ(設定ファイル、ユーザープロファイル、ログ断片)で、モデルは resources/read で取得します。Tool は実行可能な操作(クエリ、書き込み、リクエスト送信)です。Resource は「AI が参照すべきだが変更しない」情報に適しています。

URI スキーム(scheme)の慣例は次のとおりです。

  • config://app/settings — 起動時に登録する静的設定リソースです。
  • user://{id}/profile — 動的テンプレート。Client が id を渡すと Server がリアルタイム生成します。
  • file:// — ローカルファイルシステムへのマッピング(パスサンドボックス必須)です。

Resource は text/plainapplication/jsonバイナリ blob(Base64 エンコード返却)、stream(大ファイルのチャンク配信)の四種 MIME に対応します。

resources/config.py
@mcp.resource("config://app/settings")
def get_app_settings() -> str:
    return json.dumps({"env": "production", "version": "1.2.0"})

@mcp.resource("user://{user_id}/profile")
def get_user_profile(user_id: str) -> str:
    profile = db.fetch_user(user_id)
    return json.dumps(profile)

ファイルシステム Resource Server はディレクトリ配下のファイルを再帰登録でき、AI がコーディング時にプロジェクト構造と主要設定を直接「見る」ことができます。read tool をファイルごとに呼ぶ必要がありません。

MCP Prompt は事前定義されたプロンプトテンプレートです。Host が一覧表示し、ワンクリックで会話コンテキストへ注入できます。コードレビュー、インシデント振り返り、API 設計レビューなど反復ワークフローに適しています。プレースホルダパラメータと複数ターン message 構造をサポートします。

prompts/review.py
from mcp.types import PromptMessage, TextContent

@mcp.prompt()
def code_review_prompt(language: str, focus: str = "security") -> list[PromptMessage]:
    return [
        PromptMessage(
            role="user",
            content=TextContent(
                type="text",
                text=f"以下の {language} コードを {focus} の観点でレビューし、問題点と改善案を列挙してください:\n\n"
            )
        )
    ]

多段テンプレート例:第一ターンでロールと制約を注入し、第二ターンでレビュー対象コードのプレースホルダを渡し、第三ターンで構造化 JSON(severity / file / line / suggestion)の出力を要求します。チームのベストプラクティスを Prompt として固定化すれば、新メンバーが system prompt を毎回書く必要がなくなります。

stdio vs HTTP + SSE トランスポート対照
次元 stdio HTTP + SSE
デプロイ位置 ローカル子プロセス リモートサーバー / コンテナ
ネットワーク要件 不要 公網または社内網到達性が必要
水平スケール 単一マシン ロードバランサ + session affinity
認証 プロセス分離 Bearer Token / API Key
適用シナリオ 個人開発、Claude Desktop チーム共有、7×24 クラウド常駐

FastMCP は streamable-http トランスポートをサポートし、一行で切り替えられます。

server.py
if __name__ == "__main__":
    mcp.run(transport="streamable-http", host="0.0.0.0", port=8080)

本番 HTTP デプロイでは次を必ず整備します。

  • Bearer Token / API Key:リバースプロキシまたは middleware 層で Authorization ヘッダーを検証します。
  • CORS:ブラウザ版 Inspector からクロスオリジンアクセスする場合、許可オリジンを設定します。
  • Rate Limiting:IP または API Key 単位でレート制限し、モデルのループ呼び出しによるバックエンド過負荷を防ぎます。
  • TLS:公網公開時は HTTPS 必須です。Caddy / Nginx で SSL 終端する構成が一般的です。

MCP Inspector の使い方:起動後、左側に Server 能力ツリーが表示されます。Tools パネルで JSON パラメータを入力して手動トリガーでき、Notifications パネルで Server プッシュを、Logs パネルで stderr 出力を確認できます。コード変更後は Inspector が子プロセスを自動再起動します。

tests/test_tools.py
import pytest
from server import say_hello

def test_say_hello():
    result = say_hello("World")
    assert "Hello, World!" in result

@pytest.mark.asyncio
async def test_fetch_url():
    result = await fetch_url("https://httpbin.org/get")
    assert "origin" in result
MCP Server よくあるエラーと対処
現象 想定原因 対処
ツールが表示されない デコレーター漏れ、Server 起動失敗 stderr ログ確認、Inspector 再接続
JSON シリアライズ失敗 datetime/Decimal 等の非シリアライズ型 手動 json.dumps または str 変換
呼び出しタイムアウト 外部 API 遅延、timeout 未設定 ツール内 timeout 設定、Client 側 limit 拡大
権限拒否 ファイルパスがサンドボックス外、DB 認証エラー ALLOWED_PATHS と .env 設定を確認

六ステップ本番デプロイチェックリストは次のとおりです。

  1. 依存バージョン固定:pip freeze > requirements.txt を実行し、CI で Python と mcp SDK バージョンを固定します。上流 breaking change を防ぎます。
  2. Dockerfile 作成:マルチステージビルドで最終イメージはランタイム依存のみ。非 root ユーザーで Server プロセスを実行します。
  3. 環境変数設定:API Key、DB 接続文字列、ALLOWED_PATHS は secrets 管理経由とし、ハードコードしません。
  4. ホスティング選定:Railway / Render は MVP 向き。AWS Lambda + HTTP adapter は低頻度向き。Cloud Run / VPS は 7×24 常駐向きです。
  5. 監視導入:構造化ログ(JSON)、/health エンドポイント、Prometheus metrics または Sentry 例外報告を整備します。
  6. Cursor / Claude 接続検証:リモート URL + Bearer Token を MCP 設定へ書き込み、tools/list でツール一覧が完全であることを確認してから本番トラフィックを流します。

Dockerfile
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
USER nobody
EXPOSE 8080
CMD ["python", "server.py"]

ホスティングプラットフォーム選定の目安は次のとおりです。

  • Railway / Render:Git push 自動デプロイ。無料 tier は MVP 向きです。コールドスタートが SSE 長接続に影響する点に注意します。
  • AWS Lambda:呼び出し課金。HTTP-to-stdio アダプタ層が必要です。低頻度内部ツール向きです。
  • Google Cloud Run:コンテナネイティブ、自動スケール。SSE には min-instances ≥ 1 でキープアライブが必要です。
  • VPS / ベアメタル:ネットワークとディスクを完全制御。機密データを社内に留める MCP Server に適しています。

可観測性三要素は次のとおりです。

  • Logging:structlog または python-json-logger。各 tool call に name、duration_ms、status を記録します。
  • Prometheus:mcp_tool_calls_totalmcp_tool_duration_seconds 等のメトリクスを公開します。
  • Sentry:未処理例外を捕捉し、Server バージョン tag でロールバック判断を支援します。

バージョン互換性:MCP 仕様 2025-03-26 で Streamable HTTP が導入されました。デプロイ前に Client(Cursor バージョン、Claude Desktop バージョン)と Server SDK が同一トランスポートをサポートするか確認してください。README に mcp>=1.2.0 等の最低要件を明記することを推奨します。

総合実践プロジェクトとしてナレッジベース MCP Serverを挙げます。ChromaDB または Qdrant でドキュメントベクトルを保存し、watchfiles でディレクトリ変更を監視して自動 re-index します。search_knowledgewrite_note の二ツールを公開すれば、Cursor 接続後に Agent がチーム内部ドキュメントを検索でき、学習データだけに頼らなくなります。

tools/knowledge.py
@mcp.tool()
async def search_knowledge(query: str, top_k: int = 5) -> list[dict]:
    embedding = await embed(query)
    results = collection.query(query_embeddings=[embedding], n_results=top_k)
    return [{"text": doc, "score": score} for doc, score in zip(...)]

@mcp.tool()
async def write_note(title: str, content: str) -> str:
    doc_id = collection.add(documents=[content], metadatas=[{"title": title}])
    return f"Note saved: {doc_id}"

再利用推奨のコミュニティ MCP Serverは次のとおりです。

  • @modelcontextprotocol/server-filesystem — サンドボックス付きファイル読み書き
  • server-github — Issue、PR、コード検索
  • server-brave-search — リアルタイム Web 検索
  • server-postgres — 読み取り専用 SQL クエリ
  • server-slack — チャンネルメッセージと通知

2026 年 MCP エコシステムの主要データは次のとおりです。

  • エコシステム規模:2026 年時点で公開 MCP Server は 10,000 件超、GitHub modelcontextprotocol 組織リポジトリの Star 合計は 50,000 超と報告されています。
  • ベンダー採用:2026 年 Q1 に OpenAI、Q2 に Google Gemini と Microsoft Copilot がネイティブ MCP サポートを発表。ガバナンスは Linux Foundation 傘下 AAIF へ移管されました。
  • エンタープライズ統合コスト:MCP 標準インターフェース採用後、AI ツールチェーン統合開発コストが約 38–55% 削減されたという業界調査結果があります。

学習パスは次の六段階を推奨します。

  1. 公式仕様を読み、JSON-RPC メッセージフローを理解する
  2. FastMCP で Hello World + Inspector デバッグを完了する
  3. ファイル、HTTP、DB の三種以上の実ツールを実装する
  4. Resources と Prompts を追加し、三大原語の差異を体験する
  5. HTTP トランスポートへ切り替え、Docker でクラウドへデプロイする
  6. Cursor へ接続し、ナレッジベースまたは GitHub 統合の実践を完了する

以下の公開資料は仕様と SDK の検証入口として利用できます。上流リポジトリが更新された場合はリンク先を正としてください。

Model Context Protocol — 公式仕様とドキュメント

modelcontextprotocol/python-sdk — Python MCP SDK

modelcontextprotocol/typescript-sdk — TypeScript MCP SDK

modelcontextprotocol/inspector — MCP Inspector デバッグツール

スリープするノート PC 上で MCP Server を動かすと、tools/list セッション中断、OAuth 期限切れ、ディスク満杯によるベクトル DB 破損、ネットワーク切替後の SSE 断線などが日常の障害になります。「どのモデルを選ぶか」より実開発体験への影響が大きいケースが多いです。ラップトップ上の Docker でもホストのスリープ問題は残り、無料 tier PaaS のコールドスタートは長接続を頻繁に切断し、Lambda は SSE 常駐シナリオに向きません。

7×24 常駐 MCP ツールチェーン、安定 SSH、予測可能な Apple Silicon 算力が必要な場合、Cursor Agent と自前 Server を専用ベアメタルへ移す方が合理的です。NOVAKVM は多リージョンの Mac Mini M4 / M4 Pro 柔軟なレンタルを提供し、Cursor MCP、Claude Desktop リモート開発、ベクトル DB 同機デプロイに適しています。プランは 料金ページ、注文は 注文ページ、デプロイに関する質問は ヘルプセンターをご参照ください。