在云端 VPS 上自托管 Remote MCP Server:从零到远程调用完整配置
上一篇列出了 5 个必须上 VPS 的场景,决定上云了就来这里。把 MCP Server 部署到 VPS 上,结果 Claude Desktop 死活连不上——八成是因为你用的还是 stdio transport。stdio 是进程间通信协议,只能在本机两个进程之间跑,跨网络必须换成 Streamable HTTP。再加上主流客户端强制要求 HTTPS,裸 HTTP 端点直接拒连。这篇把完整链路走一遍:transport 选型 → Node.js 服务部署 → Nginx HTTPS 反代 → Bearer Token 鉴权 → systemd 守护 → 客户端接入验证,每步给可直接粘贴的命令。
前置条件:你需要什么
VPS 基础要求
最低配置:1 核 CPU、512MB 内存、Ubuntu 22.04 LTS。MCP Server 本身不重,但如果你的 Server 会调用 AI API 或做文件处理,建议 1GB 内存起步。
必须满足的三个条件:有独立公网 IP(不是 NAT 后的内网机)、80 和 443 端口未被防火墙屏蔽、有一个指向该 IP 的域名(Let’s Encrypt 签发证书需要)。
# 检查 Node.js 版本,需要 >= 18node -v
# 确认 443/80 端口监听状态(新机器应为空)ss -tlnp | grep -E ':(80|443)'
# 查看系统版本lsb_release -a本地开发环境确认
本文假设你已经有一个可以在本地跑起来的 MCP Server 项目。如果还没有,用官方 SDK 建一个最小示例:
# 初始化项目mkdir my-mcp-server && cd my-mcp-servernpm init -y
# 安装 MCP SDK,锁定版本避免 breaking changenpm install @modelcontextprotocol/sdk@1.10.2transport 选型:为什么 stdio 不能用于远程
| transport | 通信方式 | 能否跨网络 | 主流客户端支持 | 适用场景 |
|---|---|---|---|---|
| stdio | 标准输入输出(进程管道) | 仅本机 | Claude Desktop 本地模式 | 本地工具、IDE 插件 |
| SSE | HTTP 长连接,服务端推送 | 是 | Claude Desktop、Cursor(部分版本有断连问题) | 中等并发,只需服务端推送 |
| Streamable HTTP | 标准 HTTP POST + 流式响应 | 是 | Claude Desktop、Cursor、所有支持 HTTP 的客户端 | 远程部署首选 |
新项目直接用 Streamable HTTP。MCP 官方文档已将 SSE 标注为 legacy,Streamable HTTP 是 2025 年推荐的远程 transport,兼容性更好,实现也更简单。
部署 MCP Server 并配置 Nginx HTTPS 反代
安装依赖与启动 MCP Server 进程
把项目上传到 VPS,安装依赖:
# 上传项目(本地执行,替换路径和 IP)scp -r ./my-mcp-server root@your-vps-ip:/opt/mcp-server
# SSH 进入 VPSssh root@your-vps-ip
# 进入项目目录,安装生产依赖cd /opt/mcp-servernpm install --production
# 先手动跑一次确认没报错,监听在 3000 端口node server.js# 看到 "MCP Server listening on :3000" 再继续,Ctrl+C 停掉server.js 中 Streamable HTTP transport 初始化的关键代码:
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";import express from "express";
const app = express();app.use(express.json());
const server = new McpServer({ name: "my-server", version: "1.0.0" });
// 在这里注册你的 tools / resources / prompts
const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: undefined,});
app.all("/mcp", async (req, res) => { await transport.handleRequest(req, res, req.body);});
await server.connect(transport);
// 注意:绑定 127.0.0.1,不直接暴露到公网,由 Nginx 做唯一入口app.listen(3000, "127.0.0.1");Nginx 配置:反代 + 长连接支持
apt update && apt install -y nginx创建站点配置文件:
cat > /etc/nginx/sites-available/mcp-server << 'EOF'server { listen 80; server_name your-domain.com; # 替换为你的域名
location /mcp { proxy_pass http://127.0.0.1:3000/mcp; proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme;
# 流式响应禁止缓冲 proxy_buffering off; proxy_cache off;
# MCP 工具调用可能耗时较长,超时拉大 proxy_read_timeout 300s; proxy_send_timeout 300s; }}EOF
# 启用站点ln -s /etc/nginx/sites-available/mcp-server /etc/nginx/sites-enabled/nginx -t && systemctl reload nginxLet’s Encrypt 证书申请与自动续期
# 安装 Certbotapt install -y certbot python3-certbot-nginx
# 申请证书,替换邮箱和域名certbot --nginx -d your-domain.com --email you@example.com --agree-tos --non-interactive
# 验证自动续期定时任务systemctl status certbot.timer
# 测试续期流程(dry-run,不真正续期)certbot renew --dry-run证书申请成功后,Certbot 会自动把 Nginx 配置改为 listen 443 ssl 并填入证书路径。
systemd 守护进程配置
cat > /etc/systemd/system/mcp-server.service << 'EOF'[Unit]Description=Remote MCP ServerAfter=network.target
[Service]Type=simpleUser=www-dataWorkingDirectory=/opt/mcp-serverExecStart=/usr/bin/node server.jsRestart=on-failureRestartSec=5sEnvironment=NODE_ENV=production# 在此行追加注入 API Key 等环境变量:Environment=KEY=VALUE
[Install]WantedBy=multi-user.targetEOF
# 重载配置并启动systemctl daemon-reloadsystemctl enable mcp-serversystemctl start mcp-server
# 确认运行状态systemctl status mcp-server鉴权:用 Bearer Token 防止公网裸奔
MCP Server 挂上公网之后,不加鉴权等于把工具调用接口全部公开。有两个方案,按需选。
方案一:Nginx map 层鉴权(轻量,不改应用代码)
# 在 http 块内(server 块外层)添加:map $http_authorization $auth_valid { default 0; "Bearer your-secret-token-here" 1;}
# 在 location /mcp 块内添加:if ($auth_valid = 0) { return 401 '{"error":"Unauthorized"}';}生成强度足够的 token:
openssl rand -hex 32验证鉴权是否生效:
# 不带 token,应返回 401curl -s -o /dev/null -w "%{http_code}" https://your-domain.com/mcp
# 带正确 token,应返回 200 或正常 MCP 响应curl -s -o /dev/null -w "%{http_code}" \ -H "Authorization: Bearer your-secret-token-here" \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","method":"initialize","params":{},"id":1}' \ https://your-domain.com/mcp方案二:应用层鉴权(适合多用户场景)
// 加在 app.all("/mcp", ...) 之前app.use("/mcp", (req, res, next) => { const auth = req.headers["authorization"]; if (!auth || auth !== `Bearer ${process.env.MCP_SECRET_TOKEN}`) { return res.status(401).json({ error: "Unauthorized" }); } next();});在 systemd service 文件的 [Service] 段注入 token:
Environment=MCP_SECRET_TOKEN=your-secret-token-here客户端接入:Claude Desktop 与 Cursor 配置
服务跑起来之后,在客户端填入远程 MCP Server 的连接信息。
Claude Desktop(macOS 配置文件路径:~/Library/Application Support/Claude/claude_desktop_config.json):
{ "mcpServers": { "my-remote-server": { "url": "https://your-domain.com/mcp", "headers": { "Authorization": "Bearer your-secret-token-here" } } }}Cursor(.cursor/mcp.json,放在项目根目录或用户目录均可):
{ "mcpServers": { "my-remote-server": { "url": "https://your-domain.com/mcp", "headers": { "Authorization": "Bearer your-secret-token-here" } } }}保存配置后重启客户端。先用 curl 做端到端连通性验证:
curl -s \ -H "Authorization: Bearer your-secret-token-here" \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}},"id":1}' \ https://your-domain.com/mcp | python3 -m json.tool返回以下结构说明整条链路通了:
{ "jsonrpc": "2.0", "id": 1, "result": { "protocolVersion": "2024-11-05", "serverInfo": { "name": "my-server", "version": "1.0.0" }, "capabilities": {} }}常见问题
Remote MCP Server 和本地 stdio MCP Server 有什么区别?
stdio transport 通过标准输入输出在两个本地进程之间传数据,客户端和 Server 必须在同一台机器上。Remote MCP Server 用 HTTP-based transport(SSE 或 Streamable HTTP),Server 运行在任意有公网地址的机器上,客户端通过 URL 远程调用。两者的 MCP 协议层完全一致,只是传输层不同。
MCP Server 需要 HTTPS 才能远程使用吗?
是的。Claude Desktop、Cursor 等主流客户端在连接远程 MCP Server 时强制要求 HTTPS,纯 HTTP 端点直接拒绝。用 Let’s Encrypt 申请免费证书,配合 Certbot 自动续期,操作不超过 5 分钟。
VPS 上运行 MCP Server 需要什么配置要求?
MCP Server 本身极轻,512MB 内存的 VPS 完全够跑。如果 Server 内部有 AI 推理或大文件处理逻辑,建议 1–2GB 内存。CPU 要求不高,1 核足够处理正常并发。
Streamable HTTP 和 SSE 该选哪个?
新项目直接用 Streamable HTTP。MCP 官方文档已将 SSE 标注为 legacy,主流客户端的 SSE 实现在高频调用下有稳定性问题。Streamable HTTP 是当前推荐的远程 transport,兼容性更好,实现也更简单。
本文最后更新于 2026-03,MCP SDK 版本:1.10.2,Nginx 版本:1.24,测试系统:Ubuntu 22.04 LTS。MCP 协议迭代较快,建议每 3 个月检查 SDK 版本和客户端配置格式变更。
支持与分享
如果这篇文章对你有帮助,欢迎分享给更多人或赞助支持!
评论区
滚动到评论区附近或点击按钮后,再加载 Waline 脚本与请求。
云梯建站笔记