让 ChatGPT 读取本地代码:CodexPro、MCP 与固定隧道的完整实践
我经常在 ChatGPT 网页版里讨论代码,但一旦问题涉及多个目录、调用链和项目约定,复制粘贴文件就很麻烦。我想要的不是让它直接改代码,而是:讨论到某个项目时,让它按需读取我电脑上允许访问的代码。
这次我先比较了两个开源项目,最终用 CodexPro 提供本地 MCP 工具,再用 ngrok 的固定 HTTPS 地址接入 ChatGPT。整个过程整理成了 chatgpt-local-code-skill,里面有 Skill、部署辅助脚本、固定依赖快照和验证用例。
先说边界:这不是完全离线方案,不是让 ChatGPT 自动拥有整台电脑,也不是绕过账号权限。代码被工具读取后,会经过隧道服务并进入 ChatGPT 的处理链路。下面记录的是 2026 年 11 月 1 日,在 macOS Apple silicon 上的一次实践;服务条款、免费额度、界面和依赖安全状态都可能变化。
一、先分清楚:我想接的是工具,不是模型代理
一开始看到两个项目:
| 项目 | 主要解决什么 | 是否符合这次目标 |
|---|---|---|
| rebel0789/codexpro | 把明确允许的本地工作区通过 MCP 暴露给 ChatGPT,提供读文件、搜索等工具 | 符合,链路直接 |
| miuuyy/codex-chatgpt-web | 通过浏览器操作 ChatGPT 网页,再把它桥接为 Codex 可用的模型接口 | 是另一种需求,接入和维护更复杂 |
后者不仅是“让 ChatGPT 看本地文件”,还涉及浏览器运行时、登录状态、模型路由以及工具回传。我的现有 Codex 已经有自己的模型配置,没必要为了读代码接管那条路由。
因此,这次选择的是:
ChatGPT 网页对话(选择已安装的自定义 MCP 插件)
↓ HTTPS / MCP
ngrok 账号绑定的固定 dev domain
↓ 加密隧道
本机 127.0.0.1:8788,只转发 /mcp
↓
本机 127.0.0.1:8787,CodexPro 验证访问令牌
↓ 路径边界和敏感文件过滤
明确允许的本地工作区(只读)
这里每一层职责不同:ChatGPT 负责理解问题和选择工具,MCP 是交互协议,CodexPro 真正执行本机文件操作,ngrok 负责让云端可以连到本机服务。Skill 则告诉编码 Agent 如何配置、检查和维护这条链路,它不是 MCP 服务本身。
二、先在本地跑通,再考虑把代码接出去
评估时,我把两个项目放在独立目录里安装和测试,没有一开始就交出业务代码或账号登录信息。
CodexPro 使用 0.30.3,固定到提交 113472c6ff0a1516b6f223a43cd355abdf2e61ae。原始版本构建通过,13 组 smoke 测试通过,另外做了真实 HTTP MCP 检查:未认证请求被拒绝,认证后能够初始化、枚举工具、读取测试文件,敏感路径与越界路径被阻断。
另一个项目的本地测试也能通过。测试环境补齐后,根测试结果为 1114 通过、106 跳过,Launcher 测试串行执行为 430 通过、2 跳过;另外执行了本地浏览器样例和 Electron 测试。这些是本地核心和样例验证,不是用我的 ChatGPT 账号完成真实模型调用的证明。 跳过的项目也不能计入“全部功能可用”。
这个区别很重要:一个项目能编译、单元测试通过,与账号授权、线上页面结构、实际模型权限和端到端使用是不同的事情。
依赖告警不能因为功能测试通过就忽略
原始 CodexPro 锁文件的生产依赖审计报告了 10 项告警,其中包含 critical 和 high。这个结果只是特定时间的公告匹配,不等于我已经证明每个漏洞都可利用;但同样不能因为服务能跑,就直接接公网。
我没有修改原始评估仓库,而是另外建立运行副本,升级相关依赖,处理 Zod 4 的一处签名兼容:
// Zod 4 中明确指定 record 的 key schema
z.record(z.string(), z.any())
更新后,TypeScript 构建和 13 组 smoke 测试再次通过,当时 npm audit 未报告漏洞。开源 Skill 保留了这份经过测试的依赖快照,并在安装时重新审计。
这里的结论只能是“本轮检查通过”,不是“永久安全”。以后公告更新,安装可能被审计步骤阻断,需要重新评估、修复和回归,而不是无脑执行 npm audit fix --force。
三、只读是服务端配置,不是对模型说一句“别改代码”
如果只想讨论代码,我不需要让它执行命令。即使项目提供所谓 safe bash,也不等于操作系统沙箱:npm test、npm run build 仍然可能执行仓库里的任意脚本。
这次核心配置是:
CODEXPRO_HOST=127.0.0.1
CODEXPRO_BASH_MODE=off
CODEXPRO_WRITE_MODE=off
CODEXPRO_CODEX_SESSIONS=off
CODEXPRO_TOOL_MODE=standard
CODEXPRO_CONNECTION_TEST=1
ROOT、允许根目录和随机访问令牌由配置脚本写入,示例里不放真实值。
其中 CONNECTION_TEST=1 是在这个固定版本中复用的工具过滤开关:除了关闭写文件和 shell,还移除 supertool、自检写探针、导出和交接类动作,保留读文件、搜索、目录查看等能力。它的名字容易误导,不能假定未来版本仍然完全相同,所以验证脚本会再检查实际注册的工具,以及写入请求是否真的被拒绝。
我另外做了三件事:
- 运行副本、令牌、ngrok 配置和日志放在开放目录之外,避免它们通过文件工具被读出。
- 保留上游对
.env、私钥、.git等路径的过滤,并补充常见认证文件和凭据目录。 - 在隧道前加一个只允许
/mcp的本机网关,不把管理页面一起暴露出去。
我实际授权的是一个较大的本地开发工作区,但不建议把这当默认方案。能从一个仓库开始,就先从一个仓库开始。 即使路径检查和符号链接越界测试通过,也不能把它理解成容器隔离,更不能保证过滤规则能识别所有文件名不规范的密钥。
四、为什么本地服务地址不能直接填进 ChatGPT
本机服务启动后,会有这样的地址:
http://127.0.0.1:8787/mcp
这个 127.0.0.1 指向当前访问者自己的机器。ChatGPT 的 MCP 连接由云端发起,不是我的浏览器替云端读取电脑上的端口,因此仅提供 localhost 地址不够。
我先用了 Cloudflare Quick Tunnel。它适合快速验证,不需要域名,能立即得到一个临时 HTTPS 地址。问题是:隧道重建后地址可能变化,ChatGPT 插件还保存着旧地址。 这就不符合我想要的“重启电脑后直接恢复”。
Cloudflare 普通的命名隧道公网应用路线需要由 Cloudflare 管理的域名。当时我有账号,但没有独立域名,不想为了这个小需求先购买一个。
最后改用 ngrok 免费账号分配的 dev domain。根据当时的官方说明,它绑定账号,重启客户端不变;本次实际分配的是 ngrok-free.dev 后缀,文档例子也可能使用 ngrok-free.app。这是服务商提供的子域名,不是我独立持有的域名。
截至本文核验时,免费方案列出的额度包括每月 1 GB 出站流量、20,000 次 HTTP 请求。读代码可以先用,但是否够用取决于文件大小和调用频率;不要把免费服务理解成无条件、无限量、永久可用。
另外,OpenAI 也有 Secure MCP Tunnel 的官方路线,可以避免自行公开 HTTP 监听入口,但需要按它自己的 Tunnel、运行凭据和账号权限流程配置。本次没有实现那条路线,不能把它和 ngrok 配置混为一谈。
五、登录有两件事:网页账号与本机 agent 授权
这一步看起来简单,实际踩了两个坑。
1. 登录 ngrok 网页不等于本机已经登录
我在浏览器里登录 ngrok 后,本机客户端仍需要 authtoken 才能建立账号绑定的隧道。为了不把令牌留在聊天记录或 shell 历史里,我用一个隐藏输入提示把它保存到私有配置文件,权限为 0600。
曾经两次遇到 ERR_NGROK_105:令牌格式不正确。不能只听到“复制好了”就认定成功,要检查文件是否真的更新,再以客户端返回的认证结果为准。应复制令牌的值,而不是示例命令、占位符或旧剪贴板内容。
这里有两个不同的秘密:
| 秘密 | 用途 | 不应该放在哪里 |
|---|---|---|
| ngrok authtoken | 让本机 agent 连接我的 ngrok 账号 | 博文、聊天、命令参数、Git 仓库 |
| MCP 访问令牌 | 让 CodexPro 判断调用方是否有权读取代码 | 公开连接示例、截图、被开放的工作区 |
2. 免费 ngrok agent 继承 HTTP 代理会失败
本机开发环境设置了 HTTP/HTTPS 代理。第一次启动 ngrok 时继承了这些变量,返回 ERR_NGROK_9009:通过 HTTP 代理连接 agent 属于付费功能。
我没有因此升级套餐,也没有全局关闭代理,而是只给 ngrok 子进程提供干净的代理环境。本轮网络直连成功,隧道随后正常建立。用于检查公开地址的客户端是否需要代理,则是另一回事,两条网络路径要分别判断。
如果另一台机器无法直连,这个处理不保证适用。应报告网络阻碍,再选择用户认可的方案,而不是替用户改系统网络或开通付费服务。
六、ChatGPT 插件如何填写
按当前 OpenAI 官方 MCP 接入说明:
- 登录 ChatGPT 网页,进入插件页面,选择添加自定义 MCP 服务器。
- 填名称和 HTTPS 服务器 URL,配置鉴权。
- 阅读风险提示后创建,并安装这个插件。
- 在对话输入框通过
@选择插件,再发起读取请求。
菜单名称和可见性受账号、工作区权限及安全设置影响。不能保证每个账号都能看到同样入口。
CodexPro 提供了个人连接器的 query token 兼容形式:
https://YOUR-ASSIGNED-DOMAIN.ngrok-free.dev/mcp?codexpro_token=REPLACE_WITH_PRIVATE_TOKEN
这只是格式示意,不是可用地址。 实际完整地址保存在私有本机文件里,不在正文展示。
使用这种形式时,ChatGPT 界面选择 No Authentication,因为令牌已放在 URL 中;但 CodexPro 仍检查令牌,不能把它叫作“没有鉴权”。知道完整 URL 的人,相当于拿到了读取允许范围代码的钥匙。URL 还可能进入连接器配置、历史或服务日志,所以不要截图分享,不要放短链接,更不要提交 Git。
对于多人使用或长期生产部署,应评估 OAuth 等更合适的鉴权,不应把个人 query token 方案直接推广成团队标准。
第一次接通,我更愿意这样测试:
只使用这个本地代码插件,打开当前工作区,只列出根目录第一层目录,不递归,不读取文件内容。
确认项目范围后,再针对某个子目录或调用链提问,而不是一句“帮我分析全部代码”就开始大范围读取。
七、重启后不用反复添加插件,但要具备两个条件
第一个条件是固定 URL。ngrok dev domain 和本地 MCP 令牌不变,ChatGPT 插件就可以继续使用同一地址。
第二个条件是本机服务自动恢复。我配置了两个用户级 macOS LaunchAgent:一个负责 MCP 服务和网关,另一个负责 ngrok。它们在用户登录后启动,进程退出时尝试恢复;配置里不写令牌值,只引用本机私有文件。
这是“登录后自启动”,不是电脑还没登录就运行的系统守护进程。关机、休眠、断网、项目所在磁盘未挂载时,都不能保证访问。
我实际做过停止旧进程、重新交给 LaunchAgent 启动,再通过同一个 ngrok 地址执行读取验证。没有为了这次验证真正重启整台电脑。 因此可以说“服务重启后地址和访问正常”,不能写成“已经完成所有开机恢复场景验收”。
停止和永久禁用也要分清:停止本次登录会话,下次登录仍会启动;永久禁用需要卸载并移除对应 LaunchAgent。开源辅助脚本提供了两个不同命令,不让“停止了”变成一个错误的长期安全预期。
八、把过程收敛成 Skill,而不是一篇只能手抄的教程
这次开源的是 chatgpt-local-code-skill,首版重点是让另一个 Agent 在相同目标下少走弯路,而不是宣称一键兼容所有系统。
仓库包含:
SKILL.md:什么时候用、需要确认哪些目录和公开范围、什么时候停止。references/setup.md:安装、鉴权、固定隧道、自启动和常见错误。scripts/bridge.py:参数化的 macOS 部署和生命周期入口。assets/:只读网关、验证脚本、经过本轮测试的 npm 依赖快照。tests/:根目录与私有状态隔离、域名校验、文件权限和 LaunchAgent 配置测试。
它不会读取浏览器 Cookie,不会接管 Codex 模型配置,也不会替用户创建插件或购买服务。
使用示例
把 skills/chatgpt-local-code 安装到支持该格式的 Agent 的 Skill 目录,或者直接在克隆后的仓库里运行辅助脚本:
git clone https://github.com/Lianminx/chatgpt-local-code-skill.git
cd chatgpt-local-code-skill/skills/chatgpt-local-code
python3 scripts/bridge.py install
python3 scripts/bridge.py configure \
--root /absolute/path/to/repo \
--domain YOUR-ASSIGNED-DOMAIN.ngrok-free.dev \
--ngrok /absolute/path/to/ngrok \
--confirm-public-access
参数中的目录、域名和 ngrok 路径都要替换为自己的值。ngrok 客户端应从官方来源独立安装,脚本不附带二进制。辅助脚本默认使用独立状态目录,不覆盖这次实践中已经运行的服务。
先以前台方式启动并验证本地 MCP;确认后停止前台服务,再选择启用登录后自动启动和公开验证。完整步骤以仓库文档为准。
python3 scripts/bridge.py enable-autostart --confirm-autostart
python3 scripts/bridge.py verify --public
python3 scripts/bridge.py status
# 只停止当前登录会话
python3 scripts/bridge.py stop
# 取消后续登录时自动启动
python3 scripts/bridge.py disable-autostart
Skill 中的验证器使用自己创建的无敏感信息样例,而不是拿真实项目文件测试连通。这样测试 MCP 读取和权限边界时,不必先发送业务代码。
九、怎样判断“完成”,而不是只看到一个在线域名
这次我把完成拆成几层:
| 检查层 | 能证明什么 | 不能证明什么 |
|---|---|---|
| 构建、smoke、依赖审计 | 固定版本在本轮环境下的基础可用性和已知告警状态 | 没有未知漏洞、所有平台兼容 |
| 本地 MCP 初始化和工具调用 | 协议、鉴权和本机读文件路径可用 | 公网和 ChatGPT 账号可用 |
| 固定 HTTPS 地址上的 MCP 验证 | ngrok 到本机的实际读文件链路可用 | ChatGPT 会在每次对话正确选择工具 |
| 只读、越界、敏感路径、管理入口测试 | 这些具体边界按预期拒绝请求 | 所有秘密都能被识别、OS 级隔离成立 |
| 服务停止并重新启动 | 本轮服务恢复和固定域名复用正常 | 所有电脑重启、网络变化、长期可用性已验证 |
| ChatGPT 中安装、选择和实际调用 | 对应账号、对话和工具的真实使用 | 所有模型与账号都有相同权限 |
本轮部署已经验证到公开 MCP、实际文件读取、权限限制和服务重启后的复验。ChatGPT 的账号入口、插件安装和对话实际使用应单独验收;不能用命令行通过替代它。本次没有测试浏览器桥接项目的真实账号模型调用,也没有验证长时间运行或所有系统重启场景。
这也是我最想保留在 Skill 里的习惯:连接建立、权限正确、用户实际能用,是三个不同的完成条件。
最后
让 ChatGPT 读取本地代码,并不一定需要复杂的模型桥接。对于“在网页里讨论代码”这个目标,一个范围清楚的只读 MCP 服务,加上可稳定访问的 HTTPS 入口,就能把复制粘贴变成按需读取。
真正值得花时间的,不只是让域名在线,而是开放哪些目录、令牌放在哪里、哪些工具不应该出现、重启后如何恢复,以及失败时怎样给出准确证据。
能读代码是一项能力,能清楚地控制它读到什么,才是一套可以持续使用的流程。
参考资料
- CodexPro,本次读取工具的上游项目。
- codex-chatgpt-web,本次比较但未采用的浏览器桥接路线。
- 开源 Skill 与辅助脚本。
- OpenAI:添加自定义 MCP 服务器。
- OpenAI:Secure MCP Tunnels。
- ngrok 免费方案限制。
- ngrok agent 配置。
- ngrok ERR_NGROK_9009。
- Cloudflare Tunnel 前置条件。