MCPサーバーのデプロイと運用
MCP サーバーを本番運用するためのホスティング、スケール、互換性維持、運用ポリシーを解説します。2026-07-28 版でプロトコルがステートレスになったことで、リモートの MCP サーバーは「ふつうの HTTP API」と同じ方法でデプロイ・スケールできるようになりました。この文書では、その前提を使ったホスティングの選び方と、旧世代のクライアントが残る間の互換性の保ち方、運用で決めておくことを扱います。仕様の変更点は MCP最新仕様 2026-07-28 の変更点と移行、観測と監査は MCPの可観測性と監査、認可は MCPの認可 - OAuth 2.1とメタデータ探索 を参照してください。
1. このドキュメントで扱うこと
- ステートレス HTTP としてのデプロイ
- Cloudflare Workers でのホスティング
- 他のホスティング先
- 新旧クライアントの同時対応
- バージョン運用
- ゲートウェイ / WAF / レート制限
- キャッシュ
- シークレット管理
- 長時間処理
- CI/CD と SLO
- 組織内のサーバー管理
ステートレス HTTP としてのデプロイ
2026-07-28 版の MCP はステートレスなプロトコルです。initialize のハンドシェイク、Mcp-Session-Id、プロトコル層のセッションが、コアのリクエストの経路から取り除かれ、各リクエストが、プロトコルのバージョン、クライアントの識別、機能を _meta に自分で載せます。Streamable HTTP では、次のように単純になりました。
- すべての JSON-RPC のリクエストが、独立した 1 回の
POSTで届く - サーバーは、JSON の応答か、そのリクエスト専用の SSE ストリームで返す
GETのストリームの取得、DELETEによるセッションの終了、Last-Event-IDによる再開は、ない- サーバーからクライアントへの質問 (Elicitation、Sampling、Roots) は、独立したリクエストではなく、結果に埋め込まれる (MRTR、
input_required) - 変更の通知は、
subscriptions/listenへの POST の応答ストリームで届く
この結果、スティッキーセッションも、ストリームの保持も要りません。リクエストは、どのインスタンスに届いても処理でき、インスタンスの追加と削除、ローリングデプロイ、ブルーグリーンが、通常の Web API と同じ手順で行えます。仕様も、サーバーが「接続やプロセスの同一性を、会話やセッションの代わりに使ってはならない」としています。
ただし、「ステートレス」はアプリケーションが状態を持たないという意味ではありません。複数のリクエストにまたがる状態 (買い物かごや、ワークフロー) は、サーバーが明示的なハンドルを発行し、クライアントが毎回の引数として渡して、サーバーが共有のストア (データベース、キャッシュ) に保存します。インスタンスのメモリに置いた状態は、別のインスタンスに届いたリクエストからは見えません。ハンドルの設計 (推測できない、利用者に結びつける、期限を持つ) は、MCPセキュリティベストプラクティス - 攻撃と対策 の「状態ハンドルの乗っ取り」を参照してください。
運用で押さえる、HTTP のトランスポートの要件もあります。
Originヘッダーを検証する。存在して不正なら403。ローカルで動かすときは localhost にだけ bind する- すべての POST に、
MCP-Protocol-Version、Mcp-Method、(該当するものは)Mcp-Nameが付く。サーバーは、ヘッダーと本文の不一致を400と-32020で拒否する - SSE のストリームを開くときは、
X-Accel-Buffering: noを付けることが推奨される。nginx などのリバースプロキシが応答をバッファリングし、イベントの配信が遅れるのを防ぐ - 長く開いているストリーム (特に
subscriptions/listen) には、SSE のコメント行 (:で始まる行) を、定期的に送ることが推奨される。中継点やクライアントのアイドルタイムアウトで切断されるのを防ぐ - SSE の応答ストリームを閉じることは、そのリクエストのキャンセルとして扱われる。ロードバランサーのタイムアウトで接続を切ると、処理が中断される
Cloudflare Workers でのホスティング
Cloudflare Workers は、ステートレスな MCP サーバーに向いたホスティング先です。ステートレスな MCP のサーバーは、1 つの Worker で動かせて、スティッキーなルーティングも、セッションのストレージも要りません。Cloudflare Agents SDK の createMcpHandler は、公式の MCP TypeScript SDK に取り込まれました (SDK は Node.js から Web 標準の API に移り、Bun、Deno、Workers で使いやすくなっています)。
最小のサーバーは、次のようになります。
import { McpServer } from "@modelcontextprotocol/server";
import { createMcpHandler } from "agents/mcp/server";
import { z } from "zod";
function createServer() {
const server = new McpServer({
name: "hello-server",
version: "1.0.0",
});
server.registerTool(
"hello",
{
description: "Return a greeting",
inputSchema: { name: z.string().optional() },
},
async ({ name }) => ({
content: [
{
type: "text",
text: `Hello, ${name ?? "World"}!`,
},
],
}),
);
return server;
}
export default {
fetch(request, env, ctx) {
return createMcpHandler(createServer)(request, env, ctx);
},
}
ポイントは 2 つあります。
createMcpHandlerには、サーバーのインスタンスではなく、サーバーを作る関数 (ファクトリー) を渡す。ステートレスなハンドラーは、MCP のリクエストごとにサーバーを 1 つ作るため、同時に届いたリクエストが、接続済みのサーバーのインスタンスを共有しない- Worker の既定のエクスポートは、
fetchメソッドを持つオブジェクトにする。export default createMcpHandler(createServer)のように関数を既定のエクスポートにすると、Wrangler がWorkerEntrypointのクラスとして扱ってしまう
認可は、Workers OAuth Provider (@cloudflare/workers-oauth-provider) で、OAuth 2.1 の認可を Worker に追加できます。/mcp へのリクエストを保護し、認可サーバーとリソースサーバーの両方の役割を、1 つの Worker に持たせる構成を、Cloudflare のブログは次のように示しています。
import { OAuthProvider } from "@cloudflare/workers-oauth-provider";
export default new OAuthProvider({
apiRoute: "/mcp",
apiHandler: mcpHandler,
defaultHandler: authorizationHandler,
authorizeEndpoint: "/authorize",
tokenEndpoint: "/oauth/token",
clientIdMetadataDocumentEnabled: true,
resourceMetadata: {
resource: "https://mcp.example.com/mcp",
authorization_servers: ["https://mcp.example.com"],
scopes_supported: ["mcp:read"],
},
});
apiRoute の下は apiHandler に、それ以外 (/authorize の画面など) は defaultHandler に渡されます。RFC 9728 の保護リソースのメタデータと、未認証のリクエストへのチャレンジも提供されます。注意点は次のとおりです。
- 認可サーバーとして動かすには、KV namespace を
OAUTH_KVとしてバインドする。Client ID Metadata Documents を受けるなら、global_fetch_strictly_publicの互換性フラグも要る - トークン、コード、シークレットはハッシュとして保存され、
propsは暗号化される - 公開するスコープは広告されるが、強制はされない。ハンドラー側で
ctx.auth.scopeを検証する必要がある - ライブラリの README は、認可サーバーとリソースサーバーを同じ Worker に置く形を 0.x 系のものとし、役割を分ける構成を、現在の推奨としている。設計の前に、最新の README を確認する
状態が必要なときに限って、Durable Objects を使います。McpAgent は、プロトコルのためには不要になり、非推奨で、機能の追加が止まっています (削除のバージョンは発表されていません)。協調した状態を持つアプリケーションには、Durable Objects が適しています。
Workers の基礎、Wrangler、デプロイは Cloudflare Workers - Azure Functions相当のエッジ関数 を参照してください。
他のホスティング先
ステートレスになった MCP は、特別なホスティングを要しません。選択の基準は、通常の API と同じです。
| 観点 | 判断の材料 |
|---|---|
| 既存のバックエンドとの距離 | 同じネットワーク、同じ認証基盤にあるなら、コンテナー (Azure Container Apps、AKS) に同居させる |
| 処理時間 | 短い要求と応答が中心ならサーバーレス。長時間の処理は、コンテナーやジョブのキューと組み合わせる |
| 配信の範囲 | 世界中の利用者に近い場所で動かすなら、エッジ (Cloudflare Workers) |
| スケールの特性 | 突発的な負荷とゼロへのスケールにはサーバーレス。常時の負荷にはコンテナー |
| ネットワークの制約 | 社内のプライベートなリソースに触れるなら、同じ仮想ネットワークの中に置く |
Azure では、Container Apps (HTTP の負荷に応じた自動スケール)、Functions (短時間のリクエストと応答)、AKS (既存の Kubernetes の基盤) が候補です。Azure と Cloudflare の対応は Azure vs Cloudflare サービス対比表 を参照してください。
ホスティング先を問わず、共通で確認する点があります。
- SSE の応答を、プロキシやゲートウェイがバッファリングしない (
X-Accel-Buffering: no) - アイドルタイムアウトが、
subscriptions/listenのような長いストリームより短くない。または、定期的なキープアライブを送る - ヘルスチェックは、MCP のリクエスト (
server/discoverなど) ではなく、軽量な専用のエンドポイントで行う - 水平スケールで、インスタンスのメモリに状態を持たない
stdio のローカルサーバーは、利用者のマシンで動くため、デプロイというより配布の課題です。MCP Registryとサーバーの配布 と ローカルMCPサーバーの安全な運用 を参照してください。
新旧クライアントの同時対応
2026-07-28 版の公開後も、旧世代 (2025-11-25 以前、initialize のハンドシェイクを使う) のクライアントは、しばらく残ります。仕様は、サーバーが新旧の両方を受ける**デュアルエラ (dual-era)**の実装を許し、同じエンドポイントで、両方を同時に提供することも認めています。サーバーは、クライアントの開き方で挙動を選びます。
- 新世代の
_metaを持つリクエストは、この版に従って、ステートレスに処理する initializeのリクエストは、旧世代の意味づけを選び、交渉された旧版の仕様に従う (HTTP ではセッション単位)
互換性の結果は、組み合わせによって異なります。
| クライアント | サーバー | 結果 |
|---|---|---|
| 新世代 | 新世代 | 動く |
| 新世代 | 旧世代 | 失敗する (旧世代のサーバーは、実装によってエラーを返すか、無応答になる) |
| デュアル | 新世代 | 動く |
| デュアル | 旧世代 | 動く (新世代のリクエストが失敗した後、initialize にフォールバックする) |
| 旧世代 | 新世代のみ | 失敗する。必須のヘッダーがなく、400 で拒否される。旧世代のクライアントには、新しい版へ切り替える仕組みがない |
| 旧世代 | デュアル | 動く |
つまり、新世代だけを受けるサーバーに切り替えると、旧世代のクライアントはすべて接続できなくなります。新世代のみに対応するサーバーは、initialize に対するエラーに、対応するプロトコルのバージョンを含めることが推奨されます。旧世代のクライアントが利用者に見せられる診断が、それだけの場合があるためです。
Cloudflare の実装は、移行の段階を次のように用意しています。
- 同じ
/mcpが、新しいプロトコルと、2025 年版の Streamable HTTP のクライアントからのステートレスなリクエストの両方を受ける。createMcpHandlerの既定のlegacy: "stateless"は、ふつうのツール、リソース、プロンプトを旧世代のクライアントにも提供する。この場合、GETとDELETEは405を返し、セッションの状態は残らない。新世代だけのエンドポイントにするにはlegacy: "reject"を指定する - 旧世代のセッション、サーバーからクライアントへのリクエスト、独立したストリームに依存するサーバーは、旧世代の経路 (SDK 1.x 系の一時的な「レガシーレーン」) を、同じ URL の隣に残し、機能を新世代へ移し、セッションを排出し (drain)、非推奨の期間中に旧世代の経路を取り除く
- 同じ URL で両方を動かすときは、
isLegacyRequest()で振り分ける。ステートレスのハンドラーが旧世代のリクエストを横取りしないよう、そちらにはlegacy: "reject"を指定する (コードは「コード例」を参照)
セッションに依存する機能 (サーバーからの質問、独立した通知のストリーム) は、新世代の仕組みへ置き換えます。サーバーからの質問は、input_required の結果で返して、クライアントが元の操作を再試行します。リスト変更の通知は、subscriptions/listen (POST と SSE の応答) に移ります。移行前に保存していたセッションの ID は、無効になり、クライアントは再接続します。進行中の処理を旧セッションに結びつけていた場合、それは再開されません。
バージョン運用
新世代では、すべてのリクエストが自分のプロトコルのバージョンを宣言し、サーバーが各リクエストを独立に受け入れるか拒否します。ハンドシェイクでの交渉はありません。
- サーバーが対応しないバージョンには、
400とUnsupportedProtocolVersionError(-32022) を返す。data.supportedに対応するバージョンの一覧、data.requestedに要求されたバージョンを入れる。クライアントは、一覧から共通のバージョンを選んで再試行する - サーバーは
server/discoverを実装しなければならない。クライアントは、最初にこれを呼んで、対応するバージョンを知れる (必須ではない) - 運用では、
-32022の件数と、リクエストごとのMCP-Protocol-Versionの分布を監視して、古いクライアントの残り具合を把握する (MCPの可観測性と監査)
非推奨の期間にも、方針があります。機能は、有効 (Active)、非推奨 (Deprecated)、削除済み (Removed) に分類され、非推奨の機能は、少なくとも 12 か月は仕様に残ります。2026-07-28 版では、Roots、Sampling、Logging、動的クライアント登録 (DCR) が非推奨になりました。旧 HTTP+SSE のトランスポートは、これより前から非推奨で、SEP-2596 の移行措置により、最短の削除時期が、SEP-2596 が Final になってから 3 か月後と、早く設定されています。計画を立てるときは、次のように考えます。
- 公開日から 12 か月は、非推奨の機能も仕様の上では存在する。ただし、削除の対象になる前に、利用状況を見て、置き換える
- 旧世代のクライアントの割合が十分に下がったことを、メトリクスで確かめてから、旧世代の経路を閉じる
- 閉じる前に、利用者への告知期間を置く。旧世代のクライアントには、閉じた後に、新しい版への切り替え手段がない
- 自分のサーバーが提供する API の変更 (ツールの名前、引数、結果の構造) は、MCP のバージョンとは別に、互換性の方針を持つ。
inputSchemaを壊す変更は、新しい名前のツールを追加し、古いものを非推奨にする
ゲートウェイ / WAF / レート制限
Streamable HTTP のすべての POST に Mcp-Method が付き、tools/call、resources/read、prompts/get には Mcp-Name が付きます。これにより、ロードバランサー、ゲートウェイ、WAF が、JSON の本文を解析せずに、メソッドとツールの単位で、ルーティング、レート制限、許可と拒否を適用できます。
- ツールごとのレート制限、書き込み系のツールだけを別のバックエンドへ振り分ける、特定のツールを特定のネットワークからのみ許可する
- 本文の解析は、サーバーが行う。サーバーは、ヘッダーと本文の不一致を
400と-32020で拒否する。ゲートウェイがヘッダーで判断し、サーバーが本文で実行する構成のずれを、突かれないための要件 - ゲートウェイは、ヘッダーのポリシーを適用する前に、
MCP-Protocol-Versionが、ヘッダーと本文の検証を要求する版であることを確認する。古い版や、ヘッダーのないリクエストは、拒否する - ツールの引数を、ルーティングのキーにしたい場合は、
x-mcp-headerでMcp-Param-{名前}のヘッダーにミラーする。機微な値は使わない - ゲートウェイでの検証失敗は、適切な HTTP のエラー (
400など) を返せばよく、JSON-RPC のエラー本文までは求められない
ゲートウェイの制限と、サーバー内の制限 (仕様はサーバーにレート制限を求めています) を、重ねて使います。ポリシーの詳細は MCPの可観測性と監査 の「レート制限と異常検知」を参照してください。
キャッシュ
server/discover、tools/list、prompts/list、resources/list、resources/templates/list、resources/read の resultType: "complete" の結果には、サーバーがキャッシュのヒントを付けなければなりません。
| フィールド | 意味 |
|---|---|
ttlMs | クライアントが結果を新鮮と見なしてよいミリ秒数。HTTP の Cache-Control: max-age に近い。0 は直ちに古い。負の値は不可 (サーバーは 0 以上を返す) |
cacheScope | public か private。Cache-Control: public と private に近い |
cacheScope の意味は、運用上重要です。
public: 利用者固有のデータを含まない。クライアント、共有のゲートウェイ、キャッシュのプロキシが保存し、任意の利用者に返してよいprivate: 利用者ごとの内容を含む。同じ認可の文脈 (同じアクセストークン) でだけ再利用できる。共有のキャッシュに載せてはならず、認可の文脈をまたいで共有してはならない
運用の指針は次のとおりです。
- 全員で同一のツールの一覧、プロンプト、リソースのテンプレートは
public、認証された利用者ごとに変わるresources/readや、フィルターされた一覧はprivate - 認証付きのエンドポイントの
publicの結果も、共有され得る。たとえば、認証付きのtools/listのpublicの結果が、別のアクセストークンを持つ利用者にも使い回される。そのため、cacheScopeを、アクセス制御の代わりにしてはならない。ツールごとのアクセス制御は、別に実施する - ページ分割された一覧は、ページごとに独立してキャッシュされる。同じ一覧のすべてのページで、
cacheScopeを同じにする input_requiredの結果と、inputResponsesやrequestStateを伴う再試行の結果は、キャッシュしないlist_changedの通知と TTL は、併用できる。通知は即時の無効化の合図になるttlMsは、新鮮さのヒントであり、データの不変の保証ではない。変更の頻度に合わせて、短くも長くもする
MCP のリクエストは POST なので、CDN や中間のプロキシが、既定でキャッシュするとは限りません。これらのヒントは、まずクライアント (ホストアプリケーション) が従うものです。共有のゲートウェイや CDN で、メソッドと本文のパラメーターを鍵にして、public の結果をキャッシュする設定を行う場合は、お使いの製品が POST のキャッシュと、鍵の構成をどう扱うかを、別に確認してください。private の結果を、共有のキャッシュに載せない設定が、最優先です。
シークレット管理
MCP サーバーが持つシークレットは、主に次のものです。
- 下流の API の資格情報 (API キー、サービスアカウント、クライアントシークレット)
- トークンの署名鍵、暗号化の鍵
- 認可サーバーとして動かす場合のクライアントシークレット
- Cloudflare や Azure など、デプロイ先の API の資格情報
指針は、次のとおりです。
- コードやリポジトリ、イメージ、クライアントに返す結果に、シークレットを含めない。ログにも出さない
- 実行環境のシークレットの仕組みを使う。Cloudflare Workers では
wrangler secret putで登録し、wrangler.tomlの[vars]には非機密の値だけを置く (Wrangler CLI ガイド)。コンテナーでは、プラットフォームのシークレットの機能やキーボールトから、実行時に注入する - 利用者のトークンを、そのまま下流の API に渡さない (トークンの素通しの禁止)。サーバーは、自分に発行されたトークンだけを受け入れ、下流には、サーバー自身の資格情報か、トークン交換で得た別のトークンを使う (MCPの認可 - OAuth 2.1とメタデータ探索)
- 資格情報は、スコープを絞り、定期的にローテーションし、漏えいしたときに、すぐに失効できるようにしておく
- CI/CD のシークレットと、実行時のシークレットを分ける。デプロイのためのトークンは、実行中のサーバーには要らない
長時間処理
リクエストと応答の枠に収まらない処理は、同期の呼び出しにせず、次のいずれかにします。
- Tasks 拡張 (
io.modelcontextprotocol/tasks): Tasks は、2026-07-28 版でコアから拡張に移りました。サーバーがこの拡張を宣言していて、ツールがタスクとして呼ばれると、tools/callはタスクのハンドルを返し、クライアントがtasks/getでポーリングします。完了したタスクは、結果を埋め込みます。途中で追加の入力が必要になるとinput_requiredの状態になり、クライアントがtasks/updateで入力の応答を返します。拡張の詳細は MCP拡張機能 - Tasks・MCP Apps・Skills を参照してください - ジョブのキューと明示的なハンドル: ツールが、ジョブを投入して ID を返し、状態を問い合わせるツールと、結果を取るツールを別に用意する。ステートレスなプロトコルでの、素直な設計
- 進捗の通知: 同期の処理が数十秒かかる程度なら、SSE のストリームで進捗の通知を送り、最後に結果を返す。ただし、ロードバランサーのタイムアウトと、バッファリングに注意する
設計の勘所は、タイムアウトを層ごとに決めることです。クライアントのタイムアウト、ゲートウェイとロードバランサーのアイドルタイムアウト、サーバーの処理のタイムアウト、下流の API のタイムアウトを、外側ほど長く、内側ほど短くそろえます。SSE の応答ストリームが閉じられると、そのリクエストはキャンセルされたものと扱われるため、サーバーは、キャンセルに気づいた時点で、処理を止め、それ以降のメッセージを送ってはなりません。サーバーレスの実行時間の上限に収まらない処理は、キューとワーカーに分けます。
CI/CD と SLO
デプロイのパイプラインには、サーバーの種類に関係なく、次の段階を入れます。
- 単体テストとスキーマの検証 (
inputSchema/outputSchema) - プロトコルのテスト: Inspector の CLI で、
tools/listと代表的なtools/callを実行する。--strictでスキーマの移植性を確かめ、終了コードで判定する (MCPのテストとデバッグ) - コンフォーマンスのテスト: conformance のスイートを回帰テストに入れて、仕様の解釈のずれを早く見つける
- 認可のテスト: 401、403、audience の不一致、スコープ不足の経路を確認する
- ステージングへのデプロイと、スモークテスト (
server/discoverの応答、tools/listの件数) - 段階的なデプロイ (カナリア、ローリング)。ステートレスなので、新旧のインスタンスが混在しても、セッションの断絶は起きない。ただし、ツールの定義 (名前、スキーマ) の変更は、クライアントのキャッシュ (
ttlMs) が切れるまで、新旧の両方が見える点に注意する - 本番のメトリクスで、エラー率とレイテンシを見て、自動で巻き戻す
SLO の例として、次のような指標を決めます (数値は、サービスの性質に合わせて決めてください)。
| 指標 | 計測の対象 | 備考 |
|---|---|---|
| 可用性 | プロトコルの層でのエラーと 5xx を除いた、成功したリクエストの割合 | ツールの実行エラー (isError) を、利用者の入力の誤りと、下流の障害に分けて扱う |
| レイテンシ | tools/call の p50、p95、p99 (ツール別) | Mcp-Name で、ツール別に取れる |
| 認可の成功率 | 正当な利用者の、401 や 403 の割合 | 急上昇は、設定の誤りか、トークンの発行側の障害を示す |
| 古い版のクライアントの割合 | MCP-Protocol-Version の分布、-32022 の件数 | 旧世代の経路を閉じる判断に使う |
組織内のサーバー管理
組織で、多数の MCP サーバーを管理するには、承認済みのサーバーを 1 か所から提供し、承認されていないサーバーの利用 (Shadow MCP) を防ぎます。MCP Server Portals のようなゲートウェイは、組織内のサーバーを 1 つのエンドポイントに集約し、認証、ポリシー、監査を一元化します。機能と設計上の注意は、MCPの認可拡張とエンタープライズ導入 の「ゲートウェイと MCP Server Portals」にまとめています。
公開側の配布と、組織内の承認 (許可リスト、プライベートレジストリ、導入前の審査) は、MCP Registryとサーバーの配布 を参照してください。
2. ホスティングの選択肢
| 選択肢 | 向いているケース | 留意点 |
|---|---|---|
| Cloudflare Workers | ステートレスな MCP サーバー、グローバル配信 | 状態が必要なら Durable Objects を併用 |
| コンテナー (Azure Container Apps / AKS など) | 既存のバックエンドと同居、長時間処理 | 水平スケールとヘルスチェックの設計 |
| サーバーレス関数 | 短時間のリクエスト・レスポンス中心 | 実行時間とコールドスタートの制約 |
| ローカル (stdio) | 開発者のマシン上のツール | 配布と更新、サンドボックスの課題 |
3. 開発者がつまずきやすいポイント
スティッキーセッションを前提にしてしまう
2025 年版までの実装は、Mcp-Session-Id と、インスタンスのメモリのセッションの状態に頼ることが多くありました。新世代では、状態を共有のストアに置き、明示的なハンドルで引き回します。スティッキーセッションは不要で、持ち込むと、スケールとデプロイを不必要に難しくします。
新世代だけにして、旧世代のクライアントを締め出す
新世代のみのサーバーは、旧世代のクライアントの initialize を拒否し、接続できなくします。クライアントの分布を測り、デュアルのレーンで移行し、割合が下がってから閉じます。
createMcpHandler にインスタンスを渡す
ファクトリー (createServer) を渡します。createServer() の結果を渡す SDK v1 の書き方は、同時のリクエストでサーバーのインスタンスを共有します。また、export default createMcpHandler(...) のように、関数を既定のエクスポートにしてはいけません。
SSE がバッファリングされて、通知が届かない
リバースプロキシが応答をためます。X-Accel-Buffering: no を付け、プロキシ側のバッファリングの設定も確認します。長いストリームには、キープアライブのコメント行を送ります。
cacheScope: public を、アクセス制御の代わりにする
public の結果は、別の利用者に使い回され得ます。利用者ごとに異なる内容は private にし、ツールごとの認可は、キャッシュとは別に実施します。
ヘルスチェックに MCP のリクエストを使う
ヘッダーと _meta の必須フィールドが要る MCP のリクエストは、ヘルスチェックには重すぎます。軽量な専用のエンドポイントを用意します。
シークレットを環境変数の平文の設定に入れる
wrangler.toml の [vars] や、イメージに埋め込んだ設定は、機密の置き場所ではありません。プラットフォームのシークレットの機能を使います。
非推奨の機能を、12 か月たったら消えると考える
12 か月は、削除の対象になるまでの最低の期間であり、削除の日ではありません。削除の時期は、個別の仕様の改訂で決まります。計画は、メトリクスと、仕様の最新の状態を見て、更新します。
4. コード例
Cloudflare Workers での最小のステートレスなサーバー
前述の hello-server の例のとおり、ファクトリーを createMcpHandler に渡し、fetch を持つオブジェクトをエクスポートします。
新旧のレーンを 1 つの URL で振り分ける
同じ URL で、新世代のステートレスなレーンと、旧世代のセッション付きのレーンを動かすときは、isLegacyRequest() で振り分けます。ステートレスのハンドラーには legacy: "reject" を指定し、旧世代のリクエストを横取りさせません。
// 構造のみ (Cloudflare の移行ガイドの抜粋)。stateless と legacy の作り方は移行ガイドを参照
if (await isLegacyRequest(request)) {
return legacy.fetch(request, env, ctx);
}
return stateless(request, env, ctx);
旧世代を受けない、新世代だけのエンドポイントにする場合も、legacy: "reject" を指定します。オプションの渡し方は、お使いの SDK のバージョンの移行ガイドで確認してください。
ゲートウェイで、ヘッダーの検証を前提にしたポリシー (擬似コード)
if request.header("MCP-Protocol-Version") が、ヘッダー検証を要求する版でない:
reject 400
if request.header("Mcp-Method") == "tools/call":
tool = decode(request.header("Mcp-Name"))
if tool in 書き込み系ツール:
rate_limit(key = 利用者, limit = 厳しめ)
if tool in 管理系ツール and 送信元 not in 社内ネットワーク:
reject 403
forward to MCP server
ゲートウェイの製品ごとの設定の書き方は異なります。ここでは、判断の順序だけを示しています。
デプロイ後のスモークテスト
# server/discover で、対応バージョンを確認する (新世代のサーバー)
curl -sS https://mcp.example.com/mcp \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
-H 'MCP-Protocol-Version: 2026-07-28' \
-H 'Mcp-Method: server/discover' \
-d '{"jsonrpc":"2.0","id":1,"method":"server/discover","params":{"_meta":{
"io.modelcontextprotocol/protocolVersion":"2026-07-28",
"io.modelcontextprotocol/clientCapabilities":{}}}}'
# ツールの一覧が空でないことを確認する (終了コードで判定)
mcp-inspector --cli https://mcp.example.com/mcp --transport http \
--protocol-era modern --stored-auth-only --method tools/list --format json \
| jq -e '.result.tools | length > 0' > /dev/null
5. まとめ
- 2026-07-28 版の MCP はステートレスで、各リクエストが独立した
POST。スティッキーセッションも、ストリームの保持も要らず、通常の Web API と同じ手順でスケール、ローリングデプロイができる。状態が要るなら、明示的なハンドルと共有のストアで持つ - Cloudflare Workers は、1 つの Worker で動かせるステートレスな MCP サーバーに向く。
createMcpHandlerには、インスタンスではなくファクトリーを渡し、fetchを持つオブジェクトをエクスポートする。認可は Workers OAuth Provider、状態が必要なときだけ Durable Objects を使う - 他のホスティング先は、既存のバックエンドとの距離、処理時間、配信の範囲、スケールの特性で選ぶ。SSE のバッファリングとアイドルタイムアウトを確認する
- 新世代だけのサーバーは、旧世代のクライアントを締め出す。同じエンドポイントで新旧を受けるレーンを用意し、メトリクスで旧世代の割合を確認してから閉じる
- バージョンの不一致は
-32022で返る。非推奨の機能は、少なくとも 12 か月は仕様に残るが、それは最低の期間であり、計画は利用状況で決める Mcp-MethodとMcp-Nameにより、ゲートウェイや WAF が、本文を解析せずに、メソッドとツールの単位でポリシーを適用できる。ヘッダーの検証を要求する版のリクエストだけを信頼するttlMsとcacheScopeはキャッシュのヒント。privateは共有のキャッシュに載せず、publicをアクセス制御の代わりにしない- シークレットは、実行環境のシークレットの仕組みで管理し、トークンの素通しをしない。長時間処理は、Tasks 拡張か、ジョブのキューと明示的なハンドルで扱い、タイムアウトを層ごとにそろえる
- CI/CD には、Inspector の CLI とコンフォーマンスのテストを入れる。SLO は、可用性、ツール別のレイテンシ、古い版のクライアントの割合などで決める
関連ドキュメント
- Cloudflare Workers — Azure Functions相当のエッジ関数
- MCP最新仕様 2026-07-28 の変更点と移行
- MCPの可観測性と監査
- MCPのテストとデバッグ
- MCPの認可 - OAuth 2.1とメタデータ探索
- MCPの認可拡張とエンタープライズ導入
- MCP Registryとサーバーの配布