通过 Secure MCP Tunnel 连接本地 MCP 与 ChatGPT
在 Windows 上配置 OpenAI Tunnel、runtime key、tunnel-client profile、健康检查和常驻运行,使 ChatGPT 可调用私有本地 MCP。
[!abstract] 成功状态 `npm run kb:mcp:tunnel:access`、Tunnel doctor、`healthz`、`readyz` 和控制面 poll 均成功;ChatGPT 创建 App 时能选择目标 Tunnel 并发现 12 个工具。
[!warning] 不记录真实凭据 本文所有 ID 与 key 都是占位符。真实值只进入本机忽略的环境文件或安全凭据存储,不进入 `z/`、Git、截图和聊天。
[!info] related notes
通过 Secure MCP Tunnel 连接本地 MCP 与 ChatGPT
目标
让 OpenAI 控制面通过一个已关联工作区的 Tunnel 调用:
cmd /c D:\home\thought-forest\scripts\kb-mcp\run-tunnel-mcp.cmd
本机只建立出站连接,不监听公开端口。
前置条件
- ChatGPT 工作区具备自定义 MCP/Developer Mode 能力;
- 操作者能创建或查看 Tunnel;
- runtime principal 具备 Tunnel Read 与 Use,管理者按需具备 Manage;
- `tunnel-client` 已安装;当前 Windows 实现也识别 `D:\tools\tunnel-client\tunnel-client.exe`,不要求永久加入 PATH;
- 本地 MCP Server 测试通过;
- `scripts/kb-mcp/run-tunnel-mcp.cmd` 能从任意 cwd 回到仓库根启动服务。
步骤 1:创建并核对 Tunnel 身份
在 OpenAI Platform 中创建 Tunnel,记录:
- Tunnel ID;
- 所属 Platform Organization;
- 关联的 ChatGPT Workspace;
- 运行时凭据所属主体;
- 当前状态。
复制 ID 后至少做一次逐字符复核。相邻字符、数字和字母抄错会表现为 `404 Tunnel not found`,很容易被误判为 RBAC。
步骤 2:保存本机环境
在 Git 忽略的 `scripts/kb-mcp/.env` 中使用:
CONTROL_PLANE_API_KEY=<runtime-key>
TUNNEL_ID=<tunnel-id>
CONTROL_PLANE_ORGANIZATION_ID=<optional-org-id>
`CONTROL_PLANE_API_KEY` 是 tunnel-client 的控制面凭据;`kb-mcp` 本身不需要读取它。
检查:
- `.env` 被 `.gitignore` 排除;
- 没有把 key 写入 YAML、笔记或命令历史;
- key 只具备运行 Tunnel 所需最小权限;
- 多组织账号明确指定正确 Organization。
步骤 3:创建 profile
profile 的概念结构:
tunnel_id: "<tunnel-id>"
mcp:
commands:
- name: thought-forest-kb
command: 'cmd /c "D:\home\thought-forest\scripts\kb-mcp\run-tunnel-mcp.cmd"'
Windows 推荐绝对路径。包装脚本负责:
- 切换到仓库根;
- 推导 Vault root;
- 用 `npx tsx` 启动 `server.ts`;
- 保持 stdout 只承载 MCP 协议。
不要让 Tunnel 直接拼接复杂 `npx tsx …` 命令,否则 cwd、引号和 PATH 更难诊断。
步骤 4:验证控制面可见性
npm run kb:mcp:tunnel:access
成功意味着当前 runtime principal 能读取精确 Tunnel。失败时先核对输出中的 request ID,并按顺序检查:
- Tunnel ID 是否精确;
- Organization 是否正确;
- key 是否过期;
- principal 是否有 Read/Use;
- Tunnel 是否关联目标 ChatGPT Workspace。
不要在这一步失败时启动长期 poller。
步骤 5:运行 doctor
npm run kb:mcp:tunnel:doctor
Doctor 应验证:
- profile 可解析;
- tunnel-client 能读取环境;
- 本地 MCP 命令可启动;
- 控制面可访问;
- 网络代理、CA 或 mTLS 配置有效;
- 工具发现不被 stdout 日志污染。
步骤 6:常驻运行
npm run kb:mcp:tunnel:run
观察日志直到出现 connected、ready 或稳定 polling。不要同时启动多个相同 profile 的 tunnel-client;重复进程会增加日志噪声,也可能造成会话归属不清。
步骤 7:可选开机自启
pwsh -File scripts/kb-mcp/register-autostart.ps1
该脚本注册当前用户的 `ThoughtForestKbTunnel` 计划任务:用户登录时启动、以 `Limited` 权限运行、失败后间隔一分钟重试且最多三次。它在 Windows 中最接近 `systemd —user`,比 Startup 文件夹多了状态、重试和统一的启停入口,又不需要把用户级 Vault 和 runtime key 提升成系统服务。
日常管理:
# 查看定义和状态
Get-ScheduledTask -TaskName ThoughtForestKbTunnel
Get-ScheduledTaskInfo -TaskName ThoughtForestKbTunnel
# 手动启停;已有手动实例时不要再次启动
Start-ScheduledTask -TaskName ThoughtForestKbTunnel
Stop-ScheduledTask -TaskName ThoughtForestKbTunnel
# 暂停或恢复自动启动
Disable-ScheduledTask -TaskName ThoughtForestKbTunnel
Enable-ScheduledTask -TaskName ThoughtForestKbTunnel
# 观察日志
Get-Content scripts/kb-mcp/tunnel.log -Wait
[!warning] 注册成功不等于立即启动 任务显示 `Ready` 表示已启用并等待下一次登录触发。若当前已有手动 tunnel-client 占用 `127.0.x.x:8090`,不要立刻 `Start-ScheduledTask`,否则两个实例会争用同一健康检查端口。
验证任务计划程序:
- 使用当前用户;
- 工作目录和脚本路径为绝对路径;
- key 仍从 `.env` 安全加载;
- 日志写入预期文件;
- 失败有有限重试;
- 不弹出可见终端窗口。
撤销:
pwsh -File scripts/kb-mcp/unregister-autostart.ps1
验证
- `npm run kb:mcp:test` 通过
- `npm run kb:mcp:tunnel:access` 通过
- `npm run kb:mcp:tunnel:doctor` 通过
- 只存在一个目标 tunnel-client 进程
- healthz 返回 live
- readyz 返回 ready
- 控制面 poll 成功
- ChatGPT 创建 App 时可选中正确 Tunnel
- 工具发现为 12 个
回滚
- 停止目标 tunnel-client 进程;
- 撤销开机自启;
- 在 Platform 撤销 runtime key;
- 保留 Tunnel record 以便诊断,确认不再使用后再由管理员删除;
- 本地 `kb-mcp` 仍可通过 CLI 和 Codex 使用,不依赖 Tunnel。
常见问题
404 Tunnel not found
既可能是权限隐藏,也可能是 ID 错误或组织错误。先用精确 API/access 脚本验证,不要仅凭 UI 推断。
Doctor 通过但 ChatGPT 看不到
检查 Tunnel 与 ChatGPT Workspace 的 association,以及 ChatGPT App 创建时选择的连接。Doctor 只证明本地和控制面链路,不证明工作区 App 已配置。
服务代码更新但工具仍旧
ChatGPT App 可能保存工具快照。需要 Refresh、重新审核或创建新 App,取决于当前套餐和工作区能力。
本机重启后失效
检查任务计划、用户会话、PATH、`.env` 权限和日志。不要把 key 复制进任务参数作为快速修复。
常驻资源占用如何判断
2026-08-08 在 24 逻辑处理器 Windows 主机上的一次 3 秒空闲采样:整条 Tunnel 进程树工作集约 248 MB,CPU 约 0%;其中 tunnel-client 本体约 30 MB,其余主要来自 MCP 的 Node/tsx 进程和 tunnel-client 启动的 app-server。该数字是现场样本而非资源上限,Node 版本、知识库规模和客户端版本变化都会改变结果。
空闲时主要成本是常驻内存和长连接;全文检索、正文缓存预热、提案合并后的索引重建会产生短时 CPU 与磁盘 I/O。运维时还应观察 `tunnel.log` 是否持续增长,因为当前重定向日志没有轮转策略。