コンテンツへスキップ

media AI活用の最前線

ツール比較・実践ガイド 32分で読めます

【2026年最新】MCP完全実装ガイド|AIエージェント統一プロトコル

結論から言う:MCPはAIエージェント開発の「配線コスト」を消滅させる標準プロトコルだ。一度MCPサーバーを実装すれば、Claude・ChatGPT・Gemini・Cursorのどれからでも同じツールを呼び出せる。

AIエージェントが複数のツールやデータソースを統合する際、これまで各サービスがバラバラなAPIを実装していた。そのたびに認証・スキーマ・エラー処理を個別に書き直す必要があり、エージェント開発の最大のボトルネックになっていた。

MCP(Model Context Protocol)はその問題を根本から解決する。JSON-RPC 2.0をベースにした統一プロトコルで、2024年11月にAnthropicが公開し、2025年12月にはOpenAI・Blockとともにlinux Foundation傘下AIアライアンス(AAIF)へ寄贈。2026年5月現在、Claude・ChatGPT・Gemini・Cursor・VS Codeが対応し、公開MCPサーバーは10,000以上に達している。

本記事では、MCPの仕様・Python/TypeScriptによるServer・Client実装・主要フレームワークとの比較を体系的に解説する。

MCPとは何か:統一プロトコルが解決する問題

MCP(Model Context Protocol)は、AIモデルと外部ツール・データソースの接続を標準化するオープンプロトコルだ。仕様バージョンは2025-11-25が最新で、modelcontextprotocol.ioで公開されている。

MCPが解決する問題

従来、AIエージェントが外部サービスを使うには:

  • サービスごとに異なるAPI仕様を解析する
  • 認証方式(APIキー・OAuth・Basic認証)を個別に実装する
  • エラーハンドリングとレスポンス形式を統一する処理を書く
  • スキーマ定義をLLMが理解できる形式に変換する

MCPはこれを「サーバーがプリミティブを提供し、クライアント(LLM)がそれを発見・呼び出す」という単一パターンに統一した。USB-Cに例えられることが多いが、むしろ「AIのためのHTTP」と考えると分かりやすい。

アーキテクチャ概要

MCPのアーキテクチャは3層構造だ:

  • MCP Host:Claude Desktop・Cursor・VS Code などのアプリケーション
  • MCP Client:HostとServerの間でJSONメッセージをルーティング
  • MCP Server:ツール・リソース・プロンプトを提供するプロセス

通信はJSON-RPC 2.0で行われ、リクエスト・レスポンスとサーバーからのNotificationで構成される。

4つのプリミティブ:Tools・Resources・Prompts・Sampling

MCPが定義するプリミティブは4種類。それぞれ明確な役割がある。

Tools(ツール)

LLMが呼び出せる実行可能な関数。データベース検索・API呼び出し・計算などを担う。Model-controlled(LLMが自律的に呼び出す)が基本。

ツールは以下のJSON Schemaで定義される:

{
  "name": "search_database",
  "description": "データベースからレコードを検索する",
  "inputSchema": {
    "type": "object",
    "properties": {
      "query": {
        "type": "string",
        "description": "検索クエリ文字列"
      },
      "limit": {
        "type": "integer",
        "description": "取得件数(デフォルト10)",
        "default": 10
      }
    },
    "required": ["query"]
  }
}

Resources(リソース)

ファイル・データベース・URLなどの静的データ。Application-controlledで、HostがどのリソースをLLMに渡すかを決定する。URIテンプレートで動的リソースも表現できる。

{
  "uri": "file:///workspace/config.json",
  "name": "設定ファイル",
  "mimeType": "application/json"
}

Prompts(プロンプトテンプレート)

再利用可能なプロンプトとワークフロー定義。ユーザーが明示的に選択して使う。コードレビュー・翻訳・要約といった定型タスクのテンプレートをサーバー側で管理できる。

Sampling(サンプリング)

MCPサーバーがクライアント(LLM)に対してLLM生成を要求できる機能。エージェント連鎖・ループ処理の実装に使われる。セキュリティ上の理由からユーザー承認が必須となっている。

AI活用、何から始めればいい?

