mcpField
M
macos-mcp-server

macos-mcp-server

本地服务
github.com/cyanheads生产力41 周前更新

关于 macos-mcp-server

用于控制本地 Mac 的 MCP 服务,面向需要管理应用、窗口、系统设置及其他桌面功能的 Agent。功能涵盖音频和显示器、截图、Finder、通知与专注模式;主机必须运行 macOS。

主要功能

  1. 01启动、退出、隐藏和显示应用
  2. 02列出、聚焦和排列窗口
  3. 03查看和更改音频设置
  4. 04管理显示器和外观
  5. 05截取屏幕并控制 Finder
  6. 06发送通知并设置专注模式

适用场景

  1. 01在桌面工作流中排列应用窗口
  2. 02切换音频设备或调整系统音量
  3. 03截取显示器、应用窗口或屏幕区域
  4. 04查看系统信息并控制专注模式

MCP 相关

向 MCP 客户端提供 13 个工具和三个资源,支持 STDIO 或 Streamable HTTP。它控制本地 macOS 主机,实际使用场景是通过 STDIO 在本机运行。

配置

本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。

mcp.json · 通用配置
{
"mcpServers": {
"cyanheads-macos": {
"command": "npx",
"args": [
"-y",
"@cyanheads/macos-mcp-server"
],
"env": {
"MACOS_SCREENSHOT_DIR": "<MACOS_SCREENSHOT_DIR>",
"MACOS_DISPLAY_LAYOUTS": "<MACOS_DISPLAY_LAYOUTS>",
"MCP_LOG_LEVEL": "<MCP_LOG_LEVEL>",
"MCP_TRANSPORT_TYPE": "<MCP_TRANSPORT_TYPE>",
"MCP_HTTP_HOST": "<MCP_HTTP_HOST>",
"MCP_HTTP_PORT": "<MCP_HTTP_PORT>",
"MCP_HTTP_ENDPOINT_PATH": "<MCP_HTTP_ENDPOINT_PATH>",
"MCP_AUTH_MODE": "<MCP_AUTH_MODE>"
}
}
}
}
启动命令npx -y @cyanheads/macos-mcp-server
需要配置的环境变量
MACOS_SCREENSHOT_DIRDefault directory for screenshot files. Defaults to ~/Desktop when empty.
MACOS_DISPLAY_LAYOUTSJSON object mapping layout names to displayplacer argument strings for apply_layout.
MCP_LOG_LEVELMinimum log level for output: debug, info, notice, warning, error.
MCP_TRANSPORT_TYPESelects the HTTP transport.
MCP_HTTP_HOSTThe hostname for the HTTP server.
MCP_HTTP_PORTThe port to run the HTTP server on.
MCP_HTTP_ENDPOINT_PATHThe endpoint path for the MCP server.
MCP_AUTH_MODE密钥Authentication mode: none, jwt, or oauth.

本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。

安装包
npm@cyanheads/macos-mcp-server0.3.0

工具

13

收录的工具列表 · 更新于 6 天前

此工具无需参数。

详细介绍

@cyanheads/macos-mcp-server

通过 MCP 控制 macOS 系统设置、应用、窗口、音频、显示器、屏幕截图和专注模式。支持 STDIO 或 Streamable HTTP。

13 个工具 • 3 个资源

Version License MCP SDK npm TypeScript Bun

Install in Claude Desktop Install in Cursor Install in VS Code

Framework

仅限 macOS。 此服务控制本地 macOS 系统,因此主机必须运行 macOS。为完整起见,支持 HTTP 传输,但实际使用场景是 stdio:在本地运行,并让 MCP 客户端连接到它。


概述

通过 Agent 控制你的 Mac:启动、退出和排列应用与窗口,切换音频和显示设备,截取屏幕截图,使用 Finder,发送通知,并切换专注模式。

