- 人工智能
- AI Agent
- 浏览器控制
- GUI 自动化
- MCP 服务
【免费下载链接】invisible_playwright_mcp
Playwright MCP server undetected by anti-bots and captchas: AI agent browses the web on anti-detect stealth Firefox, Python, undetected browser automation, scraping, computer use.
导读:本文围绕 Playwright MCP 常见的 "Browser is already in use" 报错,讲清它的本质——这是浏览器配置目录(profile directory)上的锁,而不是「有另一个浏览器正在运行」的陈述;随后给出四种典型触发路径与对应修复手段(--isolated、独立--user-data-dir、重启服务、删除锁前的存活检查)。最后结合当前开源仓库 invisible_playwright_mcp 的源码,说明它的「一进程只服务一个身份」设计如何从根本上绕开同类的池内冲突,以及为什么持久化配置目录这一层它依然遵守浏览器的单实例约束。
一、先读懂报错:这是锁,不是「浏览器在运行」
当你在 MCP 客户端里看到类似下面的信息时:
Browser is already in use for /path/to/mcp-chrome, use --isolated to run multiple instances of the same browser它宣告的对象是配置文件目录上的锁(lock),而不是某个正在运行的浏览器进程。持久化配置目录在同一时刻只能被一个浏览器实例打开,而 Playwright MCP 默认使用一个固定配置目录。任何「取走了锁却没有归还」的东西——哪怕该进程已经不存在——都会让这个报错继续出现。
Microsoft 官方文档直接陈述了这一约束:一个持久化配置只能同时被一个浏览器实例使用,因此共享同一工作区(workspace)的并发 MCP 客户端必然互相冲突。理解这一点是后续所有排查的前提:进程列表为空、页面关闭、甚至整个客户端退出,都不能保证锁被释放。
二、走到这个报错的四种途径
按发生频率与迷惑程度排序:
第二个客户端。同一个 server 被注册在两处——比如编辑器里一个、终端助手一个,且两者都活着——两个进程都要同一个配置目录。这是该报错本来要表达的场景,在编辑器环境里最常见;GitHub Copilot 中的浏览器 MCP server 一文解释了为什么编辑器场景特别容易踩中。
上一次运行是被杀死的。会话不是以「干净关闭」结束,锁文件的生命周期长于进程。此时没有任何东西在运行,报错照样出现。这是最常见的、也是最令人困惑的一种:你的进程列表是空的,报错却还在。
关闭操作没有重置服务端自身状态。上游 server 被反复报告的缺陷:
browser_close之后,内部「in use」标志并不总是被清除,于是即使浏览器已经没了,下一次 navigate 仍会报同样的错。重启 MCP server 可以清掉它。一次安装半启动了浏览器。
browser_install可能留下一个「占用着配置目录却不可用」的实例,导致服务端对状态的认知与真实状态不一致。
三、四种修复:按你的目标选择
3.1 想要并发客户端:用--isolated
配置目录改为会话内存态、不共享任何东西,两个客户端就不再争抢。代价是每次启动都是未登录状态——对探索型任务来说,这通常恰好是你想要的。--isolated是 server 侧的启动参数,需要在注册 MCP 命令时传入。
3.2 想保留持久化:给每个客户端各自的配置目录
这是 Playwright MCP best practices 中「值得主动做出的四个决定」之一:
--user-data-dir <path>为每个客户端传不同的路径。你保留持久化,同时消除碰撞。注意:这是 server 的命令行选项,如果你的客户端只允许注册一个不带参数的命令,那么在不能手工编辑配置块的情况下,你可能根本够不到这个选项。
3.3 没有进程在跑:重启 server
在编辑器或聊天客户端里,重启意味着重载 MCP 连接,而不是只关掉标签页。这一动作清除上文中第三种情况的内存态标志;如果真正重启后报错仍在,说明锁在磁盘上——接下来要检查的正是配置目录本身。
3.4 删除任何东西之前:先确认浏览器真的还活着
陈旧锁与「活着的第二个客户端」在报错文本上完全一样,但需要完全相反的修复。删除一个正在被运行的浏览器持有的锁,会损坏配置目录。稳妥的检查方式是:查看进程列表确认对应浏览器进程的存活状态,而不是直接删除锁文件。
四、为什么在 invisible_playwright_mcp 这里不会以同样方式发生
当前仓库 invisible_playwright_mcp 的服务端走了完全相反的设计路线,这段历史值得展开,因为它正是同一个取舍从两端各看一次:
- 直到 0.39.0,一个会话可以容纳最多八个浏览器,名字由你随意发明;
- 直到 0.41.0,每个工具还携带一个会话标识符,单个进程在背后同时调度多个会话;
- 现在两者都已不存在:一个 server 在其整个生命周期内只服务一个身份,旁边只有一个不共享任何东西的辅助浏览器,
browser_open只在这两者之间选择,而不是往一个池子里添加。
以上版本节点与设计变迁来自 本文关联文档 的原始叙述;对应的当前实现可以在仓库源码中逐一印证:
- src/invisible_playwright_mcp/mcp/server.py 的模块注释开宗明义:「THERE IS NO SESSION CONCEPT HERE, AND THAT IS DELIBERATE」——该进程只服务一件工作:两个固定角色
main与support。工具枚举、命名、触达第二个浏览器的方式被彻底移除,browser参数是Literal["main", "support"],模型无法发明第三个名字。 - src/invisible_playwright_mcp/mcp/work.py 用
MAX_BROWSERS_PER_SESSION = 2把数量写死,并注释说明了从「八个浏览器 = 八个身份」收缩到「一个身份 + 一个辅助」的理由:身份活在浏览器上(seed、指纹、profile),一个会话持有八个浏览器就等于持有八个身份,而上层的对话与存档却只针对一个会话,「这是谁的」就有了两个答案。 - 决定「这个进程服务哪件工作」的唯一来源是环境变量
INVISIBLE_MCP_SESSION_ID,在 server.py 中读取一次、永远如此,绝不会变成工具参数、不会出现在 schema 里、模型既读不到也传不了;未设置时落到storage.DEFAULT_SESSION_ID = "default"(见 src/invisible_playwright_mcp/storage.py)。
4.1 没有池子,就没有池内碰撞
由于没有「浏览器池」可供碰撞,上面的冲突场景在这里无法以同样方式发生。你付出的代价是:不能通过注册一个 server 来同时跑多个身份——答案变成了再注册一个 server。两种形态不存在抽象的优劣:Microsoft 的做法是把选择放进一个 flag,本项目的做法是把选择放进「你注册了几个 server」。
4.2 持久化配置目录:同类约束仍然存在
但「同类的坑」在持久化配置这一层依然会找上门,因为这条约束属于浏览器,而不是 server——这也是为什么 已登录会话必须被刻意处理。一个目录、一个活浏览器。如果你把两个会话指向同一个profile_dir,第二个会话一样会度过糟糕的一天。
本项目的对应参数是--profile-dir(browser_open工具参数为profile,底层启动 kwargs 名为profile_dir):
uvx invisible-playwright-mcp ui --profile-dir /path/to/your-profile- src/invisible_playwright_mcp/cli.py 中声明为「Persistent profile dir; logins survive across runs」,并在 README 中解释为「A directory to keep the profile in, so logins and cookies survive restarts」。
- src/invisible_playwright_mcp/mcp/plan.py 的
_resolve_profile是唯一读取STEALTHFOX_PROFILE_DIR的地方,并把相对路径解析为绝对路径——因为相对路径会相对 server 进程的工作目录解析,调用方既看不见也不控制它,同一个字符串在不同启动目录下是不同目录,昨天的登录会静默消失。 - src/invisible_playwright_mcp/mcp/session.py 的
_attach明确区分两条启动路径:无profile_dir时是「临时模式」(Browser +new_context()),设置profile_dir时直接进入持久化 BrowserContext(没有.new_context())。测试 tests/mcp_server/test_real_launch.py 专门覆盖了持久化模式这一条路径。 - src/invisible_playwright_mcp/mcp/work.py 中
WHO_A_BROWSER_IS = ("seed", "proxy", "profile_dir")明确:一个浏览器「是谁」由这三个字段决定,也只有它们会被写进会话存档;而「用什么引擎、是否显示窗口」属于本次启动的属性,由plan.launched_here每次现场决定,绝不从文件读回——避免一次有头模式的运行替所有后续进程做决定。
也就是说:本项目绕开的是「池内多浏览器互相抢目录」这一层(因为没有池),保留的是「同一持久化目录同一时刻只能有一个活浏览器」这一层(因为这是浏览器本身的不变式)。两点合起来,就是这篇文章标题里那个「规避设计」的准确边界。
五、常见问题速答
为什么明明没东西在运行,却提示浏览器在使用中?因为锁在配置目录上,可以比取走它的进程活得更久;也因为 server 自己维护了一个标志位,硬杀进程不会清除它。
--isolated会丢掉我的登录吗?会。这正是 isolated 的含义。如果需要在没有共享配置目录的情况下获得特定的登录态,请使用--storage-state。
能不能同时跑两个 MCP 浏览器客户端?能,用--isolated,或给每个客户端一个互不相同的--user-data-dir。不能在同一个共享持久化配置上同时跑。
这是 bug 吗?一半是。配置目录锁是浏览器自身的约束,行为正确;而browser_close后标志位未重置这一点,上游已作为缺陷跟踪。
六、相关阅读与源码入口
- Playwright MCP best practices:值得主动做出的配置取舍;
- Playwright MCP vs the CLI:两种驱动方式的对比;
- the MCP server:本项目的会话模型说明;
- src/invisible_playwright_mcp/mcp/server.py:两个固定浏览器
main/support与「无会话概念」的实现; - src/invisible_playwright_mcp/mcp/work.py:
open/acting/close生命周期与MAX_BROWSERS_PER_SESSION; - src/invisible_playwright_mcp/mcp/plan.py:
plan_session与profile_dir/STEALTHFOX_PROFILE_DIR的唯一解析点; - tests/mcp_server/test_one_plan_for_one_session.py:单个会话单一计划的行为验证;
- tests/mcp_server/test_real_launch.py:持久化配置目录(profile_dir)真实启动路径的测试。
结论:遇到 "Browser is already in use",先把它读成「配置目录被锁」而不是「浏览器在跑」,按「并发 →--isolated;要持久化 → 每人一个目录;没进程 → 重启服务;要删锁 → 先验活」四步走。invisible_playwright_mcp 用「一进程一身份 + 固定双浏览器」的结构在池内根除了同类冲突,但持久化目录的单实例约束是浏览器的地盘,两个会话指向同一个profile_dir时,你依然需要上述第一条的纪律。
- 人工智能
- AI Agent
- 浏览器控制
- GUI 自动化
- MCP 服务
【免费下载链接】invisible_playwright_mcp
Playwright MCP server undetected by anti-bots and captchas: AI agent browses the web on anti-detect stealth Firefox, Python, undetected browser automation, scraping, computer use.
相关推荐
抖音无水印批量下载怎么实现:Douzy 从 Cookie 登录到归档管理的完整教程
抖音无水印批量下载怎么实现:Douzy 从 Cookie 登录到归档管理的完整教程 Douzy(douyin downloader)是一个 Python 写的抖
网页爬虫CLIn8n-workflows 的 ai-stack 启动报“Port 5678 is already in use”怎么排查?
n8n workflows 的 ai stack 启动报“Port 5678 is already in use”怎么排查? 在 n8n workflows 仓
工作流自动化文档Meshery Design 验证机制实战:以 "Design With Validation Errors" 目录设计为例排查与修复配置错误
Meshery Design 验证机制实战:以 "Design With Validation Errors" 目录设计为例排查与修复配置错误 本篇技术指南以
云原生微服务运维DevOps
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考