メインコンテンツへスキップ
友田 陽大
本番LLMアプリ実装(信頼できるAI)
MCP
Model Context Protocol
生成AI
LLM
AIエージェント
TypeScript
セキュリティ
OAuth
アーキテクチャ設計
技術選定

MCP(Model Context Protocol)本番サーバー実装ガイド 2026|アーキテクチャ・トランスポート・認可・セキュリティ

Model Context Protocol(MCP)で本番に耐えるサーバーを実装するための実践ガイド。host/client/serverアーキテクチャ、stdio/Streamable HTTPトランスポート、公式TypeScript SDK(v1)の正しいAPI、OAuth 2.1認可(RFC 9728/8707・PKCE・トークンaudience検証)、ツール安全設計と公式セキュリティベストプラクティスまで、一次情報準拠のTypeScript実コードで解説します。

公開日
読了時間
12分
著者
友田 陽大
シェア

生成AIを「賢いチャット」から「実際に業務を動かすシステム」に変える鍵は、モデルを外部のツール・データ・業務ロジックに安全に繋ぐことです。その接続を標準化したのが MCP(Model Context Protocol) ——AIモデルとツールの間の“共通端子”です。ツールごとに独自連携を作らず、1つのプロトコルで繋ぐ。だからこそ2025年以降、主要なAIクライアントとプラットフォームが一斉に採用しました。

しかしMCPサーバーを本番に載せるときの勝負どころは、プロトコルの物珍しさではありません。 「アーキテクチャの理解」「トランスポートの選択」「OAuth 2.1認可」、そして何より「ツール=任意コード実行という前提に立ったセキュリティ設計」です。この記事は、現行の安定仕様(2025-11-25)と公式TypeScript SDK(v1系)に厳密準拠し、本番で効く実装だけを実コードで示します。

本記事のバージョン・API・セキュリティ要件は、すべて公式仕様(modelcontextprotocol.io)で確認した一次情報に基づきます。仕様は日付リビジョンで更新されるため、実装前に必ず出典を確認してください。

MCPの全体像:host / client / server と3つのプリミティブ

MCPは JSON-RPC 2.0 をベースにした、ステートフルな接続プロトコルです。登場人物は3者。

役割責務(公式定義の要約)
Hostコンテナ兼コーディネータ。複数のクライアントを生成・管理し、接続許可とライフサイクル、セキュリティポリシーと同意(ユーザー認可)を強制、LLM連携とコンテキスト集約を担う
ClientHostが生成し、1つのサーバーと1:1のステートフルなセッションを張る。プロトコル交渉と双方向メッセージのルーティングを担当
Server特化した機能を提供。Tools / Resources / Prompts を公開し、独立して動作。セキュリティ制約を尊重しなければならない

サーバーが公開する3つのプリミティブ(ここが実装の中心):

  • Tools(ツール) — 「AIモデルが実行する関数」。副作用を伴う操作の入口。最も注意が必要
  • Resources(リソース) — 「ユーザーやAIが使うコンテキスト・データ」。ファイルやDBレコードなど。
  • Prompts(プロンプト) — 「ユーザー向けのテンプレート化されたメッセージ/ワークフロー」。

さらにクライアント側が提供する機能(サーバーから要求できる)として Sampling(サーバー起点のLLM呼び出し)・Roots(操作範囲のURI/ファイル境界)・Elicitation(ユーザーへの追加入力要求) があります。どの機能が使えるかは、初期化時の能力ネゴシエーション(capability negotiation) で双方が明示的に宣言して決まります。

トランスポートの選択:stdio か Streamable HTTP か

MCPの標準トランスポートは2つだけです。用途で選びます。

トランスポートいつ使う要点
stdioローカル。クライアントがサーバーをサブプロセスとして起動JSON-RPCをstdin/stdoutで改行区切り送受信(メッセージに改行を含めない)。stderrはログ専用。クライアントは可能な限りstdioを優先すべき
Streamable HTTPリモート。独立プロセスが複数接続をHTTPで処理POST/GETの単一エンドポイント。必要に応じSSEでストリーム。認証・Origin検証が必須級

⚠️ 旧 HTTP+SSE トランスポート(2024-11-05版)は非推奨で、Streamable HTTP に置き換えられました。新規実装で選ぶ理由はありません。

Streamable HTTP のセキュリティは“任意”ではありません(公式のMUST/SHOULD):

  • サーバーは全接続で Origin ヘッダを検証し、不正なら 403 を返す(DNSリバインディング対策)。
  • ローカル実行時は 0.0.0.0 ではなく 127.0.0.1 にバインドする。
  • セッションIDは MCP-Session-Id ヘッダで扱い、**暗号的に安全(安全に生成したUUID等)**でなければならない。
  • HTTPクライアントは全リクエストに MCP-Protocol-Version を付ける。