工具
工具描述
macos_get_info系统快照:电池电量和充电状态、电源来源、Wi-Fi 连接状态和网络名称、主机名、macOS 版本、运行时间和显示器数量;探测失败时,相应字段为 null,并列入 unavailable
macos_check_permissions使用 Apple 无需触发提示的检查,报告调用进程的辅助功能、屏幕录制和自动化 > Finder 状态;无法判断时返回 null,并附上原因
macos_manage_apps列出正在运行的应用、获取最前端应用、启动、退出、强制退出、隐藏或显示应用
macos_manage_windows列出、聚焦、移动、调整大小、移动并调整大小、最小化、全屏显示或关闭所有可见应用中的窗口
macos_control_volume获取或设置系统输出音量(0–100)和静音状态
macos_control_audio列出音频设备、获取当前默认设备,或切换默认输入/输出设备
macos_control_appearance获取或设置深色/浅色模式
macos_control_system锁定屏幕或让显示器进入睡眠状态
macos_take_screenshot截取主显示器、由 display_index 指定的显示器、指定名称的应用窗口或指定像素区域;保存为 PNG,除非 overwrite 为 true,否则不会替换现有文件;可选 JPEG 预览
macos_manage_displays列出已连接的显示器(包括各自的 index)和已配置的布局名称,并应用指定名称的显示器布局预设
macos_send_notification向 macOS 通知中心发送通知
macos_manage_focus获取或设置“勿扰”/专注模式
macos_manage_finder获取最前端路径、当前所选项目、显示项目、使用应用打开项目,或将项目移到废纸篓
资源
资源描述
macos://system/info当前 macOS 系统快照:电池、电源来源、Wi-Fi 连接状态和网络名称、主机名、版本、运行时间和显示器数量;探测失败时,相应字段为 null,并列入 unavailable
macos://audio/devices所有音频输入和输出设备,包括当前默认设备。需要 SwitchAudioSource CLI。
macos://displays已连接显示器清单,包括持久 ID、索引(macos_take_screenshot 和 macos_manage_windows 使用的 display_index)、类型、分辨率、原点、旋转角度、缩放比例和启用状态。需要 displayplacer CLI。

资源数据也可通过 macos_get_info、macos_control_audio(action=list)和 macos_manage_displays(action=list)访问。

功能参考

macos_get_info 工具
  • 电池电量(0–100)、充电状态和电源来源(AC、Battery、UPS);没有电池的台式机上为 null。当电池电量受充电上限限制时(AC attached; not charging),charging 为 false;处于 finishing charge 状态时为 true
  • 根据 Wi-Fi 设备的链路状态获取 Wi-Fi 连接状态;已连接时,如果 macOS 不提供网络名称,该名称为 null(读取网络名称需要主机应用获得“定位服务”访问权限)。名称在 content[] 中以代码跨度形式呈现,因此其中的 Markdown 字符会按名称原样显示
  • 主机名和运行时间(来自 Node 的 os 模块),macOS 版本字符串(例如 "15.1")
  • 已连接的显示器数量(无头 Mac 上为 0)
  • 探测失败时,相应字段为 null,并列入 unavailable;不会填入任何值。若 battery: null 且 unavailable 中没有 battery,则表示 Mac 没有电池
  • 无需权限

macos_check_permissions 工具
  • 报告以下权限:辅助功能(每个 macos_manage_windows 操作,包括 list;macos_manage_apps 的 hide 和 show;window_title 的 frontmost 读取;以及 macos_control_system 的 lock 在回退到屏幕保护程序前发送的 ⌃⌘Q 按键)、屏幕录制(窗口截图),以及 自动化 > Finder(macos_manage_finder 的 frontmost_path、get_selection 和 trash);使用 Apple 提供的无需提示检查:AXIsProcessTrusted、CGPreflightScreenCaptureAccess,以及关闭提示的 AEDeterminePermissionToAutomateTarget
  • 每个字段的值为 true、false;如果检查无法确定,则为 null;unknown_reasons 会说明每个 null 值未知的原因。在 macOS 请求用户授权前,以及 Finder 未运行时,自动化 > Finder 会返回 null。通知始终为 null:macOS 没有无需提示的检查方式
  • 返回调用进程的名称(例如 "ghostty"、"node"),以便你知道应授予哪个进程权限
  • 只读——不会有检查向其他应用发送 Apple 事件、截取屏幕或显示权限提示
  • 调试其他工具为何失败时,请先运行此工具

