
先交代背景我自己的环境是 Windows 11 Codex CLI0.4x 版本 Chrome通过 MCP 接的浏览器自动化工具。你遇到Codex 控不了浏览器大概率不是某一个单独的原因而是配置、权限、网络、鉴权几条线叠在一起。这篇文章我按实际排查的顺序写你照着走一遍大部分问题都能定位到具体环节。1. 先搞清楚你的 Codex 是通过哪种方式控浏览器的1.1 三条主流的控制链路Codex 本身是一个 AI 编程代理它没有内置浏览器操作能力。你想让它控制浏览器在 2025 年的主流做法无非就三种。第一种是走 MCP 服务器在 Codex 的 MCP 配置里挂一个浏览器控制类工具比如微软的 playwright-mcp或者社区维护的 chrome-devtools-mcp。Codex 通过 MCP 协议把任务发给这个工具工具再通过 CDPChrome DevTools Protocol或者 Playwright 驱动真实的浏览器实例。第二种是走自定义工具函数你自己写一个 Node/Python 脚本用 Puppeteer、Playwright 或者 Selenium 把浏览器操作封装成一个个函数然后以 tools 的形式注册给 Codex。这种方式的好处是可控性强坏处是 Codex 对工具入参格式非常挑剔动不动就报function call failed。第三种是走浏览器扩展安装一个带 MCP 连接能力的浏览器扩展让扩展本身成为 MCP 服务器直接把当前浏览器页面暴露给 Codex。这种方案对扩展版本、浏览器版本要求都很高也是我见过报错最玄学的方案。1.2 不同链路对应的控不住长相不一样先说结论你先得判断自己是哪条链路再去查对应链路的问题否则很容易做无用功。如果是 MCP 链路控不了浏览器最常见的长相是三种Codex 回复没有可用工具任务直接中断这意味着 MCP 服务器压根没挂载成功或者挂载了但工具没有暴露给 Codex 运行时。Codex 说有工具但一调用就报连接错误、超时、WebSocket 关闭。这通常是 MCP 服务器和浏览器之间的 CDP 连接没建立起来或者浏览器没开远程调试端口。浏览器确实启动了也有动作但结果跟预期完全不匹配。这种反而是三者里最好修的基本是选择器、页面加载等待这类业务逻辑问题。而如果是自定义工具函数链路控不住往往体现在 Codex 生成了错误的参数比如把字符串当数组传进去或者等待时间估算得太短。错误信息通常在 Codex 的调用日志里能看到形形色色但根源大多不在浏览器侧而在函数定义不够严格。最麻烦的是浏览器扩展链路。这类方案要求扩展在后台通过 Service Worker 维持一个 WebSocket 长连接一旦浏览器更新、扩展被禁用、或者 MCP 连接开关没打开表现就是 Codex 那边能发现工具但一调用就静默失败。热搜词里的浏览器扩展设置中启用 mcp 连接我猜你多少也碰过这个场景。所以第一件事是明确你的链路。不知道的话依次检查Codex 的 MCP 配置里挂了什么。有没有单独的配置文件引入自定义工具。浏览器扩展栏里有没有相关扩展以及它的 MCP 开关状态。2. 第一步排查MCP 配置与工具挂载2.1 MCP 配置有没有真正被 Codex 加载Codex 目前支持通过codex mcp命令或者项目级配置来管理 MCP 服务器。很多人的第一反应是改完配置立刻在对话里让 Codex 调用浏览器结果它完全不理你。这里有个很容易被忽略的点Codex 不会热加载 MCP 配置。你改了config.toml或者.mcp.json之后必须重启 Codex 会话甚至要完全退出重启 CLI否则新增的 MCP 服务器不会出现在当前上下文里。你可以用codex mcp list查看当前会话能识别到的 MCP 服务器列表。如果列表里根本没有浏览器工具对应的服务器那就是配置阶段出了问题。常见原因配置文件格式写错。MCP 服务器配置支持stdio和sse以及较新的http两种传输方式格式要求很严格。command字段必须是可执行程序的路径args必须是字符串数组多一个冗余字段都会导致解析失败。包源不对。不少人会在 MCP 服务器配置里指定一个 npm 包名但本地压根没装过。Codex 不会帮你自动安装你需要先确认在命令行里手动执行这个包能不能正常启动。比如npx playwright-mcplatest能正常拉起服务再去配置 MCP 才有意义。提示如果是 Windows 环境MCP 配置里尽量写命令的完整路径不要依赖 PATH 环境变量。Codex 启动子进程时的 PATH 解析跟你在终端里手动敲命令时不一样我在这上面栽过两次跟头。2.2 用一条命令确认工具列表里有没有浏览器工具MCP 服务器加载成功不等于 Codex 就能用它的工具。Codex 每次发起任务时会根据当前会话的上下文构建工具列表。你可以用一个最简单的 prompt 来测试列出你当前可以使用的所有工具名称不要调用它们。如果 Codex 返回的工具列表里没有 Chrome、playwright、browser、page 这一类关键词那真相只有一个工具没有暴露给 Codex 运行时。排除思路确认 MCP 服务器的类型和传输方式是否被 Codex 支持。新版 Codex 对http协议支持比较新如果指向的是一个只支持sse的老版本服务器可能握手成功但没有枚举到工具。确认 MCP 服务器是否必须在启动时带固定参数比如某些浏览器 MCP 服务器需要--headless或--channelchrome参数缺了参数它可能只启动服务但不注册工具。确认有没有开启工具白名单或黑名单。Codex 的 sandbox 模式如果开得太严格工具调用会被运行时阻断表现跟工具不存在几乎一样。这个阶段要做的关键动作是手动启动 MCP 服务器然后直接看它的初始化响应里有没有 tools 列表。很多 MCP 服务器在收到tools/list请求后才会真正去初始化浏览器连接如果你只看到initialize成功就以为万事大吉那后面调用必挂。所以不妨先用社区通用工具去手动发一个tools/list请求确认工具确实存在再回过来排查 Codex 侧。3. 第二步排查浏览器侧的三道关卡3.1 远程调试端口浏览器必须以 --remote-debugging-port 启动CDP 是所有主流浏览器控制方案的底层协议。Chrome、Edge、Chromium 默认不开 CDP 监听端口你必须用特定参数启动浏览器它才会对外暴露调试接口。最典型的是chrome.exe --remote-debugging-port9222 --user-data-dirD:/tmp/chrome-debug这个参数的意义是让 Chrome 监听 localhost:9222供外部程序通过 CDP 发送指令。如果你直接双击打开 Chrome那无论 MCP 服务器怎么配置永远都连不上因为浏览器根本没有开调试端口。有个隐蔽问题Chrome 本身对--remote-debugging-port有限制。如果你用一个已经打开的 Chrome 实例通过 URL 传参新参数会被忽略实例还是按原来的方式运行。这就是为什么很多人明明加了参数查端口仍然不通。正确的做法是关掉所有 Chrome 进程包括后台托盘进程然后用命令行重新启动。验证方法更简单curl http://127.0.0.1:9222/json/version能返回 JSON 就说明 CDP 通了。返回不了说明浏览器没监听或者端口被占用。3.2 托管策略与用户数据目录热搜词里有一条托管浏览器禁用此设置这个坑我印象极深。如果你用的是公司电脑或者系统里装了某些安全软件Chrome 可能被策略锁定命令行参数会被忽略界面上的某些设置项也会置灰。你可以在地址栏访问chrome://policy看看有没有RemoteDebuggingAllowed或者DeveloperToolsAvailability之类的策略条目。如果把远程调试禁掉了无论你怎么加参数Chrome 都不会监听调试端口。另外一个天坑是--user-data-dir重复。Chrome 每个用户数据目录同一时间只能有一个进程实例绑定。你上次调试用的目录还挂在一个残留进程上这次再指定同一个目录启动新实例浏览器会直接弹出该配置文件正在使用中然后退出或者干脆把命令转发给旧实例。排查方法很简单换一个全新的临时目录试试比如--user-data-dirC:/tmp/codex-chrome-$(date)。这个看起来蠢但实测特别快能立刻区分问题在参数还是进程残留。如果是用 playwright-mcp 这类方案它通常自己会启动一个专用的浏览器实例理论上不需要你去手动开调试端口。但也有个前提它启动的那个 Chromium 内核版本不能太旧否则某些 CDP 指令可能不被识别。尤其是 Chrome 更新到较新版本后旧版 Playwright 依赖的 CDP 方法被移除会导致功能异常。这类问题用 playwright-mcp 自带的版本检测命令能查出来或者直接升级到最新版。3.3 WebSocket 握手失败典型症状CDP 通信走的是 WebSocket。MCP 服务器连浏览器的过程本质上是先通过 HTTP 获取 ws 地址再发 WebSocket 握手。如果这一步失败MCP 服务器日志里会出现类似 WebSocket connection failed 或者 404 from remote debugger 的信息。我遇到过的三种情况浏览器版本太新默认对远程调试端口加了跨源限制。新版 Chrome 要求请求头里带上指定的 Origin有些老的 MCP 服务器没做兼容握手直接被拒。浏览器开启了 HTTPS-only 模式或者访问的调试地址是 IPv6MCP 服务器按 IPv4 的127.0.0.1去连结果连接被那头的 IPv6localhost解析搞懵了。这时候把地址换成明确的 IPv4 字面量通常能解决。防火墙/安全软件拦截了本地端口的 WebSocket 连接。看似玄学但在 Windows 上很常见杀毒软件会把 HTTP 调试端口的连接当威胁处理。注意如果 curl 能通、但 Codex 调工具仍然失败就抓一下 MCP 服务器日志。codex 的 MCP 服务器启动后输出一般在调试日志里能看到找到包含websocket、cdp、browser关键字的行基本能定位是哪一步断了。这个日志路径在不同版本不完全一样但用--debug或者-v启动 Codex 一定能拿到。4. 第三步排查鉴权、网络与自定义 Endpoint4.1 auth token unavailable 的背后codex auth token is unavailable这个报错如果你是在 Codex 终端里用codex login登录过但依然弹出那大概率跟会话上下文有关。Codex 的鉴权在后台会读取本地存储的 token如果当前工作区没有继承你的登录态或者你用的是 SSH 远程环境token 文件压根不存在自然就报这个错。在控不了浏览器这个场景里尤其常见的一个场景是你在本地桌面开了浏览器自动化但 Codex 运行在一个容器或远程开发环境里两边用户目录完全不同。浏览器 MCP 服务器里的 CDP 连接确实出去了但 Codex 运行时的鉴权信息没带过去。我没有看到你的具体报错位置但我建议这个环节按顺序确认在终端里重新执行codex login确认能正常登录关闭后重新打开一个 Codex 会话。确认环境变量里有没有设置过OPENAI_API_KEY这类变量如果设置的是一个已经失效的 key那 auth token 检查会被它干扰。如果用了代理环境变量比如给本地某个代理服务设置了HTTPS_PROXY尝试在测试时清空这些变量再看因为鉴权请求可能被代理服务拦截而失败。这里多说一句如果你配置的是自定义模型端点比如接入 third-party 兼容服务那么auth token unavailable可能不是 Codex 官方账号的 token而是你自己的 API key 格式没对上。有些兼容服务要求通过Authorization: Bearer头传递但 Codex 配置文件里对 key 字段的读取方式不一样容易出偏差。4.2 自定义 Endpoint 与本地代理的坑热搜词里那条 cc switch local proxy failed while handling codex endpoint /responses 描述的是一个很典型的场景Codex 在处理/responses端点的时候尝试走某个本地代理服务切换结果代理服务没起来或者端口变了请求直接失败。通俗说就是Codex 每次请求大模型生成时要通过一个配置好的 API 地址。如果你的配置里写的是一个本地代理地址比如http://127.0.0.1:8080而这个地址对应的服务当前没监听那么所有请求都会失败。浏览器自动化任务在这种状态下执行代码生不生成都是问题更别提控制浏览器了。排查很简单netstat -ano | findstr :8080看这个端口是否在监听。另外在 Codex 的配置文件里检查endpoint和proxy相关字段是否填写正确。尤其注意不要把全局系统代理和 Codex 自身的 endpoint 混为一谈。很多人在系统层面设置了代理然后下意识认为 Codex 也会走这个代理但实际上 Codex 有单独的配置项没有读取这套系统变量。排除这条线时一个非常有效的办法是在 Codex 配置里临时把模型换成官方默认端点把端点的 URL 指到一个互联网可达地址先跑一次纯粹的问答。如果问答能通说明网络链路没问题问题锁定在自动化工具的执行环节如果问答也不通那就是外层配置不对先修这个浏览器的事晚点再看。提示如果有人写到了 deepseek 接入这类的热搜词那通常意味着在 Codex 的配置里配了第三方兼容 endpoint。这类配置对响应格式要求极高一旦返回的结构和 Codex 预期不一致表现就是 Codex 看起来已经生成了任务但工具调用阶段毫无反应。你可以抓一下响应包的 JSON 结构重点看type字段是否包含function_call类型的输出没有的话说明模型根本没生成工具调用指令。4.3 沙箱与网络权限限制Codex 比较新版本里有沙箱机制。它的作用后面我会单独讲但这里你先理解一件事Codex 运行工具时子进程的网络访问可能被限制也可能被完全断开。这种限制在某些模式下是默认开启的MCP 服务器的 WebSocket 连接就会失败报错往往是网络不可达、EAI_AGAIN 或者 ECONNREFUSED。你可以在配置里关闭沙箱网络限制或者把当前目录加入可信目录列表。具体有哪些字段我这里不列了因为不同小版本的字段名有差异有些叫sandbox_workspace_write有些用workspace的权限控制。排查思路是如果你发现 Codex 离线控制浏览器的自主任务完全没反应而本地手动跑 MCP 服务器和浏览器又完全正常那八成是被沙箱拦住了。最直接的验证方式是把沙箱暂时调到最宽松再重试一次。如果在最宽松模式下能通逐级收紧权限找到具体被限制的能力。浏览器自动化必然涉及文件读写、网络 IO、子进程管理三大权限任何一个被沙箱卡住都会表现为工具存在但调用失败。这个排查思路比反复重启和重装靠谱得多。5. 实战排查清单与经验分享5.1 一套可复制的排查顺序我总结了排查Codex 控不了浏览器的标准顺序你可以照着执行。核心是从Codex 能不能感知到工具开始逐步往链路深处走步骤检查目标验证方式预期结果1MCP 配置被加载codex mcp list能看到浏览器 MCP 服务器2工具暴露给 Codex让 Codex 列出所有工具能看到 browser/playwright/chrome 关键词3MCP 服务器进程存活任务执行时另开终端看进程列表服务器进程持续运行而非闪退4浏览器调试端口开放curl http://127.0.0.1:9222/json/version返回 JSON5CDP WebSocket 联通查看 MCP 服务器日志无握手失败记录6Codex 网络链路通畅执行简单问答任务模型能正常回复7沙箱未拦截关闭沙箱后再试失败概率大幅降低8鉴权 token 有效codex login重新登录不再报 auth token unavailable每一步如果验证不通过就停在那里解决问题不要接着往下走。我见过太多人犯的错误是工具没暴露却在浏览器侧折腾了一晚上。5.2 我实际踩过的几个坑第一个坑是路径环境变量。我最初在 Windows 上把 MCP 服务器的命令写成npx playwright-mcplatestCodex 启动子进程时npx解析不到 npm 全局目录导致服务器从未真正起来。后来改成 Node 脚本的绝对路径配合--参数传参问题立刻消失。所以碰到了问题先检查 MCP 服务器能不能在普通终端里手动启动起来这是最低成本的分界点如果不能说明配置本身有问题如果能问题在后面环节。第二个坑是浏览器用户目录残留进程。我连续三次在--remote-debugging-port9222下启动失败curl 怎么都连不上。最后发现是有个隐藏的 Chrome 后台进程占用了那个user-data-dir。用--user-data-dirC:/temp/2025-11-17这种全新目录一测端口立刻通了。从那次以后我养成了一个习惯凡是调试浏览器自动化永远用全新的临时用户目录绝不复用。第三个坑最有迷惑性。Codex 说它有工具日志里也看到 MCP 服务器初始化成功了但每次调用都在 3 秒内失败。我一度怀疑是浏览器的问题后来发现是 Codex 在会话上下文里缓存了旧的工具定义。工具更新后当前会话还在用旧的 schema 去做函数调用参数对不上直接失败。解决方法很土开一个新会话。所以如果你改过 MCP 配置、升级过工具版本别留在老会话里反复纠结新开一个会话再试往往就好了。第四个坑跟 sandbox 有关。Codex 默认在某些模式下会阻止工具子进程访问网络。表现是浏览器能被 MCP 服务器启动但页面加载被挂起超时后 Codex 放弃调用。如果你一个月前同一个配置还能用现在突然不行了先想想是不是升级 Codex 之后默认启用了更强的沙箱策略。把沙箱关掉后控制浏览器的功能立刻恢复。最后再分享一个小技巧排查这种跨进程协作问题时日志要分层看。Codex 自己的日志、MCP 服务器 stdout、浏览器 CDP 输出其实是三层不同信息不要混在一起看。Codex 层的报错往往是笼统的tool call failedMCP 服务器层的日志才会告诉你 CDP 连接到哪一步断了浏览器侧则反映页面本身是否正常加载。按这三层日志去对应排查效率会高非常多。要是这两层日志你都拿到了但问题还是定位不了就在浏览器那边手动执行一次完整操作逐步和 Codex 的行为做对照总会找到一个分支上的偏差。