@songxiyuan/playwright-mcp-storage
带 upstream storage 能力的 Playwright MCP browser provider,按 Session 直接挂载
@playwright/mcp,不依赖 DSH Browser Use。
为什么需要它
两个独立的问题合在一起,逼出了这个包:
storage 能力默认关闭。
@playwright/mcp@0.0.80里browser_storage_state/browser_set_storage_state/browser_cookie_*/browser_localstorage_*等工具的capability都是storage,而filteredTools(config)只保留:tool.capability.startsWith('core') || config.capabilities?.includes(tool.capability)config.capabilities来自 CLI 的--caps。DSH 官方 browser-use 栈不在 0.1.5 线上。
@deepseek-ai/dsh-browser-use/@deepseek-ai/dsh-experimental-browser-use-runtimenpm 上最低只有0.1.6-alpha.1, 而且官方 Playwright provider(@deepseek-ai/dsh-experimental-browser-use-playwright-mcp) 把参数写死成--browser chromium --isolated(attach 再加--cdp-endpoint),没有透传口子。
本包因此不注册 ctx.browserUse,而是用 0.1.5 线自带的 @deepseek-ai/dsh-mcp-client
在每个 live Agent 的 scope 里各挂一个 @playwright/mcp 进程:每个 Session 一套浏览器、
一套 mcp__playwright-mcp__* 工具名,天然隔离。
安装
本包是 @songxiyuan/test-account 的依赖(伞包,0.1.2 起),装主包时会被自动装上,所以通常
不需要单独装本包:单独装它只会得到一个不会被挂载的普通依赖(它没有 dsh.bundle,不贡献补丁层)。
有源码的开发机在仓库根按根 README §1.2 装:
pnpm install && pnpm run build
dsh plugin --profile test-account add ./packages/test-account
本地装的是 link:,lib/ 必须先 build 出来(完整命令见根 README §1.2)。
没有源码的机器从公共 npm 装(@playwright/mcp 也在公共 npm 上,传递依赖自动解析;不需要 token):
dsh plugin --profile test-account add @songxiyuan/test-account
升级用 dsh plugin --profile test-account update @songxiyuan/test-account 后重启该 profile。
0.1.1 及更早没有这条依赖边,那时必须把两个包名都写。
cordis.patch.yml 按包名解析本 provider,走的就是主包声明的那条依赖边:DSH 启动时按
「安装目录 → 各 bundle 根」展开依赖图,bundle 根锚在包的真实目录上,因此 provider 即使在
pnpm 的嵌套 node_modules 里也能被找到;细节见根 README §1.3。
参数拼装
<@playwright/mcp>/cli.js --browser chromium
launch: --isolated [--headless] [--executable-path <p>] [--allow-unrestricted-file-access]
attach: --cdp-endpoint <endpoint>
--caps=<逗号分隔> # 默认 storage
<extraArgs...>
同时它沿用官方 provider 的做法,把继承来的 PLAYWRIGHT_MCP_* 环境变量清空,保证 profile 是浏览器
配置的唯一来源。
配置
interface Config {
mode?: 'launch' | 'attach' // 默认 'launch'
headless?: boolean // 默认 true;保存登录态建议 false
executablePath?: string // 显式指定浏览器,优先于自动探测
autoExecutablePath?: boolean // 默认 true:探测本机已装的 Chrome/Chromium/Edge
endpoint?: string // attach 模式必填
toolCallTimeoutMs?: number
caps?: string[] // 默认 ['storage']
allowUnrestrictedFileAccess?: boolean // 默认 true
extraArgs?: string[]
}
attach 不能带 endpoint 以外的 launch 参数,launch 不能带 endpoint;不合法组合在占用任何浏览器
资源之前就会抛错。这些规则有单元测试。
挂载模型
- 监听
agent/created,为每个 live Agent 用createScope(ctx, agent)起一个作用域,在作用域里ctx.plugin(McpClient, …),stdio 传输、failOnStartupError: true、不自动重连。 - Agent 结束或插件卸载时 dispose 该作用域,stdio 子进程随之关闭。
tools/execute上有一道守卫:mcp__playwright-mcp__*(以及指向本 server 的 MCP resource 工具) 只有该 Agent 自己能调,别的 Session 调用会报playwright-mcp-storage: browser tool belongs to another Session。attach模式是独占的:同一时刻只让一个 live Session 拿到外部浏览器,其余 Session 正常启动但没有 浏览器工具。
浏览器本体
钉住的 @playwright/mcp@0.0.80 默认要 chrome-for-testing(Playwright 自己那份 Chromium),
全新机器上没有,第一次调用 storage 工具会直接失败:
Browser "chrome-for-testing" is not installed; ...
Run `npx @playwright/mcp install-browser chrome-for-testing` to install
test-account 会在这条错误后面追加一段中文提示,告诉用户可以走哪两条路。provider 自己则默认
autoExecutablePath: true:按平台探测常见安装位置,命中就传 --executable-path,避免为了保存一个
登录态再下 100+ MB 的 Chromium。
| 平台 | 探测顺序 |
|---|---|
| macOS | /Applications/Google Chrome.app/… → Chromium → Microsoft Edge → Brave |
| Windows | %PROGRAMFILES% / %PROGRAMFILES(X86)% / %LOCALAPPDATA% 下的 Chrome,然后 Edge |
| Linux | /usr/bin/google-chrome{,-stable} → /usr/bin/chromium{,-browser} → microsoft-edge → /snap/bin/chromium |
探测不到(例如干净的 CI)就回退到 Playwright 自带浏览器,并打一条 warn 提示两条出路。想强制用自带
Chromium:autoExecutablePath: false + npx @playwright/mcp install-browser chrome-for-testing。
依赖自检
provider 只从自己的依赖里取 @playwright/mcp:加载时就会确认解析结果旁边的 cli.js 真的存在,
不存在就直接抛出可执行的错误,而不是等 MCP 子进程炸出一段 MODULE_NOT_FOUND:
playwright-mcp-storage: the pinned @playwright/mcp CLI is missing at …/cli.js. …
这条守卫针对的是一次真实事故:profile 从 registry 安装切到 link: 之后,被剪枝的旧
@playwright/mcp(pnpm 提升到 profile node_modules 的那份)仍可能被 Node 解析到,provider 于是拿到
一个 cli.js 已消失的路径,故障只在子进程里暴露,现场看不出该修什么。看到这条错误就重装依赖:
link: 路线在插件仓库跑 pnpm install 后重启 profile;registry 路线跑
dsh plugin --profile <p> update @songxiyuan/playwright-mcp-storage。不要把 @playwright/mcp
单独装进 profile 去"补上"——那正是这次事故的来源。
测试
pnpm --filter @songxiyuan/playwright-mcp-storage test
test/args.test.ts 覆盖默认参数、--headless 省略、权限开关、--caps 拼接与省略、
attach/launch 互斥校验、extraArgs 追加顺序,以及上面的 CLI 自检(存在性 + 缺失时的提示语)。