自架的 MCP 伺服器會越來越多。先是一台給內部文件,接著一台給資料庫,後來有人替部署流程寫了台小的。每台都有自己的連接埠、URL 和密鑰,團隊裡每個人都得把三組全貼進用戶端設定。換一次權杖就得通知所有人。
閘道把這一切變成一個位址和一個權杖。用戶端連到 https://mcp.example.com/docs/mcp、.../db/mcp 等位址。閘道驗證權杖,把每個路徑轉到 localhost 上對應的伺服器,伺服器本身從不對外暴露。需要的東西很少:一台有獨立 IPv4 的小 VPS 加上 Caddy。
下面的設定我們先在測試伺服器上架好,並用真實用戶端驗證過才寫出來。
需要準備什麼
- 一台有公開 IPv4 的 VPS,讓用戶端能連到 443。除非你的 MCP 伺服器本身要做重活,Nano-IP 就夠了。
- 一個 A 記錄指向伺服器的網域,這裡用
mcp.example.com。 - Ubuntu 24.04 或 Debian 12/13。兩者的標準套件庫裡都有 Caddy。
1. 在 localhost 上執行 MCP 伺服器
每台伺服器都以 systemd 服務執行。為了讓範例自成一體,這裡把參考伺服器 server-everything 跑了兩份;實際上換成你自己的。需要 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
第二份使用 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"
第一個請求應輸出 401,第二個輸出 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 區塊,重新載入 Caddy。誰的設定都不用改,只是在同一個主機、同一個權杖下多了一個新 URL。
需要了解的限制
- 所有人共用一個權杖很簡單,輪替也只需一個檔案,但你沒辦法只撤銷一個人的權限而不影響所有人。需要依使用者授權時,在前面加一個驗證代理,或給每個人獨立的路由與權杖。
- 閘道不會讓伺服器變安全。 能執行 shell 指令的 MCP 伺服器,對任何持有權杖的人來說仍然是一個 shell。別把權杖放在共用設定或 git 裡。
- 依伺服器而不是依閘道選規格。 Caddy 本身只占幾十 MB。如果某台伺服器要跑本地模型或為大型程式庫建立索引,決定方案的是它,而不是代理。
只有一台 MCP 伺服器?用於 MCP 伺服器的 VPS 談的是那種更簡單的情況。想讓你的代理也管理它們所在的伺服器?EQVPS MCP 伺服器做得到(偏好寫腳本的話用 REST API),有獨立 IPv4 的方案每月 $8 起。
評論
目前還沒有評論。歡迎成為第一位。