記事一覧
MCPホスティング

MCPサーバーのゼロダウンタイムデプロイ:ツール変更とロールバックでクライアントを壊さない方法【MCP Hosting・2026-07-28仕様】

MCPサーバーの新バージョンを、接続中のクライアントを壊さずに出す方法。処理中の呼び出しが消える理由、Claude Code・Claude APIの再接続とツール一覧の扱い、ツール変更の安全/破壊的の判定表、expand–contractとttlMs、SIGTERMのドレイン、Cloud Run・Kubernetes・Workersのロールバック、CI用の差分チェックまで解説。

最終更新:2026年10月18日 / 対象:MCP仕様 2026-07-28 と、セッションあり版(2025-03-26〜2025-11-25)のサーバー。クライアントの挙動は2026年10月時点のClaude Code・Claude API MCPコネクタの公式ドキュメントに基づく。ドレインと差分チェックのスクリプトは Node.js 22.22.0・jq 1.7 で動作確認済み

結論(30秒で読める要約)

MCPサーバーのゼロダウンタイムデプロイとは、すでに接続しているクライアントが止まらずに新バージョンへ切り替わるリリースのことです。切り替え中にツール呼び出しが失敗せず、会話の途中でツールが消えず、まずければすぐ戻せる状態を指します。MCP Hostingでは、プロセスの入れ替え自体は難しくありません。壊れるのはその周辺です。

  • 処理中のリクエストは古いプロセスと一緒に消えます。2026-07-28仕様でSSEの再開機能が削除され、ストリームが切れたリクエストは失われ、クライアントが新しいIDで送り直す決まりになりました。プラットフォームが強制終了する前(Cloud Runは10秒、Kubernetesは既定で30秒)に SIGTERM で処理を終わらせます。
  • クライアントはツール一覧をキャッシュします。tools/list の結果には必須の ttlMs が付き、Claude APIの mcp-client-2026-09-15 ベータは会話の最初に見たツール一覧を固定(ピン留め)できます。デプロイが「成功」しても、ツール名の変更や必須パラメータの追加でこうしたクライアントは壊れます。
  • セッションあり版のサーバーは、デプロイのたびに全セッションを失います。新しいプロセスは古い Mcp-Session-Id を知らないので、クライアントは 404 を受けて最初からやり直します。
  • 対策はexpand–contract(拡張→縮小)です。新しいツールや任意パラメータを先に追加し、古いものをキャッシュ期間以上残してから、次のリリースで消します。変更前には、DNSのTTLと同じ要領で ttlMs を下げておきます。
  • 検証→トラフィック移行→旧版を残す、の順で進めます。新リビジョンを非公開URLで確認し、本番とツール一覧の差分を取り、一部のトラフィックから移し、ロールバック用に前のリビジョンを残します。

クライアントの反応は3章、ツール変更の互換性表は4章、プラットフォーム別のコマンドは6章にまとめました。

目次

  1. MCPのゼロダウンタイムデプロイとは
  2. MCPサーバーのデプロイで何が壊れるか
  3. サーバーの再起動・ツール変更にクライアントはどう反応するか
  4. ツール変更の「安全」と「破壊的」
  5. グレースフルシャットダウン:処理中のリクエストを終わらせる(動作確認済みコード)
  6. プラットフォーム別のローリング・カナリア・ロールバック
  7. デプロイ後のスモークテストとツール一覧の差分チェック(動作確認済みスクリプト)
  8. チェックリスト
  9. トラブルシューティング(症状→原因→対処)
  10. NodeFlareならこう解決できる
  11. よくある質問

1. MCPのゼロダウンタイムデプロイとは

MCPのゼロダウンタイムデプロイとは、稼働中のMCPサーバーを新しいバージョンに置き換える際に、クライアントのリクエストを1件も失敗させず、接続中のクライアントが使っているツールも失わせない方法のことです。普通のWeb APIなら「ロードバランサーの後ろに常に健全なインスタンスがある」ことでほぼ足ります。MCPではそれに加えて、次の3つを満たす必要があります。

観点 問い よくある失敗
通信の継続 処理中のリクエストは最後まで終わるか。新しいリクエストは健全なインスタンスに届くか ストリームの途中でツール呼び出しが切れる、切り替え中に 502
契約の互換性 新しいツール一覧は、クライアントやモデルがすでに受け取った情報と食い違わないか もう存在しないツールをモデルが呼ぶ、新しい必須引数を送らない
戻せること 数分で前のバージョンに戻せるか データやスキーマの変更でロールバックできない

