セルフホストのMCPサーバーは増えていきます。最初は社内ドキュメント用が1つ、次にデータベース用、そのうち誰かがデプロイパイプライン用の小さなものを書く。それぞれにポート、URL、シークレットがあり、チーム全員がその3つをクライアント設定に貼り付けます。トークンをローテーションするたびに全員へ連絡が必要です。
ゲートウェイを使えば、それが1つのアドレスと1つのトークンになります。クライアントはhttps://mcp.example.com/docs/mcpや.../db/mcpなどに接続します。ゲートウェイがトークンを確認し、各パスをlocalhost上の正しいサーバーに送り、サーバー自体は外に出ません。必要なものはごくわずかです。専用IPv4付きの小さなVPSとCaddyだけ。
以下の構成はテストサーバーで組み、実際のクライアントで確認してから書いています。
必要なもの
- クライアントが443に届くよう、公開IPv4を持つVPS。MCPサーバー自体が重い処理をしない限り、Nano-IPで足ります。
- サーバーを指すAレコードを持つドメイン。ここでは
mcp.example.com。 - Ubuntu 24.04またはDebian 12/13。Caddyはどちらの標準リポジトリにもあります。
1. MCPサーバーをlocalhostで動かす
各サーバーはsystemdサービスとして動かします。ここでは例を自己完結させるため、リファレンスサーバーserver-everythingを2つ使います。実際には自分のサーバーに置き換えてください。Node.js 20以降が必要です。
mkdir -p /opt/mcp && cd /opt/mcp
npm install @modelcontextprotocol/server-everything
# /etc/systemd/system/mcp-everything-1.service
[Unit]
Description=MCP server everything #1
After=network.target
[Service]
WorkingDirectory=/opt/mcp
Environment=PORT=3101
ExecStart=/opt/mcp/node_modules/.bin/mcp-server-everything streamableHttp
Restart=always
User=nobody
[Install]
WantedBy=multi-user.target
2つ目のコピーはポート3102を使います。そのあと:
systemctl daemon-reload
systemctl enable --now mcp-everything-1 mcp-everything-2
テスト中に気づいた点:このサーバーは127.0.0.1だけでなくすべてのインターフェースで待ち受けます。ほかにも同じ挙動のサーバーはたくさんあります。手順3でこれに対処します。
2. Caddy:まずトークンを確認し、次にパスで振り分ける
トークンを生成し、環境ファイル経由でCaddyに渡します。
apt install -y caddy
echo "MCP_GATEWAY_TOKEN=$(openssl rand -hex 32)" > /etc/caddy/gateway.env
chmod 600 /etc/caddy/gateway.env
mkdir -p /etc/systemd/system/caddy.service.d
printf '[Service]\nEnvironmentFile=/etc/caddy/gateway.env\n' > /etc/systemd/system/caddy.service.d/env.conf
systemctl daemon-reload
Caddyfile:
mcp.example.com {
route {
@unauthorized not header Authorization "Bearer {$MCP_GATEWAY_TOKEN}"
respond @unauthorized 401
handle_path /everything/* {
reverse_proxy 127.0.0.1:3101 {
flush_interval -1
}
}
handle_path /tools2/* {
reverse_proxy 127.0.0.1:3102 {
flush_interval -1
}
}
respond 404
}
}
caddy validate --config /etc/caddy/Caddyfile && systemctl restart caddy
mcp.example.comの証明書はCaddyが自動で取得します。
routeブロックは飾りではありません。最初の版では、トークン確認とhandle_pathブロックをトップレベルに並べて書いていて、トークンなしのリクエストが200で素通りしました。Caddyはディレクティブを独自の順序で並べ替え、handle_pathはrespondより先に実行されます。routeの中では書いた順に実行されます。この構成を応用するなら、まず「トークンなし」のケースを試してください。
handle_pathはプレフィックスを取り除くので、/everything/mcpはサーバーには/mcpとして届きます。flush_interval -1はストリーミングのイベントを届いた瞬間に流します。
3. SSHとHTTPS以外はすべて閉じる
apt install -y ufw
ufw default deny incoming
ufw allow OpenSSH
ufw allow 80,443/tcp
ufw enable
このあと、私たちのテストではポート3101と3102にサーバーの外から届かなくなり、22と443は応答しました。ポート80は、Caddyが証明書の取得・更新とHTTPSへのリダイレクトをするためだけに開けておきます。
4. 確認する
TOKEN=$(cut -d= -f2 /etc/caddy/gateway.env)
INIT='{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"check","version":"1"}}}'
curl -s -o /dev/null -w '%{http_code}\n' -X POST https://mcp.example.com/everything/mcp \
-H 'Content-Type: application/json' -H 'Accept: application/json, text/event-stream' -d "$INIT"
curl -s -o /dev/null -w '%{http_code}\n' -X POST https://mcp.example.com/everything/mcp \
-H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' -H 'Accept: application/json, text/event-stream' -d "$INIT"
1つ目のリクエストは401、2つ目は200を表示するはずです。私たちの実行では、間違ったトークンと途中で切れたトークンはどちらも401、未知のパスは正しいトークンでも404でした。
次にクライアントから。たとえばClaude Code:
claude mcp add --transport http everything https://mcp.example.com/everything/mcp \
--header "Authorization: Bearer $TOKEN"
claude mcp list
両方のサーバーが✔ Connectedと表示されました。同じクライアントで間違ったトークンを使うと✘ Failed to connectでした。クライアント設定の詳細はEQVPS MCPをClaude CodeとCodexに接続するを参照してください。
次のサーバーを追加する
新しいlocalhostポートで起動し、handle_pathブロックを1つ追加して、Caddyをリロードします。誰の設定も変わりません。同じホスト・同じトークンで新しいURLが1つ増えるだけです。
知っておくべき限界
- 全員で1つのトークンは簡単で、ローテーションもファイル1つですが、全員分を変えずに1人だけのアクセスを取り消すことはできません。ユーザーごとのアクセスが必要なら、前段に認証プロキシを置くか、各自に専用のルートとトークンを与えてください。
- ゲートウェイはサーバーを安全にはしません。 シェルコマンドを実行するMCPサーバーは、トークンを持つ人にとって依然としてシェルです。トークンを共有設定やgitに置かないでください。
- サイズはゲートウェイではなくサーバーに合わせる。 Caddy自体は数十MB程度です。ローカルモデルを動かしたり大きなリポジトリをインデックスしたりするサーバーがあれば、プランを決めるのはプロキシではなくそのサーバーです。
MCPサーバーが1つだけなら、MCPサーバー向けVPSがその単純なケースを扱っています。エージェントに自分が動いているサーバーの管理もさせたいなら、EQVPSのMCPサーバーが対応します(スクリプトが好みならREST API)。専用IPv4付きプランは月$8からです。
コメント
まだコメントはありません。最初になりましょう。