macOS控制协议服务器

一个通过MCP协议将macOS系统控制和Apple应用作为结构化工具暴露给Claude的服务,支持权限管理和审计日志。

  • by lacausecrypto
  • TypeScript
  • 本地部署
  • 1 星标
  • 更新于
在 GitHub 查看 lacausecrypto/apple-mcp-server

使用说明

苹果MCP服务器

一个MCP服务器,将macOS系统控件和Apple应用程序作为Claude的结构化工具公开。使用TypeScript和官方 @modelcontextprotocol/sdk.

31个工具,303个动作,4个资源。 每个苹果应用程序都是一个MCP工具 action 参数。

需求

  • macOS(使用Apple Silicon在macOS 15上测试)
  • Node.js 18+
  • Claude CLI或任何兼容MCP的客户端
  • 某些功能需要在Safari>开发中“允许来自Apple活动的JavaScript”

设置

git clone https://github.com/lacausecrypto/apple-mcp-server.git
cd apple-mcp-server
npm install
npm run build

在MCP配置中注册(~/.claude/.mcp.json 或项目 .mcp.json):

{
  "mcpServers": {
    "apple": {
      "command": "node",
      "args": ["/absolute/path/to/apple-mcp-server/dist/index.js"]
    }
  }
}

运作原理

Claude calls apple_music with {action: "now_playing"}
  → index.ts checks permissions (OPEN → proceed)
  → dispatches to domains/music.ts handler
  → handler calls runAppleScript() in executor.ts
  → AppleScript written to temp file, executed via osascript
  → result returned to Claude: "Bohemian Rhapsody — Queen (3:21/5min)"

权限系统

行动分为三个层次。 默认保守 --破坏性行为需要明确的确认。

打开(默认)

只读和安全操作立即执行。无需确认。

Claude: apple_volume.get → "Volume: 56%"
Claude: apple_music.now_playing → "Bohemian Rhapsody — Queen"
Claude: apple_sysinfo.battery → "95%; charging"

受保护(27项行动)

破坏性或不可逆的操作将返回预览并要求 confirm: true 执行。

Claude: apple_finder.empty_trash
→ "⚠️ apple_finder.empty_trash requires confirmation.
   Reason: Permanently deletes all items in Trash
   To proceed, call again with confirm: true."

Claude: apple_finder.empty_trash with confirm: true
→ "Trash emptied"

受保护的操作包括:文件删除/移动/重命名、发送电子邮件/iMessage、在推特上发帖/点赞/回复、删除联系人/笔记/提醒/事件、强制退出应用程序、系统睡眠。

已阻止(3个动作)

从未被处决。返回一个错误,并指示取消阻止。

Claude: apple_system.shutdown
→ "🚫 apple_system.shutdown is blocked.
   Blocked by default (dangerous system action)."

默认情况下被阻止: shutdown, restart, logout.

配置

通过以下方式覆盖默认值 ~/.config/apple-mcp/permissions.json:

{
  "blocked": ["empty_trash"],
  "unprotected": ["like", "send"],
  "unblocked": ["restart"],
  "protected": ["play", "pause"]
}
关键 效果
blocked 将操作添加到BLOCKED(默认值之上)
unprotected 从受保护状态转移→ 打开(跳过确认)
unblocked 从阻塞状态移动→ 受保护(确认后允许)
protected 从OPEN移动→ 受保护(添加确认)

动作可以指定为 "actionName" (全球)或 "apple_tool.actionName" (具体)。

速率限制

防止垃圾邮件和滥用。两个独立的滑动窗口计数器:

  • 全球:每分钟最多30次工具调用(所有操作)
  • 受保护的:每分钟最多5次确认的破坏行为

超过此时间后,呼叫将被拒绝并等待一段时间。可配置:

{ "rate_limit": { "global_per_min": 60, "protected_per_min": 10 } }

干运行模式

预览每个操作而不执行。有助于测试Claude会做什么:

{ "dry_run": true }
Claude: apple_finder.delete(path: "~/old.pdf", confirm: true)
→ "[DRY RUN] Would execute apple_finder.delete (confirmed) — no action taken."

所有操作都返回预览。什么都没有被执行。审计日志记录 dry_run 事件。

审计日志