100社以上の研修実績をもとに、30分の無料相談で貴社の課題を整理します。

無料相談はこちら 資料ダウンロード(無料)

トランスポート層:stdio・SSE・Streamable HTTP

MCPは3つのトランスポート方式をサポートしており、ユースケースによって使い分ける。

トランスポート用途特徴ステートフル
stdioローカルプロセス標準入出力を使用。CLIツール・ローカルサーバー向けYes
SSE(Server-Sent Events)レガシーリモートHTTP GET でストリーミング受信、POST で送信。旧仕様Yes
Streamable HTTPスケーラブルリモートステートレス対応。HTTP POST に対してSSEまたはJSON返却Optional

Claude Desktop・Cursorのデフォルトはstdio。ChatGPT(ChatGPT Apps SDK)はStreamable HTTPのみサポート。本番環境ではStreamable HTTPが推奨される。

Pythonによるサーバー実装:FastMCP API

Claude Agent SDKとの連携でも使われるPython SDKを使ってMCPサーバーを実装してみよう。

インストール

# Python SDK(FastMCP高レベルAPI含む)
pip install "mcp[cli]"

# または poetry
poetry add mcp

最小構成のサーバー(Tools + Resources)

from mcp.server.fastmcp import FastMCP
from mcp.types import TextContent

# FastMCPインスタンス作成
mcp = FastMCP("my-server")

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

@mcp.tool()
async def fetch_weather(city: str) -> str:
    """指定した都市の天気情報を取得する"""
    import httpx
    async with httpx.AsyncClient() as client:
        resp = await client.get(
            f"https://api.weather.example.com/v1/weather",
            params={"q": city, "lang": "ja"},
            timeout=10.0
        )
        data = resp.json()
        return f"{city}: {data['description']}, {data['temp']}°C"

# --- Resources ---
@mcp.resource("config://app")
def get_config() -> str:
    """アプリケーション設定を返す"""
    return '{"version": "1.0", "env": "production"}'

@mcp.resource("users://{user_id}/profile")
def get_user_profile(user_id: str) -> str:
    """ユーザープロフィールを返す(URIテンプレート)"""
    # 実際のDBアクセスに置き換える
    return f'{{"id": "{user_id}", "name": "ユーザー{user_id}"}}'

# --- Prompts ---
@mcp.prompt()
def code_review_prompt(code: str, language: str = "Python") -> str:
    """コードレビュープロンプトテンプレート"""
    return f"""以下の{language}コードをレビューしてください:

```{language.lower()}
{code}
```

確認ポイント:
1. セキュリティリスク
2. パフォーマンス問題
3. 可読性・保守性
4. ベストプラクティスへの準拠"""

# サーバー起動(stdioトランスポート)
if __name__ == "__main__":
    mcp.run()

Streamable HTTPトランスポートで起動

if __name__ == "__main__":
    import uvicorn
    # Streamable HTTP(本番向け)
    mcp.run(transport="http", host="0.0.0.0", port=8000)
    # または
    # uvicorn.run(mcp.streamable_http_app(), host="0.0.0.0", port=8000)

Pythonクライアントからの呼び出し

import asyncio
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client

async def main():
    server_params = StdioServerParameters(
        command="python",
        args=["server.py"],
    )

    async with stdio_client(server_params) as (read, write):
        async with ClientSession(read, write) as session:
            # 初期化
            await session.initialize()

            # 利用可能ツールを一覧取得
            tools = await session.list_tools()
            print("利用可能ツール:")
            for tool in tools.tools:
                print(f"  - {tool.name}: {tool.description}")

            # ツール呼び出し
            result = await session.call_tool("add", {"a": 3, "b": 5})
            print(f"3 + 5 = {result.content[0].text}")

            # リソース取得
            resource = await session.read_resource("config://app")
            print(f"設定: {resource.contents[0].text}")

asyncio.run(main())

TypeScriptによるサーバー実装

OpenAI Agents SDKはTypeScript版MCPクライアントを内包しており、相互運用が容易だ。TypeScript SDKでMCPサーバーを実装する方法を見ていこう。

インストール

npm install @modelcontextprotocol/sdk zod
# TypeScript開発環境
npm install -D typescript @types/node tsx