// Streamable HTTP: DNS リバインディング対策として Origin を必ず検証する(公式MUST)
const ALLOWED_ORIGINS = new Set(["https://app.example.com"]);

function assertTrustedOrigin(req: Request): void {
  const origin = req.headers.get("origin");
  // Origin が付く(=ブラウザ由来)のに許可リストに無ければ拒否
  if (origin && !ALLOWED_ORIGINS.has(origin)) {
    throw new HttpError(403, "forbidden origin");
  }
}
// セッションIDは推測不能に。認証をセッションに依存させない(後述の session hijacking 対策)。
const sessionId = crypto.randomUUID();

最小の本番MCPサーバー(公式SDK v1・TypeScript)

まず“動く最小形”です。本番は安定版 @modelcontextprotocol/sdk(v1系) を使います(@modelcontextprotocol/server v2 はベータでAPIが異なるため、本番採用は時期尚早)。ツールは server.registerTool で登録し、inputSchema には Zodの「生シェイプ」z.object(...) で包まないオブジェクト)を渡すのが v1 の作法です。

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: "acme-tools", version: "1.0.0" });

// inputSchema は「生の Zod シェイプ」。境界で入力を型と値域で締める。
server.registerTool(
  "search-orders",
  {
    title: "注文検索",
    description: "テナント内の注文を検索する(読み取り専用)",
    inputSchema: { tenantId: z.string().uuid(), query: z.string().min(1).max(200) },
  },
  async ({ tenantId, query }) => {
    // 認可は「MCPの外」の決定的コードで強制する。プロンプトに委ねない。
    const orders = await searchOrders(tenantId, query);
    return { content: [{ type: "text", text: JSON.stringify(orders) }] };
  },
);

const transport = new StdioServerTransport();
await server.connect(transport);

registerTool(name, config, handler)・生シェイプのinputSchema{ content: [{ type: "text", text }] } の戻り値・server.connect(transport) は、いずれもv1.29.0の公式ドキュメントで確認した現行APIです。構造化出力が必要なら outputSchemastructuredContent を併用できます。LLM出力の検証設計は structured output の信頼性設計 と同じ発想です。

ツールを“安全に”設計する:任意コード実行という前提

MCPで最も誤解されるのが Tools です。公式仕様は明言します——「ツールは任意コード実行を表し、相応の注意をもって扱わねばならない」。つまり、MCPツールを開けることは、LLM(と、それを操作しうる攻撃者)に、あなたのシステムの関数を呼ばせることです。

だから設計原則は、LLMアプリのセキュリティ(OWASP LLM06「過剰なエージェンシー」)と完全に地続きです——破壊的な操作は、ツールに「実行」させず「提案」だけを返させる

// ツール = 任意コード実行。破壊的操作は「提案」を返し、その場では実行しない。
server.registerTool(
  "propose-refund",
  {
    title: "返金の提案",
    description: "返金の提案レコードを作成する(実行はしない・人間承認が必要)",
    inputSchema: { orderId: z.string().uuid(), amountJpy: z.number().int().positive() },
  },
  async ({ orderId, amountJpy }) => {
    // 副作用は「提案の作成」のみ(最小権限)。実際の返金は承認済みのコード経路だけ。
    const proposalId = await createRefundProposal({ orderId, amountJpy });
    return {
      content: [{ type: "text", text: `提案を作成しました: ${proposalId}(承認待ち)` }],
    };
  },
);

実際の返金は、MCPの外側で、決定的な業務ルール検証+人間承認+冪等キーを通して行います。この「提案と実行の分離+冪等性」は、私が決済基盤で本番二重課金0件を保つために使ってきた信頼性・冪等性の設計そのものです。加えて公式は、**「ツールの説明(annotation)は、信頼できるサーバー由来でない限り信頼しない」**とも述べています。つまりツールのメタデータ自体が攻撃面になり得ます。ツールの並べ方・権限設計は AIエージェントのツール実行設計 も参照してください。

リモートMCPの認可:OAuth 2.1(token passthrough は禁止)

認可はHTTPトランスポートに限った任意機能です(stdioは環境から資格情報を取得するため、通常は認可を使いません)。リモートMCPを公開するなら、公式の認可モデルに厳密に従います。

  • MCPサーバー = OAuth 2.1 の「リソースサーバー」、MCPクライアント = OAuthクライアント、別に認可サーバー(AS)が存在。
  • RFC 9728(Protected Resource Metadata) をサーバーは実装必須。401時の WWW-Authenticate/.well-known/oauth-protected-resource でASを発見させる。
  • RFC 8707(Resource Indicators) — クライアントは認可・トークン両リクエストで、MCPサーバーの正規URIresource に必ず送る。
  • PKCE(S256)必須。ASのメタデータで対応を確認できなければ続行しない。
  • トークンの検証は妥協できない(公式のMUST):