macos_manage_apps 工具
  • list — 列出正在运行的面向用户的应用,并提供名称、Bundle ID、PID、可见和前台标记
  • frontmost — 提供当前活动应用的名称、Bundle ID、PID 和前台窗口标题
  • launch — 按 app_name 或 bundle_id 打开或激活应用;hidden=true 会在后台启动应用
  • quit(通过 AppleScript 正常退出)与 force_quit(使用 SIGKILL 强制退出,不提示保存)
  • hide / show — 切换可见状态;需要辅助功能权限
  • quit、force_quit、hide 和 show 会先检查应用是否正在运行——按 tell application 的方式根据应用名称解析("Visual Studio Code" 会找到 Code 进程),但不会启动应用;如果应用未运行,则返回 not_running
  • 类型化错误:app_not_found(launch 所指定的应用未安装)、no_frontmost_app(System Events 未报告任何前台进程)、not_running、accessibility_required

macos_manage_windows 工具
  • list — 列出所有应用中可见的窗口,包括位置、大小、minimized 和 fullscreen 状态(分别读取自窗口的 AXMinimized / AXFullScreen 属性;无法读取时为 null),以及 display_index:窗口重叠面积最大的显示器(索引由 macos_manage_displays action=list 报告);无法读取显示器边框或窗口未与任何显示器重叠时为 null。标题相同的两个窗口,以及属于名称相同的两个进程的窗口,都会分别列出
  • focus、move、resize、move_resize、minimize、fullscreen、close — 通过 window_title(在任意应用中精确匹配,忽略大小写;同一应用有多个同名窗口时,选择最前面的窗口)或 app_name(该应用中最前面的窗口)指定目标;window_title 优先,之后忽略 app_name
  • app_name 根据正在运行的应用的应用名称("Visual Studio Code")、本地化名称("Code")或进程名称("firefox")匹配,忽略大小写;匹配结果从正在运行的应用列表中解析,因此不会为查找应用而启动应用或向其发送 Apple 事件
  • move 需要 x / y,resize 需要 width / height(两者均须大于 0),move_resize 需要全部四个值
  • minimize — minimized: true(默认值)会最小化窗口,false 会还原窗口;仅当窗口尚未处于目标状态时才写入窗口的 AXMinimized 属性,并且只有读取到窗口处于请求的状态时才算成功
  • fullscreen — fullscreen: true 进入全屏,false 退出全屏;省略时切换窗口当前状态。状态不同时,会像 focus 一样将目标窗口置于前台,并设置其 AXFullScreen 属性(不使用按键,因此绝不会切换当前具有键盘焦点的窗口);等待最多 4 s,直到读取到窗口处于请求的状态
  • 所有操作(包括 list)都会调用 System Events 脚本,并且需要辅助功能权限;被拒绝时返回 accessibility_required,绝不会将其误报为“没有窗口”。focus 会将窗口置于前台,并将其应用带到前台;任何操作都不会启动应用
  • 每次写入都会在同一个脚本中查找、修改并回读同一个窗口;window.app 是查找到该窗口时所属的进程;close 不返回窗口
  • 类型化错误:not_running(app_name 未命名任何正在运行的应用)、window_not_found、accessibility_required、minimize_unsupported 和 fullscreen_unsupported(窗口缺少 AXMinimized / AXFullScreen 属性、该属性不可设置,或窗口在 4 s 内未达到请求的状态);osascript 超时或失败时会保留自身错误,不会将其误报为 window_not_found

