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

Claude / GPT / Cursor がチャットだけで、データベース参照・ファイル読み取り・API 呼び出しができない——必要なのは長い Prompt ではなく、再利用可能なツール層です。本稿はバックエンドおよびフルスタック開発者向けに、Hello World から ChromaDB ナレッジベース MCP Server までを解説します。Tools / Resources / Prompts の三大能力、stdio と HTTP+SSE の二つのトランスポート、デバッグ・テスト・Docker 本番デプロイを網羅し、読了後には Cursor から直接呼び出せるカスタムツール一式と、Server をリモート Mac 上で 7×24 常駐させる方法を理解できます(プロトコル背景は MCP プロトコル解説編 を参照してください)。

01

AI に MCP Server が必要な理由:ツールのない LLM は「話せる脳」にすぎない

大規模言語モデルの学習データには期限があり、CRM、Git リポジトリ、社内 API にはアクセスできません。2024年以前は、Claude 向けに Function Calling、GPT 向けに Plugins、Cursor 向けに別形式——モデルを変えるたびに作り直しが必要でした。MCP Server では「ツール能力」を独立プロセスとしてカプセル化し、一度書けば Claude Desktop / Cursor / Gemini すべてで使えます

典型的なシナリオ:Claude Desktop で AI に Postgres の売上データを照会させる、Cursor で Agent にプロジェクトドキュメントを読ませてコードを修正させる、GPT が HTTP MCP 経由で社内チケットシステムを呼び出す——いずれも同じ Server が基盤です。

本稿の価値:概念説明ではなく、say_hello からベクトル検索付き本番級ナレッジベース Server までを実践します。対象読者:Python または TypeScript の基礎があり、IDE や Desktop で AI 能力を拡張したい開発者です。

六つの課題:「API をラップするだけ」では足りない理由

  1. 01

    モデルベンダーロックイン:OpenAI Function Calling と Claude Tool Use は形式が異なり、ベンダーを変えるたびにアダプタ層を書き直す必要があります。

  2. 02

    ツールが発見できない:REST API は静的ドキュメントに依存し、AI は実行時に tools/list で能力一覧を自律取得できません。

  3. 03

    IDE ごとにバラバラ:Cursor、VS Code 拡張、JetBrains プラグインのツール定義は再利用できず、N×M セットの統合を保守し続ける必要があります。

  4. 04

    コンテキストとデータの分断:LLM は設定ファイル、ユーザー設定、リアルタイムログを安定して読み取れず、標準化された Resources 読み取りチャネルが必要です。

  5. 05

    Prompt テンプレートの散在:Code Review や Incident レポートなどの再利用テンプレートに統一登録機構がなく、チームごとにコピペしています。

  6. 06

    ローカル vs リモートデプロイの混乱:stdio 子プロセスは開発向きですが、本番 HTTP ゲートウェイ、認証、監視には統一パラダイムが不足しています(stdio 子プロセス運用編 参照)。

「AI に MCP Server を接続するのは、プログラマーに IDE プラグインを入れるのと同じ——能力の境界が『チャット』から『現実世界を操作する』へ一気に広がります。」

02

MCP とは:Function Calling からオープンプロトコルへ

進化の経路:Function Calling(2023)ChatGPT PluginsMCP(2024年11月、Anthropic オープンソース)。Anthropic は MCP を AI と外部世界の「USB-C」と位置づけています。Host(Cursor / Claude Desktop)内に MCP Client が組み込まれ、MCP Server と 1:1 セッションを確立します。

アーキテクチャ:Client / Server と三大能力

  • Tools(ツール):AI が呼び出せる副作用のある操作——DB 照会、ファイル書き込み、HTTP リクエストなど。
  • Resources(リソース):読み取り専用コンテキスト——設定、ドキュメント断片、ユーザープロファイル。URI でアドレス指定します。
  • Prompts(プロンプトテンプレート):code review や incident postmortem など、事前定義された多段会話の骨格です。

通信は JSON-RPC 2.0 を基盤とします:initializetools/list / tools/callresources/read。トランスポート層には二つのライフサイクルがあります。

  • stdio:Host が Server を子プロセスとして起動し、stdin/stdout で JSON-RPC を交換します。プロセス終了でセッションも終了します。
  • HTTP + SSE:Client がリモート URL に接続し、Server がイベントストリームをプッシュします。チーム共有と水平スケールに適しています。

