stdioのMCPサーバーをリモートURLで動かす手順|supergateway・mcp-proxy・FastMCPで学ぶMCPホスティング実践【2026年版】
GitHubで見つけたstdio専用のMCPサーバーをリモートURLにする手順を解説。supergateway(stateless/stateful)とmcp-proxyが子プロセスをどう扱うかをソースで確認し、Node・Python別のDockerfile、Inspectorでの動作確認、2026-07-28仕様との互換性、よくある9つの失敗をまとめました。
最終更新:2026年10月14日 / 対象:MCP仕様 2026-07-28版、supergateway・mcp-proxy(2026年10月時点のGitHub上のソース)
結論(30秒で読める要約)
stdioのMCPサーバーをリモートで使うには、サーバーの前に「ブリッジ」を置きます。Streamable HTTPのリクエストを受け取り、標準入出力でstdioプロセスに中継する小さなHTTPサーバーです。大事なのはどのツールを選ぶかより、子プロセスをどう扱うかです。
- supergateway(既定のstateless)は、POSTリクエストのたびに新しい子プロセスを起動します。状態を持たないAPIラッパーなら問題ありませんが、メモリ上に状態を持つサーバーは壊れ、起動時にパッケージを取りに行くコマンドだと毎回遅くなります。
- supergatewayの
--statefulは、クライアントのセッションごとに子プロセスを1つ起動し(initialize時)、セッションが閉じると終了します。 - mcp-proxyは、起動時に子プロセスを1つだけ立ち上げ、すべてのクライアントで共有します。速くて単純ですが、全員が同じプロセス・メモリ・認証情報を使います。
- コードを変更できるなら、FastMCPや公式SDKのHTTPトランスポートでブリッジ自体をなくせます。
あとは、依存はビルド時に入れる、コンテナ内では 0.0.0.0 で待ち受ける、MCP Inspectorで確認してからホストに載せる。この順で進めれば詰まりません。
目次
- 「stdioのMCPサーバーをリモートで動かす」とは
- 3つのブリッジと子プロセスの扱い(ソースで確認)
- サーバーの性質から選ぶ
- 手順:Node.jsのサーバーをsupergatewayで
- 手順:Pythonのサーバーをmcp-proxyで
- ブリッジを使わない選択肢:ネイティブのStreamable HTTP
- デプロイ前にMCP Inspectorで確認する
- 2026-07-28版のクライアントとつながるか
- チェックリスト
- よくある失敗(症状→原因→対処)
- NodeFlareならこう解決できる
- よくある質問(FAQ)
1. 「stdioのMCPサーバーをリモートで動かす」とは
stdioのMCPサーバーをリモートで動かすとは、stdioしか話せないサーバーを、HTTPS経由で外から呼べるエンドポイントとして公開することです。MCP仕様(2026-07-28版)では、stdioは「クライアントが起動したサブプロセスの標準ストリーム上で、改行区切りのメッセージをやり取りする」方式、Streamable HTTPは「各メッセージを単一のMCPエンドポイントへのHTTP POSTとして送る」方式と定義されています。stdioのサーバーには呼び出せるポートがないので、誰かが起動して変換する必要があります。
その役目を担うのがブリッジです。ブリッジはユーザーのPCではなく、サーバーと同じコンテナで動きます。どのプラットフォームに載せるか、費用はどれくらいかといった全体像は、柱記事のMCPホスティングの選び方|リモートMCPサーバーの置き場所6択を比較にまとめています。本記事はその一段下、ブリッジそのものを扱います。
2. 3つのブリッジと子プロセスの扱い(ソースで確認)
READMEにはフラグの説明はあっても、子プロセスがいつ起動していつ終わるかはほとんど書かれていません。そこで2026年10月時点の各プロジェクトのソースコードを読んで確認しました。
| ブリッジ | 導入 | stdioの子プロセスが起動するタイミング | 共有する範囲 | 既定のエンドポイント |
|---|---|---|---|---|
| supergateway(stateless、Streamable HTTPの既定) | npm i -g supergateway または npx |
MCPパスへのPOSTのたび | 共有しない(1リクエスト1プロセス) | /mcp、ポート 8000 |
supergateway --stateful |
同上 | Mcp-Session-Id のない initialize リクエストを受けたとき |
1つのクライアントセッション | /mcp、ポート 8000 |
| mcp-proxy(sparfenyuk版) | uv tool install mcp-proxy または pip install mcp-proxy |
起動時に1回だけ | そのサーバーの全クライアント | /mcp と /sse。ホストは 127.0.0.1、ポートは未指定ならランダム |
| ネイティブHTTP(SDK/FastMCP) | 自分のコードに含める | 子プロセスなし(サーバー自身がHTTPプロセス) | コード次第 | 多くは /mcp |
ソースで確認できた具体的な挙動は次のとおりです。
- supergatewayのstatelessゲートウェイは、POSTを受けるたびに新しいサーバーとトランスポートを作り、
spawn(stdioCmd, { shell: true })を呼びます。コード中のコメントも「リクエストごとにトランスポートとサーバーの新しいインスタンスを作る」となっています。子プロセスはトランスポートが閉じるかエラーになると終了します。GETとDELETEには405(JSON-RPCコード-32000)を返します。 - statefulゲートウェイでは、
Mcp-Session-Idがなく、かつinitializeのリクエストを受けたときだけ子プロセスを起動します。トランスポートが閉じると終了し、--sessionTimeout(ミリ秒)は指定したときだけアイドルセッションを閉じます。 - mcp-proxyは、起動時に設定されたサーバーごとに
stdio_clientを1つ開き、その1セッションをプロキシで包みます。/mcp、/sse、/servers/<name>/…のどのルートも、この共有セッションにつながります。/statusは常に用意されます。
なお、supergatewayの --stdio はシェル経由で実行されるため、引用符や &&、npx -y … もそのまま動きます。これが性能に効いてくる理由は10章で説明します。
3. サーバーの性質から選ぶ
言語ではなく、サーバーが呼び出しの間に何を保持するかで選びます。
| stdioサーバーの性質 | おすすめ | 理由 |
|---|---|---|
| 状態を持たないAPIラッパー(fetch、天気、検索、SaaSのAPI) | どれでも可。supergatewayのstatelessが最も簡単 | 失う状態がない。毎回新しいプロセスでも正しく動く(遅いだけ) |
| メモリ上に状態を持つ(ナレッジグラフ、ブラウザセッション、DB接続、キャッシュ) | supergatewayの --stateful、1人で使うならmcp-proxy |
状態をセッションの間保つ必要がある |
| 使うのは1人(または1つのエージェント) | mcp-proxy | プロセスが常に温まっていて最も速い |
| ユーザーごとに別の認証情報が必要 | ユーザー(認証情報)ごとに別デプロイ | mcp-proxyは1プロセス・1つの環境変数を共有し、supergatewayも全子プロセスに同じ環境変数を渡す |
| 自分で書いたコード | ネイティブHTTP(6章) | 構成要素が1つ減る |
| 起動が遅い(JVM、重いPythonのimport、ヘッドレスブラウザ) | mcp-proxyか --stateful |
statelessだと起動コストを毎回払う |
本番で初めて気づきやすい点が2つあります。
- statelessモードはコールドスタートを掛け算します。起動に1.5秒かかるサーバーなら、すべての
tools/callが最低1.5秒遅くなります。 - 子プロセスを共有すると、すべてを共有します。mcp-proxyでは、2つのエージェントが同じメモリ上の状態に書き込み、同じAPIキーを使います。個人用なら問題ありませんが、複数ユーザー向けには向きません。
4. 手順:Node.jsのサーバーをsupergatewayで
例として、ナレッジグラフを保持するためstatefulモードが必要な公式のメモリサーバー(@modelcontextprotocol/server-memory)を使います。
ステップ1:起動コマンドを確認します。READMEで起動コマンドと必要な環境変数を確認し、手元で動くことを確かめます。
npx @modelcontextprotocol/inspector --cli npx -y @modelcontextprotocol/server-memory --method tools/list
ステップ2:Dockerfileを書きます。サーバーとブリッジはビルド時に入れ、実行時の npx -y は使いません。
FROM node:22-slim
# 本番ではバージョンを固定する:supergateway@x.y.z、server-memory@x.y.z
RUN npm install -g supergateway @modelcontextprotocol/server-memory
EXPOSE 8000
CMD ["supergateway", \
"--stdio", "mcp-server-memory", \
"--outputTransport", "streamableHttp", \
"--stateful", "--sessionTimeout", "600000", \
"--port", "8000"]
--outputTransport streamableHttp は省略できません。--stdio 使用時の既定の出力は旧方式の sse だからです。--sessionTimeout 600000 は10分アイドルのセッションを子プロセスごと閉じ、放置されたセッションがたまるのを防ぎます。
ステップ3:手元で動かします。
docker build -t memory-mcp .
docker run --rm -p 8000:8000 memory-mcp
エンドポイントは http://localhost:8000/mcp です。確認方法は7章を参照してください。状態を持たないサーバーなら、--stateful と --sessionTimeout を外すだけです。
5. 手順:Pythonのサーバーをmcp-proxyで
例として、状態を持たず1プロセス共有が向いている公式のfetchサーバー(PyPIの mcp-server-fetch)を使います。
ステップ1:手元でコマンドを確認します。
npx @modelcontextprotocol/inspector --cli uvx mcp-server-fetch --method tools/list
ステップ2:Dockerfileを書きます。npx -y と同じ理由で、起動時の uvx も避けます(起動のたびに解決・ダウンロードが走るため)。
FROM python:3.12-slim
# 本番ではバージョンを固定する
RUN pip install --no-cache-dir mcp-proxy mcp-server-fetch
EXPOSE 8080
CMD ["mcp-proxy", "--host", "0.0.0.0", "--port", "8080", \
"--pass-environment", "mcp-server-fetch"]
忘れやすいフラグが3つあります。
| フラグ | 既定値 | コンテナで必要な理由 |
|---|---|---|
--host 0.0.0.0 |
127.0.0.1 |
指定しないとコンテナの外から接続できない |
--port 8080 |
空いているポートをランダムに選ぶ | プラットフォーム側は固定ポートを前提にする |
--pass-environment(または -e KEY VALUE) |
オフ | 指定しないと、コンテナに設定したAPIキーがstdioサーバーに届かない |
ステップ3:docker run --rm -p 8080:8080 fetch-mcp で起動します。Streamable HTTPのエンドポイントは http://localhost:8080/mcp、簡単な死活確認は http://localhost:8080/status です。1つのコンテナで複数のstdioサーバーを動かしたい場合は、--named-server NAME 'COMMAND' でそれぞれを /servers/NAME/ 以下に割り当てられます。
6. ブリッジを使わない選択肢:ネイティブのStreamable HTTP
コードを変更できるなら、ネイティブのHTTPトランスポートがいちばんすっきりします。子プロセスも標準出力の解析もなく、監視するプロセスは1つです。FastMCP(Python)なら1行の変更で済みます。
from fastmcp import FastMCP
mcp = FastMCP("my-server")
@mcp.tool
def add(a: int, b: int) -> int:
return a + b
if __name__ == "__main__":
# 変更前:mcp.run()(stdio)
mcp.run(transport="http", host="0.0.0.0", port=8000) # /mcp で待ち受け
コードを触らずにFastMCPのCLIで fastmcp run server.py --transport http --port 8000 としても同じです。TypeScriptでは公式SDKのStreamable HTTPサーバートランスポートが同じ役割を担います。ただし、他人のサーバーをフォークして書き換えると、以後の保守は自分の担当になります。GitHubの他人のサーバーでブリッジが好まれるのはこのためです。
7. デプロイ前にMCP Inspectorで確認する
ブリッジの不具合をClaudeやCursorから調べるのはやめましょう。エラーメッセージが曖昧で原因を絞れません。公式のMCP Inspector(Node 22.19.0以上)をCLIモードで、手元のコンテナに向けて使います。
# Streamable HTTPでツール一覧を取得
npx @modelcontextprotocol/inspector --cli http://localhost:8000/mcp \
--transport http --method tools/list
# ツールを1つ呼んで結果を確認
npx @modelcontextprotocol/inspector --cli http://localhost:8000/mcp \
--transport http --method tools/call --tool-name read_graph --format json
確認するポイントは次の3つです。
tools/listの結果が、ステップ1でstdioで動かしたときと同じか。- 状態を持つサーバーなら、同じセッション内で「書き込むツール」のあとに「読むツール」を呼び、データが残っているか。
- 1回目と2回目の呼び出しの時間。どちらも遅ければ、リクエストごとにプロセス起動(やパッケージのダウンロード)が発生しています。
うまくいかないときは、Web UI(npx @modelcontextprotocol/inspector を起動してURLを入力)でJSON-RPCのやり取りをそのまま見ると原因がつかみやすくなります。
8. 2026-07-28版のクライアントとつながるか
多くの場合、フォールバックによってつながります。2026-07-28版ではStreamable HTTPからプロトコルレベルのセッションとGETストリームが削除され、プロトコルバージョンやケイパビリティは各リクエストの _meta に入るようになりました。一方、GitHubにあるstdioサーバーの多くは、旧来の initialize ハンドシェイクを使うSDKで作られており、ブリッジはその挙動をそのまま通します。
仕様はこの状況を明示的に扱っています。両方の世代に対応するクライアントは「まず新方式のリクエストを試して」サーバーの世代を判定してよく、400 の本文が新方式のJSON-RPCエラーでなければ「initialize にフォールバックして旧バージョンで続ける」とされています。実務上は次のように読み替えられます。
| 見えている現象 | 意味 |
|---|---|
GET /mcp が 405 を返す |
想定どおり。supergateway(stateless)も2026-07-28版のサーバーも同じ挙動 |
クライアントが Mcp-Session-Id を送って動いている |
旧世代のセッションで動いている(supergatewayの --stateful) |
| 新方式専用のクライアントが接続を拒否する | クライアントに旧世代へのフォールバックがない。両対応のクライアントを使うか、サーバーをネイティブの2026-07-28版トランスポートに移す |
ヘッダーやメッセージの詳細なルールは、参考資料にある仕様のStreamable HTTPのページを参照してください。
9. チェックリスト
- [ ] 起動コマンドと環境変数をREADMEで確認し、手元のstdioで動くことを確かめた
- [ ] サーバーの状態の持ち方に合うブリッジを選んだ(3章)
- [ ] パッケージはビルド時に入れ、起動コマンドに
npx -yやuvxがない - [ ] サーバーとブリッジのバージョンを固定した
- [ ] supergatewayで
--outputTransport streamableHttpを指定した - [ ] mcp-proxyで
--host 0.0.0.0、固定の--port、--pass-environmentを指定した - [ ]
--statefulを使う場合は--sessionTimeoutも指定した - [ ] サーバーのログは標準エラー出力(stderr)にだけ出している
- [ ] コンテナに対してInspectorの
tools/listとtools/callが成功した - [ ] 公開前に、
Originの検証と呼び出し元の認証を前段で行っている(仕様ではOriginの検証はMUST、認証はSHOULD)
10. よくある失敗(症状→原因→対処)
| 症状 | 原因 | 対処 |
|---|---|---|
| ツール呼び出しが毎回数秒かかる | supergatewayのstatelessで --stdio に npx -y を指定し、起動のたびにパッケージを解決している |
ビルド時にグローバルインストールする。--stateful やmcp-proxyも検討 |
| 前の呼び出しで書いたデータが次で消える | statelessモードがリクエストごとに新しいプロセスを起動している | --stateful にする(1人ならmcp-proxy) |
| コンテナは動いているのに接続が拒否される | mcp-proxyが既定で 127.0.0.1 で待ち受けている |
--host 0.0.0.0 を付ける |
| 手元では動くのにプラットフォームのヘルスチェックが落ちる | mcp-proxyがランダムなポートを選んだ | --port でプラットフォームのポートを指定 |
| 「APIキーがない」というエラー | mcp-proxyは既定でコンテナの環境変数を子プロセスに渡さない | --pass-environment か -e KEY VALUE を付ける |
| 他のユーザーのデータが見える | mcp-proxyが1つの子プロセスを全クライアントで共有している | ユーザー(テナント)ごとに分けてデプロイ、またはsupergatewayの --stateful |
| メモリが増え続けてコンテナが再起動する | クライアントが閉じないstatefulセッションが子プロセスを抱えたまま残る | --sessionTimeout を指定 |
| ときどきJSONのパースエラーが出る | サーバーが標準出力にログを出している(stdio仕様では、有効なMCPメッセージ以外を標準出力に書くことはMUST NOT) | ログを標準エラー出力に回す |
| 2台にスケールしたらセッションが壊れる | statefulのセッションは1台の1プロセスにしか存在しない | セッション単位のスティッキールーティング、または1台構成 |
できあがったコンテナをVPS、Cloud Run、Workers、MCP専用ホストのどこで動かすかは、MCPサーバーのホスティング先ランキングで比較しています。
NodeFlareならこう解決できる
NodeFlareは、GitHubにあるstdioのMCPサーバーを、認証つきのHTTPSエンドポイントとして動かすMCPホスティングサービスです。本記事で挙げた課題に、次のように対応します。
| 本記事の課題 | NodeFlareでの解決 |
|---|---|
| ブリッジ選びと配線、Dockerfileの作成 | GitHubリポジトリを選ぶだけ(モノレポのサブディレクトリも可)。ビルダーがランタイムと起動コマンドを検出してDockerfileを生成します(自前のDockerfileも使えます)。stdioサーバーには stdio-adapter.cjs という1ファイルを追加し、サーバーを子プロセスとして起動してHTTPに中継します |
| ブリッジが本当に起動したか分からない | 起動後にビルダーが実際にMCPの initialize を送信し(バックオフしながら最大8回)、クラッシュループならデプロイを失敗として扱います。壊れたURLを公開しません |
| Node.js以外の言語 | Node.js、Python、Go、Rust、Java(Maven/Gradle、Kotlinを含む)、C#/.NET、Dockerに対応。BunとDenoも認識します |
| 新旧のクライアント | Streamable HTTPと、旧方式のHTTP+SSE(2024-11-05)の両方に対応 |
APIキーがサーバーに届かない(--pass-environment 問題) |
シークレットは保存時に暗号化され、VMの起動時にだけ注入されます。ビルド引数、イメージレイヤー、ログには書き込まれません |
| 起動コストとアイドル時のコスト | scale-to-zeroで、アイドルのサーバーはスナップショットを取って停止し、1秒未満で復元します。計算料金は¥5/GB時間で、動いている間だけ課金。Pro以上のAlways-onなら1台を常に温めておけます |
| 再起動後も残したいデータ | 永続ディスク(¥15/GB月、任意)。scale-to-zero、再デプロイ、VMの再起動をまたいで残ります |
| スケール時にセッションが壊れる | 同時実行数に応じた水平オートスケールと、セッションの固定(session pinning) |
| どのツールで失敗したか分からない | リクエストごとのログに mcp_method、tool_name、http_status、duration_ms、session_id、error_message などを記録 |
| 前段の認証 | エンドポイントは https://<サーバー名>.nodeflare.tech/mcp。mcp_ で始まるAPIキーによる Authorization: Bearer が必要です |
料金は、Freeプランが¥0でMCPサーバー1つ・月30GB時間まで、Proプランが月¥2,980でサーバー数無制限・300GB時間込みです。
最小の始め方:
- nodeflare.techにサインインし、stdioサーバーのGitHubリポジトリを選ぶ。
- 環境変数を設定してデプロイし、
initializeのチェックが通るのを待つ。 - 表示された
/mcpのURLとAPIキーを、Claude・Cursor・自作エージェントに設定する。
1サーバーまで無料で試せます → nodeflare.tech
よくある質問(FAQ)
stdioのMCPサーバーをリモートで使えますか?
使えます。supergatewayやmcp-proxyなどのブリッジをサーバーと同じコンテナに置けば、stdioプロセスを起動してStreamable HTTPのエンドポイント(通常は /mcp)を公開できます。stdioアダプター内蔵のホストを使う方法や、コードを持っているならネイティブのHTTPトランスポートに切り替える方法もあります。
stdioのMCPサーバーをStreamable HTTPに変換するには?
コードを変えずに済ませるなら、supergateway --stdio "<コマンド>" --outputTransport streamableHttp --port 8000 か mcp-proxy --host 0.0.0.0 --port 8080 <コマンド> を使います。コードを変えるなら、SDKのHTTPトランスポートを使います(FastMCPなら mcp.run(transport="http"))。クライアントにつなぐ前にMCP Inspectorで確認してください。
supergatewayとmcp-proxyの違いは?
supergatewayはNode.js製で、既定ではリクエストごとに、--stateful ならセッションごとにstdioプロセスを起動します。mcp-proxyはPython製で、起動時に1プロセスだけ立ち上げて全クライアントで共有します。分離しやすいのはsupergateway、1人で使うなら速くて簡単なのはmcp-proxyです。
supergatewayはリクエストのたびに新しいプロセスを起動しますか?
既定のstatelessモードのStreamable HTTPでは、起動します。ソースコード上、POSTのたびに新しい子プロセスを起動しています。メモリ上に状態を持つサーバーや起動が遅いサーバーでは --stateful を使い、セッションごとに1プロセスにしてください。
statelessとstatefulはどちらを使うべき?
呼び出しの間に何も保持せず、起動も速いサーバー(APIラッパーなど)はstatelessで十分です。メモリ上の状態、開きっぱなしの接続、遅い起動があるサーバーはstatefulにし、アイドルセッションを片付けるために --sessionTimeout も指定します。
Python(uvx)のMCPサーバーもリモートで動かせますか?
動かせます。pipやuvでビルド時にパッケージを入れ、mcp-proxy(またはsupergatewayの uvx 版イメージ)の後ろで動かします。起動コマンドに uvx を使うと、コンテナ起動時にパッケージの解決とダウンロードが走るので避けてください。
ブリッジ経由のMCPサーバーで、呼び出しの間に状態が消えるのはなぜ?
多くは、ブリッジがリクエストごとに新しいプロセスを起動するstatelessモードで動いているためです。プロセスが毎回入れ替わるので、メモリ上のデータは残りません。statefulモードに切り替えるか、データをディスクや外部データベースに保存してください。
ブリッジ経由のstdioサーバーはMCP 2026-07-28版のクライアントで使えますか?
多くの場合、仕様の後方互換のフォールバックで使えます。両世代に対応するクライアントは、まず新方式のリクエストを試し、だめなら initialize ハンドシェイクに切り替えます。2026-07-28版専用のクライアントは旧世代のサーバーを拒否することがあり、その場合はサーバーをネイティブの2026-07-28版トランスポートに移す必要があります。
Claudeにつなぐ前に、リモートのMCPエンドポイントを確認する方法は?
npx @modelcontextprotocol/inspector --cli <URL> --transport http --method tools/list を実行し、続けて --method tools/call --tool-name <ツール名> でツールを1つ呼びます。両方成功すれば、ClaudeやCursorでつながらない原因はブリッジではなく、URL・認証・ネットワークのどれかです。
HTTPで使うためにMCPサーバーを書き直す必要はありますか?
ありません。ブリッジか、stdioアダプター内蔵のホストを使えば変更なしで動きます。自分のコードで、構成要素を1つ減らしたい場合には、ネイティブのHTTPトランスポートへの書き換えに価値があります。
まとめ
stdioのMCPサーバーをリモートで動かす方法はすでに確立していますが、うまく動くかどうかはブリッジのプロセスモデルで決まります。supergatewayのstatelessは1リクエスト1プロセス、--stateful は1セッション1プロセス、mcp-proxyは全員で1プロセスを共有します。サーバーの状態の持ち方と利用者に合わせて選び、依存はビルド時に入れ、コンテナ用のフラグ(0.0.0.0、固定ポート、環境変数の受け渡し)を設定し、MCP Inspectorで確認してからホストを選びましょう。