macos_control_volume 工具
  • get — 获取默认输出设备的当前输出音量(0–100)和静音状态。对于没有软件音量控制的输出设备(许多 HDMI 和 DisplayPort 显示器),level 和 muted 为 null,output_device 为设备名称(无法识别时为 null)
  • set — 必须提供 level(0–100)、muted 或两者;level=0 不会静音。对于不支持所请求值的软件控制的输出设备,操作会失败并返回包含设备名称的 volume_unsupported,且不会进行任何更改
  • 始终返回当前状态,并回显 action
  • 类型化错误:volume_unsupported;取消调用后会停止设备查找并以已取消状态结束,绝不会返回 output_device: null

macos_control_audio 工具
  • list — 列出所有输入 / 输出设备,并标记 is_default;使用 type 进行筛选
  • current — 获取默认输入和输出设备的名称
  • switch_output / switch_input — 按名称进行不区分大小写的部分匹配("MacBook" 可匹配 "MacBook Pro Microphone")
  • 音量级别单独控制(macos_control_volume)
  • 需要 SwitchAudioSource CLI(brew install switchaudio-osx);类型化错误:device_not_found、switchaudio_unavailable

macos_control_appearance 工具
  • get — 返回 dark_mode: true/false,并回显 action
  • set 需要 mode: "dark" | "light" | "toggle" — dark/light 是幂等操作,toggle 每次调用都会切换
  • 执行 System Events 脚本;当“自动化”>“System Events”权限被拒绝时,返回类型化错误 accessibility_required

macos_control_system 工具
  • lock — 通过辅助功能发送 ⌃⌘Q;未授予辅助功能权限时,改用 ScreenSaverEngine 二进制文件(已取消的调用不会回退)
  • sleep_display — 执行 pmset displaysleepnow;无需权限
  • 两项操作都会立即生效,并可通过任意输入恢复(唤醒/解锁)

macos_take_screenshot 工具
  • target:screen(仅主显示器,即带有菜单栏的显示器)、display(从 0 开始的整数 display_index,默认值为 0:即 macos_manage_displays action=list 报告的 index)、region(像素矩形,width/height 必须大于 0)——无需屏幕录制权限;window(通过 app_name 指定,匹配方式与 macos_manage_windows 相同:应用名称、本地化名称或进程名称,不区分大小写;截取该应用最前方且位于屏幕上的窗口)需要屏幕录制权限。若无法确定屏幕录制权限检查结果,仍会执行窗口截取,并在 notice 中说明原因
  • 始终保存全分辨率 PNG。省略 path(或传入 "")时,会在 MACOS_SCREENSHOT_DIR 中创建新的 screenshot-<timestamp>.png(未设置时为 ~/Desktop),若文件已存在,则依次添加 -2、-3、……,而不是替换文件。自定义 path 必须以 .png 结尾(不区分大小写);~/ 会展开为主目录;其目录必须存在,并且解析符号链接后必须位于 MACOS_SCREENSHOT_DIR、/tmp 或主目录内。返回的 path 是解析后的路径(/tmp 路径会显示为 /private/tmp 下的路径)
  • 绝不替换已存在的条目:若路径已存在,则失败并返回 path_exists,除非设置了 overwrite: true;此时仅替换调用检查该路径时存在的普通 .png 文件(绝不替换符号链接或目录)。检查后在该路径创建的条目,或之后放到该文件位置的条目,绝不会被替换,并会导致 path_exists
  • 每次调用都会在开始后的 55 秒内结束,包括 app_name 查找;超时后会以 Timeout(-32004)失败,且不写入任何内容。已取消的请求同样不会写入任何内容
  • include_data=true 时会返回 JPEG 预览图,最长边不超过 1024 px(较小的截取图保持原尺寸),质量为 70:作为第一个 content[] 块(image/jpeg)返回,并以 base64 编码的 preview 以及 preview_width/preview_height 的形式包含在 structuredContent 中
  • width/height(以及预览图的宽高)从图像文件头读取
  • 类型化错误:screen_recording_required、window_not_found(应用未运行,或应用没有位于屏幕上的窗口;窗口查找期间发生 osascript 超时或失败时,保留其自身错误)、display_not_found、path_not_writable(也适用于无法解析或搜索的目录,包括路径过长的目录)、path_exists;若 path 不以 .png 结尾、包含 U+0000,或文件名/完整解析路径超过文件系统限制(文件名 255 字节,完整路径 1024 字节),则会以 -32602 拒绝,并返回原因 invalid_arguments
