代码不上云,网页端 AI 能参与到哪一步?Local-first 与 GitHub-first 的接入边界
如果商业项目的源码不能发给云端模型,我还可以用网页端 ChatGPT 做什么?能不能让它查看本地项目状态,或者从网页上请求一次受控的 SSH 检查?如果源码允许托管,又为什么还要折腾本地 MCP?
真正要选的不是某个工具,而是让工具看见什么、在哪里执行、结果又返回到哪里。
一句话版:网页端不会自动读取你的 Mac;如果不把源码交给模型,它只能依据你明确开放的摘要或状态给建议,不能直接审查或调试没看到的代码。
本地运行 Codex,不等于模型在本地推理;Codex Cloud 不是连接到你的 Mac;MCP 隧道负责转发请求,不会自动限制工具权限;GitHub Private 只限制仓库访问权限,仓库本身仍托管在云端,若把代码交给云端模型,相关上下文也会进入模型的处理范围。下面先给出选择方法,再用两个公开示例说明接入路径和实际边界。
我为这类边界写了两个独立的公开示例:VXI Local-First MCP 和 VXI SSH Ops Bridge。前者只分享本地项目状态,后者只允许请求固定的只读 SSH 操作。
先比较接入路径:工作区在哪里,模型能看到什么?
| 路径 | 工作区与命令执行位置 | 云端模型可能看到什么 | 能力与适用场景 |
|---|---|---|---|
| ChatGPT 桌面应用中的 Codex Local environment + 本地 MCP | Mac 上的项目 / worktree;本地终端与 MCP 进程 | 取决于模型实际收到的上下文;本地执行不等于本地推理 | 在授权范围内读写本地文件、运行命令、查看 diff;适合已核实模型数据边界的项目 |
| Codex Cloud / 网页端 Cloud task | 连接的 GitHub 仓库进入隔离的云端工作区 | 云端任务可读取工作区源码和文件 | 修改代码、运行测试、交付分支或结果;适合源码获准进入 GitHub 和云端环境的项目 |
| GitHub 云端 Coding Agent | GitHub 仓库与 Agent 的临时云端环境 | 仓库文件、任务上下文和运行中可访问的内容 | 修改分支、运行检查、创建 PR;适合以 GitHub 作为协作与验收中心的团队 |
| ChatGPT 网页端 + Secure MCP Tunnel + Local-First MCP | MCP 服务运行在本机;本机主动建立出站隧道 | 该 MCP 明确返回的计数和状态枚举 | 查看有限项目元数据;适合只需项目状态、不需要源码级审查的场景 |
| ChatGPT 网页端 + Secure MCP Tunnel + SSH Ops Bridge | 本机 / 网络内主机运行 MCP 桥接器与 SSH 客户端 | 目标别名和固定格式的检查结果 | 请求一项预先限定的只读远端检查;适合只需查看非生产目标有限状态的场景 |
| 网页端模型 + 本地 coding-agent relay(需另建) | 本机适配器委派给本地 Codex / coding agent | 取决于模型推理位置和适配器回传内容 | 可设计为委派本地代码任务,但必须限制工具和输出;适合愿意另建 relay 的场景 |
先说明一个影响预期的事实:隧道部分是依据 OpenAI 官方流程整理,尚未在真实 Mac 上跑通 ChatGPT Web 与这两个 server 的端到端互通。后文是配置参考,不是已验收的一键教程。
先选路,再看怎么接
- 源码一字节都不能交给云端模型: 代码级工作留给本地推理或本地可信流程。Local-First MCP 可以分享获准的元数据,但云端看不到源码,就不能独立完成源码级审查。
- 只想让网页端查看本地项目状态: 使用 Local-First MCP 这类固定字段投影;它不需要获得源码读取或 Shell 权限。
- 只想从网页端请求 SSH 只读检查: 使用 SSH Ops Bridge 这类命名操作,把主机身份、私钥和目标白名单留在本地。
- 源码允许托管,而且希望 AI 真正读写代码: 选择 GitHub-first、Codex Cloud 或其他获准的云端 Agent,利用分支、PR、commit 和 CI 留下可复核记录。
- 希望网页端把代码任务委派给本地 Codex: 需要另建并审查 coding-agent relay;元数据 MCP 和 SSH 桥接器都不承担这项工作。
- 多个项目的数据等级不同: 按项目分别选择路径,不要因为博客仓库可以上云,就把同一套权限扩展到商业源码。
其他厂商的网页端工具需要单独核验:先确认它是否支持远程 MCP 或本地 Agent、使用什么认证方式,再看模型实际收到哪些输入和工具返回;不能因为都支持 MCP,就假设 OpenAI Secure MCP Tunnel 可以直接复用。
官方说明可参照:Codex Local environments、Codex Cloud environments、Codex MCP、OpenAI Secure MCP Tunnel 和 GitHub Copilot cloud agent。
先看三条数据流
A. 网页端读取本地项目状态
ChatGPT Web
|
OpenAI Secure MCP Tunnel endpoint
^ 本机 tunnel-client 主动建立出站 HTTPS
|
本机 vxi-local-first-mcp(stdio MCP)
|
本机项目目录:只统计文件类别,不读取文件内容
返回:计数 + PASS / FAIL / UNKNOWN
不返回:源码、路径、diff、日志、命令
B. 网页端请求固定 SSH 检查
ChatGPT Web
|
OpenAI Secure MCP Tunnel endpoint
^ 本机 tunnel-client 主动建立出站 HTTPS
|
本机 vxi-ssh-ops-bridge(stdio MCP)
|
本机 SSH 客户端 + 本地私钥 + pinned known_hosts
|
非生产 SSH 目标:固定的只读探测脚本
|
过滤后的 health / service / storage 枚举
|
返回网页端
C. GitHub-first 云端开发
获准托管的 GitHub 仓库
|
Codex Cloud / GitHub 云端 Agent
|
云端工作区读取源码、修改文件、运行检查
|
分支 + PR + commit + CI
|
人工审查与批准
这三条路径解决的不是同一件事:前两条限制云端工具能看到或能请求什么;第三条则允许 AI 在获准的云端工作区中对源码做完整的开发循环。
Codex 本地工作区、Codex Cloud 与 MCP,不要混成一个概念
ChatGPT 桌面应用中的 Codex Local environment 用于配置本地项目 / worktree 的准备步骤和常用动作,集成终端在本机执行命令,Git 工具可以查看 diff、暂存、提交、推送和创建 PR。Codex 的本地客户端也可以直接启动本机 stdio MCP 服务;这种连接是“本地 MCP host 启动本地 MCP server”,并不需要为了本地客户端调用而建立网页端隧道。
但“本地执行”只说明文件和命令在哪里运行。若模型本身是托管模型,分析时使用的源码上下文仍可能被发送到云端。只有本地推理或经过验证的本地可信流程,才可能满足严格的源码不出站要求;还需要单独审查设备上的其他进程和网络路径。
Codex Cloud 则会把选定的 GitHub 仓库放进隔离的云端工作区,准备依赖并运行任务。GitHub 云端 Agent 也能在云端环境中读取源码、修改分支、运行检查并创建 PR。这种路线更适合允许进入云端边界的项目,而不是“源码只留在 Mac”的替代方案。
MCP host 与 MCP server 的区别
- ChatGPT 桌面应用、Codex CLI 与 IDE 扩展作为 MCP host: 可以按客户端支持的方式配置本地 stdio MCP,工具进程由本地客户端启动。官方 CLI 命令形式是
codex mcp add <name> --env KEY=VALUE -- <server-command>;例如接入本地项目状态服务:
codex mcp add vxi-local-status \
--env "VXI_WORKSPACE=/absolute/path/to/project" \
-- /absolute/path/to/node /absolute/path/to/vxi-local-first-mcp/bin/vxi-local.mjs mcp
codex mcp list
若要接入 SSH Ops Bridge,可以用同一命令形式,但把环境变量改为 VXI_OPS_CONFIG=/absolute/path/to/ops.config.json,并把 server command 指向 vxi-ssh-ops-bridge/bin/vxi-ops.mjs mcp。配置中的主机名、账号、私钥路径和 ops.config.json 只留在本机;不要把运行时密钥贴进聊天或提交到 Git。ChatGPT 桌面应用、Codex CLI 与 IDE 扩展共享 Codex host 的 MCP 配置;在桌面应用的 Settings → MCP servers 中添加服务器,具体选项受工作区权限影响。
- ChatGPT 网页端作为调用方: 网页不会读取本机的
~/.codex/config.toml,也不会自动继承本地 Codex 的命令菜单。它需要使用支持的远程 MCP 连接方式;Secure MCP Tunnel 的作用,就是让本机主动连接 OpenAI 的隧道端点,再把请求转发给本地 MCP。
网页端怎样连接本机 MCP,再触发 SSH?
OpenAI 的 Secure MCP Tunnel 文档 描述了这条连接方式:在能访问私有 MCP 服务的网络内运行 tunnel-client,由它主动建立出站 HTTPS 通道,从 OpenAI 托管的端点取回请求、转发给本地 stdio 或 HTTP MCP server,再把响应通过同一隧道送回去。它不要求给本机 MCP 开公共入站端口。
命令中的路径和隧道 ID 是占位符,运行时密钥只放在本机环境变量中。
- 在 OpenAI Platform 创建或选择隧道,确认目标 ChatGPT workspace 和 Platform organization 已正确关联,并取得所需的 Tunnel 权限。
- 从 Platform 的隧道设置获取最新的
tunnel-client,先运行tunnel-client help quickstart,再为每个 MCP server 配置独立 profile。 - 先离线运行并测试 MCP server,再配置隧道。进入
vxi-local-first-mcp仓库,先跑合成测试和演示:
cd /path/to/vxi-local-first-mcp
node --version # >= 22
npm test
npm run demo
确认通过后,再为 Local-First MCP 配置隧道:
export CONTROL_PLANE_API_KEY="<runtime-key,仅保存在本地终端>"
tunnel-client init \
--sample sample_mcp_stdio_local \
--profile vxi-local-status \
--tunnel-id "<your-tunnel-id>" \
--mcp-command "/usr/bin/env VXI_WORKSPACE=/absolute/path/to/project /absolute/path/to/node /absolute/path/to/vxi-local-first-mcp/bin/vxi-local.mjs mcp"
tunnel-client doctor --profile vxi-local-status --explain
tunnel-client run --profile vxi-local-status
这里使用绝对路径,是为了避免后台进程的工作目录或 PATH 与交互式终端不同。
如果要接入 SSH Ops Bridge,先在仓库中运行 npm test 和 npm run demo,再从 ops.config.example.json 创建本地的 ops.config.json,并将 mode 从 disabled 改为 read_only。配置中只放经过独立核验的非生产目标、专用低权限账户、私钥路径和固定的 known_hosts 文件;同时按仓库 README 将只读脚本安装到测试主机的 /usr/local/bin/vxi-health-check。然后为这个 MCP server 单独配置 profile:
tunnel-client init \
--sample sample_mcp_stdio_local \
--profile vxi-ssh-readonly \
--tunnel-id "<your-ssh-tunnel-id>" \
--mcp-command "/usr/bin/env VXI_OPS_CONFIG=/absolute/path/to/ops.config.json /absolute/path/to/node /absolute/path/to/vxi-ssh-ops-bridge/bin/vxi-ops.mjs mcp"
tunnel-client doctor --profile vxi-ssh-readonly --explain
tunnel-client run --profile vxi-ssh-readonly
平台界面与客户端版本可能变化,实际参数以当前客户端的帮助输出为准。
- 保持
tunnel-client run运行。在 ChatGPT 当前支持的自定义 MCP 入口中选择 Add custom MCP server → Tunnel,选择可用隧道,完成认证并确认权限提示。若找不到隧道,优先检查 workspace / organization 关联与权限,而不是把 MCP 服务改成公网开放。 - 先调用只读的
vxi_project_status或vxi_ops_policy验证工具发现,再按需请求vxi_ssh_health。不要用真实生产主机测试;SSH 主机指纹必须通过独立渠道核验。
SSH 桥接器在本机做了什么?
本地管理员把主机名、用户名、端口、私钥路径和经过独立核验的 known_hosts 写入本地 ops.config.json。MCP 只接受诸如 lab 的允许别名,不接受模型生成的命令文本。桥接器忽略个人 SSH 配置,禁用 Agent 转发和交互式密码提示,调用固定的 /usr/local/bin/vxi-health-check,并把输出收敛为三个状态枚举。
所以网页端不是直接拿着你的私钥登录服务器;它请求的是一个固定操作。隧道负责传输,桥接器的工具清单、参数校验、本地配置、主机密钥绑定和远端脚本共同决定权限边界。
如果把 MCP 后面换成可编辑代码的本地 Agent,必须重新审查它每个工具的输入、输出、文件写入、命令执行、超时和人工批准机制。一个受限 SSH MCP 的安全结论,不能自动套用到有代码写入权限的 relay 上。
两个公开仓库各自承担什么角色?
VXI Local-First MCP:只分享本地项目元数据
这个本机 stdio MCP 暴露两个只读工具:
vxi_project_status:文件分类计数、跳过的符号链接数量,以及人工记录的lint / test / build状态(PASS / FAIL / UNKNOWN;默认是UNKNOWN,MCP 不会自动运行测试)。计数是有界扫描结果:会跳过点目录和常见构建 / 缓存目录(如.git、node_modules、dist),最多处理 20,000 个目录项;超过上限会报错,而不是返回部分计数,因此不是全项目全量统计。vxi_security_contract:说明允许输出与禁止输出的字段。
文件分类通过目录项完成,不读取文件内容;固定字段投影不包含源码、路径、diff、日志、环境变量或 Shell 接口。它的作用是让云端看到有限的项目状态,而不是把源码“加密后传上去”。
record 是本机 CLI 操作,不是 MCP 工具,也不会自动运行测试。调用方式是 node bin/vxi-local.mjs record --workspace /path/to/project --kind test --status PASS;kind 可选 lint、test 或 build,状态由操作者手动填写。它的实际用途很有限:把本地检查的粗粒度结果带入网页端讨论,而不上传源码或原始日志;它能帮助模型了解当前状态,却不能定位失败原因,也不是绑定 commit 的签名测试证明。需要排错时,应在本机分析完整日志,或由人工挑选并脱敏最小错误摘要再分享。
VXI SSH Ops Bridge:只允许固定的只读 SSH 操作
桥接器默认关闭,只暴露 vxi_ops_policy 与 vxi_ssh_health。后者接收本地配置允许的目标别名,调用固定远端脚本,并将返回值过滤成 health / service / storage 三个枚举。当前示例脚本的 health: PASS 只证明脚本成功运行;在接入并验证应用专属检查前,service 和 storage 仍为 UNKNOWN。它不提供通用 exec(command)、文件传输、部署、重启或任意日志导出。
两个仓库彼此独立:一个只暴露本地项目状态,另一个只暴露固定 SSH 检查;它们没有代码读写工具,也不会互相调用。
测试绿灯究竟证明了什么?
这些数字对应不同仓库、不同测试层级,不是同一组测试反复计数:
- Local-First MCP:
main基线有 6 个单元测试(基线 CI)。PR #3 没有增加测试用例,只增加 Ubuntu / macOS 矩阵;同样的 6 项在两个系统上分别 6/6 通过(PR #3 CI)。 - SSH Ops Bridge:
main基线有 7 个单元测试(基线 CI)。PR #3 保留这 7 项单元测试,并另加一次性 GitHub-hosted Ubuntu Runner 上的 OpenSSH localhost 集成作业;该运行的单元测试和回环 SSH 检查均通过(PR #3 CI)。 - SSH Ops Bridge PR #4: 在 7 项基础上新增 1 个测试用例,专门覆盖主机名 / 用户名缺失或类型不正确的情况;同时补上相应的字符串类型校验。该 PR 分支的 8/8 单元测试和 Demo 通过(PR #4 CI)。
回环测试也暴露过真实的夹具问题:早期 Runner 用户与 PAM 配置不匹配,导致认证失败;后来又发现服务端 ForceCommand 会覆盖客户端请求,可能让测试在没执行预期命令时产生假阳性。修正后,夹具明确不配置 ForceCommand,让真实 ssh 客户端执行代码生成的固定远端脚本;测试同时检查直接调用和经 MCP 调用的结果,并确认错误的 pinned host key 与任意 exec 请求会被拒绝。
这些结果只对应各自链接中的源码版本。截至本次核对,三个源代码 PR 仍为 Draft、尚未合并;PR #3 的跨平台矩阵与回环作业、PR #4 的输入校验修复都还不属于 main。
结论:按数据边界选择,而不是按工具名字选择
如果源码允许进入云端,GitHub-first 能让 AI 直接读写代码,并用分支、PR、commit 与 CI 留下持久的工程证据。如果源码必须留在本机,Local-First MCP 可以让网页端参与状态检查,SSH Ops Bridge 可以让网页端请求一项固定的只读远端操作;代码级开发则仍需本地人工、本地推理或其他经过审查的本地流程。
真正要守住的边界,不是“用了本地工具”或“接上了 MCP”,而是每条路径实际把什么交给了模型、模型能调用什么、返回内容又去了哪里。
这两份仓库目前仍是最小原型。除文中说明的隧道互通尚未真机验证外,本轮也没有测试外部 SSH 主机、整机源码出站审计、真正的本地模型推理,或同一任务的质量 / 耗时 / Token / 费用对比。