一、背景
在 Linux 环境下使用 Codex 进行项目开发时,经常需要通过浏览器完成前端页面验证、UI 交互测试、页面截图等任务。
如果 Linux 服务器没有图形界面,直接部署 Chrome 会增加环境维护成本。
一种更轻量的方案是:
-
Linux:运行 Codex,作为 MCP Client。
-
Windows:运行 Playwright MCP Server。
-
Chrome:安装在 Windows 上,由 Playwright MCP 控制。
-
网络:Linux 通过局域网访问 Windows 的 MCP HTTP 服务。
架构如下:
bash
Linux
└── Codex
└── MCP Client
│
│ HTTP / MCP
▼
Windows
└── Playwright MCP Server
└── Google Chrome
本次部署过程中,先后遇到了 PowerShell 脚本执行限制、Windows 防火墙权限问题,以及最关键的 MCP 403 Forbidden 和 Codex failed (0 tools) 问题。
下面记录完整的排查与解决过程。
二、环境说明
| 项目 | 环境 |
|---|---|
| MCP Client | Linux + Codex |
| MCP Server | Windows |
| 浏览器 | Google Chrome |
| MCP 实现 | @playwright/mcp |
| 传输方式 | Streamable HTTP |
| MCP 端口 | 8931 |
| Windows IP | 192.0.2.10(示例) |
| Linux IP | 192.0.2.20(示例) |
实际部署时将示例 IP 替换为真实局域网 IP。
三、Windows 安装并启动 Playwright MCP
3.1 安装 Node.js
首先在 Windows 安装 Node.js,并确认 npm 和 npx 可用:
bash
node -v
npm -v
npx.cmd -v
Playwright MCP 官方项目:
3.2 PowerShell 禁止执行 npx.ps1
直接执行:
bash
npx -y @playwright/mcp@latest
出现错误:
bash
无法加载文件 C:\Program Files\nodejs\npx.ps1,
因为在此系统上禁止运行脚本。
PSSecurityException
FullyQualifiedErrorId : UnauthorizedAccess
原因分析
PowerShell 的 Execution Policy 禁止运行 .ps1 脚本,而 npx 命令匹配到了 npx.ps1。
这并不是 Node.js 安装失败。
解决方案
直接调用 npx.cmd:
bash
npx.cmd -y @playwright/mcp@latest
该方式不需要修改 PowerShell 执行策略。
不建议为了运行 npx 而直接关闭系统级脚本执行限制。
3.3 启动 Playwright MCP HTTP 服务
bash
npx.cmd -y @playwright/mcp@latest `
--browser chrome `
--host 0.0.0.0 `
--port 8931
参数说明:
| 参数 | 作用 |
|---|---|
| --browser chrome | 使用 Google Chrome |
| --host 0.0.0.0 | 监听所有网络接口 |
| --port 8931 | MCP HTTP 服务端口 |
| --allowed-hosts | 限制允许访问服务的 HTTP Host |
注意:--host 和 --allowed-hosts 是两个不同的概念。
前者决定服务监听在哪个网络地址上,后者用于 HTTP Host 校验。
四、Windows 防火墙配置
4.1 检查网络类型
执行:
bash
Get-NetConnectionProfile
发现:
bash
NetworkCategory : Public
Windows 当前使用 Public 网络配置文件。
如果防火墙规则只指定 Private,则不会匹配当前 Public 网络。
4.2 添加防火墙规则时拒绝访问
执行:
bash
New-NetFirewallRule `
-DisplayName "Playwright MCP" `
-Direction Inbound `
-Protocol TCP `
-LocalPort 8931 `
-RemoteAddress 192.0.2.20 `
-Action Allow `
-Profile Public
出现:
bash
New-NetFirewallRule : 拒绝访问
原因
当前 PowerShell 没有管理员权限。
解决方案
以管理员身份启动 PowerShell:
bash
Start-Process powershell -Verb RunAs
然后重新执行防火墙规则创建命令。
这里使用 -RemoteAddress 限制来源 IP,而不是向整个局域网开放 MCP 服务。
4.3 验证端口连通性
在 Linux 执行:
bash
nc -vz 192.0.2.10 8931
返回:
bash
Connection to 192.0.2.10 8931 port [tcp/*] succeeded!
说明 TCP 连接已经成功。
但需要注意:
TCP 端口可连接,不代表 MCP 协议初始化成功。
五、Codex 显示 failed (0 tools)
在 Linux 的 Codex 配置文件:
bash
~/.codex/config.toml
添加:
bash
[mcp_servers.playwright]
url = "http://192.0.2.10:8931/mcp"
startup_timeout_sec = 60
tool_timeout_sec = 120
启动 Codex 后发现:
bash
playwright: failed (0 tools)
说明 Codex 没有成功发现 Playwright MCP 提供的工具。
由于前面的 nc 已经成功,问题不一定出在网络连通性上,需要进一步检查 HTTP 层。
六、定位 403 Forbidden
6.1 使用 curl 测试 MCP 地址
Linux 执行:
bash
curl -i --max-time 10 \
http://192.0.2.10:8931/mcp
返回:
bash
HTTP/1.1 403 Forbidden
Content-Length: 63
Access is only allowed at 192.0.2.10, localhost, 127.0.0.1
这个响应非常关键。
它说明:
-
TCP 连接正常。
-
HTTP 请求已经到达 MCP 服务。
-
服务端拒绝了请求。
-
拒绝原因与 HTTP Host 白名单有关。
此时继续修改 Windows 防火墙已经没有意义。
6.2 分析 HTTP Host
请求地址:
bash
http://192.0.2.10:8931/mcp
实际 HTTP Host 通常为:
bash
Host: 192.0.2.10:8931
而服务端允许的 Host 为:
bash
192.0.2.10
localhost
127.0.0.1
由此怀疑:当前 MCP 服务的 Host 校验未能正确匹配带端口的 Host。
6.3 手动修改 Host 验证
Linux 执行:
bash
curl -i --noproxy '*' \
-H 'Host: 192.0.2.10' \
http://192.0.2.10:8931/mcp
返回:
bash
HTTP/1.1 400 Bad Request
Content-Length: 15
Invalid request
这是整个排障过程中最关键的验证。
对比两次请求:
| 请求 | HTTP 状态 |
|---|---|
| 默认 Host,包含端口 | 403 Forbidden |
| 手动指定不带端口的 Host | 400 Bad Request |
两次请求访问同一个 IP、同一个端口、同一个路径,仅修改了 HTTP Host。
结果从 403 变为 400,说明请求通过了此前的 Host 校验,并进入后续请求处理流程。
因此可以确认:之前的 403 与 Host 白名单匹配有关。
但普通 GET 返回 400 并不代表 MCP 初始化成功,还需要使用符合 MCP 协议的请求验证。
七、解决方案
7.1 调整 allowed-hosts
首先尝试在 Windows 上将包含端口的地址加入白名单:
bash
npx.cmd -y @playwright/mcp@latest `
--browser chrome `
--host 0.0.0.0 `
--port 8931 `
--allowed-hosts "192.0.2.10:8931,192.0.2.10,localhost,127.0.0.1"
需要注意,不同 Playwright MCP 版本可能对带端口的 Host 有不同处理行为,必须通过实际测试确认。
重新执行:
bash
curl -i --noproxy '*' \
http://192.0.2.10:8931/mcp
如果不再出现 Host 校验导致的 403,说明问题已得到进一步解决。
7.2 验证 MCP 初始化
使用 JSON-RPC initialize 请求:
bash
curl -i --noproxy '*' \
--max-time 15 \
-X POST \
http://192.0.2.10:8931/mcp \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2025-03-26",
"capabilities": {},
"clientInfo": {
"name": "curl-test",
"version": "1.0"
}
}
}'
正常情况下,服务端应返回 MCP 初始化结果,其中包含协议版本、服务端能力以及服务端信息。
7.3 重新验证 Codex
重新启动 Codex:
bash
codex
在交互界面执行:
bash
/mcp
确认 Playwright MCP 状态正常,并且能够发现浏览器操作工具。
可以通过以下任务验证:
bash
使用 Playwright MCP 打开 https://example.com,
获取页面结构并截图。
如果 Windows Chrome 正常打开页面,且 Codex 能读取页面内容,则说明端到端调用成功。
本次实际问题最终已解决,但没有保留最终生效配置的完整输出。因此本文能够确认的是 Host 校验故障及其定位过程,不将某个未单独验证的配置变更描述为唯一有效修复。
八、更安全的部署方式:SSH 隧道
对于长期运行的环境,不建议直接向整个局域网开放具有浏览器控制能力的 MCP 服务。
更推荐:
bash
Linux Codex
|
| localhost:8931
|
SSH Tunnel
|
| Windows localhost:8931
|
Playwright MCP
|
Google Chrome
Windows 只监听本机:
bash
npx.cmd -y @playwright/mcp@latest `
--browser chrome `
--host 127.0.0.1 `
--port 8931
Linux 建立 SSH 隧道:
bash
ssh -N \
-L 8931:127.0.0.1:8931 \
windowsuser@192.0.2.10
前提是 Windows 已启用 OpenSSH Server,并允许 Linux 通过 SSH 访问。
Codex 配置:
bash
[mcp_servers.playwright]
url = "http://127.0.0.1:8931/mcp"
startup_timeout_sec = 60
tool_timeout_sec = 120
这种方式的优势:
-
不需要向局域网开放 MCP HTTP 端口。
-
通信通过 SSH 加密。
-
MCP 可以只监听 Windows 本地回环地址。
-
降低其他局域网设备直接调用浏览器工具的风险。
九、故障排查总结
| 故障现象 | 原因或排查方向 | 解决方法 |
|---|---|---|
| npx.ps1 禁止执行 | PowerShell Execution Policy | 使用 npx.cmd |
| New-NetFirewallRule 拒绝访问 | 管理员权限不足 | 提升 PowerShell 权限 |
| Linux 无法连接 8931 | 监听地址、防火墙或网络问题 | 检查监听和入站规则 |
| nc 成功但 Codex 0 tools | MCP HTTP 或协议初始化失败 | 检查 HTTP 响应 |
| HTTP 403 Forbidden | Host 白名单校验拒绝 | 检查 allowed-hosts |
| 修改 Host 后返回 400 | 已通过此前的 Host 校验 | 继续测试 MCP initialize |
| MCP 初始化成功但工具不可用 | 客户端配置或工具发现问题 | 检查 Codex 配置和日志 |
十、总结
本次问题的核心并不是 Windows 防火墙,也不是 Linux 无法连接 Windows。
实际排查链路为:
bash
PowerShell 执行策略
↓
npx.cmd 启动 MCP
↓
Windows 防火墙放通
↓
Linux nc 连接成功
↓
Codex failed (0 tools)
↓
curl 返回 403 Forbidden
↓
发现 HTTP Host 白名单校验问题
↓
修改 Host 后返回 400
↓
调整 MCP 访问配置
↓
Codex 与 Playwright MCP 恢复正常
这次故障最值得总结的一点是:
MCP 的网络可达性与 MCP 协议可用性是两个不同层次的问题。
nc 只能证明 TCP 连接成功,无法证明 MCP 初始化、工具发现以及工具调用正常。
遇到 failed (0 tools) 时,应按以下顺序排查:
-
TCP 端口是否可达。
-
HTTP 请求是否被服务端接受。
-
MCP
initialize是否成功。 -
MCP
tools/list是否返回工具。 -
Codex 是否成功加载并调用工具。
通过分层验证,可以避免反复修改防火墙、重装 Node.js 或盲目调整 Codex 配置。