完全な仕様は公式サイト modelcontextprotocol.io を参照してください。

MCP vs OpenAI FC vs LangChain ツール

次元MCPOpenAI Function CallingLangChain Tools
開放性クロスベンダーオープンプロトコル、AAIF ガバナンスOpenAI API にバインドフレームワーク内抽象、非トランスポート標準
発見機構実行時 tools/listリクエスト内 functions 配列コード登録、標準発見なし
読み取り専用データResources + URI scheme第一級市民なしRetriever 概念、プロトコルレベルではない
Prompt テンプレートPrompts 標準インターフェースなしPromptTemplate クラス
トランスポートstdio / HTTP+SSE / Streamable HTTPHTTPS API 一体型Agent ランタイム依存
再利用性同一 Server が Cursor + Claude + Gemini をサービスOpenAI エコシステムのみフレームワーク跨ぎでバインディング再実装が必要
03

開発環境の構築:Python FastMCP vs TypeScript SDK

二つの主流ルートがあります。Python mcp + FastMCP(データ/スクリプト向き)と TypeScript @modelcontextprotocol/sdk(Web/API 統合、型安全)。SDK ソース:python-sdktypescript-sdk

bash
# Python ルート
python -m venv .venv && source .venv/bin/activate
pip install "mcp[cli]" httpx pydantic

# TypeScript ルート
npm init -y && npm install @modelcontextprotocol/sdk zod
npm install -D typescript tsx @types/node

推奨プロジェクト構成

tree
my-mcp-server/
├── pyproject.toml          # または package.json
├── src/
│   ├── server.py           # FastMCP エントリポイント
│   ├── tools/              # 各ツールモジュール
│   ├── resources/          # Resource プロバイダ
│   └── prompts/            # Prompt テンプレート
├── tests/
│   └── test_tools.py       # pytest + ClientSession
├── Dockerfile
└── README.md

六ステップ環境準備チェックリスト

  1. 01

    言語スタックを選択:データ/ML チームは Python を優先、Node フルスタックは TypeScript を選択します。

  2. 02

    仮想環境を作成し依存を固定:pip freeze または package-lock.json で schema ドリフトを防ぎます。

  3. 03

    MCP Inspector をインストール:npx @modelcontextprotocol/inspector で JSON-RPC を可視デバッグします。

  4. 04

    Claude Desktop を設定:~/Library/Application Support/Claude/claude_desktop_config.json を編集し、Server の command/args を追加します。

  5. 05

    Cursor を設定:Settings → MCP → Add Server で、stdio に python -m src.server または絶対パスを入力します。

  6. 06

    Inspector 接続を検証:Server 起動 → Inspector 接続 → tools/list が非空で返ることを確認します。

json
// Cursor / Claude Desktop MCP 設定例
{
  "mcpServers": {
    "my-tools": {
      "command": "python",
      "args": ["-m", "src.server"],
      "env": { "API_KEY": "your-key" }
    }
  }
}
04

Hello World:30 秒で最初の MCP Server を動かす

FastMCP で say_hello ツールを書き、コード → Inspector → Cursor の一連のフローを検証します。

python
# src/server.py
from mcp.server.fastmcp import FastMCP

mcp = FastMCP("hello-server")

@mcp.tool()
def say_hello(name: str = "World") -> str:
    """指定した相手に挨拶する"""
    return f"Hello, {name}! MCP is working"

if __name__ == "__main__":
    mcp.run()  # デフォルト stdio トランスポート
bash
# Inspector でデバッグ
npx @modelcontextprotocol/inspector python -m src.server

# または stdio で直接起動
python -m src.server

Cursor に同じ command を追加したうえで、Agent に「say_hello で NodeMini に挨拶して」と指示してください。JSON 結果が返れば Client ↔ Server のハンドシェイク成功です。

info

ヒント:FastMCP は関数の docstring と型アノテーションから JSON Schema を自動生成するため、パラメータ説明を手書きする必要はありません。

05

Tools 実践:Schema から五つの本番級ツールへ