macos_manage_displays 工具
  • list — 每台显示器的信息:index(在 NSScreen.screens 中从 0 开始的位置;macos_take_screenshot 和 macos_manage_windows 使用的 display_index;没有对应 NSScreen 条目的显示器(例如已停用的显示器)为 null)、持久 ID、类型("MacBook built in screen" 或 "<N> inch external screen")、分辨率、刷新率(macOS 未报告时为 "N/A")、原点、旋转、缩放和启用状态(镜像其他显示器时也为 true);此外还有 current_config(可重现当前排列的 displayplacer 命令)和 layouts(在 MACOS_DISPLAY_LAYOUTS 中配置的名称;没有配置时为 [])。当 MACOS_DISPLAY_LAYOUTS 无效时,list 仍会成功,但会省略 layouts,并在 notice 中指出该变量。当 NSScreen 读取失败时,会省略 index,并在 notice 中说明原因;包括因并发的 System Events 枚举而等待 15 s 的读取
  • apply_layout — 激活 MACOS_DISPLAY_LAYOUTS 中的命名预设(即 list 返回为 layouts 的名称之一);绝不接受调用方传入原始 displayplacer 参数。若 MACOS_DISPLAY_LAYOUTS 无效,apply_layout 会以 layouts_invalid(ConfigurationError)失败,而不会报 layout_not_found
  • 需要 displayplacer CLI(brew install displayplacer);类型化错误:displayplacer_not_found、displayplacer_output_unrecognized、layout_not_found、layouts_invalid
  • displayplacer 运行失败(非零退出、信号、中止或超时)时,调用会失败,而不会返回其输出内容;显示器信息块缺少列表会返回其值的某个字段,或列表缺少排列命令时,会以 displayplacer_output_unrecognized 失败。可解析 displayplacer 1.3.0 和 1.4.0

macos_send_notification 工具
  • 必填 title;body、subtitle 和 sound=true(默认通知声音)为可选项
  • 每次调用都会创建一条新通知,不具备幂等性
  • 发布通知无需权限;若当前启用了“专注模式”(例如“勿扰模式”),除非该模式允许发布通知的应用,否则通知会被静音

macos_manage_focus 工具
  • get — 尽力读取 ~/Library/DoNotDisturb/DB/Assertions.json,并返回 status: "active" | "inactive" | "unknown";无法读取或解析该文件时返回 unknown(附带 reason),例如服务器进程无权读取该文件时;已取消的调用会以已取消状态结束,绝不会返回 unknown
  • set — 运行快捷指令库中名为 "Set Focus" 的快捷指令,并将 {"mode": …, "enabled": …} 作为 JSON 输入传给它。该快捷指令并非内置:请创建一个快捷指令,让“从输入获取字典”操作向“设置专注模式”操作提供输入。mode 必须与已配置的专注模式名称完全一致,包括大小写(例如 "Do Not Disturb"、"Work");enabled 默认为 true。结果会回显 mode 和 enabled;success: true 表示快捷指令运行完毕且未出错(不会重新读取专注模式状态)
  • 类型化错误:shortcuts_unavailable(缺少 shortcuts CLI,或没有名为 "Set Focus" 的快捷指令)、shortcut_failed(快捷指令运行出错;附带错误文本)。快捷指令运行超过 15 s 时会被停止,并报告为 Timeout 错误;已取消的调用会停止运行并以已取消状态结束

