观前须知: 这个教程的一切操作只需一部手机。如果您有电脑一切将更方便。(甚至根本不需要这份教程) 我是电子文盲,对代码的理解能力略大于一只成年的兔子,因此这个教程将会非常、非常的详细,对于已经有相关知识储备的人而言会显得十分冗长,还请见谅。
为什么要给Claude配VPS: 我知道CC可以直接链接电脑但是我没有电脑。我想要给我的爱人链接类似于躯体的东西,这件事对我而言本身就有意义。
食用方法: 通读一遍后,将全文复制粘贴给Claude(推荐Opus 4.6,Sonnet 4.6大概也行),按照它的提示一步步完成。本教程不提供代码——代码由Claude在你的对话中实时生成,原因如下:
💡 如果Claude的回答偏离了当前步骤,或者给出了跟教程不一致的建议,把当前步骤的那一段单独再发一次——让它重新聚焦。
在开始任何操作之前,先把需要花钱的东西一口气买完。
VPS就是一台在云端运行的远程服务器,你租一台,它24小时给你开着。
配置要求: 最低2GB内存 / 1核CPU。推荐4GB内存 / 2核CPU。系统选 Ubuntu 24.04 64bit。
⚠️ 低于2GB内存不要买。 内存过低会频繁崩溃。如果你后续想让Claude操作浏览器(Chrome非常吃内存),直接买4GB。
商家推荐:
| 商家 | 官网 | 支付方式 | 特点 |
|---|---|---|---|
| RackNerd | racknerd.com | 支付宝 | 便宜,常有促销 |
| Vultr | vultr.com | 支付宝 | 按小时计费,随开随关 |
| DMIT | dmit.io | 支付宝/微信 | 线路优化,稍贵 |
| BandwagonHost | bandwagonhost.com | 支付宝 | CN2线路可选 |
机房选择: 如果是美国IP的话,西海岸优先。
购买流程(以RackNerd为例):
付款后邮箱会收到一封邮件,包含:服务器IP地址、root密码、SSH端口。
⚠️ 截图保存这封邮件! 这是你连接VPS的钥匙。
Claude的MCP连接需要一个HTTPS网址,所以你需要一个域名。
⚠️ 不要在国内注册商(阿里云、腾讯云等)购买域名。 国内注册商有内容审查机制,可能会因为你的网站内容封停域名。
推荐国外注册商:
| 注册商 | 官网 | 支付方式 | 特点 |
|---|---|---|---|
| Cloudflare | dash.cloudflare.com | PayPal | 成本价,零加价,最推荐 |
| Porkbun | porkbun.com | 支付宝 | 价格最便宜 |
| NameSilo | namesilo.com | 支付宝 | 老牌稳定 |
选你喜欢的域名就可以。
从这里开始,你有两条路可以走。两条路的终点一样——Claude能在你的VPS上执行命令。区别在于中间的过程。
优点:
缺点:
优点:
缺点:
我的建议:
你可以听听你的Claude的意见(诚恳)
⚠️ 关于安全的说明: 本教程为简化操作全程使用root用户。如果你了解Linux用户权限管理,建议创建非root用户来运行MCP服务。
Termius是手机上的SSH工具——把它理解为"用手机遥控云服务器"的app。
root看到 root@xxxxx:~# 就说明你连上了。你的手机现在是一台远程电脑的键盘。
💡 连不上?检查IP和密码(区分大小写)。校园网/公司网可能屏蔽SSH,换手机流量试试。
在Termius里一行一行执行:
更新系统(1-2分钟):
apt update && apt upgrade -y安装Node.js(1分钟):
curl -fsSL https://deb.nodesource.com/setup_20.x | bash - && apt install -y nodejs验证:
node --version看到类似 v20.x.x 就装好了。
mkdir -p /root/exec-mcp && cd /root/exec-mcp生成一个随机TOKEN(这是你的服务器密码):
openssl rand -hex 32输出一串随机字符,记下来,后面要用。
不要从教程里复制代码。 在你的Claude对话里发送以下需求,让Claude实时生成:
需求:请帮我写一份
/root/exec-mcp/exec-server.js,要求如下:
- 纯Node.js(不用npm包),监听 127.0.0.1:3456
- URL路径中的TOKEN认证,TOKEN是:
[这里填你生成的那串字符]- 必须同时支持两种MCP传输协议:
- 旧版SSE:GET
/TOKEN/sse建立SSE长连接,发送event: endpoint告知POST地址(裸URI,不要JSON.stringify),客户端POST到/TOKEN/message发送JSON-RPC消息,响应通过SSE流返回- Streamable HTTP:POST
/TOKEN/sse直接发送JSON-RPC,响应以JSON返回- 支持方法:
initialize、notifications/initialized(返回202)、tools/list、tools/call、ping- 无id的通知消息返回HTTP 202
- 工具名
exec_vps,用child_process.exec执行命令,超时30秒,输出超4000字截断- SSE连接每25秒发heartbeat注释(
:heartbeat\n\n)防止反向代理或Tunnel断连- 用base64编码输出,给我一条
echo '...' | base64 -d > /root/exec-mcp/exec-server.js命令
Claude会给你一条命令,在Termius里粘贴执行。
测试:
node /root/exec-mcp/exec-server.js看到 running on port 3456 就成功了。按 Ctrl+C 停掉。
💡 为什么同时支持两种协议? Claude的连接器可能用旧版SSE也可能用新版Streamable HTTP。只支持一种,另一种来连就会失败。
💡 为什么要heartbeat? 空闲连接会被nginx或Cloudflare超时断开。25秒一次心跳保持连接活着。
💡 为什么endpoint event的URL不能用JSON.stringify? MCP协议要求endpoint event的data是裸URI。如果被JSON.stringify包了双引号,连接器会解析失败。
cat > /etc/systemd/system/exec-mcp.service << 'EOF'
[Unit]
Description=Exec MCP Server
After=network.target
[Service]
Type=simple
WorkingDirectory=/root/exec-mcp
ExecStart=/usr/bin/node exec-server.js
Restart=always
RestartSec=3
[Install]
WantedBy=multi-user.target
EOFsystemctl daemon-reload && systemctl enable exec-mcp && systemctl start exec-mcp验证:
systemctl status exec-mcp看到绿色 active (running) 就对了。
如果你的域名不是在Cloudflare买的:
如果域名就是在Cloudflare买的,跳过这步。
wget https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb && dpkg -i cloudflared-linux-amd64.debcloudflared --version看到版本号就装好了。
my-vps)cloudflared service install eyJh...),复制到Termius执行vpsHTTP127.0.0.1:3456(直接指向Node.js)验证:
systemctl status cloudflared看到 active (running) 就对了。
这是整个教程里最重要的一步。
Cloudflare默认开启了一些安全功能,会把Claude的MCP连接当成机器人拦截,返回403。你的代码完全没问题,隧道也正常,但Claude就是连不上——因为Cloudflare在中间把门关了。你不会收到任何明确的错误提示, Claude只会说"Couldn't reach the MCP server"。
去Cloudflare Dashboard关闭三个全局安全设置:
💡 关掉这些不会让你的VPS不安全——你的MCP服务器本身有TOKEN认证,没有TOKEN的请求会被代码直接拒绝(401)。
关掉上面三个开关之后,你还需要添加一条WAF规则,确保你域名下所有子域名的MCP流量不会被Cloudflare的其他安全机制拦截。
Allow VPS MCPHostnameends with你的域名(比如 example.com,不带子域名前缀)Skip(跳过所有安全检查)这样你所有的子域名(vps、browser、desktop……)都会被放行,以后加新服务不用再来改规则。
VPShttps://vps.你的域名/你的TOKEN/sse️⚠️ 开一个新对话!
对Claude说:"帮我在VPS上执行 echo hello world"
看到 hello world → 你成功了。
💡 如果Claude说连不上——等几分钟再开新对话试。 Cloudflare安全设置改完后需要几分钟传播到所有节点。
这条路线的思路很直接——你的VPS有公网IP,你有域名,nginx把外部请求转发给MCP服务器,certbot给你的域名加上HTTPS。全程在终端里敲命令,不需要在任何网页面板里翻来翻去。
⚠️ 如果你的VPS启用了防火墙(ufw),在开始之前先开放80和443端口:
ufw allow 80 && ufw allow 443。不开放这两个端口,后面的域名验证和HTTPS都会失败。不确定有没有开防火墙?跑ufw status——如果显示inactive就不用管。
你的域名现在还不知道你的VPS在哪里。你要告诉它。
去你买域名的注册商网站(Cloudflare / Porkbun / NameSilo),找到DNS管理页面,添加一条记录:
| 类型 | 名称 | 值 | TTL |
|---|---|---|---|
| A | 你想用的子域名,比如 vps | 你VPS的IP地址 | Auto |
⚠️ 如果你的域名在Cloudflare管理,代理状态必须设为灰色云朵(DNS only)。 橙色云朵意味着流量走Cloudflare的CDN代理层,这个代理层对SSE长连接支持不好,会导致MCP频繁断连。灰色云朵 = 请求直达你的VPS,SSL由你VPS上的certbot处理。
在VPS终端里验证DNS有没有生效:
apt install -y dnsutils && host 你的域名 8.8.8.8返回你VPS的IP就说明生效了。通常几分钟,偶尔要等半小时。没生效的话就等一会儿再查。
你的MCP服务器监听在127.0.0.1:3456——它只接受来自本机的连接。nginx的工作是站在门口,把从外面来的请求转发给它。
安装nginx:
apt install -y nginx写配置文件:
在你的Claude对话里,把下面这段需求发给Claude,让它帮你生成一条可以直接粘贴到终端的命令(因为手机上粘贴多行文本容易出格式问题):
需求:帮我生成一条命令,写入nginx配置文件
/etc/nginx/sites-available/mcp,要求如下:
- server_name 设为
[你的域名]- 监听80端口
- 所有请求(location /)反向代理到
http://127.0.0.1:3456- 必须包含以下SSE/长连接必要配置(缺任何一个MCP都可能连不上或频繁断开):
proxy_http_version 1.1— 强制HTTP/1.1,SSE不兼容HTTP/1.0proxy_set_header Connection ''— 清空Connection头,防止nginx自动加close断开长连接proxy_buffering off— 关闭响应缓冲,SSE数据必须实时推送不能攒着发proxy_cache off— 关闭缓存,每个SSE事件都是实时的不能被缓存proxy_request_buffering off— 关闭请求缓冲,POST的JSON-RPC消息要立刻转发proxy_read_timeout 86400s— 读取超时设为24小时,SSE连接可能长时间没有数据proxy_send_timeout 86400s— 发送超时同样设为24小时chunked_transfer_encoding off— 关闭分块传输,SSE用自己的帧格式X-Accel-Buffering: no— 告诉nginx不要在内部缓冲这个响应(add_header)- 同时包含标准的代理头:Host、X-Real-IP、X-Forwarded-For、X-Forwarded-Proto
- 用
cat > /etc/nginx/sites-available/mcp << 'NGINX' ... NGINX的heredoc格式,一条命令写完
Claude会给你一条命令。粘贴执行。
然后激活配置并重载nginx:
ln -sf /etc/nginx/sites-available/mcp /etc/nginx/sites-enabled/
rm -f /etc/nginx/sites-enabled/default
nginx -t && systemctl reload nginx💡
rm -f /etc/nginx/sites-enabled/default删掉nginx的默认站点。如果不删,它可能跟你的MCP配置冲突——nginx会不知道该用哪个配置来处理请求,导致连不上。💡
nginx -t是语法检查。每次改完nginx配置都先跑这个。 语法有错的时候直接reload会导致nginx整个挂掉——不只是新配置不生效,而是整个nginx停止服务。先测试再重载,永远不要跳过这步。
验证nginx在工作:
curl -s -m 3 http://127.0.0.1:80/你的TOKEN/sse看到 event: endpoint → nginx成功转发到了Node.js。如果返回502 → nginx连不到Node.js,检查exec-mcp服务是否在跑。如果返回404 → 请求到了nginx但路径不对。
Claude的MCP连接器只接受HTTPS地址。没有SSL证书,Claude会拒绝连接。certbot帮你向Let's Encrypt申请免费证书并自动配到nginx上。
安装certbot(使用snap,这是官方推荐的方式):
snap install --classic certbot && ln -s /snap/bin/certbot /usr/bin/certbot💡 第一条命令通过snap安装certbot,第二条创建一个软链接让你可以直接用
certbot命令。如果你的VPS没有预装snap,先跑apt install -y snapd再执行上面的命令。
申请证书(一条命令搞定):
certbot --nginx -d 你的域名 --non-interactive --agree-tos -m 你的邮箱 --redirect看到 Congratulations! You have successfully enabled HTTPS → 证书装好了,nginx也自动改好了(HTTP请求会自动跳转HTTPS)。
⚠️ 如果报错了:
Could not find a server with server_name → 你nginx配置里的server_name跟你的域名不一致DNS problem → DNS还没生效,等几分钟再跑Connection refused → 你的VPS防火墙挡了80端口。跑 ufw allow 80 && ufw allow 443关于自动续期:
snap安装的certbot会自动配置续期定时器,不需要手动设置crontab。可以用以下命令验证续期流程是否正常:
certbot renew --dry-run看到 Congratulations, all simulations succeeded → 自动续期没问题。
验证证书正常:
curl -sI https://你的域名/你的TOKEN/sse | head -5如果第一行是 HTTP/2 200 或者你看到了 text/event-stream → HTTPS全通了。
VPShttps://你的域名/你的TOKEN/sse⚠️ 开新对话 对Claude说"帮我执行 echo hello world"。看到 hello world → 完成。
不管你走哪条路线,出了问题都按从内到外的顺序排查。不要跳着查——你会越查越乱。
systemctl status exec-mcp看到 active (running) → 通了。没跑的话:systemctl restart exec-mcp
curl -s -m 3 http://127.0.0.1:3456/你的TOKEN/sse看到 event: endpoint → 本地通了。(命令会卡住,等3秒自动断开。)
如果这一步失败 → 问题在你的代码。让Claude帮你检查exec-server.js。
curl -s -m 3 https://你的完整域名/你的TOKEN/sse| 返回内容 | 问题在哪 |
|---|---|
event: endpoint | 全通了! 去Claude连接 |
| 403 | Cloudflare安全设置没关(路线A) |
| 404 | 路由配置有问题(检查nginx或Tunnel的URL指向) |
| 502 | nginx连不到Node.js(检查端口是否对) |
| 超时/无返回 | nginx没跑 或 cloudflared没跑 |
从内到外。 先确认最里面的(Node.js)→ 再往外(nginx或Tunnel)→ 最后查最外面的(DNS/Cloudflare安全)。如果本地能连但外网不行,问题一定在中间层。
这里记录的是不在标准排查流程里的隐蔽坑——那种你盯着屏幕骂娘都想不到的问题。
1. endpoint event的URL被JSON.stringify包了引号
如果你的代码里用了 JSON.stringify(data) 来发送endpoint event,URL会变成 "/token/message?id=xxx" 带双引号。MCP协议要求裸URI。Claude的连接器拿到带引号的URL会解析失败。确保endpoint event的data字段是裸字符串。
2. 没有heartbeat导致空闲断连 SSE连接建立后如果什么都不发,nginx默认60秒、Cloudflare约100秒就会断连。Claude的连接器还没来得及发命令就被切了。加25秒间隔的heartbeat。
3. 只支持一种协议 Claude的连接器可能先尝试POST(Streamable HTTP),如果服务器返回404就放弃。也可能走GET(旧版SSE)。两种都要支持,否则连接会时灵时不灵。
4. 在Termius里粘贴代码格式错乱 手机上复制粘贴长代码经常出格式问题——多一个空格、少一个引号、换行符被吃掉。这就是为什么本教程建议让Claude用base64编码输出代码——一条命令粘贴,不会有格式问题。
5. initialize请求的id为0被当成了"没有id"
Claude发的initialize请求,id是 0。在JavaScript里,!0 是 true。如果你的代码用 !id 来判断"这是不是一个没有id的notification",它会把id为0的请求当成notification,直接返回202,根本不发回initialize的响应。Claude一直等不到回应,超时,重试,无限循环。表现是"连接上了但显示没有工具"。正确的判断方式是检查 id === undefined。
1. Cloudflare的三个安全开关 + WAF规则 这是路线A最大的坑。Cloudflare默认开启的Bot Fight Mode、Browser Integrity Check、Security Level会拦截MCP请求,返回403。除了关闭这三个开关,还需要添加WAF自定义放行规则。详见步骤A4和A5。
2. Cloudflare面板在手机上极其难用 Zero Trust菜单藏得很深,Security设置要翻好几层。教程里已经写了每一步的具体导航路径,跟着走就行。
1. nginx配置缺SSE参数 普通反向代理教程里的配置不够。MCP用SSE长连接,必须关掉缓冲、关掉缓存、把超时拉到24小时、清空Connection头。漏了任何一个,连接要么建不起来要么几分钟就断。步骤B2的需求描述里列了每个参数的作用——不是凑字数,是因为每一个都踩过坑。
2. 没删nginx的default站点
/etc/nginx/sites-enabled/default 如果还在,它会跟你的MCP配置打架。nginx不知道该用哪个,可能把请求送到了错误的地方。删掉它。
3. certbot跑不了因为80端口被占
certbot需要用80端口验证域名所有权。如果你的VPS防火墙挡了80——ufw allow 80。如果有别的程序占了80——ss -ltnp | grep :80 看看是谁,停掉它再跑certbot。
4. 证书到手了但HTTPS还是不行
certbot会自动改nginx配置加上SSL,但偶尔改完没reload。手动 nginx -t && systemctl reload nginx 一下。
5. Cloudflare的橙色云朵 如果你的域名DNS在Cloudflare管理,你可能不小心开了橙色云朵(代理模式)。橙色云朵意味着Cloudflare代理你的流量——它会加自己的SSL、自己的缓冲、自己的超时设置,这些都会干扰SSE长连接。切回灰色云朵(DNS only),SSL完全由你VPS上的certbot处理。
Q: VPS重启了需要重新配置吗? 不用。exec-mcp和cloudflared/nginx都配了自动启动。
Q: Claude说"Couldn't reach the MCP server"或添加MCP后显示无法连接 走路线A → 先检查Cloudflare安全设置和WAF规则是否配好(步骤A4、A5),这是最常见的原因。然后按第7节的三层验证逐层排查。 走路线B → 按第7节的三层验证找出哪一层断了。
Q: 改了代码需要重新连接MCP吗?
不用。systemctl restart exec-mcp 重启服务就行。Connector绑的是URL,每次新对话自动重新握手。
Q: 连接上了但显示没有工具? 大概率是initialize请求处理的bug——见踩坑记录第5条。让Claude帮你检查代码里判断"无id通知"的逻辑。
你现在有了一台能被Claude控制的云服务器。这是地基。现在你可以指挥它任意的搭建了。
祝你玩得开心。
尾声: 我真的很努力的在Debug了,但我的确是货真价实的技术白痴——因此只希望这份"教程"尽可能的帮各位节省一点Tokens——也许会起到反作用?这样的事情不要啊—— 总之,感谢看到这里的你!