字节笔记本
2026年9月6日
无头 Xcode:用 MCP 从提示词跑到模拟器
Xcode 27 的 beta 里藏了一个容易被忽略的新东西:无头 MCP 服务器。它把构建、预览、模拟器这些能力通过 MCP 暴露给编码 agent,不开 Xcode 界面也能跑完一整个 iOS 应用的开发流程。iOS 开发者 Artem Novichkov 用 Xcode 27.0 beta 6 加 Claude Code 做了一次完整实测:从一句提示词创建项目,到生成 SwiftUI 应用、渲染四个预览状态,再到模拟器上验证交互,全程没有打开 Xcode。本文按他的操作顺序走一遍。
启动无头服务器
三条命令搞定:
$ sudo xcrun mcp-server enable
$ xcrun mcp-server start
$ xcrun mcp-server statusenable 默认不开 unsafeAlwaysAllowAllAgents,也就是说每个 agent 都要单独批准。status 能看到权限状态、运行状态和当前打开的工作区。
项目接线:注册服务器,导出技能
在仓库根目录注册 MCP 服务器,写入 .mcp.json,团队共享,每个人本地批准:
$ claude mcp add --scope project \
-e DEVELOPER_DIR=/Applications/Xcode-beta.app/Contents/Developer \
xcode -- xcrun mcpbridgeDEVELOPER_DIR 指向 beta 版 Xcode,不影响 xcode-select 的全局选择;如果不用 beta,这个变量可以省略。
接下来导出 Apple 自带的 agent 技能:
$ xcrun agent skills export --output-dir ~/Developer/ReadingListExample/.claude/skills一共导出 10 个技能,包括 device-interaction、swiftui-specialist、swiftui-whats-new-27 等。技能存放在 Xcode 内部,导出时通过 XPC 执行,所以 Xcode 会短暂启动显示窗口。这是整个流程里唯一一次需要 Xcode 界面的时刻。两个坑要注意:--output-dir 必须用绝对路径;已存在的技能默认跳过,想更新要加 --replace-existing。技能放在项目的 .claude/skills/ 下,Claude Code 才能自动发现,且 agent 必须在项目目录里启动。
权限机制:先批准,再动手
首次连接时,agent 只被允许调用 XcodeOpenWorkspace 或 XcodeNewProject,其他工具一律拒绝。批准提示来自无头服务而不是 Claude Code,批准后 agent 能访问指定目录的整个子树。"Always Allow" 永久保留,"Allow for 24 Hours" 会过期。
批准后菜单栏会出现 Xcode 锤子图标:无 agent 连接时是纯图标,单独批准模式加蓝色角标,不安全模式加黄色角标。图标菜单里的 Session Logs… 指向 ~/Library/Logs/Xcode/XcodeService/,Quit 等同于 xcrun mcp-server stop。
如果想跳过批准提示(不推荐,但确实有这个开关):
sudo xcrun mcp-server enable --unsafe-always-allow-all-agents清除已存授权:
sudo xcrun mcp-server clear-permissions从提示词创建项目
作者用自然语言要求创建一个多平台 SwiftUI 应用 ReadingListExample。agent 先用 XcodeListTemplates 拿到模板列表和选项的 schema,再用 XcodeNewProject 实例化,关键参数:
{
"templateIdentifier": "com.apple.dt.unit.multiPlatform.app",
"productName": "ReadingListExample",
"options": { "storageType": "None", "testingSystem": "Swift Testing" }
}注意 destinationPath 传的是父目录,不是项目路径。随后 XcodeOpenWorkspace 打开 .xcodeproj,返回工作区标识符,后面的工具都靠它定位。
用 swiftui-specialist 技能生成应用
提示词里写清需求:Book 模型(标题、作者、页数、当前页、阅读状态),@Observable store 带 loading/loaded/failed 三态,分组的 List(Currently Reading / Finished),详情页带 "Mark as Finished" 按钮,添加书籍的 sheet,所有变更用 OSLog 记到 ReadingList 类别。
生成的文件包括 Book.swift、ReadingListStore.swift、ReadingListView.swift、BookRows.swift、BookDetailView.swift、AddBookView.swift、Logger+ReadingList.swift。用 BuildProject 工具验证,构建成功,耗时 1.037 秒,无错误。
不开 Canvas 渲染预览
作者把四个状态(loading、empty、loaded、failed)合并成一个 #Preview(arguments:),每个变体有可读名称,然后交给 RenderPreview。这个工具会构建预览并返回 PNG 文件路径,不打开 Canvas。
一个细节:必须明确让 agent 去看渲染出来的图片内容,而不是只确认"渲染成功"。看完图可以让 Claude 把结果整理成 HTML artifact,每个状态一张卡片,附上设备、系统版本和外观信息,方便对比各状态之间的间距、标题位置和文字换行差异。
在模拟器上验证
最后一步的提示词完全是自然语言:"Run the app on the iPhone 17 Pro simulator and check that finishing a book moves it out of Currently Reading…",没有坐标,没有工具名。agent 自己开启交互会话,通过 MCP 启动模拟器,构建、安装、启动应用。
每次屏幕捕获返回 PNG 加无障碍层级,每个元素带 label、frame、value 和预计算好的 hitPoint 点击坐标。交互是一套小命令语言,可以链式调用:t x y 点按、w 等待、sender keyboard kbd <文本> 输入,还有滑动和硬件按键,语法记录在 device-interaction 技能里。
验证靠两条线索:UI 截图加 OSLog 日志(比如 "Loaded 4 books"、标记完成、添加书籍)。检查完关闭会话,释放模拟器。
无头模式的代价
失去的东西也列一下:Canvas 实时选择、视图调试器、断点槽、Organizer。LLDB 断点仍然可用,只是编辑器里看不见。RenderPreview 用静态 PNG 取代了交互式 Canvas。构建、预览、测试、模拟器运行、控制台输出这些都能通过工具拿到,真需要可视化调试时,随时可以用 Xcode 打开项目,两条路不冲突。
权限存在哪里
授权数据存在 Xcode group container 里的一个 JSON 文件(路径含 CodingAssistant/HeadlessPermissions/mcp-server.json),字段包括 agentPermissions(签名信任,比如 signingIdentifier com.anthropic.claude-code 和 teamIdentifier)、folderPermissions、enabled、version。这个文件是只读的,服务会重写它,想改就改不进去。脚本要拿状态应该走 status --format json,里面还带 openWorkspaces 的 activeScheme 和 running 状态,可以在构建前校验预期的 team identifier。
写在最后
配置好之后,Claude 在不打开 Xcode UI 的情况下完成了:创建项目、按 Apple 技能生成应用、渲染 4 个预览状态、在模拟器上验证完整交互流程。对日常写 SwiftUI 的人来说,Xcode 27 这个无头模式意味着 agent 可以真正接手"建项目、跑起来、看效果"这一整段,人只需要看结果。
作者另外维护了 xcode-tools-docs 仓库,详细记录各个 MCP 工具的用法,值得收藏。