Tool は MCP の中核能力です。AI は tools/call 経由で副作用のある操作を実行します。各 Tool は名称、説明、inputSchema を公開する必要があり、FastMCP は Pydantic モデルでバリデーションを強化します。

python
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(params: SearchInput) -> str:
    """ドキュメントライブラリを検索する"""
    results = await index.search(params.query, params.limit)
    return json.dumps(results, ensure_ascii=False)

五つのよく使う Tool パターン

  • calculator:安全な eval または ast.literal_eval を使用し、exec は禁止します。
  • file_read / file_write:ルートディレクトリのホワイトリストを設け、パストラバーサルを防ぎます。
  • fetch_url(async):httpx で非同期リクエストし、timeout とドメインホワイトリストを設定します。
  • db_query:読み取り専用 SQL + パラメータ化クエリのみ許可し、DDL は禁止します。
  • get_current_time:ISO8601 タイムゾーン付き時刻を返し、LLM の時間幻覚を解消します。
python
import httpx
from datetime import datetime, timezone

@mcp.tool()
async def fetch_url(url: str) -> str:
    """HTTP GET で URL コンテンツを取得(ドメインホワイトリスト内)"""
    allowed = ("api.github.com", "nodemini.com")
    if not any(url.startswith(f"https://{d}") for d in allowed):
        raise ValueError(f"Domain not allowed: {url}")
    async with httpx.AsyncClient(timeout=10.0) as client:
        resp = await client.get(url)
        resp.raise_for_status()
        return resp.text[:8000]

@mcp.tool()
def get_current_time() -> str:
    """現在の UTC 時刻を返す"""
    return datetime.now(timezone.utc).isoformat()

エラーハンドリングのベストプラクティス

  • 構造化エラーを返す:raise ValueError("human-readable msg") を使うと、Client が message を LLM に返します。
  • リトライ可能 vs 致命的を区別:ネットワークタイムアウトは retryable とマークし、権限拒否は即 fail します。
  • ログとマスキング:tool 名と所要時間を記録し、API Key や PII の全文は log しません。
  • 冪等設計:書き込み操作に idempotency_key を付け、Agent の重複呼び出しによるデータ汚染を防ぎます。
06

Resources:読み取り専用コンテキストと URI スキーム

Tool vs Resource:Tool は副作用があり AI が能動的に呼び出します。Resource は読み取り専用コンテキストで、Host が会話前に注入するか、AI が resources/read で取得します。URI スキームは config://user://file:// など自由に定義できます。

python
@mcp.resource("config://app/settings")
def app_settings() -> str:
    """静的アプリ設定(text/plain)"""
    return open("config/settings.json").read()

@mcp.resource("user://{user_id}/profile")
def user_profile(user_id: str) -> str:
    """動的ユーザープロファイル(application/json)"""
    return json.dumps(get_user(user_id))

Resource コンテンツタイプ

MIME タイプ用途
text/plainログ、READMEfile://logs/app.log
application/json設定、API レスポンスconfig://env
application/octet-streamバイナリ(base64)PDF 要約
text/event-streamリアルタイム購読ログ tail、metrics ストリーム

ファイルシステム Resource Server パターン:resources/list でディレクトリをスキャン、resources/read で URI 読み取り、resources/subscribe で watchfiles 変更を監視して更新をプッシュ——コードベースドキュメントを Cursor Agent に公開するのに適しています。

07

Prompts:再利用可能な多段会話テンプレート

MCP Prompt はServer に登録された会話の骨格です。Client は prompts/get でメッセージリストを取得し、user / assistant ロールとパラメータプレースホルダを含められます——チームで Code Review や Incident フローを共有でき、各自が Prompt ファイルを保守する必要がありません。

python
from mcp.types import PromptMessage, TextContent

@mcp.prompt()
def code_review_prompt(language: str = "python") -> list[PromptMessage]:
    """標準化 Code Review 多段テンプレート"""
    return [
        PromptMessage(role="user", content=TextContent(
            type="text",
            text=f"あなたはシニア {language} エンジニアです。以下の diff を安全性・性能・可読性の三軸でレビューしてください。"
        )),
        PromptMessage(role="assistant", content=TextContent(
            type="text",
            text="diff を貼り付けるか PR 番号を指定してください。CHECKLIST に沿った構造化 review を出力します。"
        )),
    ]

