Back

Model Context Protocol (MCP): 本当に動くAIエージェントを構築する完全ガイド

2025年にAIアプリケーションを作っていると、みんな同じ壁にぶつかりますよね。LLMはテキスト生成は神がかってるのに、実際のDBやAPIに繋ごうとすると途端にAPIを無理やり繋ぎ合わせてプロンプトで祈る羽目になる。

そこで登場したのが**Model Context Protocol(MCP)**です。AI開発においてREST APIレベルの基盤になりつつあるオープン標準です。Anthropicが作って業界全体で採用が進んでおり、AIエンジニアリング最大の課題を解決します:AIエージェントに外部世界への安全なアクセスをどう与えるか?

このガイドでは、MCPとは何か、なぜ重要か、どう動くか、そして最も大事な実装方法まで全て解説します。

MCPが解決する問題

MCPを理解する前に、どんな苦痛を解決するのか見ていきましょう。

インテグレーション地獄

従来のAIアプリケーション開発はこんな感じでした:

┌─────────────────────────────────────────────────────────────┐
│                    あなたのAIアプリケーション                   │
├─────────────────────────────────────────────────────────────┤
│                                                              │
│   ┌──────────┐    ┌──────────┐    ┌──────────┐             │
│   │ OpenAI   │    │ Database │    │ Slack    │             │
│   │ API      │    │ Queries  │    │ API      │             │
│   └────┬─────┘    └────┬─────┘    └────┬─────┘             │
│        │               │               │                    │
│   カスタムパーサー  カスタムパーサー  カスタムパーサー           │
│        │               │               │                    │
│   プロンプトハック  プロンプトハック  プロンプトハック           │
│        │               │               │                    │
│   エラーハンドラー  エラーハンドラー  エラーハンドラー           │
│        │               │               │                    │
│        └───────────────┴───────────────┘                    │
│                        │                                     │
│              ┌─────────▼─────────┐                          │
│              │  スパゲッティコード │                          │
│              └───────────────────┘                          │
└─────────────────────────────────────────────────────────────┘

各インテグレーションで必要なもの:

  • カスタム認証処理
  • 専用レスポンスパース
  • LLMにツールを説明するプロンプトエンジニアリング
  • インテグレーションごとに違うエラーハンドリング
  • 手動スキーマ管理

典型的なAIエージェントが10個以上のインテグレーションを必要とすると、メンテナンス地獄になります。

Function Callingの限界

OpenAIのfunction callingは助けになりますが、根本的にLLMベンダー固有です。GPT-4向けに丁寧に作った関数定義がClaude、Gemini、最新のオープンソースモデルでは動きません。

// OpenAIではこう... const tools = [{ type: "function", function: { name: "get_weather", description: "現在の天気を取得", parameters: { type: "object", properties: { location: { type: "string" } } } } }]; // でもClaudeは別フォーマット... // Geminiも違う... // Llamaも違う...

本当に必要なもの

理想的なソリューションは:

  1. ユニバーサル: LLMプロバイダー問わず動作
  2. 標準化: 全データソースに一つのインテグレーションパターン
  3. 双方向: AIがデータ取得も更新受信もできる
  4. セキュア: 認証と権限管理が組み込み
  5. 発見可能: AIがランタイムで利用可能なツールを学習

MCPはまさにこれを提供します。

Model Context Protocolとは?

MCPはAIアプリケーションが外部データソースやツールに接続する方法を標準化するオープンプロトコルです。**「AI用USB」**と考えてください。どのAIモデルでもどのデータソースにも差し込めるユニバーサルコネクタです。

アーキテクチャ

MCPはクライアント・サーバーアーキテクチャです:

