MCPとは何か: プロトコルとしての立ち位置
Model Context Protocol(MCP)は2024年後半にAnthropicが公開した、LLMホストと外部ツールを接続するためのオープンプロトコルである。2026年4月現在、公式SDKはTypeScript・Python・Go・Rust・Javaの5言語に拡充され、Claude Code、Claude Desktop、VS Code Copilot、Zedなど主要ホストが標準対応している。いまやMCPは「LLM版のLanguage Server Protocol」と呼ばれ、ツール統合の事実上の標準となった。
プロトコルの骨格は極めてシンプルで、JSON-RPC 2.0上に tools/list、tools/call、resources/list、resources/read、prompts/list といったメソッドを定義しているだけである。トランスポートはstdioとStreamable HTTPの2種類があり、ローカル統合ではstdio、リモートサービスではHTTPが推奨される。重要なのは「ホストとサーバーが対称ではない」点で、サーバー側が能力を宣言し、ホスト側がユーザー同意を仲介する設計になっている。
最小MCPサーバーの骨格
TypeScript SDKでの実装は驚くほど簡潔だ。@modelcontextprotocol/sdk/server/mcp から McpServer を import し、registerTool で関数シグネチャを宣言するだけでプロトコル準拠のサーバーになる。
```typescript import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { z } from "zod";
const server = new McpServer({ name: "db-tools", version: "1.0.0" });
server.registerTool( "query_orders", { title: "Query Orders", description: "Run parameterized SELECT on orders table", inputSchema: { customer_id: z.string().uuid(), since: z.string().datetime().optional(), }, }, async ({ customer_id, since }) => { const rows = await db.query( "SELECT id, total, created_at FROM orders WHERE customer_id=$1 AND created_at > $2", [customer_id, since ?? "1970-01-01"] ); return { content: [{ type: "text", text: JSON.stringify(rows) }] }; } );
await server.connect(new StdioServerTransport()); ```
この15行強でClaude Codeから呼び出せるDBクエリツールが完成する。zodスキーマがそのままJSON Schemaに変換され、Claude側には型情報付きで提示される。
実務ユースケース: Slack、DB、ファイルシステム
Slack統合のMCPサーバーでは、send_message、search_messages、list_channels、get_thread の4ツールが典型的な最小セットになる。OAuth トークンは環境変数ではなくMCPのconfig経由で受け取るのが現代的で、ユーザーごとのトークン分離が容易になる。チャネルIDではなくチャネル名でLLMが指定できるよう、内部でキャッシュを持たせるのがUX上の定石だ。
DB問い合わせ系は「スキーマ露出の粒度」が設計の肝になる。全テーブルをlist_tablesで返すとトークン消費が膨れ上がるため、resources/listで階層的に提示する。resources/read時に初めてカラム定義を返す遅延読み込み戦略が性能・コスト両面で優れる。加えて、read_onlyフラグをサーバー側で強制し、DROP・DELETE・UPDATEをパーサレベルで弾く二重ガードが鉄則である。
ファイルシステムアクセスは最も危険なカテゴリで、公式のfilesystem serverもallowed_directoriesを起動時に明示列挙させる設計になっている。パストラバーサル攻撃への耐性を持たせるため、realpath解決後のallowlist判定が必須だ。シンボリックリンク経由で/etc/shadowを読み出せるような実装は2025年に複数のCVEが発行されている。
セキュリティモデル: 三層防御
MCPの権限モデルは三層構成である。第一層はホスト側のユーザー同意で、Claude Codeであれば .claude/settings.json の permissions で「どのツールを確認なしで許可するか」を宣言する。第二層はサーバー側の能力制限で、先述のread_onlyや allowed_directoriesがこれにあたる。第三層はOS層のサンドボックスで、macOSならsandbox-exec、Linuxならbwrapやnsjailで起動コマンドを包む。
特に公開プラグインとして配布する場合、第三層を前提にしてはいけない。多くのユーザーはサンドボックス設定をしないため、サーバー単体で「危険な操作は絶対にできない」設計にする責任がある。Anthropicのプラグインマーケットプレースは2026年1月から audit badge 制度を導入し、静的解析で shell 実行やネットワーク送信が検出されたプラグインに警告を表示するようになった。
パフォーマンス: 起動時間とストリーミング
stdioサーバーの起動時間はユーザー体験に直結する。Node.js実装で200ms以上かかる場合、Claudeのツール一覧取得が目に見えて遅延する。@modelcontextprotocol/sdk はtop-level awaitと動的importを最小化し、esbuildで単一ファイルにバンドルすることで50ms台まで短縮できる。Pythonサーバーはインタプリタ起動が遅いため、常駐プロセス化+UNIXソケット経由が実務解だ。
大きな結果を返すツールでは、content配列に type: "text" を複数エントリで返すストリーミング風の設計が有効である。SDK 0.9以降は正式にstreamable responsesをサポートし、ログ出力を逐次flushできるようになった。長時間走るマイグレーション系ツールでは必須機能と言える。
OpenAI Function Calling・ChatGPT Pluginsとの比較
OpenAI Function Callingは「チャット補完APIの拡張」であり、ツール定義をリクエストごとに送る必要がある。MCPは「プロトコル」であり、ツール定義は一度サーバー側に配置すれば全ホストから再利用できる。この再利用性の差が、エコシステム形成速度の決定的要因となった。
ChatGPT Plugins(現GPTs Actions)はOpenAPI仕様ベースで、HTTP前提のため認証・レイテンシ・状態管理が複雑化しがちだ。対してMCPはstdio中心なので、ローカル開発で即座に試せる。一方でリモート統合ではMCPのStreamable HTTPがまだ発展途上で、認可周りはOAuth 2.1ドラフトに追随しつつある段階だ。
結論として、LLM横断で使える資産を作るならMCP、単一サービスに閉じた統合ならFunction Callingというのが2026年時点の住み分けになっている。弊社KGA ITでもクライアントPoCはMCPで着手し、本番商用APIはFunction Callingに切り出す二段構成を標準パターンとしている。