每个工具调用都以结构化JSON行的形式记录在 ~/.local/occ/rag/apple-mcp-audit.jsonl:

{"ts":"2026-03-31T18:36:49Z","tool":"apple_volume","action":"get","permission":"open","confirmed":false,"dry_run":false,"result":"ok","duration_ms":104,"output":"Volume: 56%"}
{"ts":"2026-03-31T18:36:52Z","tool":"apple_finder","action":"empty_trash","permission":"protected","confirmed":false,"dry_run":false,"result":"protected_no_confirm","duration_ms":0}

可通过jq查询:

# All blocked calls
cat apple-mcp-audit.jsonl | jq 'select(.result=="blocked")'

# Calls per tool
cat apple-mcp-audit.jsonl | jq -s 'group_by(.tool) | map({tool: .[0].tool, count: length})'

# Average duration per tool
cat apple-mcp-audit.jsonl | jq -s 'group_by(.tool) | map({tool: .[0].tool, avg_ms: (map(.duration_ms) | add / length)})'

工具

工具 # 操作 已保护/已阻止
apple_volume 7 get set up down mute unmute info —
apple_brightness 2 up down —
apple_clipboard 3 get set clear —
apple_apps 7 open quit is_running list_running hide activate force_quit force_quit
apple_sysinfo 19 battery disk uptime ip macos_version cpu memory top_processes hostname resolution summary bluetooth_devices audio_devices printers displays usb_devices network_interfaces serial_number model —
apple_music 26 play pause stop next prev restart now_playing shuffle_on shuffle_off repeat_off repeat_one repeat_all play_playlist list_playlists set_volume love dislike search play_song queue_next add_to_playlist create_playlist get_lyrics radio remove_from_playlist delete_playlist delete_playlist remove_from_playlist
apple_spotify 17 play pause next prev now_playing set_volume shuffle playpause play_playlist list_playlists like repeat_off repeat_on search play_track current_album current_artist —
apple_safari 17 open_url current_url current_title list_tabs close_tab new_tab reload page_text reading_list js_execute back forward bookmarks history_recent private_window close_window tab_count —
apple_chrome 14 open_url current_url current_title list_tabs close_tab reload js_execute back forward new_window new_incognito close_window tab_count new_tab —
apple_mail 10 unread_count check unread_list read_body search draft mailboxes send mark_all_read move_to_trash send mark_all_read move_to_trash
apple_calendar 8 list today tomorrow create_event week date_events delete_event modify_event delete_event modify_event
apple_reminders 10 list_lists list add complete set_due set_priority add_note overdue create_list delete delete
apple_notes 9 list create read search folders move append count delete delete
apple_finder 20 open_folder reveal desktop_files trash_count create_folder file_info copy get_selection tags set_tag list_folder disk_info empty_trash delete move rename eject_disk eject_all set_wallpaper create_alias empty_trash delete move rename eject_disk eject_all set_wallpaper
apple_windows 14 frontmost_app minimize minimize_all maximize left_half right_half fullscreen close hide_app hide_others center resize list_windows switch_to —
apple_system 30 sleep_display lock dark_mode dnd_on dnd_off caffeinate decaffeinate wifi_status wifi_on wifi_off wifi_network bluetooth_on bluetooth_off run_shortcut list_shortcuts open_prefs display_settings sound_settings network_settings airdrop screen_saver login_items time_machine audio_output audio_input sleep eject_all_disks shutdown restart logout sleep eject_all_disks ·已阻止: shutdown restart logout
apple_screenshot 5 full clipboard timed area window —
apple_notification 1 send —
apple_keyboard 8 type_text press_key copy paste undo redo save select_all —
apple_tts 3 say list_voices stop —
apple_twitter 11 draft save list_drafts feed notifications dm_check post post_draft reply like retweet post post_draft reply like retweet
apple_facetime 2 call audio —
apple_maps 2 open directions —
apple_contacts 9 search get list create groups group_members delete update_phone update_email delete update_phone update_email
apple_photos 10 albums recent search favorites album_contents export count create_album add_to_album import —
apple_messages 5 recent unread conversation search send send
apple_podcasts 7 now_playing play pause next shows episodes search —
apple_books 5 library reading_now collections search open —
apple_iwork 11 pages_create pages_open pages_export_pdf numbers_create numbers_open numbers_export_pdf keynote_create keynote_open keynote_export_pdf keynote_start_slideshow keynote_stop_slideshow —
apple_preview 4 open list_open close close_all —
apple_textedit 7 create open get_text set_text save close list_open —