┌────────────────────────────────────────────────────────────────┐
│                        MCPアーキテクチャ                         │
├────────────────────────────────────────────────────────────────┤
│                                                                 │
│  ┌─────────────────┐         ┌─────────────────────────────┐  │
│  │   MCP Client    │         │        MCP Servers          │  │
│  │                 │         │                             │  │
│  │  ┌───────────┐  │         │  ┌─────────┐ ┌─────────┐   │  │
│  │  │ AI Model  │  │ JSON-RPC│  │ GitHub  │ │ Slack   │   │  │
│  │  │(GPT/Claude│◄─┼─────────┼─►│ Server  │ │ Server  │   │  │
│  │  │ /Gemini)  │  │   over  │  └─────────┘ └─────────┘   │  │
│  │  └───────────┘  │  stdio/ │                             │  │
│  │                 │ SSE/WS  │  ┌─────────┐ ┌─────────┐   │  │
│  │  ┌───────────┐  │         │  │Database │ │ Custom  │   │  │
│  │  │Host App   │  │         │  │ Server  │ │ Server  │   │  │
│  │  │(Your App) │  │         │  └─────────┘ └─────────┘   │  │
│  │  └───────────┘  │         │                             │  │
│  └─────────────────┘         └─────────────────────────────┘  │
│                                                                 │
└────────────────────────────────────────────────────────────────┘

コアコンポーネント:

  1. MCP Client: AIアプリ内にある。MCPサーバーを発見して接続。
  2. MCP Server: データソースやツールを標準フォーマットで公開。
  3. Transport Layer: stdio、SSE、WebSocket上のJSON-RPC 2.0。

3つのプリミティブ

MCPはAI-外部世界のほぼ全てのやり取りをカバーする3つのコア要素を定義:

1. Resources

AIが読める静的・動的データ。AIがアクセスできる「ファイル」と思ってください。

{ "uri": "file:///project/README.md", "name": "Project README", "mimeType": "text/markdown" }

2. Tools

AIがアクションを実行するために呼び出せる関数。

{ "name": "create_github_issue", "description": "GitHubリポジトリに新しいissueを作成", "inputSchema": { "type": "object", "properties": { "repo": { "type": "string" }, "title": { "type": "string" }, "body": { "type": "string" } }, "required": ["repo", "title"] } }

3. Prompts

パラメータ付きで呼び出せる再利用可能なプロンプトテンプレート。

{ "name": "code_review", "description": "ベストプラクティスのコードレビュー", "arguments": [ { "name": "code", "description": "レビューするコード", "required": true } ] }

なぜ今MCPが重要なのか

AIエージェント爆発

2025年はAIエージェントの年です。OpenAIのOperatorからClaudeのコンピュータ利用機能まで、AIはチャットを超えて自律行動に移行しています。でも裏の真実があります:自律AIは実世界へのアクセスの質でしか賢くならない

以下を確実にできないAIエージェントは:

  • コードベースを読む
  • データベースにクエリする
  • カレンダーを確認する
  • チームにメッセージを送る

...ただの高いチャットボットです。

MCPはこれらの統合を信頼性高く、一貫して、メンテナンス可能にします。

標準化の瞬間

2000年代初頭のWebサービス時代と同じ転換点にいます。当時はCORBA、DCOM、プロプライエタリプロトコルが覇権を争っていました。RESTが勝ち、突然みんなが相互運用可能なWebサービスを作れるようになりました。

MCPはAI統合のRESTになろうとしています。主要プレイヤーはすでに参加:

  • Anthropic: プロトコルを作成・維持
  • Microsoft: Copilotに統合中
  • Cursor: AI IDEでネイティブMCPサポート
  • Sourcegraph: コードインテリジェンス用MCPサーバー

MCPサーバーを動かすのは新しいWebサーバーを動かすこと

大胆な予測:2026年には「MCPサーバー動かせる?」が今日の「REST API作れる?」と同じくらい一般的な面接質問になります。

なぜ?価値あるデータを持つ全ての企業がAIエージェントに制御された標準化された方法で公開したいからです。つまり:

  • 社内ドキュメント用MCPサーバー
  • 顧客データ用(適切な認可付き)
  • ビジネスプロセス用
  • ドメイン固有ツール用