// リモートMCP = OAuth 2.1 の resource server。
// 受け取ったトークンは「自分向け(aud)」だけ受理する。
function verifyAccessToken(rawToken: string): Claims {
  const claims = verifyJwt(rawToken, JWKS); // 署名・iss・exp を検証
  // aud に自分の正規URIが無いトークンは拒否(他サービス向けの流用を防ぐ)
  if (!claims.aud?.includes(MCP_SERVER_CANONICAL_URI)) {
    throw new HttpError(401, "token audience mismatch");
  }
  return claims;
  // ★ このトークンを下流の外部APIへ「素通し」してはならない(公式で明確に禁止)。
  //   下流呼び出しは、MCPサーバー自身の資格情報/別トークンで行う。
}

**token passthrough(受領トークンをそのまま下流へ横流しする)は、認可仕様で明確に禁止された“アンチパターン”**です。監査証跡を壊し、信頼境界を越えさせ、セキュリティ制御を回避させます。

本番セキュリティ設計:公式ベストプラクティスの脅威と対策

公式の Security Best Practices が名指しする代表的な脅威と、対策の要点です。

脅威何が起きる対策の要点
Confused Deputyプロキシが静的client_id+動的登録+同意Cookieを持つと、Cookie再利用で同意を飛ばして認可コードを窃取プロキシはクライアントごとの同意(per-client consent)を必須実装。承認済みclient_idを都度確認、redirect_uriは完全一致
Token Passthrough自分向けでないトークンを受理し下流へ素通し自分向け以外のトークンを受理しない。仕様で明確に禁止
Session HijackingセッションIDの推測・なりすましセッションを認証に使わない。推測不能なセッションID(安全なUUID)。全受信リクエストを検証。IDはユーザーに束ねる
SSRF発見用フィールドに内部URL(例:クラウドメタデータ169.254.169.254)を仕込むHTTPSを強制、プライベートIP帯をブロック、リダイレクト先を検証(自作IP判定は符号化バイパスに注意)
ローカルサーバー起動悪意ある起動コマンドで任意コード実行クライアントは起動コマンドを明示し明示的同意を要求、サンドボックス化
過剰スコープadmin:* 等の広すぎるスコープで被害が拡大最小権限のスコープを段階的に。ワイルドカード/万能スコープを避ける

これらは特別な話ではなく、**「MCPサーバーを、信頼できないクライアント(と、その先のLLM)に晒される普通のWeb APIとして防御する」**ことに尽きます。

可観測性・回復性・冪等性:本番運用の三点

MCPツールは外部システムを叩くため、可観測性が欠かせません。ツール呼び出し名・引数(PIIはマスク)・レイテンシ・成否・拒否をユーザー/セッション単位で記録し、異常を検知します(土台は OpenTelemetryによる本番可観測性)。加えて、外部呼び出しにはタイムアウトとリトライ(指数バックオフ)、副作用のある操作には冪等キーを必ず入れます。LLMは同じ意図でツールを複数回呼ぶことがあり、冪等でない実行は二重課金・二重発送に直結します。

バージョニングと将来への備え

MCPの版は日付ベース(後方非互換な変更があった日付)。現行の安定版は 2025-11-252026-07-28 はリリース候補(RC)で未確定です。SDKも過渡期で、安定版 @modelcontextprotocol/sdk(v1)と、APIの異なるベータ @modelcontextprotocol/server(v2)が並存します。本番はv1に固定し、MCP-Protocol-Version によるネゴシエーションで版差を吸収、破壊的変更に備えます。

導入チェックリスト(本番公開前)

  • トランスポート — ローカルはstdio、リモートはStreamable HTTP(旧HTTP+SSEは使わない)。
  • Origin検証 — Streamable HTTPでOriginを検証し不正は403。ローカルは127.0.0.1バインド。
  • セッション — 推測不能なセッションID。認証をセッションに依存させない。全受信を検証。
  • 認可(リモート) — OAuth 2.1、RFC 9728/8707、PKCE(S256)。トークンのaudを検証し自分向けのみ受理。
  • token passthrough禁止 — 受領トークンを下流へ横流ししない。下流は別資格情報で。
  • ツール最小権限 — 破壊的操作は「提案」に留め、実行は人間承認+冪等キー付きのコード経路で。
  • 入力検証inputSchema(Zod)で型・値域を締め、認可は決定的コードで再確認。
  • 可観測性 — ツール呼び出しのログ/トレース、タイムアウト、リトライ、異常検知。
  • SDK/仕様の固定 — v1安定SDK、仕様版を明示、更新は一次情報で追う。

