在 WSL2 中搭建 WebCodex + OpenAI Secure MCP Tunnel:让 ChatGPT 直接操作本地项目

隐私说明

本文已对账号、邮箱、API Key、Tunnel ID、Organization ID、Windows 用户名、WSL 用户名、真实项目名和绝对路径做脱敏处理。文中出现的 tunnel_xxxsk-proj-xxxmy-project 等均为示例占位符,不能直接作为真实凭据使用。

最终效果

这套方案的目标,是让 ChatGPT 通过 OpenAI Secure MCP Tunnel 安全地连接到运行在本机 WSL2 中的 WebCodex,再由 WebCodex Runner 去访问本地代码仓库。

最终链路如下:

ChatGPT

   │ OpenAI Secure MCP Tunnel

OpenAI Tunnel Service

   │ control-plane poll / response

tunnel-client(WSL2)

   │ http://127.0.0.1:8080/mcp

WebCodex Server

   │ WebSocket

WebCodex Runner


本地 Git 项目

完成后,ChatGPT 可以通过 MCP 工具直接执行项目级操作,例如读取文件、查看目录结构、检查 Git 状态、生成补丁、运行测试、执行构建命令等。

在当前部署中,已经验证以下链路均正常:WebCodex Server 正常监听、Runner 在线、项目已注册、MCP 会话初始化成功、Tunnel 正常启动、Tunnel readiness 返回 HTTP 200。


一、环境设计

我的部署环境是 Windows + WSL2。WebCodex Server、Runner 和 tunnel-client 全部运行在 WSL2 中,本地代码通过 /mnt/d/... 这类路径访问 Windows 磁盘。

核心端口采用:

127.0.0.1:8080   WebCodex Server / MCP
127.0.0.1:8081   tunnel-client health / admin UI

这里特意把 Tunnel 的健康检查端口放在 8081,避免与 WebCodex 的 8080 冲突。

Important

OpenAI Tunnel 并不要求把 WebCodex 的 8080 暴露到公网。tunnel-client 在本机访问 127.0.0.1:8080/mcp,再通过 OpenAI Tunnel control plane 与 ChatGPT 通信。


二、WebCodex 的职责划分

WebCodex 由两个核心部分组成。

WebCodex Server 负责提供 MCP API、项目管理接口和控制面;WebCodex Runner 则真正接触本地文件系统、Git、Shell、编译器和项目目录。

因此,真正需要注册本地项目的是 Runner,而不是 Tunnel。

例如一个脱敏后的 Runner 配置结构可能类似:

Server URL:      http://127.0.0.1:8080
Transport:       websocket
Allowed root:    /mnt/d/workspace
Project:         /mnt/d/workspace/my-project

只要 Runner 显示 online,项目显示 connected=true,ChatGPT 通过 MCP 就能够访问这个项目。


三、WebCodex 认证如何传给 Tunnel

WebCodex MCP 默认启用了认证,因此直接访问:

http://127.0.0.1:8080/mcp

会得到 HTTP 401,这是正常现象,并不表示服务坏了。

WebCodex 用户 Token 保存在本地配置目录。为了不把 Token 明文写进 systemd unit,我把它转换成一个仅本机用户可读的 Authorization header 文件:

TOKEN_FILE="$HOME/.config/webcodex/<server-profile>/webcodex-user-token"
AUTH_FILE="$HOME/.config/webcodex/tunnel-auth-header"
 
{
    printf 'Bearer '
    tr -d '\r\n' < "$TOKEN_FILE"
} > "$AUTH_FILE"
 
chmod 600 "$AUTH_FILE"

然后交给 tunnel-client

MCP_EXTRA_HEADERS=Authorization: file:$HOME/.config/webcodex/tunnel-auth-header
MCP_DISCOVERY_EXTRA_HEADERS=Authorization: file:$HOME/.config/webcodex/tunnel-auth-header

这样 tunnel-client 在探测和调用 WebCodex MCP 时会自动携带认证信息。

Warning

webcodex-user-tokentunnel-auth-header、Runtime API Key 都属于敏感凭据。不要放入 Git、截图、博客、Shell History、公开日志或 Obsidian Sync 的公共仓库。


四、OpenAI Secure MCP Tunnel

OpenAI Tunnel 使用两项核心信息:

CONTROL_PLANE_TUNNEL_ID=tunnel_xxx
CONTROL_PLANE_API_KEY=sk-proj-xxx

Runtime API Key 对应的组织主体需要具备 Tunnel 的 Read / Use 权限。

实际部署中最容易踩坑的一点,是 API Key 自身的 AllFull 权限,并不等价于组织 RBAC 中已经具备 Tunnels: Use。如果 control plane 返回:

401
code: tunnel_use_forbidden

说明 API Key 已经被识别,但对应主体仍没有通过 Tunnel Use 授权检查。

