像Claude Code、Cursor这样的AI编程助手,在阅读和编写代码方面已经取得了显著的进步。但一旦它们需要浏览网页时,就会遇到障碍——它们无法查看你的测试环境网站,也无法读取分析仪表板中的错误信息,更无法确认自己刚刚构建的表单是否真的能够成功提交。
通常的解决办法是为这些助手提供无头浏览器,比如使用Puppeteer或Playwright来驱动全新的Chromium实例。这种方法确实有效,但问题在于,每次会话开始时,无头Chromium都会处于“空白状态”:没有登录信息、没有cookie、也没有会话记录。它还会占用额外的CPU资源,导致风扇加速运转。而且越来越多的网站会在检测到无头浏览器时直接阻止其访问。
其实还有另一种选择,在Mac系统上这种选择非常合适:让助手使用你已经登录的Safari浏览器——也就是那个已经连接到GitHub、分析系统和测试环境的浏览器。Safari MCP正是这样的工具。它是一个开源的MCP服务器,通过大约80种工具让任何具备MCP功能的助手都能使用Safari浏览器,而无需额外安装Chromium或WebDriver,也不需要管理额外的浏览器实例。
在这个教程中,你将把Safari MCP与AI助手连接起来,运行你的第一个自动化任务,然后完成一些无头浏览器根本无法实现的事情:在你已经登录的网页环境中执行自动化操作。通过这个过程,你不仅会了解如何配置这些工具,还会明白在什么情况下使用原生浏览器自动化才是最合适的选择,而在什么情况下则不然。
你需要准备以下物品:
-
一台Mac电脑(Safari MCP仅适用于macOS系统——关于这一限制我们稍后会详细讨论)
-
Node.js 18或更高版本
-
任何具备MCP功能的AI助手——本教程使用了Claude Code和Cursor,但其他类似的工具也同样适用
目录
什么是MCP?为什么浏览器自动化需要它?
在开始配置任何工具之前,先了解一下Safari MCP中的“MCP”到底代表什么,会很有帮助。
MCP即模型上下文协议,这是一种用于将人工智能代理与外部工具及数据连接起来的开放标准。你可以将其理解为类似于USB端口的概念:在USB技术出现之前,每一种设备都需要配备独立的连接器;而MCP的作用就在于为所有相关设备规定一个统一的“连接器”——使用MCP协议的代理可以与应用同样支持该协议的任何工具进行交互,双方都无需编写任何定制的集成代码。
MCP服务器提供了一组工具。而MCP客户端——也就是你的AI代理——会发现这些工具并调用它们。服务器会描述每一项工具的功能(名称、用途以及所需参数),而代理则会决定在何时使用这些工具。当Claude Code需要读取网页内容时,它并不会自行运行浏览器代码,而是会调用MCP服务器提供的相应工具。
对于这种模型来说,浏览器自动化技术是非常适合的。代理的任务是进行推理——“我需要查看测试网站上的内容,然后检查控制台是否有错误”。而具体的操作步骤——如打开标签页、等待页面加载、读取DOM结构、捕获控制台输出等——都是已经被明确定义好的操作,这些操作应该通过一个稳定的接口来执行。而MCP服务器恰恰提供了这样的接口。
Safari MCP就是这样的一个服务器。它作为本地进程运行,提供了大约80种浏览器工具(如导航、点击、填写信息、读取内容、截图、提取数据等),任何MCP客户端都可以使用它。代理根本不需要接触AppleScript或WebKit的内部机制,它只需要调用`safari_navigate`这个函数即可得到结果。
之所以选择“USB端口”这种架构,是出于实际考虑:本教程中的内容并不针对Claude这一特定工具而设计。无论是将Safari MCP与Cursor、Cline、Windsurf还是你自己的MCP客户端结合使用,这些工具的功能都是完全相同的。
为什么选择Safari而不是Chrome或Playwright?
如果你之前曾经进行过浏览器自动化操作,那么你很可能使用过Puppeteer、Playwright或Selenium来控制Chrome浏览器。那么,为什么现在要选择Safari呢?
关键在于三个方面的差异:当驱动浏览器的是一个AI代理而不是测试脚本时,这些差异就显得尤为重要了。
1. 这是你的真实浏览器,使用的是你自己的会话信息。Playwright启动的无头Chromium环境其实是一个“洁净室”——它从未登录过任何账户。如果你想让代理访问你的分析仪表盘,就必须先解决认证问题:存储用户名和密码、编写登录脚本、处理双重身份验证流程以及更新令牌等。而Safari MCP则完全省去了这些步骤。它使用的就是你每天都在使用的Safari浏览器实例,这个实例已经登录到了你的仪表盘、GitHub账户或电子邮件账户中,因此代理可以直接使用这些会话信息。
2>它不会让你的笔记本电脑过热。无头Chromium是一个独立运行的浏览器引擎,它会与你已经打开的浏览器同时运行。而在笔记本电脑上,这种额外的浏览器引擎会消耗更多的CPU资源和内存,甚至还会让风扇更加忙碌。而Safari MCP使用的则是Mac系统自带的WebKit引擎,因此根本不需要额外启动任何引擎。实验数据显示,使用Safari进行自动化操作时,CPU利用率大约降低了60%;而且自动化进程是在后台运行的,因此不会占用你的屏幕显示空间。
3>网站不会将其视为机器人程序。无头浏览器往往会暴露一些识别信息,比如`navigator.webdriver`对象,或者带有明显的自动化特征。而各种反机器人检测系统——比如Cloudflare的挑战页面、reCAPTCHA以及许多B2B网站使用的WAF系统——都已经能够非常有效地识别这类程序。但是,通过操作系统正常运行的真实Safari浏览器,看起来就完全像是一个普通用户的浏览器而已。(需要明确的是:这种技术是用来自动化操作你自己的账户和网站的,并不是用来绕过他人设置的访问控制机制的。)
所有这些措施所带来的显而易见的一个代价是:Safari MCP仅适用于macOS系统。它基于WebKit和AppleScript开发而成,因此不支持Windows或Linux平台。如果你的自动化工具运行在Linux系统的持续集成环境中,那么Safari MCP就并不适合你使用;但如果你是在自己的Mac上使用它——对于一个用于代码开发的自动化工具来说,这种情况非常常见——那么这个选择确实是值得的。最后我们会诚实地讨论一下它的局限性。
安装Safari MCP
实际上,整个安装过程只需要执行一条命令即可完成,但在安装之前还需要先调整两个Safari设置。让我们按步骤来操作吧。
步骤1——启用Safari的开发功能
Safari MCP是通过在Safari中运行JavaScript来读取并控制页面内容的。因此,有两项设置必须处于开启状态:
-
打开Safari → 设置 → 高级选项,然后勾选“显示网页开发者功能”。这样就能看到“开发”菜单了。
-
进入新出现的开发菜单,再勾选“允许JavaScript执行Apple Events相关操作”。
其中第二项设置尤为重要。它使得外部的进程——也就是MCP服务器——能够要求Safari在页面上运行JavaScript代码。如果没有这个设置,所有的工具调用都会失败。
步骤2——运行服务器
npx safari-mcp
整个安装过程就是这么简单。npx命令会下载并执行相应的软件包,无需进行任何编译工作。当自动化工具第一次尝试调用相关功能时,macOS系统会弹出权限提示框,内容大概是“终端程序希望控制Safari”。点击确定即可。这是标准的自动化权限设置,你也可以 later 在系统设置 → 隐私与安全 → 自动化选项中查看这一设置。
如果你希望这个工具永久安装在你的系统中,可以执行以下命令:
npm install -g safari-mcp
步骤3——告知你的自动化工具关于MCP服务器的信息
你的AI自动化工具需要知道MCP服务器的存在。对于Claude Code来说,只需执行一条命令即可完成配置:
claude mcp add safari -- npx safari-mcp
而对于Cursor工具来说,需要在项目中创建一个名为.cursor/mcp.json的文件,并在其中添加以下内容:
{
"mcpServers": {
"safari": {
"command": "npx",
"args": ["safari-mcp"]
}
}
}
对于所有类型的自动化工具——无论是Claude Desktop、Cline、Windsurf、Continue还是VS Code——操作步骤都是相同的。你只需要告诉这些工具:“存在一个名为safari的MCP服务器,可以通过执行npx safari-mcp来启动它。”
重新启动你的自动化工具(或者重新加载其中的MCP服务器设置),它就会连接到这个服务器上。在Claude Code中,你可以使用/mcp命令来查看当前连接的服务器及其可使用的工具列表。你应该会看到safari服务器以及大约80种可用的工具。
就这样,你的自动化工具现在就已经拥有了一个浏览器功能了。
你的第一个自动化任务:读取网页内容
让我们通过最简单的任务来验证这种交互机制是否有效:让代理程序读取一个网页。
在你的代理程序中,只需用简单的语言发出指令即可:
“使用Safari的工具打开example.com,然后告诉我页面上显示了什么内容。”
实际上,在收到这个请求后,代理程序会执行两个操作。首先它会导航到目标网页:
{ "tool": "safari_navigate", "arguments": { "url": "https://example.com" } }
然后它会读取网页的内容:
{ "tool": "safari_read_page", "arguments": {} }}
safari_read_page会返回页面的标题、URL以及去除了HTML标签后的文本内容——这正是大型语言模型所需要的信息。代理程序会得到如下这样的结果:
Example Domain
https://example.com/
此域名仅用于文档中的示例说明。您可以在文学作品中使用该域名,而无需事先协调或申请许可。
然后代理程序会将这些信息传递给你。这样你就亲眼看到了自己的代理程序是如何浏览网页的。
需要特别注意的是,代理程序“如何”查看网页内容其实会影响到后续的所有操作。safari_read_page非常适合用来获取页面上的文字信息,但当代理程序需要执行具体操作(比如点击按钮、填写表格)时,仅仅拥有文本信息是不够的。这时就需要使用safari_snapshot功能:
{ "tool": "safari_snapshot", "arguments": {} }
这个功能会返回页面的访问结构树,其中每个交互元素都会被赋予一个稳定的ref标识符:
[textbox ref=0_8] "Full Name" value=""
[combobox ref=0_10] "Subject"
"Submit"
这些ref标识符是代理程序进行操作的可靠依据。因为当页面重新加载时,CSS选择器可能会失效,但快照中的ref标识符在整个页面生命周期内都是有效的。请记住这一点——这正是决定自动化脚本是一次性成功还是每次都能正常运行的关键因素。
自动化登录后的工作流程所带来的好处
读取example.com这个网页其实只是用来测试这种交互机制是否有效而已。但有一点是,无头浏览器确实无法完成的任务:
请选择你在Safari中当前处于登录状态的某个网站——比如你的分析工具、项目管理平台或者持续集成监控界面。我们就以GitHub为例吧,因为每个开发者都应该有这个账户,而查看通知页面其实是一件挺烦人的事情。我们的任务是:让代理程序打开你的GitHub通知页面,然后整理出哪些通知确实需要你关注。
向代理程序发出指令:
“打开我的GitHub通知页面,阅读内容,并将它们分为‘需要回复’和‘仅供参考’两类。”
代理程序会执行相应的操作:
{ "tool": "safari_navigate", "arguments": { "url": "https://github.com/notifications" } }
停下来,留意一下那些没有发生的事情。没有登录界面,没有OAuth认证流程,环境变量中也没有个人访问令牌。由于Safari已经完成了对你的身份验证,因此代理程序会直接进入你的真实通知页面。而一个无头版的Chromium浏览器在这里就会遇到登录障碍并停止运行。
通知列表是分阶段加载的,因此代理程序需要等待内容全部加载完毕后再进行读取。safari_wait_for这个函数会不断检查页面,直到某个选择器或特定文本出现,或者超时时间结束:
{
"tool": "safari_wait_for",
"arguments": {
"text": "Inbox",
"timeout": 10000
}
}
之后它就会开始读取内容。safari_read_page这个函数专门用于读取通知区域的内容,并将结果以纯文本的形式返回:
{
"tool": "safari_read_page",
"arguments": {
"selector": "main"
}
}
代理程序会分析这些内容,然后为你生成一个整理好的摘要。整个流程——导航、等待、读取、总结——其实只需要调用几个函数而已。
当你需要以某种特定格式获取数据,而不是只是获取原始文本——比如为了将其传递给其他系统,或者写入文件——这时就可以使用safari_evaluate函数。这个函数会在页面上执行自定义的JavaScript代码,从而得到你所需要的结果:
{
"tool": "safari_evaluate",
"arguments": {
"expression": "JSON.stringify([...document.querySelectorAll('li')].map(li => li.innerText.trim()))"
}
}
代理程序会直接使用它在页面上看到的结构来执行这个表达式,你根本不需要手动指定选择器。
你可能会想:GitHub已经有API了,为什么还要爬取页面呢?确实有这个疑问。但对于GitHub来说,它的API已经非常完善了。但这个道理是普遍适用的——你每天看到的大多数仪表盘页面,无论是你的计费门户、错误跟踪系统中的筛选视图、客户的数据分析报告,还是公司购买的某些工具的管理面板,要么没有可用的API,要么使用这些API需要花费大量的时间来进行OAuth配置。而通过Safari MCP,你正在查看的这个页面本身就已经是一个API了。代理程序就是利用你正在使用的浏览器来读取这些内容的。
这就是无头自动化所无法企及的地方。不是速度,也不是功能,而是访问权限。
处理那些棘手的问题
任何自动化的流程在开始时看起来都很简单,但往往会在执行过程中遇到一些问题。
标签页安全——代理程序绝不能劫持你的标签页
这是最可怕的一种故障情况:当你正在某个标签页上输入内容时,代理程序却切换到了那个标签页,导致你之前所做的所有工作都会丢失。Safari MCP通过为每个自动化操作的标签页添加一个标识符来防止这种情况发生——它使用window.name这个属性,因为这个属性在页面切换后依然有效——并且在每次调用时都会根据这个标识符来确定“代理程序所在的标签页”。如果无法识别出自己的标签页,代理程序就会拒绝执行操作,并会抛出一个错误,而不会进行猜测。
对于你来说,实际操作规则是这样的:让自动化脚本使用safari_new_tab打开一个新的标签页,这样它就能正常运行而不会干扰你的其他操作。千万不要直接让它指向“当前标签页”然后就认为一切都没问题。
等待动态内容加载
现代网页是在完全加载完成后才会显示内容的。如果自动化脚本过早开始执行,它就会看到一个空白的页面结构。因此,不要使用固定的延迟时间让脚本等待——应该使用safari_wait_for这个函数,它会不断检查某个选择器或特定文本是否出现,直到它们出现或者超时为止:
{
"tool": "safari_wait_for",
"arguments": {
"selector": ".results-list",
"timeout": 8000
}
}
这是解决“当我手动一步步执行自动化脚本时它能正常工作,但自动运行时就失败”这一问题的最常见方法。
框架表单的处理
如果你直接为React或Vue框架中的输入元素设置.value属性,那么该框架根本不会察觉到这个变化——它的内部状态仍然会显示为空值,因此你填写好的表单最终也会被当作空白表单提交。Safari MCP提供的safari_fill和safari_fill_form函数会使用浏览器原生的值设置机制,并触发框架所监听的input和change事件,这样一来,React、Vue、Angular以及Svelte等框架的状态就能保持同步了:
{
"tool": "safari_fill_form",
"arguments": {
"fields": [
{ "selector": "#email", "value": "jane@example.com" },
{ "selector": "#message", "value": "Looks great." }
]
}
}
对于那些依赖大量框架结构且CSS选择器容易出错的网页来说,应该回到上一节中介绍的快照引用方法——使用{ "ref": "0_9" }而不是{ "selector": "#email" }。因为快照引用可以在页面重新渲染后仍然保持有效,而CSS选择器则无法做到这一点。
这些方法其实都并不复杂。它们只不过是区分了演示用脚本和可以真正长期运行的自动化脚本之间的区别而已。
使用限制:何时不应使用它
如果一个工具教程只列举了自己的优点,那么这个教程其实并没有多大价值。在这种情况下,Safari MCP显然不是一个合适的选择。
Safari MCP仅适用于macOS系统,这是由它的技术架构决定的。该工具是基于WebKit和AppleScript开发的,因此不会为Windows或Linux平台提供版本。如果你的自动化脚本需要在Linux环境下运行,那么应该使用Playwright这个工具。
Safari MCP只能在同一台Mac电脑上驱动一个Safari浏览器进行自动化操作。它实际上是为“你的”机器专门设计的自动化工具——它是与你一起工作的编程辅助工具,而不是用于管理多台浏览器的系统。如果你需要在数据中心让50个浏览器同时执行爬取任务,那么应该使用基于Chromium的无头容器技术,而Safari MCP并不适合这种场景。
跨浏览器的测试方案仍然应该使用Playwright来编写。如果你需要编写那些必须在Chrome、Firefox和Safari上都能正常运行的端到端测试,那么就应该使用专为这些平台设计的工具。因为Safari MCP只能驱动WebKit这一个浏览器引擎。
Safari MCP会与你的电脑共享同一个浏览器窗口。由于它使用的是你自己的Safari浏览器,因此自动化脚本和你会在同一个窗口中运行。这正是它的设计初衷——但这也意味着你应该让自动化脚本在单独的标签页中运行,而不是与你在同一个窗口里争夺资源。
坦率地说,Safari MCP正是为一种特定的场景而设计的——也就是让人工智能代理在您正在使用的Mac上,针对您已经登录的网站来执行浏览操作。在这种场景下,它确实无与伦比。而在其他情况下,则应该选择那些不依赖于特定浏览器的工具。真正关键的是要弄清楚自己正处于哪种场景中。
总结
您所看到的,是一个从只能理解代码的人工智能代理,发展成了一个能够感知真实网页、并能在您已登录的账户环境下进行操作的人工智能代理。
回顾一下您所做的工作:您了解了MCP是什么,以及为什么浏览器自动化功能应该通过这样的接口来实现;您明白了为什么在Mac上使用原生的Safari引擎,会比使用Chromium这样的独立工具更有效;您只需执行一条命令并设置两个参数,就能安装并使用Safari MCP;您还进行了初步测试,并最终完成了真正有意义的事情——在已登录的页面中实现了自动化操作,而且整个过程完全不需要任何认证代码。最后,您也了解到了一些需要注意的地方,比如标签页的安全性、动态内容的处理方式、框架表单的使用方法,以及在哪些情况下应该选择其他工具。
这个理念非常重要,值得我们牢记。人工智能代理的能力取决于其所连接的工具。如果给它提供一个真正的浏览器,那么“让我编写代码”这种要求,就会变成“去查看测试网站,找出问题所在,然后告诉我出了什么故障”。这样的智能助手,才是真正高效的合作伙伴。
Safari MCP是基于MIT许可证开发的开源项目,除了您在这里使用过的那些工具外,它还提供了大约80种其他功能工具,包括截图功能、网络监控工具、存储管理工具、无障碍访问检测工具以及多标签页工作流程支持。该项目的代码仓库及所有工具的详细信息,请访问github.com/achiya-automation/safari-mcp。尝试将您的人工智能代理与这个项目连接起来,看看当它真正能够“观察周围环境”时,能展现出什么样的能力。