最初のMCPサーバーを構築

実際にやってみましょう。シンプルなTodoリストAPIを公開するMCPサーバーを作ります。

プロジェクトセットアップ

# 新規プロジェクト作成 mkdir mcp-todo-server cd mcp-todo-server npm init -y # 依存関係インストール npm install @modelcontextprotocol/sdk zod npm install -D typescript @types/node tsx

基本サーバー構造

src/index.tsを作成:

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"; // インメモリTodoストレージ interface Todo { id: string; title: string; completed: boolean; createdAt: Date; } const todos: Map<string, Todo> = new Map(); // MCPサーバー作成 const server = new Server( { name: "todo-server", version: "1.0.0", }, { capabilities: { tools: {}, resources: {}, }, } ); // 利用可能なツール一覧 server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: [ { name: "create_todo", description: "新しいTodoアイテムを作成", inputSchema: { type: "object", properties: { title: { type: "string", description: "Todoアイテムのタイトル", }, }, required: ["title"], }, }, { name: "complete_todo", description: "Todoアイテムを完了にする", inputSchema: { type: "object", properties: { id: { type: "string", description: "完了にするTodoアイテムのID", }, }, required: ["id"], }, }, { name: "delete_todo", description: "Todoアイテムを削除", inputSchema: { type: "object", properties: { id: { type: "string", description: "削除するTodoアイテムのID", }, }, required: ["id"], }, }, ], }; }); // ツール呼び出し処理 server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; switch (name) { case "create_todo": { const id = crypto.randomUUID(); const todo: Todo = { id, title: args.title as string, completed: false, createdAt: new Date(), }; todos.set(id, todo); return { content: [ { type: "text", text: JSON.stringify({ success: true, todo }, null, 2), }, ], }; } case "complete_todo": { const todo = todos.get(args.id as string); if (!todo) { return { content: [ { type: "text", text: JSON.stringify({ error: "Todoが見つかりません" }) }, ], isError: true, }; } todo.completed = true; return { content: [ { type: "text", text: JSON.stringify({ success: true, todo }, null, 2), }, ], }; } case "delete_todo": { const deleted = todos.delete(args.id as string); return { content: [ { type: "text", text: JSON.stringify({ success: deleted }, null, 2), }, ], }; } default: return { content: [ { type: "text", text: JSON.stringify({ error: "不明なツール" }) }, ], isError: true, }; } }); // 利用可能なリソース一覧 server.setRequestHandler(ListResourcesRequestSchema, async () => { return { resources: [ { uri: "todo://list", name: "Todoリスト", description: "現在の全Todoアイテム一覧", mimeType: "application/json", }, ], }; }); // リソース読み取り server.setRequestHandler(ReadResourceRequestSchema, async (request) => { if (request.params.uri === "todo://list") { const todoList = Array.from(todos.values()); return { contents: [ { uri: "todo://list", mimeType: "application/json", text: JSON.stringify(todoList, null, 2), }, ], }; } throw new Error(`不明なリソース: ${request.params.uri}`); }); // サーバー起動 async function main() { const transport = new StdioServerTransport(); await server.connect(transport); console.error("Todo MCPサーバーがstdioで起動しました"); } main().catch(console.error);

Claude Desktop設定

Claude Desktopで使うにはclaude_desktop_config.jsonに追加:

{ "mcpServers": { "todo": { "command": "npx", "args": ["tsx", "/path/to/mcp-todo-server/src/index.ts"] } } }

これでClaudeが:

  • Todo作成:「買い物リストにTodo追加して」
  • Todo完了:「買い物Todo完了にして」
  • Todo一覧:「今のTodoリスト見せて」

高度なMCPパターン

パターン1:データベース統合

最も強力なMCP活用の一つがAIにDB読み取り(時には書き込み)アクセスを与えること:

import { Pool } from 'pg'; const pool = new Pool({ connectionString: process.env.DATABASE_URL, }); server.setRequestHandler(CallToolRequestSchema, async (request) => { if (request.params.name === "query_database") { const { query } = request.params.arguments as { query: string }; // 重要:クエリ検証とサニタイズ if (!isReadOnlyQuery(query)) { return { content: [{ type: "text", text: "SELECTクエリのみ許可されています" }], isError: true, }; } try { const result = await pool.query(query); return { content: [{ type: "text", text: JSON.stringify(result.rows, null, 2), }], }; } catch (error) { return { content: [{ type: "text", text: `クエリエラー: ${error.message}` }], isError: true, }; } } }); function isReadOnlyQuery(query: string): boolean { const normalized = query.trim().toLowerCase(); return normalized.startsWith('select') && !normalized.includes('into') && !normalized.includes('update') && !normalized.includes('delete') && !normalized.includes('insert') && !normalized.includes('drop') && !normalized.includes('alter'); }

パターン2:OAuth統合

ユーザー認証が必要なAPI向け:

import { OAuth2Client } from 'google-auth-library'; const oauth2Client = new OAuth2Client( process.env.GOOGLE_CLIENT_ID, process.env.GOOGLE_CLIENT_SECRET, 'http://localhost:3000/callback' ); server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: [ { name: "list_calendar_events", description: "今後のカレンダーイベント一覧", inputSchema: { type: "object", properties: { maxResults: { type: "number", description: "返す最大イベント数", default: 10, }, }, }, }, ], }; });

パターン3:長時間処理と進捗

時間のかかる処理はMCPが進捗通知をサポート:

server.setRequestHandler(CallToolRequestSchema, async (request, extra) => { if (request.params.name === "analyze_codebase") { const files = await getAllFiles(request.params.arguments.path); const total = files.length; for (let i = 0; i < files.length; i++) { // 進捗更新を送信 await extra.sendNotification({ method: "notifications/progress", params: { progressToken: request.params._meta?.progressToken, progress: i, total, }, }); await analyzeFile(files[i]); } return { content: [{ type: "text", text: `${total}ファイルの分析完了`, }], }; } });

MCPセキュリティベストプラクティス

1. 全入力を検証

AIからのデータを絶対に信頼しない。常に検証:

import { z } from 'zod'; const CreateTodoSchema = z.object({ title: z.string().min(1).max(200), priority: z.enum(['low', 'medium', 'high']).optional(), }); server.setRequestHandler(CallToolRequestSchema, async (request) => { if (request.params.name === "create_todo") { const parsed = CreateTodoSchema.safeParse(request.params.arguments); if (!parsed.success) { return { content: [{ type: "text", text: `検証エラー: ${parsed.error.message}`, }], isError: true, }; } // parsed.dataは型安全で検証済み } });

2. Rate Limiting実装

暴走するAIエージェントから保護:

import { RateLimiter } from 'limiter'; const limiter = new RateLimiter({ tokensPerInterval: 100, interval: 'minute', }); server.setRequestHandler(CallToolRequestSchema, async (request) => { if (!await limiter.tryRemoveTokens(1)) { return { content: [{ type: "text", text: "レート制限超過。後でやり直してください。", }], isError: true, }; } // リクエスト処理... });

3. 監査ログ

セキュリティとデバッグのため全ツール呼び出しをログ:

function logToolInvocation(name: string, args: unknown, result: unknown) { console.log(JSON.stringify({ timestamp: new Date().toISOString(), tool: name, arguments: args, result: result, })); }

4. 最小権限の原則

必要なものだけ公開:

// ダメ:生DBアクセスを公開 tools: [{ name: "execute_sql", description: "任意のSQLクエリを実行", // セキュリティ悪夢! }] // 良い:特定範囲の操作を公開 tools: [ { name: "get_user_orders", description: "特定ユーザーの注文を取得", inputSchema: { type: "object", properties: { userId: { type: "string" }, limit: { type: "number", maximum: 100 }, }, required: ["userId"], }, }, ]

本番MCP:学んだ教訓

教訓1:失敗を想定した設計

AIエージェントは予想外の方法でツールを呼びます。防御的に作る:

server.setRequestHandler(CallToolRequestSchema, async (request) => { try { const result = await handleTool(request); return result; } catch (error) { // AIが復旧できる構造化エラーを返す return { content: [{ type: "text", text: JSON.stringify({ error: error.message, suggestion: "別のパラメータで試してください", validExamples: [ { title: "買い物" }, { title: "母に電話" }, ], }), }], isError: true, }; } });

教訓2:豊富な説明を提供

ツール説明の質がAIの使い方に直結:

// ダメ { name: "search", description: "アイテム検索", } // 良い { name: "search_products", description: `商品カタログを検索。クエリに一致する最大20商品を返却。 カテゴリ、価格帯、在庫状況フィルター対応。結果には商品名、価格、 在庫状態、サムネイルURLを含む。最良の結果のため、 一般的な単語より具体的な商品名やカテゴリを推奨。`, inputSchema: { type: "object", properties: { query: { type: "string", description: "検索クエリ。例:'ワイヤレスヘッドホン'、'10万円以下のノートPC'", }, }, }, }

教訓3:サーバーバージョン管理

MCPサーバーの進化時に後方互換性を維持:

const server = new Server( { name: "my-server", version: "2.1.0", // セマンティックバージョニング }, { capabilities: { tools: {}, resources: {}, }, } ); // 移行中は新旧ツール名両方サポート server.setRequestHandler(CallToolRequestSchema, async (request) => { const name = request.params.name; // レガシーツール名を処理 if (name === "old_tool_name") { console.warn("非推奨: 'new_tool_name'を使ってください"); return handleNewTool(request); } if (name === "new_tool_name") { return handleNewTool(request); } });

MCPの未来

今後の展開

  1. ストリーミングレスポンス: ツール出力ストリーミングのファーストクラスサポート
  2. マルチモーダルツール: 画像、音声、動画を返すツール
  3. ツール合成: 複数ツールをワークフローに結合
  4. 強化セキュリティ: 組み込みOAuthフローと権限スコープ

MCP vs. 代替手段

機能MCPOpenAI FunctionsLangChain Tools
ベンダー非依存
標準化プロトコル
リソースアクセス内蔵
進捗通知部分的
コミュニティサーバー成長中N/A限定的

まとめ:MCP学習のベストタイミング

MCPはまだ初期段階ですが、方向性は明確です。2010年代にRESTを知らなければWeb開発者じゃなかったように、2020年代のAI開発者はMCPを知る必要があります。

ポイントまとめ:

  1. MCP = AI-外部世界連携の標準
  2. まず簡単なサーバーから — SDKが簡単にしてくれる
  3. セキュリティは最優先 — AIは予測不能
  4. 説明を丁寧に — AIが読むAPIドキュメント
  5. エラー対応をしっかり — AIが復旧できるように

MCPを早く習得したチームがAI時代で先行します。まだAPIを無理やり繋いでるチームより遥かに速く動けますから。

質問は「MCP学ぶべき?」じゃなくて、先行するか後追いするかです。


クイックリファレンス:MCP概念

概念説明
Serverツールとリソースを公開DBアクセスサーバー
Clientサーバーに接続、AIが使用Claude Desktop
Tool呼び出し可能な関数create_github_issue
Resource読み取り可能なデータfile:///project/README.md
Prompt再利用可能なテンプレートコードレビューテンプレート
Transport通信レイヤーstdio, SSE, WebSocket

リソース:

mcpai-agentsllmanthropicopenaiai-engineeringmodel-context-protocol

関連ツールを見る

Pockitの無料開発者ツールを試してみましょう