用 Caddy + ChmlFrp 暴露 DevSpace 给 ChatGPT
一份可直接照抄的配置教程:在 WSL 中运行 DevSpace,用 Caddy 提供本地 HTTPS,再通过 ChmlFrp 暴露为公网地址,最终接入 ChatGPT。
这篇文章只做一件事:把 WSL 里的 DevSpace 通过 Caddy 和 ChmlFrp 暴露成公网 HTTPS 地址,然后接入 ChatGPT。
本文假设你已经安装好 DevSpace、Caddy 和 ChmlFrp,并且已经准备好 ChmlFrp 隧道、独立域名和对应的 DNS 解析。下面只讲配置和验证,可以从上往下直接照抄。
为什么我选择这套方案
Cloudflare Tunnel 确实更省事,但对我这种「个人开发机 + WSL + DevSpace」场景,核心需求不是 CDN 或全球加速,而是让 ChatGPT 直接使用本机项目,同时把代码、运行环境和网络链路掌握在自己手里。WSL 提供 Linux 环境,DevSpace 让 ChatGPT 获得读取、搜索、修改和执行项目的能力,Caddy + ChmlFrp 负责提供公网入口。这样 VS Code、DevSpace 和 ChatGPT 操作的是同一份本地代码,链路也更直接。简单说:AI 获得了操作能力,但代码和运行环境仍然在自己的机器上。
最终架构
先看清楚整条链路,后面的所有配置都围绕这四层展开。
ChatGPT 到 WSL 的最终连接链路
公网请求先进入 ChmlFrp,再转发到 Caddy,最后由 Caddy 代理到 DevSpace。
/mcp HTTPS tunnel 127.0.0.1:7677 127.0.0.1:7676 https://your-domain.example.com/mcp 最终填到 ChatGPT 里的公网地址。
127.0.0.1:7677 ChmlFrp 在 WSL 内部转发到这里。
127.0.0.1:7676 Caddy 最终反向代理到真正的 MCP 服务。
这套配置来自一台已经实际跑通的 WSL 环境,但正文只保留可迁移的部分:不要求固定 Node 安装方式、不绑定特定用户目录,也不要求复制某一台机器的启动脚本。
开始前先替换这些占位符
全文统一使用以下占位符。复制配置后,把它们替换成自己的真实值。
| 占位符 | 替换内容 |
|---|---|
YOUR_WSL_USER | WSL 用户名 |
YOUR_CHMLFRP_USER | ChmlFrp 用户 ID |
YOUR_CHMLFRP_TOKEN | ChmlFrp Token |
YOUR_CHMLFRP_NODE_IP | ChmlFrp 节点 IP |
YOUR_TUNNEL_NAME | ChmlFrp 面板中的隧道名称 |
your-domain.example.com | 自己的公网域名 |
/home/YOUR_WSL_USER/projects/example | 想让 DevSpace 访问的项目目录 |
YOUR_NODE_BIN_DIR | node 所在目录,例如 /usr/bin 或 NVM 的实际 bin 目录 |
YOUR_DEVSPACE_BIN | devspace 可执行文件的完整路径 |
YOUR_CHMLFRP_SERVER_PORT | ChmlFrp 节点信息提供的服务端口 |
YOUR_FRPC_BIN | frpc 可执行文件的完整路径 |
YOUR_FRPC_CONFIG | frpc.ini 的完整路径 |
配置顺序
每一层通过验收,再继续下一层
WSL 启用 systemd,确认 Node 和 DevSpace 实际路径,再完成 DevSpace 初始化。
启动 7676,本地请求 /mcp 返回 401。
启动 7677,本地 HTTPS 请求 /mcp 返回 401。
公网 https://your-domain.example.com/mcp 返回 401。
完成授权后,实际读取 allowedRoots 中的测试文件。
第一步:配置 WSL
编辑:
/etc/wsl.conf
写入:
[boot]
systemd=true
[interop]
appendWindowsPath = true
保存后,在 Windows PowerShell 中完全关闭 WSL:
wsl --shutdown
然后重新打开 Ubuntu。
检查 systemd 是否可用:
systemctl is-system-running
appendWindowsPath 必须放在 [interop] 下,不要写进 [boot]。
第二步:确认 Node / DevSpace 路径,并完成初始化
不同机器的 Node 可能来自系统包、NVM、Volta、asdf 或其他环境。不要照抄别人的 Node 路径,先检查自己的实际路径:
command -v node
command -v devspace
例如可能返回:
/usr/bin/node
/usr/local/bin/devspace
也可能是:
/home/YOUR_WSL_USER/.nvm/versions/node/vXX/bin/node
/home/YOUR_WSL_USER/.nvm/versions/node/vXX/bin/devspace
记录这两个结果:
YOUR_NODE_BIN_DIR = node 所在目录
YOUR_DEVSPACE_BIN = devspace 的完整路径
例如 command -v node 返回 /usr/bin/node,那么:
YOUR_NODE_BIN_DIR=/usr/bin
先确认当前版本的启动命令:
devspace --help
本文以这个启动命令为例:
devspace serve
然后初始化 DevSpace:
devspace init
完成初始化后确认这两个文件都存在:
ls ~/.devspace/config.json ~/.devspace/auth.json
必须看到:
~/.devspace/config.json
~/.devspace/auth.json
auth.json 属于私密文件,不要提交到公开仓库。
第三步:配置并启动 DevSpace
编辑:
~/.devspace/config.json
完整配置:
{
"host": "127.0.0.1",
"port": 7676,
"allowedRoots": [
"/home/YOUR_WSL_USER/projects/example"
],
"publicBaseUrl": "https://your-domain.example.com",
"allowedHosts": [
"localhost",
"127.0.0.1",
"::1",
"your-domain.example.com"
]
}
这里主要修改三处:
YOUR_WSL_USERallowedRoots里的项目目录your-domain.example.com
配置 DevSpace systemd 服务
新建:
/etc/systemd/system/devspace.service
写入:
[Unit]
Description=DevSpace MCP server
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=YOUR_WSL_USER
WorkingDirectory=/home/YOUR_WSL_USER
Environment=DEVSPACE_TOOL_MODE=codex
Environment=DEVSPACE_TRUST_PROXY=1
Environment=PATH=YOUR_NODE_BIN_DIR:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
ExecStart=YOUR_DEVSPACE_BIN serve
Restart=always
RestartSec=2
[Install]
WantedBy=multi-user.target
把 YOUR_NODE_BIN_DIR 和 YOUR_DEVSPACE_BIN 替换成上一步实际检测到的值。
systemd 不会自动继承你平时终端里的 Node 环境,所以这里显式写入 Node 所在目录,并直接使用 DevSpace 可执行文件的完整路径。
这里只保留和接入目标直接相关的环境变量。日志格式、Widget 等偏好属于个人配置,不写进通用教程。
DEVSPACE_TOOL_MODE=codex:本文使用编码工作区工具模式。DEVSPACE_TRUST_PROXY=1:DevSpace 前面还有反向代理,需要信任代理转发信息。
执行:
sudo systemctl daemon-reload
sudo systemctl enable --now devspace.service
检查状态:
systemctl status devspace.service --no-pager -l
目标:
active (running)
验收 DevSpace:7676 必须返回 401
执行:
curl -sS -o /dev/null -w '%{http_code}\n' \
http://127.0.0.1:7676/mcp
预期:
401
这个 401 是好结果:DevSpace 已经启动,/mcp 路径存在,只是当前请求没有认证。
第四步:配置并验收 Caddy
编辑:
/etc/caddy/Caddyfile
写入:
{
https_port 7677
}
https://your-domain.example.com {
bind 127.0.0.1
reverse_proxy 127.0.0.1:7676
}
这段配置只做一件事:
Caddy 127.0.0.1:7677 / HTTPS
↓
DevSpace 127.0.0.1:7676 / HTTP
重启并启用 Caddy:
sudo systemctl restart caddy
sudo systemctl enable caddy
检查状态:
systemctl status caddy.service --no-pager -l
目标状态:
active (running)
确认 7676 和 7677 都在监听
执行:
ss -ltn | grep -E ':(7676|7677)\b'
应该同时看到:
127.0.0.1:7676
127.0.0.1:7677
验收 Caddy:本地 HTTPS 必须返回 401
执行:
curl -sk \
--resolve your-domain.example.com:7677:127.0.0.1 \
-o /dev/null \
-w '%{http_code}\n' \
https://your-domain.example.com:7677/mcp
预期:
401
做到这里,可以确认:
DevSpace 7676 正常
+
Caddy 7677 HTTPS 正常
如果这一步还没成功,不要先排查 ChmlFrp。
第五步:配置并验收 ChmlFrp
编辑:
~/.chmlfrp/frpc.ini
完整模板:
[common]
server_addr = YOUR_CHMLFRP_NODE_IP
server_port = YOUR_CHMLFRP_SERVER_PORT
tls_enable = false
login_fail_exit = false
user = YOUR_CHMLFRP_USER
token = YOUR_CHMLFRP_TOKEN
[YOUR_TUNNEL_NAME]
type = https
local_ip = 127.0.0.1
local_port = 7677
custom_domains = your-domain.example.com
最关键的是这两行:
type = https
local_port = 7677
这里的 local_port 必须指向 Caddy,不要指向 DevSpace。
这一段只有 local_ip = 127.0.0.1、local_port = 7677 和 type = https 是本文架构里的固定关系;服务端地址和端口按你自己的 ChmlFrp 节点信息填写。
另外:
[YOUR_TUNNEL_NAME]
必须替换成 ChmlFrp 面板里的真实隧道名称。
新建:
/etc/systemd/system/chmlfrp-frpc.service
写入:
[Unit]
Description=ChmlFrp FRPC
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=YOUR_WSL_USER
ExecStart=YOUR_FRPC_BIN -c YOUR_FRPC_CONFIG
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
把上面的占位符都替换成自己的实际值后执行:
sudo systemctl daemon-reload
sudo systemctl enable --now chmlfrp-frpc.service
检查:
systemctl status chmlfrp-frpc.service --no-pager -l
再看最近日志:
journalctl -u chmlfrp-frpc.service -n 50 --no-pager -l
正常时应该看到类似:
login to server success
proxy added: [YOUR_TUNNEL_NAME]
[YOUR_TUNNEL_NAME] start proxy success
验收公网 MCP:必须返回 401
执行:
curl -sS -o /dev/null -w '%{http_code}\n' \
https://your-domain.example.com/mcp
预期:
401
如果公网也返回 401,说明这条链路已经真正打通:
公网 HTTPS
→ ChmlFrp
→ Caddy 7677
→ DevSpace 7676
再验证 OAuth metadata
执行:
curl -sS https://your-domain.example.com/.well-known/oauth-protected-resource/mcp
正常时应该返回类似:
{
"resource": "https://your-domain.example.com/mcp",
"authorization_servers": ["https://your-domain.example.com/"],
"scopes_supported": ["devspace"],
"resource_name": "DevSpace"
}
第六步:让 Windows 登录后保持 WSL 运行
WSL 退出后,里面的 DevSpace、Caddy 和 frpc 都会停止。
最简单的做法是在 Windows 启动目录放一个 VBS 脚本,让 WSL 在登录后保持运行。
文件路径:
%APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup\DevSpace-WSL-KeepAlive.vbs
文件内容:
Set shell = CreateObject("WScript.Shell")
shell.Run """C:\Windows\System32\wsl.exe"" -d Ubuntu --exec sleep infinity", 0, False
这里的:
Ubuntu
要和下面命令显示的发行版名称一致:
wsl -l -v
例如看到:
NAME STATE VERSION
Ubuntu Running 2
那 VBS 中就继续使用:
-d Ubuntu
第七步:一次性检查所有服务
先检查 WSL:
wsl -l -v
目标:
Ubuntu Running 2
再检查三个 systemd 服务:
systemctl status devspace.service caddy.service chmlfrp-frpc.service --no-pager -l
三个都应该是:
active (running)
第八步:在 ChatGPT 中连接,并完成实际调用验收
最终填写:
https://your-domain.example.com/mcp
不要填写:
https://your-domain.example.com:7677/mcp
也不要填写:
http://127.0.0.1:7676/mcp
ChatGPT 只需要公网 HTTPS 地址。
填写地址后,完成 DevSpace 的授权流程。
然后在 allowedRoots 中准备一个最简单的测试文件:
printf 'devspace-ok\n' > \
/home/YOUR_WSL_USER/projects/example/DEVSPACE_VERIFY.txt
接着让 ChatGPT 通过 DevSpace 读取:
DEVSPACE_VERIFY.txt
预期内容:
devspace-ok
常见的坑
正文配置照抄后,如果仍然连不上,优先检查下面几项。
先检查这三个通用问题
systemd 找不到 Node 或 DevSpace
终端里能运行 devspace,不代表 systemd 也能找到 Node。
重新检查:
command -v node
command -v devspace
然后确认 devspace.service 中:
Environment=PATH=...
ExecStart=...
使用的就是这两个实际路径。
DevSpace 启动命令和本文不同
先执行:
devspace --help
确认当前版本是否使用:
devspace serve
不要把其他机器或其他版本的自定义启动脚本直接复制过来。
auth.json 不存在
检查:
ls ~/.devspace/auth.json
如果不存在,先确认已经执行:
devspace init
不要只手工创建 config.json 就直接启动服务。
ChmlFrp 指错端口
`local_port` 写成了 DevSpace 的 7676,而不是 Caddy 的 7677。
- 错误:
local_port = 7676 - 正确:
local_port = 7677
publicBaseUrl 多写了 /mcp
DevSpace 的 `publicBaseUrl` 只写公网 Origin,不包含 MCP 路径。
- 错误:
https://domain/mcp - 正确:
https://domain
隧道名称不一致
`frpc.ini` 的 section 名称必须和 ChmlFrp 面板中的隧道名称一致。
- 检查
[YOUR_TUNNEL_NAME] - 再看日志是否有
start proxy success
把 7677 写进公网 URL
7677 只在 WSL 本地使用,不属于 ChatGPT 的连接地址。
- 错误:
https://domain:7677/mcp - 正确:
https://domain/mcp
WSL 已经退出
WSL 停止后,里面的 DevSpace、Caddy 和 frpc 会一起停止。
- 先执行
wsl -l -v - 确认 Ubuntu 状态是
Running
只看面板,不看公网验证
ChmlFrp 面板显示 HTTPS,不代表整条链路已经真的可用。
- 先请求 OAuth metadata
- 再请求
/mcp看是否返回 401
最终检查清单
这套配置真正需要记住的只有一条:公网 HTTPS 先到 ChmlFrp,再进入 Caddy 的 7677,最后由 Caddy 转发到 DevSpace 的 7676。前三层都返回 401,最后 ChatGPT 能实际读取测试文件,才算整条链路真正完成。