この記事では、リモートでホスティングしているサーバーを対象に3つすべてを扱います。まだローカルのstdioで動かしている場合は、先にstdioのMCPサーバーをリモートで動かす方法を参照してください。

2. MCPサーバーのデプロイで何が壊れるか

デプロイ時の失敗の多くは、プラットフォームではなく、MCPとクライアントの5つの挙動から来ています。各行に仕様やドキュメントの根拠を示します。

デプロイ時に起きること MCPで問題になる理由 出典
古いプロセスが SIGTERM、続いて SIGKILL を受ける 時間のかかる tools/call の応答はSSEストリームのことが多い。2026-07-28以降は Last-Event-ID による再送がなく、「切れた応答ストリームは処理中のリクエストを失い、クライアントは新しいリクエストIDで送り直さなければならない(MUST)」 MCP 2026-07-28 変更履歴
リクエストの送り直し 副作用のあるツール(チケット作成、メッセージ送信など)は、サーバー側で処理が終わっていて応答だけ失われた場合、2回実行されうる 上の規定からの帰結
セッションあり版サーバーの再起動 サーバーが知らないセッションIDには 404 Not Found を返さなければならず(MUST)、クライアントは新しいセッションを始めなければならない(MUST)。セッションごとの状態は消える MCP仕様 2025-11-25 Transports
ツール一覧の変更 tools/list の結果に ttlMs(ミリ秒単位の鮮度ヒント。クライアントはこの間キャッシュしてよい)が付く。listChanged を宣言したサーバーは、subscriptions/listen ストリームを開いたクライアントに通知すべき(SHOULD) MCP 2026-07-28 変更履歴・Tools
ツール定義はモデルのコンテキストに入っている 名前・スキーマ・説明文が変わればモデルの行動が変わる。会話全体でツール一覧を固定するクライアントもある Claude API MCPコネクタのドキュメント

つまり、プラットフォームのどの指標で見ても「健全」なデプロイでも、動いているエージェントをすべて壊しうるということです。通信の継続とツールの契約は、分けて管理する必要があります。

3. サーバーの再起動・ツール変更にクライアントはどう反応するか

再起動からの復帰の仕方も、新しいツール一覧に気づくタイミングも、クライアントごとに大きく違います。サポートするクライアントのうち、いちばん保守的なものに合わせて設計します。2026年10月時点で公式に書かれている挙動は次のとおりです。

クライアント サーバーが再起動・切断したとき ツール一覧が変わったとき デプロイへの影響
Claude Code(対話モード) 切断したリモートサーバーに、1秒から倍々で待つ指数バックオフで最大5回再接続する。最初の接続が 5xx・接続拒否・タイムアウトで失敗した場合は最大3回リトライする list_changed 通知を受けると、そのサーバーから最新の一覧を取り直す。取り直しに失敗しても以前のツールを保持する(v2.1.214以降) バックオフの合計(1+2+4+8+16秒、約30秒)より短い再起動なら吸収される。list_changed を送れば再接続なしで新ツールが届く
Claude Code(claude -p、Agent SDK) 同じく5回試し、一時的なエラーなら約30分までバックグラウンドで再試行を続ける list_changed ではツール一覧だけを更新する 長時間動くエージェントは遅いロールアウトにも耐えるが、プロンプトやリソースは古いまま動くことがある
Claude Code(新しいプロトコル版のサーバー) 通知用のストリームを開いたままにする。10秒以内に再び閉じた場合の再オープンは最大3回、1時間に5回再オープンすると次は約6時間待つ ストリームが開いている間しか変更が届かない 長いストリームを切るホストでは、新ツールが数時間見えないことがある。ユーザーには /mcp から再接続してもらう
Claude API MCPコネクタ(mcp-client-2026-09-15) 記載なし ツール一覧を mcp_tool_listing ブロックに記録し、「サーバーがツールを変えても、会話の途中でClaudeに見えるものは変わらない」。tools に一覧を固定すると、APIはサーバーに一覧を問い合わせなくなる 消したツールや名前を変えたツールも会話が終わるまでモデルに見え続け、その呼び出しは新しいバージョンに届く
2026-07-28対応のクライアント全般 ストリームが切れたリクエストを送り直す tools/list を ttlMs の間キャッシュしてよい ツールの変更が最大で ttlMs の間見えないことがある
セッションあり版のクライアント全般 古いセッションに 404 を受け、新しいセッションを始める クライアントによる 再起動のたびにセッションの状態がリセットされる(5章参照)