TypeScript MCPサーバー(stdioトランスポート)

import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import {
  CallToolRequestSchema,
  ListToolsRequestSchema,
  ListResourcesRequestSchema,
  ReadResourceRequestSchema,
} from "@modelcontextprotocol/sdk/types.js";
import { z } from "zod";

// サーバーインスタンス作成
const server = new Server(
  { name: "typescript-mcp-server", version: "1.0.0" },
  { capabilities: { tools: {}, resources: {} } }
);

// ツール定義
const tools = [
  {
    name: "search_docs",
    description: "ドキュメントを全文検索する",
    inputSchema: {
      type: "object" as const,
      properties: {
        query: { type: "string", description: "検索キーワード" },
        category: {
          type: "string",
          enum: ["api", "guide", "tutorial"],
          description: "カテゴリフィルター",
        },
      },
      required: ["query"],
    },
  },
  {
    name: "generate_code",
    description: "指定した仕様からコードスニペットを生成する",
    inputSchema: {
      type: "object" as const,
      properties: {
        spec: { type: "string", description: "生成する機能の仕様" },
        language: {
          type: "string",
          enum: ["python", "typescript", "go"],
          default: "typescript",
        },
      },
      required: ["spec"],
    },
  },
];

// ツール一覧ハンドラー
server.setRequestHandler(ListToolsRequestSchema, async () => ({
  tools,
}));

// ツール実行ハンドラー
server.setRequestHandler(CallToolRequestSchema, async (request) => {
  const { name, arguments: args } = request.params;

  switch (name) {
    case "search_docs": {
      const { query, category } = args as {
        query: string;
        category?: string;
      };
      // 実際の検索ロジックに置き換える
      const results = [
        { title: `${query}に関するドキュメント`, url: `/docs/${query}` },
        { title: `${query} APIリファレンス`, url: `/api/${query}` },
      ].filter((r) => !category || r.url.includes(category));

      return {
        content: [
          {
            type: "text" as const,
            text: JSON.stringify(results, null, 2),
          },
        ],
      };
    }

    case "generate_code": {
      const { spec, language = "typescript" } = args as {
        spec: string;
        language?: string;
      };
      return {
        content: [
          {
            type: "text" as const,
            text: `// ${spec}\n// Language: ${language}\n// TODO: 実装`,
          },
        ],
      };
    }

    default:
      throw new Error(`Unknown tool: ${name}`);
  }
});

// リソースハンドラー
server.setRequestHandler(ListResourcesRequestSchema, async () => ({
  resources: [
    {
      uri: "file:///workspace/README.md",
      name: "プロジェクトREADME",
      mimeType: "text/markdown",
    },
  ],
}));

server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
  const { uri } = request.params;
  if (uri === "file:///workspace/README.md") {
    return {
      contents: [
        {
          uri,
          mimeType: "text/markdown",
          text: "# My Project\n\nMCPサーバーの説明",
        },
      ],
    };
  }
  throw new Error(`Unknown resource: ${uri}`);
});

// サーバー起動
async function main() {
  const transport = new StdioServerTransport();
  await server.connect(transport);
  console.error("MCP Server running on stdio");
}

main().catch(console.error);

TypeScript MCPクライアント

import { Client } from "@modelcontextprotocol/sdk/client/index.js";
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";

async function main() {
  // stdioトランスポートでサーバーに接続
  const transport = new StdioClientTransport({
    command: "npx",
    args: ["tsx", "server.ts"],
  });

  const client = new Client(
    { name: "my-client", version: "1.0.0" },
    { capabilities: {} }
  );

  await client.connect(transport);

  // ツール一覧取得
  const { tools } = await client.listTools();
  console.log("利用可能ツール:", tools.map((t) => t.name));

  // ツール呼び出し
  const result = await client.callTool({
    name: "search_docs",
    arguments: { query: "MCP", category: "api" },
  });

  const content = result.content[0];
  if (content.type === "text") {
    console.log("検索結果:", content.text);
  }

  // リソース読み込み
  const { contents } = await client.readResource({
    uri: "file:///workspace/README.md",
  });
  console.log("README:", contents[0].text);

  await client.close();
}

