cli-anything-safari 是做什么的?
Safari 浏览器自动化的命令行接口,适用于 macOS。它使用 Python Click CLI 封装了 safari-mcp Node.js MCP 服务器。
保证功能完全一致。 每个 Click 命令都从 safari-mcp 的工具模式(打包为 resources/tools.json)自动生成。所有 84 个工具均可通过 MCP 服务器期望的精确参数名称和类型进行调用。
何时使用此 CLI
每次 CLI 调用都会启动一个新的子进程,因此每次调用都有开销。如果您的代理框架原生支持 MCP(Claude Code、Cursor、Cline 等),直接通过 MCP stdio 使用 safari-mcp 会更快。
在以下场景使用此 CLI:
- 您的代理框架不支持 MCP(Codex CLI、GitHub Copilot CLI、自定义脚本、较旧的代理框架)。
- 您需要从 bash 脚本化浏览器自动化——
cli-anything-safari --json tool snapshot | jq '...'。 - 您在 CI/CD 中运行,希望获得可 cron 化、子进程友好的输出。
- 您正在 交互式调试(从终端)。
安装
前提条件
- macOS —— Safari MCP 仅适用于 macOS。
- Safari —— 已安装在 macOS 上。
- Node.js 18+ ——
brew install node或从 https://nodejs.org/ 下载。 - Python 3.10+
- 为 Safari 启用 Apple Events:Safari → 开发 → 允许来自 Apple Events 的 JavaScript
安装 CLI
cd safari/agent-harness
pip install -e .
首次调用 tool 时会下载 safari-mcp npm 包(一次性,约几 MB)。
命令结构
CLI 有 5 个顶级命令:
| 命令 | 用途 |
|---|---|
tool | 调用 safari-mcp 的任何 84 个工具(动态,模式驱动) |
tools | 检查打包的工具注册表(list、describe、count) |
raw | 逃生出口——通过完整名称和原始 JSON 参数调用工具 |
session | 内存中的会话状态(上次 URL、当前标签页) |
repl | 交互式 REPL(未提供子命令时的默认行为) |
使用示例
发现工具面
# 工具计数(健全性检查——必须匹配 safari-mcp 注册的工具)
cli-anything-safari tools count
# → 84
# 列出所有工具
cli-anything-safari tools list
cli-anything-safari tools list --filter click # 按子字符串过滤
# 单个工具的完整模式(JSON 或人类可读格式)
cli-anything-safari tools describe safari_scroll
cli-anything-safari --json tools describe safari_click
调用工具(模式驱动)
# 导航
cli-anything-safari tool navigate --url https://example.com
# 快照(优于截图——结构化文本带引用 ID)
cli-anything-safari --json tool snapshot
# 通过引用点击(引用来自快照;下次快照时失效!)
cli-anything-safari tool click --ref 0_5
# 通过选择器或可见文本点击
cli-anything-safari tool click --selector "#submit"
cli-anything-safari tool click --text "Log in"
# 填充字段
cli-anything-safari tool fill --selector "#email" --value "[email protected]"
# 按方向/数量滚动(不是 x/y——注意模式!)
cli-anything-safari tool scroll --direction down --amount 500
# 将一个元素拖到另一个元素上
cli-anything-safari tool drag \
--source-selector ".card" \
--target-selector ".trash"
# 截图——在 stdout 中返回 base64 JPEG。解码:
cli-anything-safari --json tool screenshot --full-page \
| python3 -c "import sys,json,base64; \
d=json.load(sys.stdin); \
open('/tmp/shot.jpg','wb').write(base64.b64decode(d['data']))"
# 保存为 PDF(此命令直接写入磁盘)
cli-anything-safari tool save-pdf --path /tmp/page.pdf
# 执行 JavaScript(注意:参数是 --script,不是 --code)
cli-anything-safari tool evaluate --script "document.title"
单次往返导航和阅读
cli-anything-safari --json tool navigate-and-read --url https://example.com
批量表单填充
safari_fill_form 接受一个 数组 的 {selector, value} 对象。
将其作为 JSON 字符串传递:
cli-anything-safari tool fill-form --fields '[
{"selector": "#email", "value": "[email protected]"},
{"selector": "#password", "value": "hunter2"}
]'
运行 cli-anything-safari tools describe safari_fill_form 查看精确模式,包括 safari-mcp 上游添加的任何新字段。
网络监控
cli-anything-safari tool start-network-capture
cli-anything-safari tool navigate --url https://example.com
cli-anything-safari --json tool network
cli-anything-safari tool performance-metrics
存储
cli-anything-safari tool get-cookies
cli-anything-safari tool set-cookie --name session --value abc123 --domain example.com
cli-anything-safari tool local-storage --key theme
# export-storage 返回 JSON 到 stdout——没有 --path 参数。通过管道输出到文件:
cli-anything-safari --json tool export-storage > /tmp/storage.json
原始 JSON 逃生出口
当需要传递复杂的嵌套对象或希望从预先构建的 JSON blob 驱动 CLI 时:
cli-anything-safari raw safari_evaluate \
--json-args '{"code":"[...document.querySelectorAll(\"a\")].map(a => a.href)"}'
交互式 REPL
cli-anything-safari
REPL 横幅打印此 SKILL.md 的绝对路径,以便代理可以自我发现功能。
JSON 输出
所有命令都支持 --json 作为全局标志:
cli-anything-safari --json tool snapshot
cli-anything-safari --json tool list-tabs
cli-anything-safari --json tools list
状态管理
CLI 仅为 REPL 显示维护少量内存状态:
last_url—— CLI 上次导航到的 URL(每次成功的tool navigate、tool navigate-and-read或tool new-tab后更新)current_tab_index—— 上次已知的活动标签页索引
没有持久化会话,没有撤销/重做,没有文档模型。每次 CLI 调用都以全新状态开始。Safari MCP 本身每次调用都是无状态的:每个 tool 命令都会启动一个全新的 npx safari-mcp 子进程,执行操作后退出。这是一个深思熟虑的设计选择;有关偏离标准撤销/重做模式的理由,请参阅 HARNESS.md 和 TEST.md。
输出格式
所有命令都支持双输出模式:
- 人类可读(默认):字典结果的缩进键值文本、数组的列表、其他情况下的纯文本。
- 机器可读(
--json标志):供代理消费的结构化 JSON。
# 人类输出
cli-anything-safari tool snapshot
# 代理的 JSON 输出
cli-anything-safari --json tool snapshot
cli-anything-safari --json tools list
cli-anything-safari --json tools describe safari_click
针对 AI 代理
以编程方式使用此 CLI 时:
- 始终使用
--json标志 以获得可解析的输出。 - 检查返回码 —— 0 表示成功,非零表示错误(URL 验证失败、MCP 调用失败、无效的 JSON 参数)。
- 从 stderr 解析错误消息;使用 stdout 获取数据。
- 文件处理工具的参数名称不一致 —— 始终先检查
tools describe <name>:tool save-pdf --path /tmp/x.pdftool upload-file --selector ... --file-path /tmp/x.txt(注意:--file-path,而不是--path)tool export-storage—— 没有路径参数;将 JSON 输出通过管道输出到文件tool import-storage --path /tmp/x.jsontool screenshot/screenshot-element—— 在 JSON 响应中返回 base64,无路径参数(自行解码)
- 点击前先快照 ——
tool snapshot的引用在下一次快照后失效。始终连续执行:快照 → 查找引用 → 点击。 - 通过
tools list发现工具 —— 打包的注册表是可用工具的真实来源。不要硬编码可能上游更改的工具名称。 - 使用
tools describe <name>了解精确模式(必需参数、枚举选择、JSON 类型参数),然后再构造调用。永远不要根据描述猜测参数名称 —— 例如,safari_evaluate接受--script(而不是--code),尽管描述说的是“要执行的 JavaScript 代码”。
特定代理指南
找到正确的工具
使用内省命令。CLI 保证与 MCP 服务器 1:1 对应:
# 查找所有与点击相关的工具
cli-anything-safari tools list --filter click
# 获取完整模式(包括每个参数的类型、描述、必需/可选、枚举选择、默认值)
cli-anything-safari --json tools describe safari_click
工具选择策略
tool snapshot优先于tool screenshot—— 结构化文本带引用 ID 成本低得多,并且包含点击所需的引用。tool click --ref优先于tool click --selector—— 引用在单个快照内稳定,选择器可能脆弱。tool navigate-and-read优先于navigate+read-page—— 节省一次往返。tool click-and-read优先于click+read-page—— 节省一次往返。tool native-click仅在常规点击返回 405/403 错误时使用(WAF 阻止、G2、Cloudflare)—— 它会物理移动光标。
引用过期
tool snapshot 的引用在拍摄新快照时过期:
- 第一个快照:引用
0_1、0_2、0_3... - 第二个快照:引用
1_1、1_2、1_3...
始终连续执行:快照 → 点击。如有疑问,再次快照。
标签页所有权安全
Safari MCP 按会话跟踪标签页所有权。修改标签页(导航、点击、填充)的工具在会话未打开的标签页上被阻止。要操作特定页面,始终从 tool new-tab --url ... 开始。
错误处理
常见错误:
npx not found—— 安装 Node.js 18+safari-mcp package not found on npm registry—— 检查网络Not macOS—— 工具仅适用于 macOSAppleScript denied—— 在 Safari → 开发中启用“允许来自 Apple Events 的 JavaScript”Blocked URL scheme: file—— URL 验证拒绝了输入(按设计)
URL 验证
CLI 在将 URL 传递给 safari_navigate、safari_navigate_and_read 和 safari_new_tab 之前会验证它们。被阻止的方案:file、javascript、data、vbscript、about、chrome、safari、webkit、x-apple 以及其他浏览器内部方案。raw 命令也对导航工具强制执行此验证。
多会话警告
Safari MCP 通过杀死超过 10 秒的陈旧 Node.js 进程来强制单个活动会话。如果同时运行两个 CLI 实例,一个会杀死另一个的后端。目前没有守护模式 —— 对于延迟敏感的工作流,请从长时间运行的 Python 脚本驱动 CLI,该脚本直接导入 cli_anything.safari.utils.safari_backend.call(),以避免每次调用时重新生成子进程。
链接
安全考虑
URL 验证
所有导航工具(tool navigate、tool navigate-and-read、tool new-tab 和 raw safari_navigate*)都会将 url 参数通过 utils/security.py 进行处理,该模块会阻止危险方案,并可选择阻止私有网络(设置 CLI_ANYTHING_SAFARI_BLOCK_PRIVATE=1)。
标签页隔离
Safari MCP 在上游强制每个会话的标签页所有权——工具无法操作会话未打开的标签页。
配置文件隔离
设置 SAFARI_PROFILE 环境变量以使用单独的 Safari 配置文件进行自动化:
export SAFARI_PROFILE="Automation"
cli-anything-safari tool navigate --url https://example.com
这将保持 cookie/登录/历史记录与用户的主浏览分开。
JavaScript 执行
tool evaluate 和 tool run-script 在页面上下文中运行任意 JavaScript。对待不受信任的输入要像对待任何动态代码执行路径一样小心。
剪贴板
tool clipboard-read 和 tool clipboard-write 会触及系统剪贴板。在用户的活跃会话内运行时请小心——中途覆盖剪贴板会带来干扰。
重新生成工具注册表
如果升级了 safari-mcp,请重新生成打包的模式:
python scripts/extract_tools.py \
"$(npm root -g)/safari-mcp/index.js" \
cli_anything/safari/resources/tools.json
一致性测试(test_parity.py)固定了预期的工具数量;当上游工具列表发生变化时更新它。
更多信息
- 完整文档: 包内的
cli_anything/safari/README.md - 测试覆盖: 包内的
cli_anything/safari/tests/TEST.md - 架构分析:
safari/agent-harness/SAFARI.md - 方法:
cli-anything-plugin/HARNESS.md - MCP 后端模式:
cli-anything-plugin/guides/mcp-backend.md
版本
1.0.0 —— 目标 safari-mcp 2.7.8(84 个工具)。打包的工具注册表在 safari-mcp 升级时通过 scripts/extract_tools.py 重新生成。