この表から2つのルールが出てきます。1つ目は、再起動を短く、かつ処理を終わらせてから行うことです。クライアントは短い中断なら吸収できますが、長い中断は吸収できません。2つ目は、どこかのクライアントが会話の終わりまで前のツール一覧を使い続けると想定し、新しいバージョンでもそれを受け付けられるようにすることです。

4. ツール変更の「安全」と「破壊的」

ツールの変更が「安全」とは、前のツール一覧を持っているクライアントが、新しいサーバーを問題なく呼べることを指します。リリースに含まれる変更は、出す前にすべて次の表で分類します。

変更 安全か 理由 出し方
ツールを追加 安全 古い一覧には入っていないだけ いつでも出してよい。list_changed を送る
任意パラメータを追加 安全 古い呼び出しは省略し、既定値が使われる いつでも出してよい
必須パラメータを追加 破壊的 古いスキーマを見ているモデルは送らない 既定値つきの任意パラメータとして出し、必須化は次のリリースで。または新しいツールにする
ツール名を変更 破壊的 キャッシュや固定された一覧は古い名前で呼び、-32602「Unknown tool」になる 新しい名前を追加し、古い名前を別名としてキャッシュ期間以上残してから消す
ツールやパラメータを削除 破壊的 名前の変更と同じ まず説明文で非推奨と明記し、後で消す
パラメータの型や意味を変更 破壊的 古い呼び出しは古いスキーマで組み立てられている 新しいパラメータか新しいツールを作る
outputSchema や structuredContent の形を変更 一部のクライアントで破壊的 クライアントは構造化結果をスキーマで検証すべき(SHOULD)とされている ツールをバージョン分けする(report_v2 など)
説明文を書き換え プロトコル上は安全だが挙動が変わる モデルのツール選択が変わり、プロンプトキャッシュも無効になる。予期しない説明文の変更は、セキュリティ面で「ラグプル」と見なされることもある 意図して出し、変更履歴に書く
ツールの並び順を変更 避ける クライアントのキャッシュとプロンプトキャッシュのため、サーバーは決まった順序で返すべき(SHOULD)とされている コード側で名前順にソートする

これは、データベースのマイグレーションで使うexpand–contractをツールに当てはめたものです。拡張(新しい形を足す)→移行(クライアントとモデルが新しい形に移る)→縮小(古い形を消す)の順に進めます。具体的には次のとおりです。

  1. リリースN(拡張):get_page と並べて fetch_page を追加する。get_page の説明文に「非推奨:fetch_pageを使ってください」と書く。どちらも動く状態にする。
  2. キャッシュ期間以上待つ:ttlMs の最大値、よくある会話の最長時間、ツール一覧を固定すると分かっているエージェントの利用期間のうち、いちばん長いもの。
  3. リリースN+1(縮小):get_page を消す。しばらくは isError: true のツール実行エラーで「このツールは廃止されました」と返し、まだ呼んでくるモデルが立て直せるようにする。

計画的にツールを変える前には、ttlMs を下げておきます。普段 "ttlMs": 300000(5分)を返しているなら、リリースの1TTL前に 60000 に下げ、リリース後に元に戻します。ヒントを守るクライアントは1分以内に変更を取り込みます。DNSの移行前にTTLを下げるのと同じ考え方です。仕様は ttlMs と cacheScope を「既存の listChanged 通知を補うもの」と位置づけているので、subscriptions/listen ストリームを開いているクライアント向けに notifications/tools/list_changed も送ります。

害がないように見えて実は影響が大きいのが、説明文の変更です。予期しない定義の変更を攻撃の兆候としてどう検知するかは、MCPツールポイズニングの検知と封じ込めで解説しています。

5. グレースフルシャットダウン:処理中のリクエストを終わらせる(動作確認済みコード)

グレースフルシャットダウンとは、プラットフォームから SIGTERM を受けた時点で新しい接続の受付をやめ、処理中のMCPリクエストを終わらせ、SIGKILL が来る前に自分で終了することです。これがないと、デプロイのたびにその瞬間に動いていたツール呼び出しが切れます。

使える時間はプラットフォームによって違います。

プラットフォーム シグナルと猶予時間 変更方法
Google Cloud Run SIGTERM の約10秒後に SIGKILL 固定。処理の完了待ちは10秒未満に収める
Kubernetes SIGTERM の後、terminationGracePeriodSeconds(既定30秒)で SIGKILL Podの設定。プロセスが止まる前にエンドポイントから外れるよう、短い preStop のsleepを入れる
Docker / Compose docker stop は SIGTERM を送り、既定で10秒後に SIGKILL docker stop -t または stop_grace_period