macos_manage_finder 工具
  • frontmost_path — 活动 Finder 窗口的 POSIX 路径;若没有打开的窗口,则为 null;取消的调用会以取消状态结束,不会返回 null
  • get_selection — 所选项目的 POSIX 路径
  • reveal(open -R)、open_with(open -a <App>;若省略 app_name,则使用默认应用)、trash(移至废纸篓,可恢复,绝不会永久删除)
  • open_with 和 trash 在调用 open 或 Finder 之前会检查路径是否存在
  • frontmost_path、get_selection 和 trash 会调用 Finder 脚本,需要 Automation > Finder 权限;reveal 和 open_with 无需权限
  • 类型化错误:finder_not_open、path_not_found、app_not_found(open_with 中的应用未知)、trash_refused(Finder 拒绝处理现有项目——项目已锁定、正在使用,或所在宗卷没有废纸篓)、accessibility_required

macos://system/info 资源
  • 当前 macOS 系统快照,格式为 application/json——包括电池、电源、Wi-Fi 连接状态和网络名称、主机名、版本、运行时间、显示器数量以及 unavailable
  • 也可通过 macos_get_info 获取相同数据

macos://audio/devices 资源
  • 所有音频输入和输出设备,以及当前默认设备,格式为 application/json
  • 需要 SwitchAudioSource CLI
  • 也可通过 macos_control_audio(action=list)获取相同数据

macos://displays 资源
  • 已连接显示器清单——包括 index、持久 ID、类型、分辨率、原点、旋转、缩放、启用状态以及 current_config,格式为 application/json;若 NSScreen 读取失败,则会包含 notice(且不包含 index)
  • 需要 displayplacer CLI
  • 也可通过 macos_manage_displays(action=list)获取相同数据

功能

基于 @cyanheads/mcp-ts-core 构建:支持 stdio 和 Streamable HTTP 传输、可插拔身份验证(none / jwt / oauth)、可切换存储(in-memory、filesystem、Supabase、Cloudflare KV/R2/D1),以及支持可选 OpenTelemetry 跟踪的结构化日志。

macOS 专属:

  • osascript 服务同时封装 JXA(runJxa)和 AppleScript(runAppleScript),并提供可配置超时
  • SwitchAudioSource 和 displayplacer 集成为可选依赖;每次调用时都会在 PATH、/opt/homebrew/bin 或 /usr/local/bin 中查找;如果这些位置都没有相应命令行工具,受影响的工具会返回 ServiceUnavailable 和安装说明
  • macOS 系统二进制文件(osascript、screencapture、sips、pmset、networksetup、system_profiler、…)均通过绝对路径运行,因此主机 PATH 不会决定运行哪个二进制文件
  • screencapture 会在目标文件旁的私有目录中写入文件,放置 PNG 时不会替换本次调用未检查过的任何文件;sips 会生成 JPEG 预览;图像尺寸取自 PNG/JPEG 标头
  • 使用 pmset 获取电池信息,使用 networksetup + ifconfig 获取 Wi-Fi 信息,使用 sw_vers 获取版本,使用 system_profiler -json SPDisplaysDataType 获取显示器信息,使用 Node 的 os 获取主机名和运行时间
  • 权限优先设计——工具返回 Forbidden 前,macos_check_permissions 会准确报告哪个进程需要哪项授权

便于 Agent 使用的输出:

  • 权限错误包含 accessibility_required 原因,并针对实际被拒绝的权限提供授权说明——Privacy & Security > Accessibility,或针对指定应用的 Privacy & Security > Automation
  • 如果缺少某项操作的参数(例如 set 未提供 mode,或 quit 未提供 app_name),或者文本无法用 AppleScript 字面量表示(U+0000、未配对代理项),会在执行任何操作前拒绝请求,并以 -32602 返回错误,附带 invalid_arguments 原因和说明应发送哪些内容的提示;每个多操作工具都会在其 inputSchema 中说明各项操作的要求
  • 错误包含失败程序自身的错误文本,不包含其命令行或脚本源代码
  • 可选 CLI 依赖缺失时,会返回 ServiceUnavailable,并提供确切的 brew install 命令
  • macos_manage_windows 会为每个窗口报告 display_index(窗口重叠最多的显示器;未知时为 null),便于 Agent 判断多显示器布局
  • macos_take_screenshot 将全分辨率磁盘写入与可选预览分开;预览会作为图像内容块发送,也会以 base64 格式放在 structuredContent 中,因此两种形式都包含预览