main().catch(console.error);

主要クライアントとの設定方法

Claude Desktopへの登録(stdio)

~/Library/Application Support/Claude/claude_desktop_config.json(macOS)に追加:

{
  "mcpServers": {
    "my-python-server": {
      "command": "python",
      "args": ["/path/to/server.py"],
      "env": {
        "API_KEY": "your-api-key"
      }
    },
    "my-typescript-server": {
      "command": "npx",
      "args": ["tsx", "/path/to/server.ts"]
    }
  }
}

Cursorへの登録

~/.cursor/mcp.json(または設定UI):

{
  "mcpServers": {
    "my-server": {
      "command": "python",
      "args": ["/path/to/server.py"],
      "transport": "stdio"
    }
  }
}

OpenAI Responses APIからの利用

OpenAIは2025年4月からResponses APIでリモートMCPサーバーをネイティブサポートした。toolsパラメータにMCPサーバーURLを渡すだけで接続できる。

from openai import OpenAI

client = OpenAI()

response = client.responses.create(
    model="gpt-4o",
    tools=[
        {
            "type": "mcp",
            "server_label": "my-mcp-server",
            "server_url": "https://my-mcp-server.example.com/mcp",
            "require_approval": "never",  # 本番では"always"推奨
        }
    ],
    input="データベースから最新のユーザー情報を取得して",
)
print(response.output_text)

MCPと他プロトコルの比較

Claude Codeのサブエージェント並列実行でも外部ツール連携が重要になるが、MCPはその標準化レイヤーとして機能する。既存の手法との違いを整理しよう。

項目MCPOpenAI Function CallingLangChain ToolsAnthropic Tools API
プロトコルJSON-RPC 2.0(標準化済み)OpenAI独自仕様Python APIのみAnthropic独自仕様
マルチLLM対応Claude・ChatGPT・Gemini・Cursor等全対応OpenAIモデルのみ抽象化あり・実装依存Claudeのみ
サーバー分離サーバーは独立プロセス(再利用可)クライアント側に関数定義エージェントに直接定義クライアント側に定義
リソース概念あり(ファイル・DB・URLを標準化)なし限定的なし
Samplingあり(サーバーからLLM呼び出し)なしなしなし
エコシステム10,000+サーバー(2026年5月)プラグイン形式(廃止済み)独自ToolセットのみOSS小規模
標準化団体Linux Foundation / AAIF(2025年12月)OpenAI社内LangChain IncAnthropic社内
ライセンスMIT独自MIT独自

最大の差別化はサーバーの再利用性だ。MCPサーバーは一度実装すれば、Claude・ChatGPT・Cursor・VS Codeのどれからでも同じように使える。Function CallingやTools APIは実装をLLMごとに書き直す必要がある。

MCPエコシステムの現状(2026年5月)

MCPは発表から1年半で急速に普及した。主要な数字をまとめる。

サーバー・SDK規模

  • 公開MCPサーバー:10,000以上(GitHub・npm・PyPI合計)
  • Python/TypeScript SDKの月間ダウンロード:9,700万回
  • 主要公式サーバー:GitHub・Slack・Google Drive・Notion・Postgres・Puppeteer・Brave Search など

対応クライアント(2026年5月時点)

  • Claude Desktop / Claude.ai:2024年11月からネイティブサポート
  • ChatGPT:2025年9月(Plus/Pro向け、Streamable HTTPのみ)
  • Google Gemini:2026年3月(Workspace連携)
  • Cursor:エディタに直接統合、stdio/SSE対応
  • Windsurf:Cascade AIのMCP統合
  • VS Code:GitHub Copilot経由でMCPサポート
  • JetBrains AI Assistant:2025年末から
  • Zed:拡張形式でMCP統合

ガバナンス

2025年12月、AnthropicはMCP仕様をLinux Foundationの傘下のAI Alliance(AAIF)に寄贈した。OpenAIとBlockも共同創設者として参加。特定ベンダーに依存しない中立的なガバナンスが確立された。

本番運用でのベストプラクティス

セキュリティ設計

MCPサーバーをプロダクションにデプロイする際の注意点:

from mcp.server.fastmcp import FastMCP
from functools import wraps
import hmac, hashlib, os

mcp = FastMCP("secure-server")

# APIキーを環境変数から読み込む
API_KEY = os.environ.get("MCP_API_KEY", "")

def require_auth(func):
    """ツール呼び出し時の認証デコレーター"""
    @wraps(func)
    async def wrapper(*args, **kwargs):
        # 実際にはHTTPヘッダーから取得
        # MCPではSamplingリクエストにメタデータを付与可能
        return await func(*args, **kwargs)
    return wrapper

@mcp.tool()
@require_auth
async def sensitive_operation(query: str) -> str:
    """認証が必要なツール"""
    # 入力サニタイズ
    sanitized = query.replace(";", "").replace("--", "")[:500]
    return f"処理完了: {sanitized}"

# レート制限(サンプル)
from collections import defaultdict
import time

_call_counts: dict[str, list[float]] = defaultdict(list)

@mcp.tool()
def rate_limited_tool(input: str) -> str:
    """レート制限付きツール(1分10回まで)"""
    now = time.time()
    client_id = "default"  # 実際はセッションIDを使用

    # 1分以内のコール数を確認
    recent = [t for t in _call_counts[client_id] if now - t < 60]
    if len(recent) >= 10:
        raise RuntimeError("レート制限超過: 1分あたり10回まで")

    _call_counts[client_id] = recent + [now]
    return f"処理: {input}"

エラーハンドリング

from mcp.server.fastmcp import FastMCP
from mcp.types import TextContent
import logging

logger = logging.getLogger(__name__)
mcp = FastMCP("robust-server")

@mcp.tool()
async def fetch_data(url: str) -> str:
    """エラーハンドリング付きデータ取得"""
    import httpx

    try:
        async with httpx.AsyncClient(timeout=10.0) as client:
            resp = await client.get(url)
            resp.raise_for_status()
            return resp.text[:5000]  # 上限設定
    except httpx.TimeoutException:
        logger.warning(f"タイムアウト: {url}")
        return "エラー: リクエストがタイムアウトしました(10秒)"
    except httpx.HTTPStatusError as e:
        logger.error(f"HTTPエラー {e.response.status_code}: {url}")
        return f"エラー: サーバーが {e.response.status_code} を返しました"
    except Exception as e:
        logger.exception(f"予期しないエラー: {url}")
        return f"エラー: {type(e).__name__}"

テスト戦略

import pytest
import asyncio
from mcp import ClientSession
from mcp.client.stdio import stdio_client, StdioServerParameters

@pytest.mark.asyncio
async def test_add_tool():
    server_params = StdioServerParameters(
        command="python", args=["server.py"]
    )
    async with stdio_client(server_params) as (read, write):
        async with ClientSession(read, write) as session:
            await session.initialize()
            result = await session.call_tool("add", {"a": 2, "b": 3})
            assert result.content[0].text == "5"

@pytest.mark.asyncio
async def test_tool_list():
    server_params = StdioServerParameters(
        command="python", args=["server.py"]
    )
    async with stdio_client(server_params) as (read, write):
        async with ClientSession(read, write) as session:
            await session.initialize()
            tools = await session.list_tools()
            tool_names = [t.name for t in tools.tools]
            assert "add" in tool_names
            assert "fetch_weather" in tool_names

AIエージェントとMCPの統合パターン

MCPはエージェントフレームワークとの統合が急速に進んでいる。AIエージェント完全ガイドでも解説している通り、ツール連携はエージェントの核心機能だ。またClaude Codeのサブエージェント並列実行でも、MCPサーバーをツールとして接続するパターンが増えている。

Claude Agent SDKとMCPの組み合わせ

import anthropic

client = anthropic.Anthropic()

# MCPサーバーをAnthropicクライアントから直接使う場合
# (Claude Desktopのconfig経由が一般的だが、Responses相当はAPI経由でも)
response = client.messages.create(
    model="claude-opus-4-5",
    max_tokens=4096,
    tools=[
        {
            "name": "search_docs",
            "description": "ドキュメントを検索する",
            "input_schema": {
                "type": "object",
                "properties": {
                    "query": {"type": "string"}
                },
                "required": ["query"]
            }
        }
    ],
    messages=[
        {"role": "user", "content": "MCPの仕様について調べて"}
    ]
)