次のNode.jsのコードが最小構成です。標準の http モジュールだけを使うので、Node.jsのHTTPサーバー上で動くMCP SDKのトランスポートなら、どれにも組み合わせられます。

// drain.mjs - stop taking new MCP requests on SIGTERM, finish in-flight ones, then exit
import http from "node:http";

const GRACE_MS = Number(process.env.DRAIN_MS ?? 8000); // keep below the platform's grace period
let inFlight = 0;

const server = http.createServer(async (req, res) => {
  inFlight++;
  res.on("close", () => inFlight--);
  // ...hand the request to your MCP transport here; a slow tools/call is simulated:
  await new Promise((r) => setTimeout(r, 3000));
  res.writeHead(200, { "Content-Type": "application/json" })
     .end(JSON.stringify({ jsonrpc: "2.0", id: 1, result: { resultType: "complete", content: [] } }));
});

process.on("SIGTERM", () => {
  server.close(() => process.exit(0));   // stop accepting; runs when all sockets have closed
  server.closeIdleConnections();         // drop idle keep-alive sockets immediately
  setTimeout(() => {                     // hard stop before the platform's SIGKILL
    console.error(`forcing exit with ${inFlight} request(s) in flight`);
    process.exit(1);
  }, GRACE_MS).unref();
});

server.listen(Number(process.env.PORT ?? 8000));

動作確認では、3秒かかるリクエストを送り、0.5秒後に SIGTERM を送ってから、すぐに同じポートへ接続してみました。処理中のリクエストは3.04秒後に 200 で完了し、新しい接続は拒否され、リクエストが終わった時点でプロセスは終了コード0で終わりました。DRAIN_MS はプラットフォームの猶予より短く(Cloud Runの10秒なら8秒)、いちばん長いツール自体のタイムアウトは DRAIN_MS より短く設定します。そうしないと、そのツールは結局強制終了されます。

MCPならではの追加点が3つあります。

  • 副作用のあるツールは冪等にする。ストリームが切れたリクエストはクライアントが送り直すので、冪等キー(request_key 引数など)を受け取るか、作成前に既存の結果を確認します。
  • 長く続く subscriptions/listen ストリームは「終わらない」。完了を待たずに SIGTERM の時点で閉じます。クライアントは新しいインスタンスで開き直します。
  • ブリッジ経由のstdioサーバーは、ブリッジ側も処理を終わらせる必要がある。SIGTERM を受けるのはブリッジです。ブリッジが子プロセスをすぐ殺すなら、サーバー側のコードに関係なく処理中の呼び出しは切れます。サーバーだけでなく、ブリッジの終了時の挙動も確かめてください。

6. プラットフォーム別のローリング・カナリア・ロールバック

主要なホストはどこも、トラフィックを落とさずにインスタンスを入れ替えられます。違いは、新バージョンを非公開で試せるか、トラフィックを割合で移せるか、1コマンドで戻せるかです。以下は2026年10月時点の各社ドキュメントに基づきます。

プラットフォーム 既定のロールアウト 先に非公開でテスト 割合でのカナリア ロールバック MCPでの注意点
Google Cloud Run 新リビジョンに100% --no-traffic --tag green で https://green---myservice-….a.run.app のようなURLができる update-traffic --to-revisions REV=5 update-traffic --to-revisions OLD_REV=100 ロールバックは即時で、デプロイ時のヘルスチェックは再実行されない。処理中のリクエストは落とされない
Kubernetes ローリングアップデート(maxSurge・maxUnavailable とも25%) 別のDeploymentかService サービスメッシュやIngressの重み付け kubectl rollout undo deployment/mcp(既定で古いReplicaSetを10個保持) readinessProbeがないと、サーバーが待ち受ける前にトラフィックが来る
Cloudflare Workers wrangler deploy でバージョンを置き換え wrangler versions upload(デプロイされない) wrangler versions deploy で割合を指定 wrangler rollback(直近に公開した100バージョンまで) ロールバックしても接続先のリソース(バインディング)は変わらない。Durable Objectは同時に1バージョンしか動かない
Fly Machines rolling:1台ずつ入れ替え canary:新しいMachineを1台起動して確認 割合指定ではない 前のイメージで再デプロイ bluegreen は新しいMachineがすべてヘルスチェックに通ってから切り替えるので、チェックを定義しておく
VPS+Docker(1台) 旧コンテナ停止→新コンテナ起動(停止時間あり) 新コンテナを別ポートで起動 リバースプロキシに2つのupstreamを置く 前のイメージタグで起動し直す 前のイメージを残す。latest だけではロールバックが当て推量になる