多段テンプレートには {ticket_id}{severity} などの変数をネストでき、Server 側でバージョン管理すれば Client が Server をアップグレードするだけで全員が最新 review 基準に同期されます。

08

HTTP トランスポート:stdio から Streamable HTTP 本番デプロイへ

次元stdioHTTP + SSE / Streamable HTTP
デプロイローカル子プロセス、Host が起動独立サービス、URL 接続
スケール単一マシン、水平拡張困難ロードバランサ、複数レプリカ
認証Host 環境変数に依存Bearer Token / API Key / mTLS
デバッグInspector 直結curl + SSE クライアント
適用個人開発、Cursor ローカルチーム共有、SaaS 統合
python
# Streamable HTTP モード(FastMCP 2026)
from mcp.server.fastmcp import FastMCP

mcp = FastMCP("prod-server", host="0.0.0.0", port=8080)

# ... tools を登録 ...

if __name__ == "__main__":
    mcp.run(transport="streamable-http")

本番必須項目:Bearer Token 検証ミドルウェア、CORS ホワイトリスト(自社 Host ドメインのみ)、rate limit(例:100 req/min/IP)、HTTPS はリバースプロキシで終端します。リモートゲートウェイ運用の詳細は HTTP ゲートウェイ運用編 を参照してください。

warning

注意:HTTP MCP を認証なしで公網に公開しないでください——2026年現在も未認可で露出した Server が多数存在します。必ず認証と IP 制限を追加してください。

09

デバッグとテスト:Inspector + pytest ユニットテスト

MCP Inspector は公式の可視デバッガです。stdio または URL に接続後、tools/listtools/call を手動送信し JSON-RPC の往復を確認できます。Cursor ログを推測するより遥かに効率的です。

python
# tests/test_tools.py
import pytest
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client

@pytest.mark.asyncio
async def test_say_hello():
    params = StdioServerParameters(command="python", args=["-m", "src.server"])
    async with stdio_client(params) as (read, write):
        async with ClientSession(read, write) as session:
            await session.initialize()
            result = await session.call_tool("say_hello", {"name": "MCP"})
            assert "MCP" in result.content[0].text

よくあるエラー対照表

現象原因修正
Server 起動直後に終了stdout が print で汚染されているログは stderr へ、stdout への print 禁止
tools/list が空デコレータ未登録または import 順序エラー@mcp.tool()run() 前に実行されることを確認
Cursor が disconnected 表示command パス誤りまたは venv 未アクティブ絶対パスを使用、config に python フルパスを記載
JSON-RPC parse error非 JSON 出力が stdio に混入debug banner を無効化、ライブラリログレベル WARNING 以上
10

本番デプロイ:Docker、クラウドプラットフォームと可観測性

dockerfile
FROM python:3.12-slim
WORKDIR /app
COPY pyproject.toml .
RUN pip install --no-cache-dir .
COPY src/ src/
EXPOSE 8080
HEALTHCHECK CMD curl -f http://localhost:8080/health || exit 1
CMD ["python", "-m", "src.server", "--transport", "streamable-http"]

デプロイプラットフォーム選定:Railway / Render は迅速な検証に適しています。AWS ECS / GCP Cloud Run はエンタープライズコンプライアンス向きです。VPS + Docker Compose はコスト最低ですが、パッチ管理は自前です。

引用可能な本番パラメータ(EEAT)

  • プロトコルバージョン:MCP 2025-03-26 spec から Streamable HTTP をサポート。Client ハンドシェイク時に initializeprotocolVersion を協議し、Server は互換範囲を宣言します。
  • リソース基線:軽量 Tool Server は約 128MB RAM。ChromaDB ベクトルインデックス付きは 2GB RAM 以上と SSD 永続ボリュームを推奨します。
  • 可観測性スタック:構造化 JSON ログ → Loki/CloudWatch。Prometheus で /metrics を公開(tool 呼び出し QPS、P99 レイテンシ)。Sentry で未処理例外を捕捉。/health で K8s liveness を供給します。
11

実践プロジェクト:ChromaDB ナレッジベース MCP Server