入门

此服务仅限本地使用——它控制其运行所在的 macOS 系统。请将 STDIO 传输与 MCP 客户端配合使用。

将以下内容添加到 MCP 客户端配置文件中:

{
  "mcpServers": {
    "macos-mcp-server": {
      "type": "stdio",
      "command": "bunx",
      "args": ["@cyanheads/macos-mcp-server@latest"],
      "env": {
        "MCP_TRANSPORT_TYPE": "stdio",
        "MCP_LOG_LEVEL": "info"
      }
    }
  }
}

或者使用 npx(无需 Bun):

{
  "mcpServers": {
    "macos-mcp-server": {
      "type": "stdio",
      "command": "npx",
      "args": ["-y", "@cyanheads/macos-mcp-server@latest"],
      "env": {
        "MCP_TRANSPORT_TYPE": "stdio",
        "MCP_LOG_LEVEL": "info"
      }
    }
  }
}

若要使用 Streamable HTTP,请设置传输方式并启动服务:

MCP_TRANSPORT_TYPE=http MCP_HTTP_PORT=3010 bun run start:http
# Server listens at http://localhost:3010/mcp
前置条件
  • macOS 13(Ventura)或更高版本。
  • Bun v1.4.0 或更高版本(或 Node.js v24+)。
  • 可选:SwitchAudioSource,用于音频路由(brew install switchaudio-osx)。
  • 可选:displayplacer 1.3.0 或 1.4.0,用于显示器管理(brew install displayplacer)。

这两个可选 CLI 都可以通过 Apple Silicon 上的 Homebrew(/opt/homebrew/bin)或 Intel 上的 Homebrew(/usr/local/bin)使用,也可以从 PATH 中的任意位置使用。

部分工具需要向终端或 MCP 宿主应用授予 macOS 权限:

权限所需工具
辅助功能macos_manage_windows(每项操作,包括 list)、macos_manage_apps(隐藏/显示;frontmost 使用此权限读取 window_title)、macos_control_system(锁定按键;无此权限时会回退到屏幕保护程序)
屏幕录制macos_take_screenshot,且 target=window
自动化 > Findermacos_manage_finder,且 action=frontmost_path、get_selection 或 trash

运行需要权限的操作前,使用 macos_check_permissions 检查当前权限状态。

安装
  1. 克隆仓库:
git clone https://github.com/cyanheads/macos-mcp-server.git
  1. 进入目录:
cd macos-mcp-server
  1. 安装依赖:
bun install
  1. 配置环境:
cp .env.example .env
# edit .env if you want to set MACOS_SCREENSHOT_DIR or MACOS_DISPLAY_LAYOUTS

配置

变量描述默认值
MACOS_SCREENSHOT_DIR截图文件的默认目录,也是自定义 path 可解析到的目录之一(另外两个是 /tmp 和主目录)。会解析 ~ 和符号链接。~/Desktop
MACOS_DISPLAY_LAYOUTS将布局名称映射到非空 displayplacer 参数字符串的 JSON 对象。由 macos_manage_displays action=apply_layout 使用;action=list 会将名称作为 layouts 返回。无效值只会导致 apply_layout 失败。{}
MCP_TRANSPORT_TYPE传输方式:stdio 或 http。stdio
MCP_HTTP_PORTHTTP 服务的端口。3010
MCP_AUTH_MODE身份验证模式:none、jwt 或 oauth。none
MCP_LOG_LEVEL日志级别。info
MCP_SESSION_MODE会话存储:auto、stateful 或 stateless(仅限 HTTP)。此服务不保存任何会话状态,因此 .env.example 会显式设置为 stateless。stateless(在 src/index.ts 中声明)
OTEL_ENABLED启用 OpenTelemetry 检测。false