Cloud Runでの一連の流れは次のとおりです。

# 1. Deploy the new revision without traffic, behind a private tag URL
gcloud run deploy mcp --image "$IMAGE" --no-traffic --tag green

# 2. Smoke-test https://green---mcp-<hash>.a.run.app/mcp (section 7)

# 3. Canary 5%, watch errors, then go to 100%
gcloud run services update-traffic mcp --to-tags green=5
gcloud run services update-traffic mcp --to-latest

# Rollback: send all traffic back to the previous revision
gcloud run services update-traffic mcp --to-revisions mcp-00041-abc=100

割合でのカナリアには、MCP特有の落とし穴が2つあります。

  • セッションあり版のサーバーでは、1つのセッションが2つのバージョンにまたがる。新リビジョンに5%を流すと、片方のリビジョンが initialize に答え、続く呼び出しがもう片方へ行き、MCPサーバーの水平スケールで解説したセッションエラーが起きます。ステートレスモードにするか、割合ではなくクライアント単位(テスター用の別URL)でカナリアします。
  • 2つのツール一覧が同時に存在する。カナリア中は、同じクライアントがあるリクエストでは古い一覧、次のリクエストでは新しい一覧を受け取ることがあります。問題にならないのは、変更が4章の意味で「安全」な場合だけです。これもexpand–contractで出す理由の1つです。

ロールバックには隠れた前提があります。前のバージョンが、今のデータで動くことです。リリースNでデータベースの列を移行したり、永続ディスク上のファイル形式を変えたりしていれば、コードを戻してもデータは戻りません。CloudflareはWorkersのバインディングについてこれを明記していますが、どのプラットフォームでも同じです。ロールバックに頼る前に、データの変更も後方互換にしておきます(ここでもexpand–contractです)。

7. デプロイ後のスモークテストとツール一覧の差分チェック(動作確認済みスクリプト)

新バージョンにトラフィックを流す前に、3つを確かめます。tools/list に答えること、ツールの契約が本番と互換であること、読み取り専用のツールを1回実際に呼べることです。6章の非公開URL(またはステージング環境)に対して実行します。

手順1:両方のツール一覧を取得する。sed は、サーバーが application/json で返してもSSEストリームで返しても、JSONの行だけを取り出します。

list_tools() {
  curl -s -X POST "$1" \
    -H "Authorization: Bearer $MCP_API_KEY" \
    -H "Content-Type: application/json" \
    -H "Accept: application/json, text/event-stream" \
    -H "Mcp-Method: tools/list" \
    -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' \
  | sed -n 's/^data: //;/^{/p'
}
list_tools "$PROD_URL/mcp"   > old.json
list_tools "$CANARY_URL/mcp" > new.json

セッションあり版のサーバーは initialize の後でないと tools/list に答えないので、MCP InspectorかSDKのクライアントで一覧を取得してください。2026-07-28版のサーバーには、SDKが求める _meta も付けて送ります。

手順2:契約の差分を取る。次のスクリプトは、4章の破壊的変更(ツールの削除・名前変更、必須パラメータの追加、パラメータの削除)を検出し、安全な変更も一覧にします。