安全

这提供了什么

许可制度 --执行前执行三级访问控制(打开/保护/阻止)。受保护的行动需要 confirm: true.可通过JSON文件配置。

速率限制 --滑动窗口计数器:全局30次/分钟,保护5次/分钟。防止滥用循环。

干运行模式 --所有操作都会返回预览而不执行。安全测试克劳德会做什么。

结构化审计日志 --每个调用都记录为JSON行(工具、操作、权限、结果、持续时间)。可通过jq查询。

输入验证 --不是沙盒。服务器在将输入传递给AppleScript或shell之前对其进行验证:

功能 它的作用
safePath() 允许名单:只允许以下路径 ~/, /tmp, /Volumes/nvme, /Applications其他都被拒绝了。
safeAS() 逃跑 \ 和 " 用于AppleScript字符串插值。防止在引号字符串内注入脚本。
safeSQL() 逃脱报价,条 ;, --, /* */、UNION、DDL关键字、十六进制文字。用于iMessage sqlite3查询。
safeAppName() 从应用程序名称中删除shell元字符。

并发 — withLock() 序列化Safari和Chrome选项卡操作以防止竞争条件。

执行安全 --AppleScript通过temp运行 .scpt 文件(不是 -e 旗)。Shell命令使用 execFile 带数组参数(无shell插值)。

自动启动 --未运行的应用程序会自动启动 -600 错误,重试一次。

日志记录 --所有工具调用、权限检查和错误都记录到 ~/.local/occ/rag/apple-mcp.log.

这没有提供什么

  • 没有沙盒。 Node进程以您的完全用户权限运行。它可以做任何你能做的事情。
  • 无网络隔离。 服务器通过stdio(仅限本地)进行通信,但它执行的操作(电子邮件、推特等)会到达互联网。
  • 没有加密。 日志文件和权限配置是纯文本的。
  • MCP传输上没有身份验证。 任何可以连接到stdio的进程都可以调用工具。
  • safePath是一个排外主义者,而不是监狱。 它阻止在系统路径上进行操作,但是 ~/ 是允许的,包括 ~/Library, ~/.ssh等等。

这适用于您自己的Mac上的个人使用。 它不是为多用户环境或对抗环境而设计的。

资源

URI 描述
apple://now-playing 来自Apple Music或Spotify的当前曲目
apple://system-info 电池、磁盘、正常运行时间、CPU负载、RAM
apple://open-tabs/safari 所有打开的Safari选项卡
apple://open-tabs/chrome 所有打开的Chrome标签页

添加新域

  1. 创建 src/domains/myapp.ts (模式见现有域)
  2. 导入并注册 src/registry.ts
  3. 如果具有破坏性,请添加 DEFAULT_PROTECTED 在 src/permissions.ts
  4. npm run build && npm test

测试

npm run build
npm test    # 135 tests (executor + registry + permissions)

已知限制

  • 书籍和播客 AppleScript支持有限。某些操作返回通用错误。
  • 声破天 不会通过AppleScript显示播放列表或“点赞”。使用URL方案作为回退。
  • Safari JS执行 需要在Safari>开发中选择“允许来自Apple事件的JavaScript”。
  • 推特/X 操作取决于当前的DOM结构,而DOM结构可能会发生变化。
  • safePath 允许所有 ~/ --包括敏感目录,如 ~/.ssh 和 ~/Library.

日志记录

~/.local/occ/rag/apple-mcp.log

项目结构

src/
  index.ts          Server entry, permission enforcement, tool dispatch
  permissions.ts    Three-level permission system with config file
  types.ts          Interfaces: ExecResult, DomainModule, DomainAction, ResourceDef
  executor.ts       Execution layer: AppleScript, shell, caching, locking, safety
  registry.ts       Domain registry and JSON Schema builder
  domains/          31 domain files (one per Apple app/category)
  resources/        4 MCP resource providers
tests/
  executor.test.js  Safety function tests
  registry.test.js  Domain registration tests
  permissions.test.js  Permission system tests

许可证

麻省理工学院