另外,普通 Secure MCP Tunnel 不需要强制启用 --cloudflared.managed。如果 Tunnel 并没有配置 managed Cloudflare runtime material,访问 managed runtime endpoint 会得到:

404
Managed Cloudflare tunnel runtime material not found

此时正确做法是直接使用普通 control-plane 模式:

tunnel-client run \
  --health.listen-addr 127.0.0.1:8081

启动成功后通常会看到:

mcp session initialized
poller started
tunnel metadata fetched
🟢 tunnel-client started

这几行比单纯看到进程存在更有意义,因为它说明 MCP、本地 WebCodex 和 OpenAI control plane 都已经接通。


五、把 Tunnel 做成 systemd 系统服务

为了避免每次打开终端手动运行 tunnel-client run,我把它做成了 systemd system service。

服务文件:

/etc/systemd/system/openai-webcodex-tunnel.service

一个脱敏后的配置如下:

[Unit]
Description=OpenAI Secure MCP Tunnel for WebCodex
Wants=network-online.target webcodex.socket
After=network-online.target webcodex.socket
 
[Service]
Type=simple
User=<wsl-user>
Group=<wsl-user>
WorkingDirectory=/home/<wsl-user>
Environment="HOME=/home/<wsl-user>"
 
Environment="MCP_EXTRA_HEADERS=Authorization: file:/home/<wsl-user>/.config/webcodex/tunnel-auth-header"
Environment="MCP_DISCOVERY_EXTRA_HEADERS=Authorization: file:/home/<wsl-user>/.config/webcodex/tunnel-auth-header"
 
ExecStart=/home/<wsl-user>/.local/bin/tunnel-client run \
  --control-plane.tunnel-id=tunnel_xxx \
  --control-plane.api-key=file:/home/<wsl-user>/.config/tunnel-client/runtime-api-key \
  --mcp.server-url=http://127.0.0.1:8080/mcp \
  --health.listen-addr=127.0.0.1:8081 \
  --log.level=info \
  --log.format=struct-text
 
Restart=always
RestartSec=5
TimeoutStopSec=20
 
[Install]
WantedBy=multi-user.target

Runtime API Key 单独保存在:

$HOME/.config/tunnel-client/runtime-api-key

并限制权限:

chmod 600 "$HOME/.config/tunnel-client/runtime-api-key"

启用服务:

sudo systemctl daemon-reload
sudo systemctl enable --now openai-webcodex-tunnel.service

查看状态:

sudo systemctl status openai-webcodex-tunnel.service

实时日志:

sudo journalctl -u openai-webcodex-tunnel.service -f

六、WebCodex Server 与 Runner 的持久化

WebCodex Server 使用 systemd socket/service:

sudo systemctl enable --now webcodex.socket

Runner 则作为用户级 systemd 服务:

systemctl --user enable --now webcodex-runner.service

为了让用户级 service 在没有登录 shell 时仍能运行,需要:

sudo loginctl enable-linger <wsl-user>

验证:

systemctl --user status webcodex-runner.service

Runner 如果显示项目已经配置但 runtime check: skipped,并不等于 Runner 离线。进一步使用带 Server URL 和 Token 的状态/项目检查即可确认真实连接状态。


七、健康检查

Tunnel 的 readiness endpoint:

curl -i http://127.0.0.1:8081/readyz

正常结果:

HTTP/1.1 200 OK
 
ready

WebCodex 项目状态应至少满足:

runner_status: online
connected: true
git_available: true

如果出现:

no_recommended_smoke_project

这只是 WebCodex 没把当前项目标记为“推荐自动 smoke test 项目”,并不表示项目不能被 ChatGPT 正常访问。


八、在 ChatGPT 中创建自定义 MCP App

当本地服务全部正常后,在 ChatGPT 的 Apps / Connectors 中创建自定义 MCP App。

连接方式选择:

Connection: Tunnel
Tunnel:     <自己的 Tunnel>
Auth:       None

这里选择 None 是因为 WebCodex 的 Bearer Token 已经由本机 tunnel-client 注入,ChatGPT 不需要再保存一份 WebCodex Token。

创建完成后,可以用类似下面的请求做验证:

使用 WebCodex 列出当前可用项目,并检查目标项目的 Git 状态。

如果返回了正确的项目路径、分支、Git 修改状态,就说明完整链路已经建立。


九、以后能不能直接用?

可以,但有一个 WSL2 特有的前提:WSL 实例本身必须处于运行状态。

systemd 能保证 WebCodex、Runner 和 Tunnel 在 WSL 启动后自动运行,但 Windows 重启后,如果没有任何程序触发 WSL,WSL 发行版本身未必已经启动。

因此日常使用可以理解为:

Windows 已启动

WSL 已启动