#!/usr/bin/env bash
# tooldiff.sh OLD.json NEW.json - flag tool changes that can break existing clients
set -euo pipefail
summ='[.result.tools[] | {name, required: ((.inputSchema.required // []) | sort),
       props: ((.inputSchema.properties // {}) | keys)}] | sort_by(.name)'
o=$(mktemp); n=$(mktemp); jq "$summ" "$1" > "$o"; jq "$summ" "$2" > "$n"
jq -rn --slurpfile o "$o" --slurpfile n "$n" '
  ($o[0] | map({(.name): .}) | add // {}) as $old |
  ($n[0] | map({(.name): .}) | add // {}) as $new |
  ( $old | keys[] | select($new[.] == null) | "BREAKING removed or renamed tool: \(.)" ),
  ( $new | keys[] | select($old[.] == null) | "safe     added tool: \(.)" ),
  ( $old | keys[] | select($new[.] != null) as $t |
      ( ($new[$t].required - $old[$t].required)[] | "BREAKING \($t): new required param \(.)" ),
      ( ($old[$t].props - $new[$t].props)[]       | "BREAKING \($t): param removed \(.)" ),
      ( ($new[$t].props - $old[$t].props - $new[$t].required)[] | "safe     \($t): new optional param \(.)" ) )'

get_page の名前を変え、search_docs に必須パラメータと任意パラメータを1つずつ足したテストでの出力です。

BREAKING removed or renamed tool: get_page
safe     added tool: fetch_page
BREAKING search_docs: new required param space
safe     search_docs: new optional param limit

CIでは、BREAKING で始まる行が1つでもあればリリースを止めます(計画した「縮小」のリリースを除く)。説明文はあえて比較対象から外しています。モデルの挙動を変えるので、目で見て確認してください。

手順3:実際に1回呼ぶ。既知の引数で読み取り専用のツールを呼び、"isError": false を確認します。その後トラフィックを移し、少なくとも ttlMs 1回分の間、エラー率と tools/call のレイテンシを見てからリリース完了とします。

8. チェックリスト

  • [ ] SIGTERM で処理を終わらせ、プラットフォームの猶予(Cloud Run 10秒、Kubernetes 既定30秒、Docker 既定10秒)より前に終了する
  • [ ] 各ツール自体のタイムアウトが、処理完了待ちの時間より短い
  • [ ] 副作用のあるツールが冪等キーを受け取るか、重複を検知する
  • [ ] subscriptions/listen ストリームは、終了時に待たずに閉じる
  • [ ] リリース内のツール変更を、すべて安全か破壊的かに分類した(4章)
  • [ ] 破壊的な変更は、2回以上のリリースに分けてexpand–contractで出す
  • [ ] 計画的なツール変更の1TTL前に ttlMs を下げ、list_changed を送る
  • [ ] ツールを決まった順序で返す
  • [ ] 新バージョンが非公開URLで tools/list と読み取り専用の tools/call に答えた
  • [ ] tooldiff.sh(または同等のもの)を本番に対してCIで実行している
  • [ ] 前のイメージやリビジョンを残し、ロールバックのコマンドを書き残してある
  • [ ] データやディスク上の形式の変更が、前のリリースと互換である
  • [ ] セッションあり版のサーバーは、割合ではなくクライアント単位でカナリアする

9. トラブルシューティング(症状→原因→対処)

症状 原因 対処
デプロイのたびに数秒間ツール呼び出しが失敗する グレースフルシャットダウンがなく、処理中のまま古いプロセスが殺されている SIGTERM で処理を終わらせる(5章)
デプロイ後にチケットやメッセージが重複する ストリームが切れたリクエストをクライアントが送り直したが、1回目は成功していた 副作用のあるツールに冪等キーを入れる
リリース後、一部の会話でだけ「Unknown tool」が出る キャッシュや固定されたツール一覧が古い名前を使っている 古い名前を別名としてキャッシュ期間以上残す
リリース後、モデルがある引数を埋めなくなった 新しい必須パラメータが、モデルに渡されたスキーマにない まず既定値つきの任意パラメータとして出す
Claude Codeに新しいツールが数時間出てこない ホストへの通知ストリームが何度も切れ、Claude Codeが再接続を控えている /mcp から再接続する。ホスト側でストリームを維持する。ttlMs を下げる
デプロイのたびに全ユーザーのセッションが切れる セッションあり版のサーバーがメモリにセッションを持ち、クライアントが 404 を受けている セッションの状態を共有ストアに移すか、ステートレスにする
カナリア中に 400/404 のセッションエラーがランダムに出る initialize とその後の呼び出しが別リビジョンに分かれている ステートレスモードにするか、クライアント単位のURLでカナリアする
サーバーが待ち受ける前にPodへトラフィックが来る readinessProbeがない MCPのポートを確認するreadinessProbeを入れる
ロールバックは成功したのに旧バージョンがエラーになる リリースNで、N−1が読めない形にデータを変えた データ変更を後方互換にする。修正版で前に進める
Cloud Runでロールバックしたら壊れたリビジョンにトラフィックが行った ロールバックではデプロイ時のヘルスチェックが再実行されない 実際にトラフィックを正常に処理した実績のあるリビジョンにだけ戻す

10. NodeFlareならこう解決できる

NodeFlareは、GitHubリポジトリのMCPサーバー(stdio専用のものも含む)を https://<your-server>.nodeflare.tech/mcp のHTTPSエンドポイントとしてデプロイするMCPホスティングサービスです。この記事で挙げたデプロイの課題には、次のように対応しています。

この記事の課題 NodeFlareでの対応
「デプロイは成功したが、本当に応答するのか」 起動後に本物のMCP initialize リクエストを送り(バックオフつきで最大8回)、結果を Verified(正常)、Broken(5xx、接続不可、OOM、クラッシュ、待ち受けなし)、Inconclusive(判定保留)で表示する
クラッシュを繰り返すプロセスが健全に見える 子プロセスがクラッシュを繰り返す場合は、成功と誤報せずデプロイを失敗にする
手書きのDockerfileがリリースごとにずれる ビルダーがリポジトリからランタイム・トランスポート・起動コマンドを検出してDockerfileを生成する。ビルドが失敗したらDockerfileを修正して最大3回再試行する。デプロイの状態は Pending → Building → Deploying → Succeeded/Failed/Cancelled と進む
stdioサーバーとブリッジの再起動 内蔵のstdioアダプターがサーバーを子プロセスとして動かし、クラッシュ時は一定の回数内で1秒から30秒までの指数バックオフで再起動する。回数を使い切ると /health が503を返す
トランスポート移行中の新旧クライアント 同じエンドポイントでStreamable HTTPと旧来のHTTP+SSE(2024-11-05)の両方に対応
リリースのたびに変わるシークレット 環境変数は暗号化して保存され、VMの起動時にだけ注入される。ビルド引数・イメージのレイヤー・ログには書き込まれず、既知の形式のトークンはビルドログから伏せ字にされる
「リリース後、どのツールから失敗し始めたか」 リクエストログに、メソッド、ツール名、引数のキー(値は記録しない)、HTTPステータス、結果、所要時間、セッションID、User-Agentを記録し、ツール名やエラーメッセージで検索できる。保持期間はFreeで7日、Pro・Teamで30日
再デプロイ後も残すべきデータ 永続ディスクを付けない限り、再デプロイでファイルシステムはリセットされる。永続ディスクはPro以上で、1GBあたり月¥15
新リリースでメモリが足りなくなる メモリは256MB・512MB・1GB・2GBから選べる。適正化の提案でサイズを下げてメモリ不足が起きた場合は、自動で元のサイズに戻る

NodeFlareが対応していないこと:割合によるトラフィック分割や、以前のビルドへのワンクリックのロールバックは、公式ドキュメントに記載がありません。リリースごとにGitでタグを付け、ロールバックしたいときは前のタグを再デプロイしてください。4章と5章のグレースフルシャットダウンとexpand–contractは、引き続きサーバーのコード側で行う必要があります。

料金:Freeプランは¥0で、MCPサーバー1つと月30GB時間の計算資源が使えます。Proは月¥2,980で、サーバー数無制限、計算資源300GB時間とストレージ10GBが含まれます。超過分の計算資源は1GB時間あたり¥5で、スケールtoゼロで止まっているサーバーは¥0です。

最小の始め方:

  1. nodeflare.techにサインインし、MCPサーバーのGitHubリポジトリを選ぶ。
  2. 環境変数を設定してデプロイし、Verifiedと表示されるのを待つ。
  3. クライアントに登録する前に、新しい /mcp のURLに対して7章のツール一覧取得と tooldiff.sh を実行する。

1サーバーまで無料で試せます → nodeflare.tech

11. よくある質問

MCPサーバーを停止時間なしでデプロイするには?

SIGTERM を受けたら処理中のリクエストを終わらせてから終了し、常に健全なインスタンスがトラフィックを受けるローリングやリビジョン単位のロールアウトを使い、ツールの契約を後方互換に保ちます。新バージョンは非公開URLで確認し、トラフィックを段階的に移し、前のバージョンをロールバック用に残しておきます。

デプロイ中に処理中のMCPツール呼び出しはどうなる?

応答が終わる前にプロセスが殺されると、その呼び出しは失われます。2026-07-28仕様でSSEの再開機能が削除されたため、クライアントは新しいIDでリクエストを送り直します。処理中のリクエストを待つグレースフルシャットダウンで防げ、ツールを冪等にしておけば送り直しも安全になります。

クライアントを壊さずにMCPのツールを更新するには?

expand–contractを使います。新しいツールや任意パラメータを追加し、古い形をキャッシュ期間以上動かしたまま残し、後のリリースで消します。ツール名の変更や必須パラメータの追加を1回のリリースで済ませてはいけません。キャッシュや固定されたツール一覧が古い契約のまま使われるからです。

MCPツールに必須パラメータを足すのは破壊的変更?

はい。前のスキーマで動いているモデルはそのパラメータを送らないので、呼び出しが検証で失敗します。まず妥当な既定値つきの任意パラメータとして追加し、クライアントが新しいスキーマを取り込んでから必須にします。

ツール一覧が変わったことを、MCPクライアントはどうやって知る?

listChanged を宣言したサーバーは notifications/tools/list_changed を送るべきとされ、2026-07-28対応のクライアントは subscriptions/listen ストリームでそれを受け取ります。クライアントは返された ttlMs の間 tools/list をキャッシュすることもあり、mcp-client-2026-09-15 を使うClaude APIのように、会話全体で一覧を固定できるクライアントもあります。

MCPの tools/list にある ttlMs とは?

ttlMs は、2026-07-28仕様で tools/list などの一覧の結果に必須となったフィールドです。ミリ秒単位の鮮度のヒントで、クライアントがその結果をどれだけキャッシュしてよいかを示します。計画的にツールを変える前に下げておくと、クライアントが早く取り直します。

MCPサーバーが再起動したら、Claude Codeは再接続する?

リモートサーバーなら再接続します。Claude Codeは、切断したHTTP・SSEサーバーに1秒から始まる指数バックオフで最大5回再接続し、最初の接続が一時的なエラーで失敗した場合は最大3回リトライします。stdioサーバーは自動では再接続されません。

Cloud RunでMCPサーバーをロールバックするには?

gcloud run services update-traffic SERVICE --to-revisions REVISION=100 で、正常だったリビジョンにすべてのトラフィックを戻します。Cloud Runは即座に反映し、デプロイ時のヘルスチェックは再実行しないので、以前に正常にトラフィックを処理したリビジョンを選びます。

MCPサーバーをカナリアデプロイできる?

できますが、割合でのカナリアが向くのはステートレスなサーバーです。セッションあり版のサーバーでは1つのセッションが2つのバージョンにまたがってセッションエラーになるため、クライアント単位(テスター用の別URL)でカナリアします。カナリア中は2つのツール一覧が同時に存在するので、変更が後方互換であることも確認してください。

セッションあり版のMCPサーバーは、再デプロイでセッションを失う?

セッションをプロセスのメモリに持っているなら失います。新しいプロセスは古い Mcp-Session-Id を知らないので 404 を返し、クライアントは新しいセッションを始めます。セッションの状態を共有ストアに移すか、SDKをステートレスモードにすれば、デプロイはクライアントから見えなくなります。

まとめ

ゼロダウンタイムのMCP Hostingには、2つの課題があります。通信の課題は、いつもの方法で解決できます。プラットフォームの猶予時間内に SIGTERM で処理を終わらせ、リビジョンやローリングアップデートで入れ替え、前のバージョンをすぐ出せる状態にしておくことです。MCP特有の課題は、ツールの契約です。クライアントはツール一覧を ttlMs の間キャッシュし、会話全体で固定するものもあり、モデルは渡されたスキーマどおりに動きます。ツールの変更はexpand–contractで出し、変更前に ttlMs を下げ、list_changed を送り、新しいツール一覧と本番との差分をCIで確認します。この両方をそろえれば、リリースはユーザーに気づかれない作業になります。

参考資料

NodeFlare

MCP専用ホスティングサービス

GitHubにあるMCPを簡単デプロイ・簡単運用で最適化
無料で始める

他の記事

MCPトークン2026年10月18日

MCPのプロンプトキャッシュ:ツール一覧でキャッシュが外れる原因とMCPトークン費用の抑え方【2026年版・計算例つき】

MCPのツール定義とプロンプトキャッシュの関係を解説。Claude 5.5の書き込み・読み取り単価、計算例(安定なら−89%、毎回外れると+25%)、ツール順・動的な説明文・list_changedなどキャッシュが外れる原因、ツール検索との両立、5分と1時間TTLの選び方、安定性チェックのスクリプト、ヒット率の測り方まで。

MCPセキュリティ2026年10月18日

MCPのSSRF対策:狙われる3つの経路、2026年のCVE3件、Egress許可リストでの封じ方【MCPセキュリティ】

MCPのSSRF対策を解説。OAuthディスカバリ・URLを受け取るツール・CIMD取得という3つの経路、2026年のCVE3件、仕様のブロック一覧から漏れる範囲、DNS固定まで行う動作確認済みPythonコード、observeからenforceへ進めるEgress許可リストの導入手順まで。

MCPセキュリティ2026年10月17日

MCPの監査ログ設計:ツール呼び出しごとに記録すべき14項目・残してはいけない情報・8つのアラート【MCPセキュリティ/2026-07-28版】

リモートMCPサーバーの監査ログ設計を解説。2026-07-28版で非推奨になったLogging機能が監査ログにならない理由、記録すべき14項目、残してはいけない情報、動作確認済みのハッシュチェーン実装、OpenTelemetryとの対応、保管期間、8つのアラートまで。

MCPサーバーのゼロダウンタイムデプロイ:ツール変更とロールバックでクライアントを壊さない方法【MCP Hosting・2026-07-28仕様】 | Blog | Nodeflare