よくある落とし穴と対処法

MCPサーバーを本番で運用する際に遭遇しやすい問題:

  1. stdioバッファリング問題:サーバーが大量データを返す際にバッファが詰まる。sys.stdout.flush()を明示的に呼ぶか、print(..., flush=True)を使う
  2. セッション切断の検知漏れ:クライアントが突然切断された際の後片付け処理を必ず実装する。FastMCPの@mcp.on_shutdownを使うと安全
  3. ツール定義の過剰化:ツール数が多すぎるとLLMのツール選択精度が下がる。20個以上になったらサーバーを分割し、用途別に接続を切り替える
  4. JSONスキーマの型不一致:LLMが文字列で渡してくる数値をintとして受け取ると型エラーになる。Union[int, str]でフォールバック処理を入れるか、Pydanticのcoercionを活用する

まとめ:MCPが変えるAIエージェント開発の未来

MCPは「AIのためのUSB規格」として、エージェント開発のコスト構造を根本から変えつつある。

  • 開発コスト削減:一度MCPサーバーを作れば、Claude・ChatGPT・Gemini・Cursorで同じツールが動く。プロバイダーごとの実装が不要になる
  • エコシステムの恩恵:10,000以上の公開サーバーを即座に使えるため、Slack・GitHub・Notion・Postgres連携を数分で実現できる
  • スタンダード化リスクの低減:Linux Foundationへの寄贈により、特定ベンダーによる仕様変更リスクが最小化された

Python FastMCPかTypeScript SDKのどちらかで最初の1サーバーを作ることをすすめたい。既存のAPIラッパーを数十行でMCPサーバーに変換できる。一度体験すると、なぜこのプロトコルが急速に普及しているかが肌感覚でわかるはずだ。

AIエージェント開発・MCP統合を社内に展開しませんか

Uravationでは、MCP対応エージェント開発・Claude Code活用の法人研修を提供しています。

研修内容を問い合わせる


📅 5月開催|Uravation主催 Zoomウェビナー

講師: 株式会社Uravation代表 佐藤傑(X @SuguruKun_ai) / Yusei Tataka


あわせて読みたい:

参考・出典

執筆者:佐藤傑(Uravation CEO)
生成AIの企業導入・研修を手がける株式会社Uravation代表。MCP・エージェントフレームワークの法人研修を国内複数社に提供。Claude Code・OpenAI SDK・LangGraphを日常業務で活用。

関連記事: AIエージェントセキュリティ完全ガイド|OWASP対応 — AIエージェントのセキュリティ実装ガイドです。

佐藤傑
この記事を書いた人 佐藤傑

株式会社Uravation代表取締役。早稲田大学法学部在学中に生成AIの可能性に魅了され、X(旧Twitter)で活用法を発信(@SuguruKun_ai、フォロワー10万人超)。100社以上の企業向けAI研修・導入支援を展開。著書累計3万部突破。SoftBank IT連載7回執筆(NewsPicks最大1,125ピックス)。

この記事をシェア

📧 週1回、AIツール最新情報をお届け

Claude Code・Codex・Cursorなど最新AI実務情報を、月8-12本の厳選記事から要約してメール配信。すでに3,000人以上のAI担当者が購読中です。

※ いつでも登録解除できます。配信頻度は週1〜2回程度。

AIエージェントを企業に安全に導入したい方へ

Claude Code・OpenClaw・Codex等のAIエージェントを、ガバナンス設計込みで導入支援。権限制御・監査ログ・停止条件まで含めた「ハーネス設計」で運用リスクをゼロに。

✓ 1対1のマンツーマン ✓ 全12回・3ヶ月 ✓ 実務ベースの指導
AIエージェント導入支援を見る まずは無料相談

contact お問い合わせ

生成AI研修や開発のご依頼、お見積りなど、
お気軽にご相談ください。

Claude Code 個別指導(1対1・12セッション)をご希望の方はこちらから別途お申し込みください

Claude Code 個別指導 無料相談