systemd 自动启动 WebCodex + Runner + Tunnel

ChatGPT 可以直接使用 WebCodex MCP

如果希望 Windows 登录后完全自动拉起 WSL,可以额外建立 Windows Task Scheduler 任务,在登录时运行:

wsl.exe -d <你的发行版名称> --exec /bin/true

发行版名称可以通过 PowerShell 查看:

wsl -l -q

这样 Windows 登录后会主动启动 WSL,随后 systemd 服务自动拉起整套 MCP 链路。

Tip

如果平时本来就会打开 WSL、Docker Desktop、VS Code Remote WSL 等,那么通常不需要额外创建计划任务,因为这些操作本身就会启动 WSL。


十、日常维护命令

# Tunnel 状态
sudo systemctl status openai-webcodex-tunnel
 
# Tunnel 实时日志
sudo journalctl -u openai-webcodex-tunnel -f
 
# WebCodex Server
sudo systemctl status webcodex.socket
 
# WebCodex Runner
systemctl --user status webcodex-runner.service
 
# Tunnel readiness
curl -i http://127.0.0.1:8081/readyz
 
# 重启 Tunnel
sudo systemctl restart openai-webcodex-tunnel
 
# 重启 Runner
systemctl --user restart webcodex-runner.service

这些命令基本足够覆盖日常维护。


十一、需要长期注意的事情

  • 不要把 API Key 写进公开笔记、Git 仓库、截图或博客。 本文只保留 sk-proj-xxx 占位符。
  • 不要公开真实 Tunnel ID、Organization ID、用户邮箱和本地绝对路径。 单独看它们未必都等同于密码,但组合起来会增加环境指纹和攻击面。
  • API Key 一旦出现在聊天记录、终端录屏、日志或截图中,应立即旋转。 新 Key 写入本地权限为 600 的文件后,再重启 Tunnel 服务。
  • 本地项目权限边界由 WebCodex Runner 决定。 allowed_roots 不要设置得过宽,尽量只开放真正需要 ChatGPT 操作的开发目录。
  • ChatGPT 能执行的实际操作取决于 WebCodex 暴露的工具。 在重要仓库中,执行删除、覆盖、大规模重构、数据库迁移、Git reset/rebase 等操作前,仍应检查 Git diff 和备份。
  • OAuth discovery failed 在当前这种本地 Bearer Header 模式下可以是非致命警告。 如果后面已经出现 mcp session initializedtunnel-client started,就不要仅因为这一条 warning 判定服务失败。
  • Codex Tunnel MCP plugin 是可选项。 它主要给 Codex 自身提供更方便的 Tunnel 控制入口,不是 ChatGPT 使用 WebCodex 的必需组件。
  • Windows 挂载盘要存在。 如果项目位于 /mnt/d/...,D 盘不可用、BitLocker 未解锁或挂载异常时,Runner 会看到项目不可访问。
  • 升级前先记住当前可工作的版本。 WebCodex、tunnel-client、MCP 协议和 ChatGPT Connector 都可能继续迭代。升级后如果出问题,优先核对版本、服务日志和 MCP initialize 是否成功。

十二、故障排查顺序

遇到 ChatGPT 突然无法访问本地项目时,不要一上来重装。按链路从内到外检查:

项目目录是否存在

Runner 是否 online

WebCodex Server 是否正常

127.0.0.1:8080/mcp 是否可达

tunnel-client 是否 active

/readyz 是否 200

tunnel-client 日志是否出现 started / poller started

ChatGPT MCP App 是否仍绑定正确 Tunnel

推荐命令:

systemctl --user status webcodex-runner.service
sudo systemctl status webcodex.socket
sudo systemctl status openai-webcodex-tunnel.service
curl -i http://127.0.0.1:8081/readyz
sudo journalctl -u openai-webcodex-tunnel.service -n 100 --no-pager

如果 readyz=200、Runner online、项目 connected,而 ChatGPT 仍不能使用工具,再去检查 ChatGPT App/Connector 本身。


结语

这套方案的核心价值,不是简单地“把一个 MCP 暴露到公网”,而是把本地开发环境拆成了清晰的权限边界:

ChatGPT
  只知道 Tunnel
 
OpenAI Tunnel
  只负责请求传输
 
tunnel-client
  只连接本机 MCP
 
WebCodex Server
  提供 MCP 控制面
 
WebCodex Runner
  决定真正可以访问哪些项目和目录

相比直接把本地 MCP Server 暴露到公网,这种结构更适合长期使用,也更容易做服务化、权限隔离和故障排查。

对于个人开发环境,完成 systemd 持久化和 WSL 自动启动之后,日常基本可以做到:打开 ChatGPT,直接让它进入本地项目工作,而不需要每次重新配置 Tunnel、重新启动 Runner 或重新粘贴 Token。