最近为了给 Claude Code 加上“能自己操作浏览器”的能力,我在 Windows 上折腾了 Playwright 的 MCP 服务。说好听点是配置,说直白点就是踩坑:光“MCP server 连不上”这一个问题,就让我翻日志翻到怀疑人生。前前后后花了两三个小时才把整条链路跑通,最后发现拦路的全是 Windows 环境里不起眼的细节。这篇就把我踩的三个坑写清楚:npx 怎么配、浏览器内核怎么装、stdio 和 HTTP 两种模式怎么选,以及最终跑通的最小配置长什么样。如果你也打算在 Windows 下让 Claude Code 接入 Playwright MCP,按这篇文章的顺序走,大概率能少折腾两个小时。
1. 先理顺思路:这套配置到底在做什么
Claude Code 是跑在终端里的 AI 编程助手,写代码、改文件、跑命令都很强。但默认情况下,它看不到浏览器里发生了什么,点不了按钮,提交不了表单。如果哪天你想让 AI 去访问一个页面、抓点数据、跑几轮端到端测试,光靠语言模型本身是不够的,它需要一个能驱动真实浏览器的“手”。Playwright MCP 就是这只手。
MCP 的全称是 Model Context Protocol,一套让 AI 助手连接外部工具的标准协议。你可以把 MCP 想象成 USB-C 接口:Claude Code 是电脑,Playwright 是显示器,MCP 就是那根标准化的数据线。只要大家都遵守同样的协议,插上就能用。Playwright MCP 服务器启动后,Claude Code 可以调用 browser_navigate、browser_snapshot、browser_click 这类工具,从而完成页面跳转、截图、点击、读取 DOM 等浏览器操作。
这套东西在 macOS 和 Linux 上配置相对顺滑,一条npx @playwright/mcp@latest往往就完事了。但在 Windows 上,同样的命令会因为环境差异、可执行文件命名规则、代码页等问题翻车。我这次的经历就是一个典型样本:不是协议多难,不是 Playwright 本身多难,而是三个非常具体的 Windows 细节拦在路中间。
1.1 Claude Code、Playwright、MCP 三者怎么配合
先画清楚调用链:你在 Claude Code 会话里给 AI 提需求,AI 决定需要操作浏览器,于是通过 MCP 协议向 Playwright MCP server 发起工具调用请求。MCP server 接收到请求后,用 Playwright 库去驱动真实的 Chromium 内核,执行页面跳转、点击、截图等动作,再把结果返回给 Claude Code。
所以整条链路由四块组成:Claude Code(客户端)、MCP 协议(通信标准)、@playwright/mcp(MCP server 实现)、Playwright 浏览器内核(真正干活的浏览器实例)。任何一个环节断了,AI 那边的表现都是“工具调用失败”或“无法连接 MCP”。
我在 Windows 上踩的三个坑,刚好对应这三个非 Claude Code 的环节:第一个坑是 MCP server 根本没启动起来,第二个坑是 MCP server 起来了但浏览器内核找不到,第三个坑是 MCP server 和浏览器内核都正常,但 Claude Code 这边的传输方式对不上。搞清楚这个分层逻辑,后面排查问题会快很多。
1.2 Windows 下容易出问题的三层原因
Windows 环境的问题集中在三层:环境层、依赖层、配置层。
环境层指的是 Node.js、npx、PATH 这些基础环境。Windows 下 Node 相关的命令不是以 .exe 文件存在的,而是 .cmd 脚本,这会导致一些不经过 shell 直接 spawn 进程的程序找不到命令。这是坑一的来源。
依赖层指的是 Playwright 浏览器内核。@playwright/mcp 这个包本身不捆绑浏览器,需要额外执行npx playwright install chromium下载。Windows 下下载容易超时,而且如果机器上同时装了 Python 版 Playwright 之类的东西,浏览器缓存的版本还可能互相干扰。这是坑二的来源。
配置层指的是 Claude Code 里 MCP server 的注册方式。Claude Code 支持 stdio 和 HTTP 两种传输模式,@playwright/mcp 也支持这两种模式,但很多人不知道默认是 stdio,还硬要加--port参数,结果两边模式不匹配。Windows 中文系统还自带一个 GBK 编码坑,会让 stdio 管道里的中文消息变成乱码。这是坑三的来源。
把这三层分开看待,就很容易理解为什么同样的配置在别人电脑上跑得好好的,在你 Windows 上就各种报错。接下来按我踩坑的顺序逐个拆解。
2. 坑一:npx 找不到,MCP server 启动不了
这是第一个拦路虎,症状非常典型:我按照网上最常见的命令执行:
claude mcp add playwright -- npx @playwright/mcp@latest终端提示添加成功,当时还挺高兴。结果打开 Claude Code 会话,输入/mcp查看状态,playwright 这一行赫然显示着 disconnected 或者 failed。再打开调试日志,里面能看到类似这样的报错:
spawn npx ENOENT Fatal error: ENOENT: no such file or directory, spawn 'npx' ENOENT看到 ENOENT 的第一反应是检查 Node 装了没有。但我在 cmd 里敲npx --version明明能正常输出版本号。这就让人很困惑:命令行能跑,凭什么 Claude Code 就跑不了?
2.1 症状与第一反应
这里容易犯的错误就是反复重装 Node、反复改 PATH,甚至怀疑是 Claude Code 版本问题。我也一度以为是不是自己装了多个 Node 版本,导致 PATH 里指向了不存在的路径。检查了半天,where npx也能定位到C:\Program Files\nodejs\npx.cmd,路径完全没问题。
真正的问题不在 PATH,而在可执行文件的类型。
Windows 下的 npm 和 npx 实际是npm.cmd和npx.cmd,不是npx.exe。Claude Code 的 MCP 客户端在启动子进程时,底层用的是 Node.js 的child_process.spawn。spawn 在 Windows 上不会像 cmd 那样自动去解析.cmd和.bat文件的执行逻辑,它只会去找真正可执行的.exe文件,或者调用cmd.exe /c来包装一层。如果你给 spawn 传了npx,它期望找到的是npx.exe,但系统里只有npx.cmd,于是直接 ENOENT。
这个坑在英文社区的解决方案里几乎不会提到,因为 macOS 和 Linux 上npx就是一个真正的二进制文件,没有这种区别。Windows 用户照抄文档,第一脚就踩进去。
2.2 为什么会这样:Windows 的可执行文件与 spawn
稍微展开一点讲原理。Windows 操作系统中,可执行文件的后缀名有严格的约定:.exe才是真正能被 CreateProcess 直接拉起程序,.cmd和.bat本质上是脚本,需要由 cmd.exe 解释执行。当你自己在终端里输入npx时,终端(cmd 或 PowerShell)会先找到npx.cmd,然后用 cmd 的规则去执行它,所以一切正常。
但 Claude Code 在启动 MCP server 的时候,不会走终端那套交互 shell 的逻辑,而是直接调用系统 API 去创建进程。它拿到npx这个名字,去 PATH 里找,结果只找到了npx.cmd,不符合直接执行的条件,就返回 ENOENT 了。
想验证这个结论很简单:在 cmd 里直接运行where npx,你大概率会看到一条路径,指向的正是.cmd结尾的文件。这说明系统认得它,但 spawn 不认。
2.3 解决方案:npx.cmd 与 PATH 双保险
解决办法有两个,我建议两个都做。
第一个办法,也是最直接的:添加 MCP server 的时候,把npx改成npx.cmd。
claude mcp add playwright -- npx.cmd @playwright/mcp@latest这个写法等于明确告诉 spawn:去执行这个.cmd文件。实际测试下来,npx.cmd是可以被直接 spawn 的,Claude Code 收集了命令就会正常运行。
第二个办法,是手动编辑配置文件,把命令指定成绝对路径,更稳妥。Claude Code 的 MCP 配置可以写在项目级文件(比如.mcp.json)或者用户级配置里。手动编辑时,Windows 路径里的反斜杠记得转义:
{ "mcpServers": { "playwright": { "command": "C:\\Program Files\\nodejs\\npx.cmd", "args": ["@playwright/mcp@latest"] } } }如果你安装 Node 时改了路径,先执行where npx拿到实际路径,再填进去。如果不想写绝对路径,至少确保系统 PATH 里包含 Node.js 的安装目录,并且使用npx.cmd这个写法。
另外提一个很容易被忽略的点:改完 PATH 之后,已经打开的终端窗口不会自动生效,必须新开一个终端再启动 Claude Code。我有一次改完 PATH 忘了重启终端,又在原地折腾了十分钟,属于纯笨。
3. 坑二:浏览器内核装不上,AI 有工具也用不了
第一个坑解决之后,MCP server 总算从 failed 变成了 connected。我当时以为大功告成,马上给 Claude 下指令:“打开 example.com,看看页面上有什么”。结果它调用了 browser_navigate 工具,很快就返回了一段刺眼的报错:
browserType.launch: Executable doesn't exist at C:\Users\...\ms-playwright\chromium-XXXX\chrome-win\chrome.exe Please run "npx playwright install chromium"看到这个我第一反应是:我不是已经装了 Playwright 吗?怎么还要装?后来才意识到,这里说的“安装 Playwright”和“安装浏览器内核”是两码事。
3.1 现象:所有浏览器操作都报错
从现象上看,只要 AI 尝试做任何和浏览器有关的动作,结果都是同一个错误:找不到可执行文件。这不是因为 Playwright 库没装,而是因为 Playwright 库和浏览器内核是分离的。库负责写自动化逻辑,内核才是真正渲染页面、执行 JavaScript 的那个浏览器进程。
npm 包 @playwright/mcp 默认在启动时会尝试找你本机上对应版本的 Chromium。如果没有,它就罢工。这是设计上的选择,不是 bug——浏览器内核体积不小,如果每个依赖 Playwright 的包都自动下载一份,磁盘很快就炸了。
我当时电脑上其实装过 Python 版 Playwright,也跑过playwright install,但 Node 生态的 Playwright 浏览器缓存索引和 Python 版的不完全互通,尤其是版本号对不上的时候,Node 这边依然会认为“找不到合适的浏览器”。
3.2 深层原因:MCP 包不内置浏览器
更准确地说,每个通过 npm 安装的 Playwright 相关包,都对应一个具体的浏览器版本。@playwright/mcp 在安装时会继承当前环境里 Playwright 库的版本,并期望浏览器缓存目录里存在那个版本的内核。你用 Python 的 Playwright 装的内核版本可能不同,缓存在同一个%USERPROFILE%\AppData\Local\ms-playwright目录下,但目录名是按版本号区分的。Node 这边要找 chromium-XXXX,Python 那边可能装的是 chromium-YYYY,两者互不认账。
所以解决方式不是“我装过 Playwright 了啊”,而是“在 Node 环境里,把当前版本对应的内核安装一遍”。
在 Windows 下,我建议先确保当前工作目录的 PATH 和网络环境都正常,然后执行:
npx playwright install chromium命令执行完会去微软和 Google 等 CDN 下载几十到上百 MB 的内核文件。如果网络状况一般,很容易下载到一半卡死,特别是首次安装时。遇到这种问题,可以配置国内的 npm 镜像同步源作为 Playwright 的下载地址。
3.3 两种解法:手动安装内核 / 直接调用系统 Chrome
解法一就是手动安装内核,上面已经写了。怕下载慢的话,把环境变量指到 npmmirror:
CMD 下执行:
set PLAYWRIGHT_DOWNLOAD_HOST=https://cdn.npmmirror.com/binaries/playwright npx playwright install chromiumPowerShell 下执行:
$env:PLAYWRIGHT_DOWNLOAD_HOST = "https://cdn.npmmirror.com/binaries/playwright" npx playwright install chromium下载完成后,建议重启一下 Claude Code,让 MCP server 重新启动,再去测试浏览器操作。
解法二更省事:如果你的 Windows 系统装了 Chrome、Edge 或 Chromium 系浏览器,可以让 @playwright/mcp 直接使用系统浏览器,省掉下载内核那一步。Edge 在 Windows 上基本是预装的,用起来很稳。
claude mcp add playwright -- npx.cmd @playwright/mcp@latest --browser chrome--browser chrome会让 Playwright 去寻找本机的 Chrome,不再要求 ms-playwright 缓存目录里有对应版本的 Chromium。如果你系统里只有 Edge,可以把参数写成--browser msedge。我用 Edge 试过,速度和稳定性都很不错,毕竟 Edge 和 Chromium 同源。
注意:
--browser chrome参数必须加在 MCP server 的 args 里。如果你用的是之前的配置文件,记得把 args 改成类似["@playwright/mcp@latest", "--browser", "chrome"]。
两个解法选一个就行。我自己的建议是:如果电脑上有 Edge 或 Chrome,直接走解法二,最快;如果没有,就走解法一,反正装一次后面都能复用。
4. 坑三:传输模式与端口问题,配置对了还是连不上
前两个坑解决之后,MCP server 显示已连接,浏览器内核也有了,但诡异的事情出现了:AI 调用某些工具的时候正常,另一些工具却一直转圈,到最后直接超时。更迷惑的是,有一次我在 MCP server 的启动参数里加了--port 8931,结果 Claude Code 彻底连不上了。这时候我才意识到,传输模式这个坑比前面两个更隐蔽。
4.1 三种“连接不上”的真实场景
先说三种我当时遇到的真实场景。
场景一:Claude Code 显示 connected,但 AI 一调用工具就超时,日志里没有明显报错。这种情况通常是因为 MCP server 进程起来了,但两边对消息的处理方式不一致,或者消息卡在管道里没被正确解析。
场景二:我在手动测试npx @playwright/mcp@latest的时候,为了让其他程序也能访问,加了--port 8931,然后还把这个参数写进了 Claude Code 的配置。结果 Claude Code 以 stdio 模式去连接一个启动在 HTTP 模式下的 server,两边完全对不上,自然连不上。
场景三:换成 HTTP/SSE 模式之后,端口被其他程序占用了,MCP server 启动失败,Claude Code 持续显示红色摇头状态。
这三类问题的根源都指向一件事:Claude Code 和 @playwright/mcp 之间到底用什么模式通信,必须明确。
4.2 stdio 与 HTTP:别把两种模式混着配
@playwright/mcp 默认是 stdio 模式:MCP server 作为一个子进程被 Claude Code 拉起,双方通过标准输入输出流一条一条交换 JSON-RPC 消息。这个模式不需要端口,不占网络资源,也不存在局域网暴露的问题,是本机使用最合理的选择。
如果你手动执行 @playwright/mcp 命令行工具,什么都不加,它就是用 stdio 模式。Claude Code 的claude mcp add默认添加的也是 stdio server。
只有当你指定--port参数时,@playwright/mcp 才会切换成 HTTP 模式,监听一个本地端口,等着客户端通过 HTTP 请求来连。此时 Claude Code 那边应该用--transport http加 URL 的方式注册,而不是默认的 stdio 方式。
手动编辑配置时,stdio 模式对应的配置是:
{ "mcpServers": { "playwright": { "command": "npx.cmd", "args": ["@playwright/mcp@latest"] } } }如果你偏要用 HTTP 模式,那配置应该长这样(不同版本字段名会有细微差异):
{ "mcpServers": { "playwright": { "type": "http", "url": "http://127.0.0.1:8931/mcp" } } }这里最容易犯的错就是混着配:command 和 args 都写了,还加了一个type: http,或者后来把 URL 也写上。结果 Claude Code 内部就晕了,到底该走子进程还是该访问 URL,状态一直表现为 failed 或超时。我在这一步反复删了加、加了删,最后才反应过来,文档里写的是“二选一”,不是“都写上”。
提示:在本机日常使用,强烈建议用 stdio 模式,也就是不要加
--port,保持默认。HTTP 模式适合 MCP server 独立部署、或者局域网远程连接这种真正的服务化场景。另外,如果你因为某种原因开了 HTTP 模式,务必确认端口只绑定 127.0.0.1,不要绑 0.0.0.0。MCP server 可以控制浏览器,等于一个没有鉴权的远程控制接口,暴露出去非常危险。
4.3 端口占用排查与 UTF-8 编码保护
如果你确实要用 HTTP 模式,端口 8931 是我随便举例用的,实际选个没被占用的端口就行。Windows 下查端口占用很简单:
netstat -ano | findstr 8931如果结果里有 LISTENING 状态且不是你预期的进程,用:
tasklist | findstr PID号看是哪个程序占着,然后去任务管理器结束它,或者换个端口重新启动。
还有一种 Windows 特有的坑需要单独说:编码。这个坑在中文系统上特别容易出现,尤其是当你的 Windows 系统语言是中文,默认代码页是 936(GBK),而 MCP 协议本身要求 UTF-8 编码的 JSON-RPC 消息。如果 Claude Code 和 @playwright/mcp 之间传的中文消息以 GBK 字节流出,接收方按 UTF-8 解析,轻则日志乱码,重则直接导致协议层消息解析失败。症状就是:MCP server 明明起来了,但 Claude Code 那边始终收不到有效响应,工具调用一直卡住。
有效的处理办法有两种。一是在启动 Claude Code 之前的终端里执行chcp 65001,把代码页切到 UTF-8,再启动claude。二是在 Windows 设置里勾选“使用 Unicode UTF-8 提供全球语言支持”,一劳永逸,不过需要重启。对于大部分只想快速跑通的人来说,推荐直接用chcp 65001,副作用最小。
5. 一次跑通的完整配置流程(Windows 11 实测)
三个坑都说完了,但光知道坑不够,还得给一套能直接照抄的流程。下面这套配置是我在 Windows 11 上实际跑通的,目标是让 Claude Code 能通过 Playwright MCP 打开浏览器、访问页面、截图、读取页面内容。整个过程大约十分钟。
5.1 环境准备清单
开始之前,确认三件事:
- Node.js 版本不低于 18,推荐 20 LTS。命令行检查:
node -v。 - Claude Code 已安装并能正常启动。命令行检查:
claude --version。 - 磁盘剩余空间不少于 2GB,浏览器内核要占几百 MB。
如果你电脑上有 Chrome 或 Edge,流程会少一步。没有的话也不碍事,后面装内核就行。
5.2 添加 MCP 并验证连接
第一种情况:用系统 Chrome。
claude mcp add playwright -- npx.cmd @playwright/mcp@latest --browser chrome第二种情况:没有 Chrome,想用 Playwright 自带的 Chromium 内核,先装内核,再添加 MCP。
npx playwright install chromium claude mcp add playwright -- npx.cmd @playwright/mcp@latest添加完,执行:
claude mcp list看到 playwright 那一行的状态不是 failed,说明注册这一步过了。如果显示 disconnected,回看第 2 章的内容,检查是不是又写成npx没写npx.cmd。
然后启动 Claude Code 会话,输入:
/mcp正常情况下,playwright 会出现在已连接列表里。如果这里显示没有连接,先用claude mcp remove playwright删掉,再重新添加,别直接在配置里手动改,很容易改乱。
5.3 用一个真实任务测试端到端链路
命令行验证通过后,在 Claude Code 里提一个简单任务:
“使用 playwright 打开 https://example.com,截取页面截图保存到当前目录,并告诉我页面标题。”
我实测的时候,Claude 会先调用 browser_navigate 打开页面,再调用 browser_snapshot 获取页面结构,最后调用 browser_screenshot 保存图片。如果你的配置正确,这个过程通常不会超过 30 秒。如果它说“没有找到截图工具”,怀疑是传输模式配置错了,回到第 4 章检查。
任务完成后,当前目录下会多一个 PNG 图片文件,打开确认内容是不是 example.com 的页面。页面上那一行经典的 “Example Domain” 能被 AI 读取出来,就说明整条链路已经通了。
注意:我推荐用 example.com 这种完全中立的测试站点,避免访问不稳定的外部服务导致误判。先把链路跑通,再让它去操作你真正要测的网站。
5.4 常见失败时的回滚技巧
如果中途改坏了配置,最稳妥的做法不是手动编辑 JSON,而是用 Claude Code 自己的命令删掉重加:
claude mcp remove playwright claude mcp add playwright -- npx.cmd @playwright/mcp@latest删除之前,可以在claude mcp list里看一眼当前的配置详情。如果连 list 都出不来,多半是 JSON 语法坏了,去项目目录或用户目录找.mcp.json和~/.claude.json,备份一份之后,把关于 playwright 的段落删掉,再重新添加。
6. 常见问题速查与经验总结
这段时间过来,我把 Windows 上配置 Playwright MCP 的问题整理成一个速查表。以后再遇到类似情况,直接按表排查,速度会快很多。
6.1 问题速查表
| 症状 | 可能原因 | 解决方法 |
|---|---|---|
| spawn npx ENOENT,MCP 一直 disconnected | Windows 下 npx 是 .cmd,spawn 找不到 | 把 npx 改成 npx.cmd,或用 where npx 取绝对路径 |
| 浏览器操作报 Executable doesn't exist | @playwright/mcp 默认不含浏览器内核 | 执行 npx playwright install chromium,或用 --browser chrome/msedge 调用系统浏览器 |
| 下载 chromium 卡住或失败 | 网络访问 CDN 不稳定 | 设置 PLAYWRIGHT_DOWNLOAD_HOST 为国内镜像源,重新执行 install |
| MCP server 已连接,但工具调用一直超时 | stdio 模式与 HTTP 模式混配,或编码问题 | 去掉 --port 参数,保持 stdio;在终端执行 chcp 65001 切到 UTF-8 |
| 端口启动失败,提示地址被占用 | HTTP 模式下端口被其他程序占用 | 用 netstat -ano 查找占用进程,结束进程或换端口 |
| 中文日志乱码,协议消息异常 | 中文 Windows 默认 GBK 代码页 | chcp 65001,或在系统设置里开启 UTF-8 Beta 选项 |
| 修改配置后仍然失败 | 手动改 JSON 导致语法或结构错误 | 用 claude mcp remove 删除后重新添加,不要手硬改 |
6.2 个人使用体会与建议
Windows 下做浏览器自动化这件事,这几年其实已经很成熟了,Playwright 官方对 Windows 的支持本来就不差。真正让人头疼的往往不是框架本身,而是环境细节。比如 .cmd 文件、代码页、PATH 不生效、端口占用,它们单个看起来都不难,叠在一起就特别容易让人崩溃。
我自己跑通之后最大的体会是:不要拿着一份 macOS 的教程在 Windows 上无脑复制。遇到报错先看 Clade Code 的 debug 日志,MCP server 这层出了问题,日志里多半有明确线索。其次,能用命令行完成的操作不要手动编辑 JSON,降低出错概率。最后,先把简单场景跑通,再叠加复杂度,不要一上来就让 AI 操作一个登录后才能访问的站点,那样即使失败了,你也分不清是 MCP 的问题还是目标网站的问题。
如果后续你想玩更深一点,我建议试试这几个方向:用 screenshot 做视觉回归对比,用 browser_snapshot 让 AI 理解动态页面的结构,或者把 @playwright/mcp 跑在固定端口上,让多个 Claude Code 会话共享同一个浏览器控制服务。每个方向都有各自的坑,但基于现在这条已经跑通的链路,探索起来会轻松很多。