Skip to main content
上一篇列出了 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 版本,需要 >= 18
node -v

# 确认 443/80 端口监听状态(新机器应为空)
ss -tlnp | grep -E ':(80|443)'

# 查看系统版本
lsb_release -a

本地开发环境确认

本文假设你已经有一个可以在本地跑起来的 MCP Server 项目。如果还没有,用官方 SDK 建一个最小示例:
# 初始化项目
mkdir my-mcp-server && cd my-mcp-server
npm init -y

# 安装 MCP SDK,锁定版本避免 breaking change
npm install @modelcontextprotocol/sdk@1.10.2

transport 选型:为什么 stdio 不能用于远程

transport通信方式能否跨网络主流客户端支持适用场景
stdio标准输入输出(进程管道)仅本机Claude Desktop 本地模式本地工具、IDE 插件
SSEHTTP 长连接,服务端推送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 进入 VPS
ssh root@your-vps-ip

# 进入项目目录,安装生产依赖
cd /opt/mcp-server
npm 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 nginx

Let’s Encrypt 证书申请与自动续期

# 安装 Certbot
apt 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 Server
After=network.target

[Service]
Type=simple
User=www-data
WorkingDirectory=/opt/mcp-server
ExecStart=/usr/bin/node server.js
Restart=on-failure
RestartSec=5s
Environment=NODE_ENV=production
# 在此行追加注入 API Key 等环境变量:Environment=KEY=VALUE

[Install]
WantedBy=multi-user.target
EOF

# 重载配置并启动
systemctl daemon-reload
systemctl enable mcp-server
systemctl 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,应返回 401
curl -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": {}
  }
}

常见问题

stdio transport 通过标准输入输出在两个本地进程之间传数据,客户端和 Server 必须在同一台机器上。Remote MCP Server 用 HTTP-based transport(SSE 或 Streamable HTTP),Server 运行在任意有公网地址的机器上,客户端通过 URL 远程调用。两者的 MCP 协议层完全一致,只是传输层不同。
是的。Claude Desktop、Cursor 等主流客户端在连接远程 MCP Server 时强制要求 HTTPS,纯 HTTP 端点直接拒绝。用 Let’s Encrypt 申请免费证书,配合 Certbot 自动续期,操作不超过 5 分钟。
MCP Server 本身极轻,512MB 内存的 VPS 完全够跑。如果 Server 内部有 AI 推理或大文件处理逻辑,建议 1–2GB 内存。CPU 要求不高,1 核足够处理正常并发。
新项目直接用 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 版本和客户端配置格式变更。