−25%

Windows 按年付费,截至 10 月 31 日。 查看套餐

EQVPS
开始使用

用于 MCP 网关的 VPS:多个 MCP 服务器共用一个地址和一个令牌

团队在用三个自托管的 MCP 服务器,每个人手里都有三个 URL 和三个密钥。把它们放到一台小 VPS 的同一个 HTTPS 地址后面:Caddy 校验 bearer 令牌、按路径路由,并让服务器本身不暴露在公网。这是一套测试过的配置。

自托管的 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 起。

常见问题

为什么要把 MCP 服务器放在网关后面?

整个团队只用一个地址和一个令牌,而不是每台服务器各一套;TLS 集中在一处处理;服务器本身只监听 localhost。加第四台服务器只需改网关的三行配置,不用再给所有人发新密钥。

需要什么套餐?

带独立 IPv4 的套餐,因为客户端必须能访问 80 和 443 端口。Nano-IP(每月 $8,2 vCPU,1 GB)足够跑网关和几个轻量 MCP 服务器。如果某台服务器自己要干重活,就按它来选配置。

流式传输能经过 Caddy 吗?

可以。Streamable HTTP 使用普通 POST 请求加 server-sent events。在反向代理上设置 flush_interval -1,事件就会立即转发,而不是堆在缓冲区里。

一个共享令牌够安全吗?

对小团队来说是个合理的起点:令牌从不离开 HTTPS,改一个文件就能轮换。当你需要按人授权或撤销权限时,在前面加一层真正的认证。共享令牌是底线,不是上限。

NAT 套餐能用吗?

不能作为公开网关。NAT 套餐只有一个给 SSH 用的转发端口,没有其他入站,客户端访问不到 443。只有当所有客户端都在同一台服务器上时才可行。

评论

暂无评论。来做第一个吧。

发表评论

评论在显示前会经过审核。