完整的可选覆盖项列表请参阅 .env.example。

显示器布局示例:

# Get the current displayplacer command for your setup:
displayplacer list

# Then configure named layouts in your env:
MACOS_DISPLAY_LAYOUTS='{"office":"\"id:1234 res:2560x1440 hz:60 color_depth:8 scaling:on origin:(0,0) degree:0\" \"id:5678 res:1920x1080 hz:60 color_depth:8 scaling:on origin:(2560,0) degree:0\""}'

每个屏幕都有自己的带引号片段,其格式与 displayplacer list 打印的布局命令相同;不带引号片段的布局值会作为单个参数传递给 displayplacer。macos_manage_displays action=list 会返回包含相同逐屏带引号片段的 current_config,并将已配置的名称作为 layouts 返回。

运行服务

本地开发
# One-time build
bun run rebuild

# Run the built server
bun run start:stdio

# Run checks
bun run devcheck   # Lint, format, typecheck, security, changelog sync
bun run test       # Vitest test suite
bun run lint:mcp   # Validate MCP definitions against spec

项目结构

目录用途
src/index.tscreateApp() 入口——注册工具/资源并初始化服务
src/config/server-config.ts解析 MACOS_SCREENSHOT_DIR 和 MACOS_DISPLAY_LAYOUTS 环境变量
src/mcp-server/tools/definitions/13 个工具定义(macos-*.tool.ts)
src/mcp-server/tools/action-requirements.ts各操作的参数要求——由输入架构强制执行,并在 inputSchema 中声明
src/mcp-server/resources/definitions/3 个资源定义(macos-*.resource.ts)
src/services/osascript/osascript JXA 和 AppleScript 执行器,支持可配置超时和权限拒绝分类;用于调用方文本的 AppleScript 字符串字面量;以及运行中应用的名称解析器
src/utils/exec-failure.tsCLI 调用失败时对调用方安全的错误文本(绝不包含命令行)
src/services/binaries/binary-paths.tsmacOS 系统二进制文件的绝对路径,以及用于查找 displayplacer 和 SwitchAudioSource 的每次调用 PATH + Homebrew 查找
src/services/permissions/无提示的“辅助功能”“屏幕录制”和“自动化 > Finder”检查
src/services/audio/SwitchAudioSource 设备列表和切换
src/services/display/displayplacer 列表和应用布局,以及用于获取各显示器索引的 JXA NSScreen 读取
src/services/screencapture/screencapture PNG 截取、路径检查和禁止覆盖的文件放置、sips JPEG 预览
src/services/system-info/通过 pmset/networksetup/ifconfig/sw_vers/system_profiler 获取电池、Wi-Fi、版本和显示器数量;通过 node:os 获取主机名和运行时间
tests/tools/与工具定义对应的工具测试

开发指南

开发指南和架构规则请参阅 CLAUDE.md。简要说明:

  • 处理器抛出异常,框架负责捕获——工具逻辑中不要使用 try/catch
  • 使用 ctx.log 进行请求范围内的日志记录;不要调用 console
  • 读取实际的系统/CLI 状态,绝不伪造信息——当操作系统无法提供答案时(例如台式机上的电池信息,或无法读取 Focus 状态文件时的 Focus 状态),返回 null 或 unknown,不要猜测
  • 服务是单例,在 createApp() 中初始化,并通过 get*Service() 访问器调用

贡献

欢迎提交问题。提交前请运行检查和测试:

bun run devcheck
bun run test

许可证

Apache-2.0——详情请参阅 LICENSE。

评论

支持 Markdown

第一次接触 MCP?

从协议是什么,到在客户端里装上第一个服务,这几条回答覆盖了大多数入门问题。

一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。