社内 Wiki / Markdown ドキュメントをベクトル化し、index_documentsearch_knowledgewrite_note の三つの Tool を公開すれば、Cursor Agent が「社内ナレッジベースを検索してからコードを書く」ことができます。

要件:docs/ の増分インデックス(watchfiles 監視)、セマンティック検索 Top-K、オプションの scratchpad 書き込みをサポートします。

python
import chromadb
from chromadb.utils.embedding_functions import SentenceTransformerEmbeddingFunction

client = chromadb.PersistentClient(path="./data/chroma")
collection = client.get_or_create_collection(
    "wiki", embedding_function=SentenceTransformerEmbeddingFunction()
)

@mcp.tool()
def search_knowledge(query: str, top_k: int = 5) -> str:
    """社内ナレッジベースをセマンティック検索する"""
    hits = collection.query(query_texts=[query], n_results=top_k)
    return json.dumps(hits["documents"][0], ensure_ascii=False)

@mcp.tool()
def index_document(path: str) -> str:
    """単一 Markdown ファイルをインデックスする"""
    text = open(path).read()
    collection.upsert(ids=[path], documents=[text], metadatas=[{"path": path}])
    return f"Indexed: {path}"

Cursor デモクエリ:「ナレッジベースで MCP HTTP デプロイに関するドキュメントを検索し、三ステップの本番チェックリストを要約して」——Agent は先に search_knowledge を呼び、検索結果に基づいて回答します。大規模向けには Qdrant(リモート gRPC)への置き換えも可能です。

12

エコシステム、2026年トレンドと学習パス

公式およびコミュニティには、すぐ使える Server が多数存在します。車輪の再発明は不要です。

  • mcp-server-filesystem — 安全なサンドボックスファイル読み書き
  • mcp-server-github — PR/Issue API
  • brave-search / postgres / slack — 検索、SQL、チームコラボレーション

2026年トレンド:MCP Marketplace の台頭、OAuth 2.1 ツール認可が spec ロードマップに、Streamable HTTP が純 SSE を段階的に置き換え。学習パス checklist:① spec を読む → ② Hello World → ③ 3 つの Tool を書く → ④ Resource を追加 → ⑤ pytest → ⑥ Docker 本番 → ⑦ Cursor に接続。

まとめ

say_hello から ChromaDB ナレッジベースまで、MCP Server のフルスタック能力を習得しました。Tools 実行、Resources コンテキスト、Prompts テンプレート、二重トランスポート、テストと本番運用——次のステップは、コミュニティ Server を fork するか、社内 API をチーム標準ツール層としてカプセル化することです。

純ローカル stdio は個人実験に適していますが、複数 Server 並行、ベクトルインデックス常駐、HTTP 長接続は 16GB ノート PC で頻繁に swap を発生させます。安価な Linux VPS では macOS 専用ツールチェーンを動かせません。自前 HTTP ゲートウェイに session アフィニティと認証が欠けると、接続リークと未認可露出が起きやすく、長期安定性は期待どおりになりません。

MCP を本番インフラとして運用し、Cursor Agent と iOS/macOS CI を並行するチームには、MCP Server を独占可能なクラウド Mac 上で 7×24 稼働させる方が、ローカルノート PC や汎用 VM より制御しやすいことが多いです。NodeMini Mac Mini クラウドレンタルは MCP + Agent 実行層として機能し、基盤 LLM を変更しても SSH ノードと Server 設定はそのまま維持できます。スペックは レンタル料金、接続手順は ヘルプセンター をご覧ください。

FAQ

よくある質問

Python FastMCP は最も早く始められ、データ/スクリプト系ツールに適しています。TypeScript SDK は型安全で Node エコシステムとシームレスです。両者はプロトコル完全互換です。7×24 で複数 Server を稼働する場合は レンタル料金 も参照してください。

Function Calling は OpenAI にバインドされます。MCP は Claude/GPT/Gemini/Cursor を横断するオープンプロトコルで、Resources と Prompts もサポートします。背景は MCP プロトコル解説編 を参照してください。

軽量 stdio はローカルで動作します。複数 Server + ベクトル DB + HTTP 長接続 には独占リモート Mac を推奨します。接続手順は ヘルプセンター、運用は stdio 子プロセス運用編 と合わせてご覧ください。