MCP サーバーを remote で公開したいが、古い SSE ベースの記事やサンプルを見ているうちに、何を新実装すべきか分からなくなる。/sse と /messages を分けるべきなのか、session は必要なのか、404 になった session をどう扱うのか、OAuth はどこまで必須なのか。このあたりで止まりやすい人向けの記事です。
結論から言うと、今から remote MCP を出すなら、前提を Streamable HTTP に切り替えた方がよいです。 2025-03-26 版の MCP 仕様では、旧 HTTP+SSE transport から Streamable HTTP transport へ置き換えられました。単なる名前変更ではありません。endpoint 設計、client との接続の張り方、session の扱い、認証の考え方、DNS rebinding 対策まで変わっています。
この記事では、MCP の changelog、transport specification、Claude Code の MCP docs を踏まえて、SSE 時代の設計からどこを直すべきか を実務目線で整理します。Claude Code から MCP をつなぐ実務手順を先に見たい場合は Claude Code MCP ガイド、remote MCP の承認境界や tool poisoning まで含めて詰めたい場合は MCP prompt injection 対策ガイド を併読すると、transport と安全運用の責務を分けて考えやすいです。MCP の組織配布や監査まで含めた話は MCP エンタープライズ運用ガイド、Claude Code 側の allowlist や managed-mcp.json まで見たい場合は Claude Code managed settings 設計ガイド を先に読むとつながります。
先に結論: 旧HTTP+SSEの知識のまま実装するとズレる
私なら、移行判断を次の 4 点で整理します。
- endpoint を単一化する
Streamable HTTP では、MCP endpoint は基本的に 1 本です。POSTとGETの両方を受けます。 - response は JSON か SSE の両対応にする
POSTに対して、即時なら JSON、ストリームが必要なら SSE を返す実装が必要です。 - session を設計で決める
stateful にするならMCP-Session-Idを返し、以後のリクエストで引き回します。単純な read-only server なら stateless も現実的です。 - remote 公開ならセキュリティを transport 層で持つ
仕様上もOriginheader 検証、localhost bind、認証が強く推奨されています。
ここを押さえずに「SSE endpoint を 1 本立てればよい」と考えると、client 互換も運用も中途半端になります。
何が変わったのか: HTTP+SSE から Streamable HTTP へ
MCP の 2025-03-26 版 changelog では、主要変更の 1 つとして 旧 HTTP+SSE transport が Streamable HTTP transport に置き換えられた と明記されています。つまり、remote MCP の標準実装はもう「SSE endpoint と別 POST endpoint を並べる前提」ではありません。
新しい transport で重要なのは、HTTP を request/response だけでなく、必要に応じて SSE も返せる単一 transport として扱う 点です。
整理すると、発想はこう変わります。
- 旧HTTP+SSE:
GET /sseのような購読 endpointPOST /messagesのような別 endpoint- client 側が「どの URL にどう送るか」を transport 前提で強く知っている
- Streamable HTTP:
https://example.com/mcpのような 単一 endpointPOSTで JSON-RPC message を送る- server は JSON で即答してもよいし、
text/event-streamで流してもよい GETは server-to-client notification や stream 再接続に使える
この変更で嬉しいのは、reverse proxy や SaaS の API gateway に乗せやすくなることです。一方で、server 実装者は「SSE を返す場合」「JSON で終わる場合」「接続を閉じて再接続させる場合」を以前より明示的に設計しないといけません。
実装で最初に直すべきは endpoint 設計
旧HTTP+SSE 実装から移るとき、最初に見るべきは handler の切り方です。
仕様上、Streamable HTTP server は 単一の MCP endpoint が POST と GET を両方サポート します。たとえば https://api.example.com/mcp のような形です。
POST の役割:
- client から JSON-RPC request / notification / response を送る
- JSON-RPC request に対しては
application/jsonで単発 response を返すかtext/event-streamで複数 message を返す
GET の役割:
- server から client へ通知や request を流す SSE stream を開く
- 切断後の resume に使う
ここで大事なのは、POST = 書き込み API、GET = 読み取り API という REST 的な発想で分けないこと です。これはあくまで transport です。中身は JSON-RPC message であり、HTTP method は通信路の都合で決まっています。
もし既存実装が /sse と /messages を前提にしているなら、今後の標準 client に合わせるには mcp endpoint へ寄せる方が安全です。後方互換が必要なら、旧 endpoint をしばらく残しつつ、新 endpoint でも initialize を受けられるようにするのが現実的です。
JSON と SSE をどう使い分けるか
Streamable HTTP で実装が雑になりやすいのがここです。
仕様では、client が POST する際に Accept header で application/json と text/event-stream の両方を受けられる前提を示します。server は request の性質に応じて返し方を選べます。
私なら基準をこう置きます。
- JSON で返すケース
- 処理が短い
- response が 1 回で終わる
- progress 通知や server-side request が不要
- SSE で返すケース
- 長時間ツール実行がある
- progress を出したい
- 途中で追加の JSON-RPC notification / request を返したい
read-only な軽い resource server なら、最初は JSON 中心でも構いません。逆に、長い検索、外部 API 呼び出し、server からの追加イベントが必要な server を JSON 固定で作ると、あとで transport を作り直すことになります。
MCP Inspector や各 SDK の最近の examples でも、Streamable HTTP は「direct response も SSE streaming も両方扱える transport」として実装されています。つまり、SSE 専用 server を作るのではなく、HTTP transport の中に streaming を持つ 発想に変えるべきです。
session は必須ではないが、stateful か stateless かを曖昧にしない
Streamable HTTP では MCP-Session-Id header が導入されています。server が initialize response で session ID を返した場合、client はその後の request で同じ ID を送る必要があります。
ここで迷いやすいのは、「session を実装しないといけないのか」です。答えは 必須ではないが、どちらで行くかを先に決めるべき です。
加えて、session を持つなら lifecycle も transport とセットで決める 必要があります。古い session ID に対して server が 404 を返したときに client をどう再初期化させるか、終了時に DELETE をどこで受けるかを曖昧にすると、「接続できるが復旧しない」 server になりやすいです。
stateless が向くケース
- read-only ツール中心
- tool 実行ごとに独立して完結する
- 再接続や進行中状態の保持がいらない
- API gateway 配下で水平分散しやすくしたい
stateful が向くケース
- 長い SSE stream を扱う
- message resume をやりたい
- server からの notification を継続的に送りたい
- 実行中ジョブや接続状態を memory / store に持ちたい
私の感覚では、社内 read-only MCP や docs / search 系は stateless 寄り、agent control plane や長時間ジョブは stateful 寄り です。ここを曖昧にすると、ロードバランサや再接続時に破綻します。
remote MCP で事故りやすいのは transport より認証
もう 1 つの大きな変化が認証です。2025-03-26 版の MCP 仕様では authorization framework が入り、OAuth 2.1 ベースの考え方が前提に置かれました。Claude Code の remote MCP docs でも、HTTP transport の server に対して /mcp から OAuth 認証を進める流れが整理されています。
ここで重要なのは、remote MCP を「社内 API にBearer tokenを1本刺せば終わり」と考えないこと です。
最低限見るべき論点は次です。
- human user が対話しながら使うのか
- background job が使うのか
- authorization server discovery をどう出すのか
- dynamic client registration 前提でよいのか
- 固定 callback port が必要か
たとえば Claude Code では、OAuth callback port を固定しないと事前登録型の redirect URI に合わない場合があります。逆に、server 側が Dynamic Client Registration を持たないなら、client 側に client_id を明示させる運用が必要です。
このあたりは transport の違いというより、remote 化した瞬間に発生する運用の仕事 です。組織配布や allowlist 運用まで含めて見るなら MCP エンタープライズ運用ガイド と AIコーディングツールの社内ガイドライン を分けて読んだ方が整理しやすいです。
Security で最初に見るべきは Origin 検証
仕様を読まずに実装すると落としやすいのがここです。
Streamable HTTP transport の security warning では、server 実装者に対して次が求められています。
Originheader を検証する- ローカル運用なら
0.0.0.0ではなく localhost bind を優先する - 適切な認証を入れる
特に Origin 検証は、remote server だけでなく ローカルでブラウザや inspector を併用する時にも見落としやすい です。DNS rebinding を意識しないままローカル MCP を広く bind すると、便利な開発サーバーではなく危ない穴になります。
私は remote 化の前に、次のチェックを固定します。
Origin不一致を 403 にできるか- localhost 以外 bind の必要性を説明できるか
- access token を query string に載せていないか
- read-only で始められるか
- 失敗ログと拒否ログを残せるか
MCP の security は prompt injection だけではありません。transport 境界そのものの防御 を先に作るべきです。
後方互換は「古いclientをいつまで残すか」で決める
既存利用者がいる server では、いきなり旧HTTP+SSE endpoint を消せないことがあります。MCP 仕様にも、backwards compatibility の考え方は明記されています。
私なら移行を 3 段階でやります。
1. 新 endpoint を先に追加する
https://example.com/mcpを追加- 新 client はここへ寄せる
- initialize が Streamable HTTP で通ることを確認する
2. 旧 endpoint は read-only 互換期間にする
/sseと旧 POST endpoint は残す- 新機能は追加しない
- deprecation date を内部に明示する
3. session / auth / logging を新 endpoint に集約する
- 監査対象を新 endpoint に寄せる
- token / callback / session 問題は新 transport でだけ直す
- 旧 endpoint は撤去計画を切る
ここで「両方ずっと残す」は避けた方がよいです。server 実装も運用手順も二重化して、結局どちらも中途半端になります。
私ならこう設計する: 最小の移行チェックリスト
最後に、実務で使うならこの順番が扱いやすいです。
- 単一 MCP endpoint を作る
POSTで JSON と SSE の両返却を受けられるようにするGETで stream 再接続を扱えるようにする- stateful / stateless を決める
Origin検証と認証を先に入れる- OAuth discovery と callback 方針を固める
- 旧HTTP+SSE endpoint の撤去期限を決める
MCP Streamable HTTP は、単なる transport の新名称ではありません。remote MCP を API としてちゃんと運用できる形へ寄せるための整理 です。
SSE 時代の理解のまま進めると、「つながるけど運用できない」 server を作りやすいです。逆に、単一 endpoint、session 方針、OAuth、Origin 検証まで最初に決めれば、Claude Code や今後の MCP client から扱いやすい remote server になります。
まずは transport を置き換えるのではなく、server の責務を整理する ところから始めるのが正解です.