关于 hammerspoon-mcp
hammerspoon-mcp 是连接 MCP 客户端与 Hammerspoon 的 macOS 自动化服务,面向希望通过 Agent 管理窗口、应用和系统功能的用户。它使用静态 Lua 工具体并将参数单独传递,默认仅开放安全操作。
主要功能
- 01列出、聚焦和调整窗口
- 02启动及切换应用
- 03查询 Hammerspoon API 文档
- 04读取控制台并重载配置
- 05查看和控制系统功能
- 06默认关闭任意 Lua 执行
适用场景
- 01整理桌面窗口布局
- 02查找 Hammerspoon API 用法
- 03编辑配置后重载并排查错误
- 04切换音频设备或桌面空间
MCP 相关
通过 stdio 向 MCP 客户端提供类型化工具,以调用 Hammerspoon 执行 macOS 操作。默认提供安全工具;设置 HS_MCP_TOOLS=all 后才注册任意 Lua 等受限工具。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
npx -y @vukvukovich/hammerspoon-mcpHS_MCP_TOOLSWhich tools to expose. "safe" (default) exposes inspection, window management, and system tools only. "all" additionally exposes hs_eval, hs_applescript, and hs_ui_press, which run arbitrary Lua and AppleScript and press UI elements - anything the user can do.HS_MCP_TRANSPORTHow Lua reaches Hammerspoon. "socket" (default) keeps one persistent Unix domain socket, self-installed on first call, with automatic fallback to spawning; "spawn" runs one hs process per call.HS_MCP_HS_PATHExplicit path to the Hammerspoon hs command line tool. Only needed when Hammerspoon is installed somewhere unusual.HS_MCP_DOCS_PATHExplicit path to Hammerspoon's bundled docs.json, used by hs_api_search.HS_MCP_LOG_LEVELLog verbosity on stderr: debug, info (default), warn, or error.本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。
工具
38收录的工具列表 · 更新于 2 周前
此工具无需参数。
详细介绍
简介
Hammerspoon 是一款使用 Lua 编写脚本的 macOS 自动化应用。它通过 hs.ipc 模块提供命令行接口,因此 hs -c "<lua>" 会在正在运行的 Hammerspoon 进程中执行 Lua。
本项目是一个 MCP(Model Context Protocol)服务,位于该命令行接口前端。它为 Agent 提供一组有类型的工具(列出窗口、移动窗口、聚焦应用、搜索 Hammerspoon API 文档、查看控制台日志、重新加载配置),并将每次工具调用转换为由 Hammerspoon 执行的 Lua 程序。
它通过 stdio 使用 MCP,因此任何 MCP 客户端都可以使用它:Claude Code、Claude Desktop,或你自己的客户端。
项目状态
已在 npm 上发布,版本为 v0.4.x:包含 38 个安全级别工具,另有 3 个工具需设置 HS_MCP_TOOLS=all 才可使用。项目仍处于 1.0 之前,因此工具名称和参数形式可能会在次要版本之间发生变化。
与众不同之处
1. 从设计上防止注入
“帮我运行 Lua”桥接中的大部分风险,都出现在构建 Lua 的那一刻。通常的做法是将参数插入源码字符串,并转义危险字符。转义是一种需要遵循的规范,而规范总有疏漏的时候。
此服务绝不进行插值。每个工具的 Lua 主体都是 TypeScript 源码中的静态常量。参数单独传递:
- 将经过验证的参数对象编码为 JSON。
- 将该 JSON 编码为 base64。
- 将 base64 文本仅拼接一次,放入一行固定的前置代码中:
local ARGS = hs.json.decode(hs.base64.decode("<base64>"))
随后,工具主体会读取 ARGS.title、ARGS.windowId 等字段。
base64 字符集为 A-Z、a-z、0-9、+、/ 和 =。它不包含引号、反斜杠、换行符、方括号或连字符。因此,载荷无法闭合 Lua 字符串、无法开始转义序列、无法开启长括号,也无法开启注释。注入之所以不可能,是因为字符集的限制,而不是因为有人记得正确转义。
这里也没有 shell 层。此服务通过持久的 Unix 套接字与 Hammerspoon 通信;如果不可用,则回退到使用 argv 数组调用 spawn(hsPath, ["-c", lua]),因此这两种路径都不会由 sh 解析命令。
2. 分级工具,默认安全
默认级别为 safe:读取、检查和排列操作。任意 Lua 求值功能以 hs_eval 的形式提供,但除非设置 HS_MCP_TOOLS=all,否则不会启用。
请参阅安全性,了解相关原因。简而言之:威胁来自提示词注入,而不是你。
3. 专为配置开发循环打造
Hammerspoon 的大部分价值都来自你自己的 init.lua。因此,服务不仅能帮你操控 Hammerspoon,还能帮你编写配置:
hs_api_search可搜索 Hammerspoon 内置的 API 文档,让 Agent 查找hs.window.moveToUnit的实际签名,而不是凭空猜测。hs_console_tail可读取 Hammerspoon 控制台的输出,让 Agent 查看自己的错误。hs_reload_config可在编辑后重新加载init.lua。
编辑、重新加载、读取控制台、修复。Agent 可以自行完成这个循环。
快速开始
前提条件
-
macOS。
-
Node.js 24 或更高版本。
-
已安装并运行的 Hammerspoon:
brew install --cask hammerspoon -
在 Hammerspoon 配置中加载
hs.ipc模块。将此行添加到~/.hammerspoon/init.lua:require("hs.ipc")然后从 Hammerspoon 菜单栏图标重新加载配置。这样就会安装并启用
hs命令行工具。没有它,hs -c就无从交互。
连接任何客户端之前,请先手动验证桥接是否正常:
hs -c "return 1 + 1"
如果输出为 2,就表示已准备就绪。
将其添加到 MCP 客户端
Claude Code:
claude mcp add hammerspoon -- npx -y @vukvukovich/hammerspoon-mcp
任何接受 mcpServers JSON 配置块的客户端:
{
"mcpServers": {
"hammerspoon": {
"command": "npx",
"args": ["-y", "@vukvukovich/hammerspoon-mcp"]
}
}
}
要启用不安全层,请添加环境变量。Claude Code:
claude mcp add hammerspoon -e HS_MCP_TOOLS=all -- npx -y @vukvukovich/hammerspoon-mcp
JSON:
{
"mcpServers": {
"hammerspoon": {
"command": "npx",
"args": ["-y", "@vukvukovich/hammerspoon-mcp"],
"env": {
"HS_MCP_TOOLS": "all"
}
}
}
}
让 Agent 首先调用 hs_health。它会报告是否找到了 hs 二进制文件、Hammerspoon 是否正在运行,以及 hs.ipc 是否有响应。
工具参考
共 41 个工具:安全层中有 38 个,另有 3 个受限工具。
| 工具 | 等级 | 功能 |
|---|---|---|
hs_health | safe | 报告桥接状态:已解析的 hs 路径、Hammerspoon 是否有响应及其版本。 |
hs_api_search | safe | 搜索 Hammerspoon 内置的 API 参考,并返回准确的签名。 |
hs_console_tail | safe | 返回 Hammerspoon 控制台的最后 N 行。 |
hs_reload_config | safe | 重新加载 ~/.hammerspoon/init.lua。 |
hs_notify | safe | 显示临时屏幕提醒,不会抢占焦点。 |
hs_list_windows | safe | 列出窗口及其 ID、标题、所属应用、屏幕和窗口框架。 |
hs_focus_window | safe | 按 ID 或标题中的子字符串聚焦窗口。 |
hs_move_window | safe | 按绝对屏幕像素移动或调整指定 ID 的窗口大小。 |
hs_window_layout | safe | 将窗口贴靠到命名预设位置,例如 left-half 或 quarter-top-left。 |
hs_list_apps | safe | 列出正在运行的应用及其 bundle ID、PID 和窗口数量。 |
hs_launch_app | safe | 按名称启动应用;如果应用已在运行,则将其聚焦。 |
hs_focus_app | safe | 将已在运行的应用置于前台。 |
hs_screens | safe | 列出屏幕及其 ID、名称、框架,以及哪个屏幕为主屏幕。 |
hs_machine_status | safe | 一次调用即可获取电池、亮度、Wi-Fi、空闲时间、音频和主机信息。 |
hs_audio_devices | safe | 列出音频输出和输入设备,并显示当前默认设备。 |
hs_audio_set_device | safe | 切换默认输出或输入设备,例如切换到耳机。 |
hs_audio_volume | safe | 获取或设置默认设备的音量和静音状态。 |
hs_brightness | safe | 获取或设置内置显示器的亮度。 |
hs_media_control | safe | 通过系统媒体按键播放、暂停、跳过或返回。 |
hs_list_spaces | safe | 按屏幕列出桌面(Spaces)、其位置以及当前桌面。 |
hs_goto_space | safe | 按 ID 或从 1 开始的位置序号切换桌面。 |
hs_eval | unsafe | 执行任意 Lua 代码。需要设置 HS_MCP_TOOLS=all。 |
hs_applescript | unsafe | 运行 AppleScript。可访问 Mail、Notes、Reminders 和 Finder。需要设置 HS_MCP_TOOLS=all。 |
hs_ui_press | unsafe | 按下由 hs_ui_inspect 找到的 UI 元素。若未提供检查结果中的 expectLabel 或 expectRole,则拒绝执行;若该元素当前已与检查结果不匹配,也会拒绝执行。需要设置 HS_MCP_TOOLS=all。 |
hs_window_layout 预设:left-half、right-half、top-half、bottom-half、 | ||
maximize、center、thirds-left、thirds-center、thirds-right、 | ||
two-thirds-left、two-thirds-right,以及四个 quarter-* 角落布局。 | ||
| 位置根据屏幕的可用区域计算,因此会避开菜单栏和 Dock;在原点为负值的第二台显示器上也能正常使用。 |
hs_ui_inspect 只返回结构和标签,不会返回文本字段或文档的内容。Agent 需要结构才能执行操作;而密码管理器中存放的正是内容。
除非选择启用,否则不会注册 unsafe 层级中的工具。使用默认设置连接的客户端不会在工具列表中看到 hs_eval、hs_applescript 或 hs_ui_press。
配置
所有配置均为环境变量,在启动时读取一次。
| 变量 | 值 | 默认值 | 含义 |
|---|---|---|---|
HS_MCP_TOOLS | safe | all | safe | 要注册的层级。all 会添加 unsafe 层级:hs_eval、hs_applescript 和 hs_ui_press。 |
HS_MCP_TRANSPORT | socket | spawn | socket | Lua 连接 Hammerspoon 的方式:使用持久 Unix 套接字(速度约快 10 倍,首次调用时自动安装,必要时自动回退到 spawn),或每次调用启动一个 hs 进程。 |
HS_MCP_HS_PATH | 绝对路径 | 自动检测 | hs 二进制文件的路径。如果安装位置不常见,请设置此项。 |
HS_MCP_DOCS_PATH | 绝对路径 | 来自应用程序包 | Hammerspoon 内置 API 文档 JSON 的路径,供 hs_api_search 使用。 |
HS_MCP_LOG_LEVEL | debug | info | warn | error | info | stderr 日志的详细程度。日志不会写入承载协议的 stdout。 |
如果 HS_MCP_TOOLS 的值无法识别,系统会记录醒目的警告,并回退到
safe,而不会切换到权限更宽泛的层级。拼写错误可能导致你无法使用受限工具,但
绝不会因此授予这些工具。有关 hs 路径默认值的查找顺序,请参阅
docs/ARCHITECTURE.md。
安全
将 HS_MCP_TOOLS=all 之前,请先阅读本节。
服务运行位置
该服务是一个本地进程。MCP 客户端会启动它,通过 stdio 与它通信;它以你的用户账户运行。它不会监听网络,也没有远程暴露面。
这也意味着它会继承你的权限。Hammerspoon 拥有 macOS TCC (透明度、同意与控制)授权,例如辅助功能权限,也可能拥有屏幕录制和自动化权限。在 Hammerspoon 内运行的任何程序都会使用这些授权。此服务不会增加权限,也无法撤销任何权限。
实际威胁模型
风险不在于你不可信,风险在于提示词注入。
Agent 会不断读取不可信文本:网页、README 文件、问题正文、日志行以及其他工具的输出。其中任何内容都可能包含指令。有时 Agent 会照做。这并非假设,而且尚未得到解决。
因此,对每个工具都要问:如果 Agent 被说服调用它,会造成多大危害?
- 精心设计的动词只有很小的影响范围。使用
hs_move_window时,最糟糕的情况是窗口被移到了错误的位置。令人烦恼,但可以撤销,也看得见。 - 任意 Lua 的影响范围没有上限。Hammerspoon 的 Lua 可以运行 shell 命令、读取剪贴板、捕获屏幕、监视按键并发起网络请求。只要注入成功一次,就能悄无声息地完全控制这台机器。
这就是划分层级的全部原因。
HS_MCP_TOOLS=all 的含义
它会注册 hs_eval、hs_applescript 和 hs_ui_press。从那一刻起,Agent 就能在持有你授予的辅助功能权限的进程中,执行它能编写的任意 Lua 或 AppleScript,并按下界面元素。请把它视作交出一个同时能访问屏幕和键盘的 shell。
这确实是一种很实用的模式。让 Agent 直接尝试代码片段,可以大大加快编写和调试 Hammerspoon 配置的速度。请在你能监看的会话中,将它用于你要求的工作,然后再将其关闭。不要在会浏览网页的长期运行或无人值守的 Agent 中启用它。
默认安全并不是说你不能被信任去使用危险工具,而是说,启用它应该是你在特定日期、出于特定原因,有意做出的决定。
有意不提供的功能
这些并非疏忽,而是有原因的拒绝。
- 原始 shell 执行。 Agent 已经有由其自身宿主沙箱隔离并审计的 shell 工具。Mac 控制服务不需要再变成一个更糟糕的 shell。
- 按键和点击模拟。 向当前获得焦点的任意窗口模拟输入,等同于换了种方式执行任意代码。如果该窗口是终端,那么“输入这段文本”和“运行这条命令”就是同一个操作。
- 读取剪贴板。 你的剪贴板里存有密码、令牌和私人消息,而且往往就在你复制后几秒内。能读取剪贴板的工具,就是一种能窃取你最敏感的短期数据的手段。
- 屏幕截图。 理由相同。屏幕捕获包含所有可见内容,包括 Agent 未被要求查看的窗口。
其中一些功能以后可能会重新加入,各自需要明确选择启用,就像现在对 hs_eval 的限制一样。它们永远不会出现在默认层级中。
报告漏洞
请在代码库中提交安全公告,不要公开提交问题。
有一点需要了解:调用会排队
Hammerspoon 在单线程上运行 Lua,因此无论收到多少调用,它一次只会执行一个。实测结果:同时发出的 4 个 400ms 调用需要 1629ms,而不是 417ms。
因此,服务会相应排队,一次最多处理 4 个正在执行的调用。这不是为了限流而限流。如果不加限制,同时发起的调用不只是需要等待,还会开始失败(测试中 15 个调用只有 5 个成功),而且这种模式曾在 Hammerspoon 自身的 IPC 层中导致两次崩溃。
实际影响是:一个缓慢的工具会阻塞其他工具,因为所有调用共用一个队列。如果感觉卡住了,通常是有一个调用正在占用队列。ARCHITECTURE.md中列出了实测数据。
故障排查
先运行 hs_health。它会告诉你遇到的是以下哪种情况。
找不到 hs。 服务会检查固定的一系列位置,然后检查
PATH。如果你的 Hammerspoon 安装在其他位置,请将 HS_MCP_HS_PATH 设置为二进制文件的绝对路径。请注意,通过图形界面启动的 MCP 客户端通常只有精简的 PATH,其中不包含 Homebrew,因此在终端中可用的路径对服务来说可能无效。如有疑问,请设置该变量。
Hammerspoon 未运行。 hs CLI 是客户端,需要 Hammerspoon 应用运行后才能与其通信。启动 Hammerspoon,然后重试。
未加载 hs.ipc。 Hammerspoon 正在运行,但没有响应,或者 hs 二进制文件根本不存在。这两种情况通常都意味着 ~/.hammerspoon/init.lua 中缺少 require("hs.ipc")。添加该语句,从菜单栏图标重新加载配置,然后在终端中运行 hs -c "return 1 + 1" 检查。
客户端的工具列表中缺少工具。 如果缺少的是 hs_eval,说明默认层级正在按预期工作。在客户端的服务配置中设置 HS_MCP_TOOLS=all,然后重启客户端,让服务在新环境下重新启动。
工具超时。 Hammerspoon 是单线程的。如果配置陷入循环,或模态对话框阻塞了运行,调用就不会返回。使用 hs_console_tail 检查控制台,或重新加载配置。
开发
git clone https://github.com/vukvukovich/hammerspoon-mcp.git
cd hammerspoon-mcp
npm install
| 脚本 | 功能 |
|---|---|
npm run build | 使用 tsc 将 TypeScript 编译到 dist/。 |
npm run typecheck | 对所有内容进行类型检查,不生成输出。 |
npm run lint | ESLint。 |
npm run lint:fix | 使用自动修复运行 ESLint。 |
npm run format | 使用 Prettier 格式化并写入。 |
npm run format:check | 仅使用 Prettier 检查格式。 |
npm test | 单元测试(Vitest)。 |
npm run test:watch | 以监视模式运行单元测试。 |
npm run test:coverage | 运行单元测试并生成覆盖率报告。 |
npm run test:integration | 集成测试。需要真实运行的 Hammerspoon。 |
npm run check | CI 运行的全部检查:类型检查、lint、格式检查、测试。 |
技术栈:严格模式下的 TypeScript 5.9、仅限 ESM、Node 24+、@modelcontextprotocol/server v2 SDK、用于架构定义的 Zod v4、Vitest 4,以及搭配 Prettier 3 的 typescript-eslint 8。使用纯 tsc 构建,不使用打包工具。
贡献前,请阅读 CONVENTIONS.md(约束性规则)和 CONTRIBUTING.md(工作流和提交格式)。设计说明见 docs/ARCHITECTURE.md。
许可证
MIT。版权所有 (c) 2026 Vuk Vukovich。详见 LICENSE。
Hammerspoon 是一个独立项目,拥有自己的许可证,与本项目无关。
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。