ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

CLI-Anything Safari:用 Click 命令行驱动 safari-mcp,实现 macOS 浏览器自动化

2026/9/11 13:51:48 拓冰建站 浏览量
CLI-Anything Safari:用 Click 命令行驱动 safari-mcp,实现 macOS 浏览器自动化 CLI-Anything Safari用 Click 命令行驱动 safari-mcp实现 macOS 浏览器自动化【免费下载链接】CLI-AnythingCLI-Anything: Making ALL Software Agent-Native -- CLI-Hub: https://clianything.cc/项目地址: https://gitcode.com/GitHub_Trending/cl/CLI-AnythingclI-Anything 的 Safari 自动化 harnesscli-anything-safari将一个标准 MCP 服务器包装成 84 个可直接调用的 Click 命令使任何非 MCP 的 Agent 框架、bash 流水线、CI/cron 脚本和终端调试场景都能像操作普通 CLI 一样驱动真实 Safari 浏览器。读完本文你将掌握该 CLI 的安装、命令结构、快照驱动工作流、安全模型与工具注册表再生成机制并理解其 schema 驱动的“与上游 MCP 1:1 特征对等”设计原理。背景为什么需要一个 Safari 的 CLI 包装层safari-mcp 描述的核心思路是把 MCP 工具面“平移”到命令行让不支持 MCP 的框架也能用。该 CLI 的关键设计决策是schema 驱动它不是手写 84 个命令而是从捆绑的resources/tools.json工具注册表出发在 import 时用 Click 动态生成每一个命令。这一设计保证了特征对等safari-mcp 暴露的每个工具、每个参数都能以相同名称和类型被调用见 tool_registry.py 与 safari_cli.py 的_register_all_tools()--help离线可用内省命令只读本地注册表不触碰网络、不启动 MCP 服务器不干扰并发的 safari-mcp 实例下文 Singleton-killer 一节会详细解释。一个需要提前明确的运行模型每次 CLI 调用都会派生一个新的npx safari-mcp子进程执行一次工具调用后立即退出见 safari_backend.py。因此如果你的 Agent 原生支持 MCP直接使用 safari-mcp 会更快本 CLI 的价值在于非 MCP 框架、bash 管道、CI/cron 和终端调试。环境与安装前提条件macOSDarwin——Safari MCP 仅支持 macOSNode.js 18——brew install node或从 Node.js 官网安装Python 3.10Safari开启Develop → Allow JavaScript from Apple Events菜单项。安装步骤cd safari/agent-harness pip install -e .首次执行任意tool调用时CLI 会通过npx自动下载safari-mcpnpm 包约几 MB。入口为main.py命令名统一为cli-anything-safari。快速开始# 探明工具面 cli-anything-safari tools count # → 84 cli-anything-safari tools list cli-anything-safari tools describe safari_click # 调用任意工具 cli-anything-safari tool navigate --url https://example.com cli-anything-safari --json tool snapshot cli-anything-safari tool click --ref 0_5 cli-anything-safari tool fill --selector #email --value userexample.com cli-anything-safari --json tool screenshot --full-page \ | python3 -c import sys,json,base64; djson.load(sys.stdin); open(/tmp/shot.jpg,wb).write(base64.b64decode(d[data])) cli-anything-safari tool evaluate --script document.title # 交互式 REPL cli-anything-safari其中tool evaluate --script是值得注意的参数名——上游 safari-mcp 的参数就叫script而不是某些文档误写的codetest_parity.py 专门为此设了回归锁测试防止文档与实现漂移。截图工具返回的是 Base64 编码的图片内容_unwrap将 MCP 的ImageContent转为{type:image,data:base64,mimeType:image/jpeg}解码后即可落盘见 safari_backend.py 的_unwrap()实现。命令结构命令用途tool调用 safari-mcp 的任意一个工具共 84 个动态生成、schema 驱动tools内省捆绑的工具注册表list、describe、countraw逃生舱——按完整 MCP 名称 原始 JSON 参数调用工具session进程内会话状态上次 URL、当前标签页repl交互式 REPL不指定子命令时默认进入每个子命令都可用cli-anything-safari command --help查看详细说明。toolschema 驱动的动态命令组tool组的每个命令由注册表中的ToolSchema在 import 期构建_register_all_tools()。参数映射规则tool_registry.py命名转换MCP 的 camelCase 参数如urlPattern自动转为 kebab-case 的 CLI flag--url-pattern见_camel_to_kebab()类型映射JSON Schema 的string/integer/number/boolean映射为 Click 对应类型带enum的参数映射为click.Choice大小写不敏感布尔参数以--flag/--no-flag成对形式暴露必填布尔参数在 Click 层无法用requiredTrue强制因此在 runner 里显式校验safari_cli.pyobject/array 参数以 JSON 字符串传入运行时用coerce_arg_value()解码tool_registry.py解码失败会给出友好错误而非堆栈回溯。raw绕过 schema 的逃生舱当你手头已有一份 JSON 参数块、或想调用新工具时使用cli-anything-safari raw safari_evaluate --json-args {script:document.title}--json-args必须解码为 JSON 对象且即使走 raw 路径导航类工具的 URL 仍会经过安全校验safari_cli.py不会成为绕过安全层的后门。tools注册表内省tools count——打印注册表工具数量脚本友好纯文本 84--json输出{tool_count: 84}tools list [--filter 子串]——列出工具支持按名称子串过滤tools describe 名称——支持完整名safari_scroll或短名scroll查询输出每个参数的 CLI flag、类型、必填性、默认值和枚举取值。内省命令tools组是唯一跳过后端可用性探测的子命令——因为tools只读本地注册表见 safari_cli.py。session 与 replSafari MCP 本身按调用无状态每次派生新进程但 CLI 在进程内保留极少量内存态session.pylast_url最近成功导航的 URL与current_tab_index最近活跃标签页索引。session status可查看REPL 的提示符会以tabN url形式展示当前上下文方便在交互调试中感知所处页面。REPL 支持的命令tool name 调用任意 safari-mcp 工具用 tools list 查看名称 tools list 列出所有可用工具 tools describe name 查看工具完整 schema raw name 通过 JSON 参数调用工具 session status 查看当前会话状态 help 显示帮助 quit 退出 REPLREPL 内部通过shlex.split解析输入行后复用同一cli.main()入口并在捕获UsageError后继续循环safari_cli.py。JSON 输出所有命令都支持全局--json标志输出结构化 JSON 供脚本与 Agent 解析cli-anything-safari --json tool snapshot cli-anything-safari --json tools list错误也会统一以 JSON 形式输出{error: ..., type: ...}且--json模式下错误信息同样保持机器可读safari_cli.py。环境变量透传给 safari-mcp 的变量变量用途SAFARI_PROFILESafari 配置文件名称如 AutomationMCP_MAX_TABS每会话最大标签页数默认 6MCP_MEMORY_CHECK_MS内存检查间隔默认 60000 毫秒MCP_WEBKIT_LIMIT_MBWebKit 内存上限默认 3000 MB这些变量在派生子进程时通过envos.environ.copy()原样透传safari_backend.py。CLI 自身消费的变量变量用途CLI_ANYTHING_SAFARI_BLOCK_PRIVATE设为1或true时阻止访问私有网络地址CLI_ANYTHING_SAFARI_ALLOWED_SCHEMES覆盖允许的 URL scheme 白名单逗号分隔CLI_ANYTHING_FORCE_INSTALLED测试模式要求已安装 CLI 命令其中私有网络阻止的默认值是关闭开发友好便于自动化本地仪表盘和开发服务器test_security.py 明确验证了默认允许localhost、127.0.0.1与192.168.x.x。快照驱动工作流推荐Safari MCP 的核心交互范式是“快照驱动”snapshot返回结构化文本其中每个可交互元素都带有ref ID按 ref 点击比按 CSS 选择器更便宜、更可靠cli-anything-safari --json tool snapshot /tmp/snap.json # Agent 读取 /tmp/snap.json找到 ref 为 3_12 的 Submit 按钮 cli-anything-safari tool click --ref 3_12关键约束ref 在每次新快照后都会过期5_xx会变为6_xx因此“快照 → 点击”要紧接着执行不要在中间穿插其他快照操作。这一模式对应safari_snapshot/safari_accessibility_snapshot工具CLI 不做重复的状态缓存详见 tests/TEST.md 中关于 Session 无 undo/redo/snapshot 的设计说明。安全模型多层防御标签页隔离上游 safari-mcp 强制按会话隔离标签页所有权避免一个会话操作另一个会话打开的标签页URL 校验导航类工具_URL_VALIDATED_TOOLS集合按“参数名为url且类型为 string”这一启发式自动识别见 safari_cli.py在调用后端前先经过 security.py 的validate_url()。校验覆盖三层危险 scheme 黑名单file、javascript、data、vbscript、about、chrome、webkit、safari、x-apple、feed等 15 个 scheme 一律阻止私有网络访问仅当CLI_ANYTHING_SAFARI_BLOCK_PRIVATE1时启用匹配 RFC 1918 网段、loopback、link-local 及对应 IPv6 前缀scheme 白名单默认只允许http和https可用CLI_ANYTHING_SAFARI_ALLOWED_SCHEMES覆盖。Profile 隔离用SAFARI_PROFILE单独创建自动化专用配置文件与用户日常浏览数据分离。威胁模型validate_url()要防的是三类攻击security.py 的模块注释SSRFSafari 可访问 localhost/内网、scheme 注入javascript:/file:/data:可在本地执行代码、标签页所有权绕过。校验的边界情况空串、纯空白、非字符串、缺少 scheme、缺少 hostname、未知 scheme在 test_security.py 中有 22 个专项用例覆盖。⚠️ Singleton-killer 警告Safari MCP 在启动时会强制单实例它会杀掉任何启动时间超过 10 秒的其他node …/safari-mcp/index.js进程。这意味着运行cli-anything-safari或任何其他 safari-mcp 客户端会终止机器上并发的 safari-mcp 实例——包括正在为 Claude Code、Cursor 或其他 Agent 会话服务的那个不要在两个 shell 里并行运行 CLI 调用不要在本 CLI 运行期间让其他 Agent 通过 MCP 传输层活跃使用 safari-mcp这正是 E2E 测试套件必须用SAFARI_E2E1门控的原因——直接运行会杀掉任何活跃的 safari-mcp 实例tests/TEST.md 对此有详细论证并说明端口 9224 被占用时上游会进入代理模式从而在实践中保护主实例但门控仍作为防御性措施保留。工具注册表如何再生成safari-mcp升级后需要重新生成捆绑的 schema 以保持特征对等python scripts/extract_tools.py \ /path/to/safari-mcp/index.js \ cli_anything/safari/resources/tools.jsonextract_tools.py 是一个离线、零依赖的手写解析器它扫描index.js源码中的server.tool(...)调用用深度感知扫描器解析 Zod 修饰链.optional()、.default()、.describe()、嵌套的z.array(z.object(...))从而避免把嵌套字段的描述或可选性错误泄漏到外层参数。再生成后运行对等测试若 safari-mcp 工具数量变化则更新固定的工具计数python -m pytest cli_anything/safari/tests/test_parity.py测试与质量保障整个 harness 共95 个测试详见 tests/TEST.md 的测试清单与实测结果分层如下文件数量类别是否需要 Safaritest_core.py16单元测试mock 后端与会话否test_security.py36安全 / URL 校验否test_parity.py24CLI ↔ MCP schema 对等否test_full_e2e.py19E2ECliRunner 子进程是门控离线运行结果为 76 passed / 19 skipped设置SAFARI_E2E1 CLI_ANYTHING_FORCE_INSTALLED1后全套 95 个测试通过其中 3 个测试真实连接 Safari列出标签页、导航并读取页面标题、子进程 JSON 往返。实测日志还验证了截图工具返回的 Base64 解码后是合法的 JPEGmagic bytesff d8 ff e0见 tests/TEST.md 的“Live verification log”一节。其中parity 测试是本 CLI “与上游 MCP 完全一致”承诺的核心保障它逐一断言注册表中的每个工具都能作为tool short-name被调用、每个 MCP 参数都有对应的 Click option、必填参数正确标记、枚举取值一致、tool组命令数与注册表严格相等test_parity.py。这类测试还锁定了若干历史上的解析器回归例如safari_evaluate的参数必须是script、safari_run_script的参数必须是steps数组、safari_mock_route.response必须为必填 object 且描述来自外层.describe()而非嵌套的status字段。重新运行测试# 离线套件快无需 Safari python -m pytest cli_anything/safari/tests/ -v --tbno # 含 E2E 的完整套件需要 Safari macOS Apple Events SAFARI_E2E1 CLI_ANYTHING_FORCE_INSTALLED1 \ python -m pytest cli_anything/safari/tests/ -v -s # 仅对等检查“与 MCP 完全一致”承诺的核心 python -m pytest cli_anything/safari/tests/test_parity.py -v故障排查现象原因与解决npx not found未安装 Node.js 18brew install nodesafari-mcp package not found on npm registry网络问题或 npm 不可达先检查npm view safari-mcp versionAppleScript execution failed未开启 Safari → Develop → Allow JavaScript from Apple EventsTool cannot operate on tab it did not open标签页所有权保护触发先打开新标签页再操作cli-anything-safari tool new-tab --url https://example.com随后再click非 macOS 环境CLI 会在平台检查阶段直接拒绝is_available()返回错误并退出此外若捆绑的resources/tools.json缺失_register_all_tools()会打印警告并提示重新运行 extract 脚本safari_cli.py。总结cli-anything-safari以“schema 驱动 动态命令生成”的方式把 84 个 Safari MCP 工具完整、可内省、可脚本化地暴露到命令行tool提供类型安全的参数映射raw提供 JSON 逃生舱tools提供离线内省repl提供交互调试而validate_url()与标签页隔离则守住浏览器自动化的安全底线。对于需要把 Safari 自动化接入 bash 流水线、CI/cron 或非 MCP Agent 框架的开发者这是一个开箱即用的桥接方案其“注册表对等测试锁死上游变更”的做法也为其他 MCP→CLI 类 harness 提供了可复用的工程范式。想进一步了解设计取舍可参考 HARNESS.mdharness 架构深析、SAFARI.mdSafari 专项分析与 tests/TEST.md测试计划与结果。【免费下载链接】CLI-AnythingCLI-Anything: Making ALL Software Agent-Native -- CLI-Hub: https://clianything.cc/项目地址: https://gitcode.com/GitHub_Trending/cl/CLI-Anything创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考