まとめ

MCPは、AIを業務システムに繋ぐための強力な標準です。しかし本番の成否を分けるのは、プロトコルの新しさではなく——「ツール=任意コード実行」「LLM=確率的で信頼できないコンポーネント」という前提に立ち、その周りを決定的なアーキテクチャで囲えるかです。

  • アーキテクチャ:host/client/server と3プリミティブ、能力ネゴシエーションを理解する。
  • トランスポート:stdio(ローカル)/ Streamable HTTP(リモート)を用途で選び、Origin・セッションを守る。
  • 認可:OAuth 2.1のリソースサーバーとして、audを検証し token passthrough を禁じる
  • ツール安全性:最小権限・提案/実行分離・人間承認・冪等性で、被害半径を封じ込める。

MCPサーバーのセキュリティは、LLMアプリのセキュリティの一部です。「速く繋ぐ」ことと「安全に運用する」ことは、正しく設計すれば両立します。

よくある質問

MCP(Model Context Protocol)とは何ですか?
AIモデル(LLM)を外部のツール・データ・プロンプトに接続するためのオープンな標準プロトコルです。host / client / server の3者が JSON-RPC 2.0 でやり取りし、サーバーは Tools / Resources / Prompts を公開します。各ツールを個別に実装するのではなく、標準インターフェースで繋げる『AIのための共通端子』のような役割です。
stdio と Streamable HTTP、どちらのトランスポートを使うべき?
ローカルで動かす(クライアントがサーバーをサブプロセスとして起動する)なら stdio、ネットワーク越しのリモートサーバーなら Streamable HTTP です。旧来の HTTP+SSE トランスポートは非推奨となり、Streamable HTTP に置き換えられました。
MCPサーバーのセキュリティで最も重要な点は?
3点です。①ツールは『任意コード実行』として最小権限で設計し、破壊的操作は人間承認を通す。②リモートは OAuth 2.1 で、受領した access token の aud(audience)を検証して自分向けのみ受理し、下流へ素通ししない(公式で禁止)。③Streamable HTTP は Origin ヘッダを検証(DNSリバインディング対策)し、ローカルは 0.0.0.0 ではなく 127.0.0.1 にバインドする。
公式のTypeScript SDKはどれを使えばいい?
本番は安定版の @modelcontextprotocol/sdk(v1系)を使います。ツールは server.registerTool で登録し、inputSchema には Zod の『生シェイプ』(z.object(...) で包まないオブジェクト)を渡します。@modelcontextprotocol/server(v2)はまだベータで、Zodの渡し方などAPIが異なるため、本番では避けます。
MCPの仕様バージョンはどう管理されていますか?
日付ベースのリビジョン(YYYY-MM-DD)で管理され、後方非互換な変更があった日付が版名になります。現行の安定仕様は 2025-11-25 です(2026-07-28 はリリース候補で未確定)。HTTPクライアントは MCP-Protocol-Version ヘッダで版を明示し、初期化時に能力ネゴシエーションを行います。
プロンプトインジェクションでMCPツールが悪用される危険はありますか?
あります。公式仕様も『ツールは任意コード実行を表し、相応の注意が必要』『信頼できるサーバー由来でなければツールの説明(annotation)は信頼できないものとして扱うべき』と警告しています。防御はLLMアプリ全体のセキュリティ設計(出力のゼロトラスト検証・ツール最小権限・人間承認)と同じで、詳細は本文末尾のセキュリティ記事に集約しています。

参考文献

友田

友田 陽大

経済産業大臣賞 受賞プロダクト開発者。TypeScript + Python + AWS で、SaaS・業界DX・実用レベルの生成AI(RAG)を、要件定義からインフラ・運用まで一人で完遂します。

この記事の実装を、案件として承ります

セキュリティエンジニアリングを、設計から実装・運用まで承ります

脅威モデリングによる設計レビュー、暗号・認証認可の正しい実装、ログ設計と検知(検知エンジニアリング)、インシデント対応の体制づくりまで。一人 × 生成AIで経済産業大臣賞のB2B SaaSや本番二重課金0件の決済基盤を作ってきた知見で、御社のプロダクトを“速く作り、かつ守れる”状態に伴走します。設計でしか守れない縦のリスクは監査としても承ります。

プロジェクト単位(請負)・技術顧問のどちらにも対応可能です。まずは30分の無料技術相談から。

最短ルート:カレンダーから直接予約

相談内容が固まっている方は、フォーム送信よりその場で日程を確定する方がスムーズです。下記から空き時間をお選びください。

  • 30分のオンライン無料相談
  • Google Meet / Zoom / Microsoft Teams
  • NDA 商談前締結可・無理な営業はいたしません
無料相談の空き枠を予約する

あわせて読みたい