{
  "markdown": "# hammerspoon-mcp\n\n> Give AI agents deep control over macOS — windows, audio, Bluetooth, Spaces, Focus mode, and 200+ OS APIs — through one MCP server.\n\n[![macOS 13+](https://img.shields.io/badge/macOS-13%2B-blue)](https://hammerspoon.org/)\n[![Node 22+](https://img.shields.io/badge/Node-22%2B-green)](https://nodejs.org/)\n[![MIT License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)\n[![TypeScript](https://img.shields.io/badge/TypeScript-5.7-blue)](https://typescriptlang.org)\n\n## What is this?\n\nAn [MCP](https://modelcontextprotocol.io/) server that bridges AI agents to [Hammerspoon](https://hammerspoon.org/) — the most powerful macOS automation tool available. Unlike screenshot-and-click tools, this gives agents **programmatic OS-level access**: manage windows by ID, switch audio devices by name, move apps between Spaces, toggle Focus mode, and subscribe to real-time OS events.\n\n**75 tools. 12 subscribable resources. Zero native dependencies.**\n\n## Quick Start\n\n```bash\n# 1. Install Hammerspoon (if you haven't)\nbrew install --cask hammerspoon\n\n# 2. Enable the IPC module (one-time setup)\n#    Add this line to ~/.hammerspoon/init.lua:\n#    require(\"hs.ipc\")\n#    Then reload your Hammerspoon config (Cmd+Alt+Ctrl+R or restart the app)\n\n# 3. (Optional) Install the hs CLI symlink for convenience:\n#    Run in Hammerspoon console: hs.ipc.cliInstall()\n#    Or manually: ln -sf /Applications/Hammerspoon.app/Contents/Frameworks/hs/hs ~/.local/bin/hs\n\n# 4. Run the MCP server\nnpx hammerspoon-mcp\n```\n\n> **Note:** The server auto-discovers the `hs` binary — it checks `~/.local/bin/hs`, `/usr/local/bin/hs`, `/opt/homebrew/bin/hs`, and the app bundle path. You don't need `hs` on your PATH if Hammerspoon is installed to `/Applications`.\n\n## Architecture\n\n```\n┌─────────────────────────────────────────────────────────┐\n│  MCP Client (Claude, Cursor, MeshClaw, etc.)            │\n└──────────────────────────┬──────────────────────────────┘\n                           │ stdio (JSON-RPC)\n┌──────────────────────────▼──────────────────────────────┐\n│  hammerspoon-mcp (Node.js)                              │\n│  ├─ tools → execSync(\"hs -c '...'\")  (request/response)│\n│  └─ watchers → named pipe reader     (push events)     │\n└──────────────────────────┬──────────────────────────────┘\n                           │ hs CLI / named pipe\n┌──────────────────────────▼──────────────────────────────┐\n│  Hammerspoon.app (Lua runtime + ObjC bridge)            │\n│  200+ modules: window, screen, spaces, audio, wifi,     │\n│  bluetooth, battery, USB, filesystem, accessibility...  │\n└──────────────────────────┬──────────────────────────────┘\n                           │\n              macOS APIs (CoreGraphics, IOKit, etc.)\n```\n\n## MCP Client Configuration\n\n<details>\n<summary><strong>Claude Desktop</strong></summary>\n\nAdd to `~/Library/Application Support/Claude/claude_desktop_config.json`:\n```json\n{\n  \"mcpServers\": {\n    \"hammerspoon\": {\n      \"command\": \"npx\",\n      \"args\": [\"hammerspoon-mcp\"]\n    }\n  }\n}\n```\n</details>\n\n<details>\n<summary><strong>Claude Code</strong></summary>\n\n```bash\nclaude mcp add hammerspoon -- npx hammerspoon-mcp\n```\n</details>\n\n<details>\n<summary><strong>Cursor / VS Code / Copilot</strong></summary>\n\nAdd to `.vscode/mcp.json`:\n```json\n{\n  \"servers\": {\n    \"hammerspoon\": {\n      \"command\": \"npx\",\n      \"args\": [\"hammerspoon-mcp\"]\n    }\n  }\n}\n```\n</details>\n\n<details>\n<summary><strong>Kiro</strong></summary>\n\nAdd to `~/.aws/amazonq/mcp.json`:\n```json\n{\n  \"mcpServers\": {\n    \"hammerspoon\": {\n      \"command\": \"npx\",\n      \"args\": [\"hammerspoon-mcp\"]\n    }\n  }\n}\n```\n</details>\n\n## Tools\n\n### 🪟 Window Management (7)\n\n| Tool | Description |\n|------|-------------|\n| `hs_list_windows` | All visible windows with app, title, frame, id |\n| `hs_focus_window` | Bring window to front by id or title match |\n| `hs_move_window` | Move/resize window by id |\n| `hs_window_layout` | 16 presets (halves, thirds, quarters, grid, cascade, center…) + custom fractional layouts |\n| `hs_save_layout` | Snapshot all window positions as a named layout |\n| `hs_restore_layout` | Restore a saved layout (matches by app + title) |\n| `hs_list_layouts` | List saved layout names |\n\n### 🖥️ Spaces / Virtual Desktops (9)\n\n| Tool | Description |\n|------|-------------|\n| `hs_spaces_list` | All spaces per screen with active markers |\n| `hs_spaces_active` | Currently focused space + screen |\n| `hs_spaces_goto` | Switch by ID or index |\n| `hs_spaces_move_window` | Move window to another space (optionally follow) |\n| `hs_spaces_add` | Create a new space |\n| `hs_spaces_remove` | Remove a space (safety checks) |\n| `hs_spaces_windows` | Windows on a specific space |\n| `hs_spaces_window_spaces` | Which space(s) a window is on |\n| `hs_spaces_mission_control` | Toggle Mission Control / Exposé / Launchpad / Show Desktop |\n\n### 📱 Application Control (3)\n\n| Tool | Description |\n|------|-------------|\n| `hs_list_apps` | Running applications with bundle ID, PID, hidden state |\n| `hs_launch_app` | Launch or activate an app by name |\n| `hs_kill_app` | Quit an app |\n\n### ⌨️ Input (3)\n\n| Tool | Description |\n|------|-------------|\n| `hs_click` | Click at coordinates with button + modifier options |\n| `hs_type` | Type text (Unicode, CJK, emoji) |\n| `hs_hotkey` | Press key combo (e.g. `\"cmd+shift+4\"`) |\n\n### 🖼️ Screen (3)\n\n| Tool | Description |\n|------|-------------|\n| `hs_screenshot` | Capture full screen or region (base64 PNG) |\n| `hs_screens` | Display info (resolution, name, rotation) |\n| `hs_mouse_position` | Current cursor coordinates |\n\n### 🔊 Audio Devices (6)\n\n| Tool | Description |\n|------|-------------|\n| `hs_audio_devices` | List all input/output with volume, mute, transport type |\n| `hs_audio_default` | Get/set default devices (fuzzy name match) |\n| `hs_audio_volume` | Volume on any specific device (0–100) |\n| `hs_audio_mute` | Mute/unmute/toggle any device |\n| `hs_audio_datasources` | List/switch data sources (speakers vs headphones) |\n| `hs_audio_watch` | Watch for device changes |\n\n### 🔵 Bluetooth (5)\n\n| Tool | Description |\n|------|-------------|\n| `hs_bluetooth_status` | Power state and discoverability |\n| `hs_bluetooth_power` | On / off / toggle |\n| `hs_bluetooth_devices` | List paired or connected devices |\n| `hs_bluetooth_connect` | Connect by name (fuzzy) or MAC address |\n| `hs_bluetooth_disconnect` | Disconnect a device |\n\n> Requires [blueutil](https://github.com/toy/blueutil): `brew install blueutil`\n\n### 🌙 Focus / Do Not Disturb (6)\n\n| Tool | Description |\n|------|-------------|\n| `hs_focus_status` | Check if DND/Focus is active + profile name |\n| `hs_focus_toggle` | Toggle via Control Center (no setup needed) |\n| `hs_focus_shortcut` | Enable/disable via macOS Shortcuts (most reliable) |\n| `hs_focus_install_shortcut` | One-time setup guide |\n| `hs_focus_schedule` | DND for N minutes with auto-disable |\n| `hs_focus_cancel_timer` | Cancel scheduled disable |\n\n### 🔋 System State (6)\n\n| Tool | Description |\n|------|-------------|\n| `hs_battery` | Charge %, power source, time remaining |\n| `hs_wifi` | Current SSID and interface details |\n| `hs_volume` | System volume get/set |\n| `hs_dark_mode` | Get/toggle dark mode |\n| `hs_notify` | Post a macOS notification |\n| `hs_clipboard` | Read/write system pasteboard |\n\n### 🔆 Display (1)\n\n| Tool | Description |\n|------|-------------|\n| `hs_brightness` | Get/set screen brightness (0–100) |\n\n### ⏱️ Timers (3)\n\n| Tool | Description |\n|------|-------------|\n| `hs_timer_start` | Named countdown with notification on fire |\n| `hs_timer_cancel` | Cancel by name |\n| `hs_timer_list` | List active timers |\n\n### 📁 Finder / Files (4)\n\n| Tool | Description |\n|------|-------------|\n| `hs_finder_selection` | Get currently selected Finder items |\n| `hs_finder_open` | Open/reveal path in Finder |\n| `hs_trash` | Move to Trash (reversible) |\n| `hs_quicklook` | Quick Look preview |\n\n### 🛠️ Utilities (5)\n\n| Tool | Description |\n|------|-------------|\n| `hs_system_info` | CPU, memory, thermal state, uptime |\n| `hs_caffeinate` | Prevent sleep for N minutes |\n| `hs_execute` | Run shell command (user environment) |\n| `hs_open_url` | Open URL in browser or specific app |\n| `hs_alert` | HUD-style on-screen overlay message |\n\n### 👁️ Watchers — Live OS Event Streams (13)\n\n| Tool | Events |\n|------|--------|\n| `hs_watch_apps` | App activated, launched, terminated, hidden |\n| `hs_watch_wifi` | Network changes |\n| `hs_watch_usb` | Device connect/disconnect |\n| `hs_watch_battery` | Charge level, power source |\n| `hs_watch_screens` | Display configuration |\n| `hs_watch_paths` | Filesystem changes |\n| `hs_watch_sleep` | Sleep/wake/lock/unlock |\n| `hs_watch_clipboard` | Every pasteboard change |\n| `hs_watch_clipboard_history` | Ring buffer with source app tracking |\n| `hs_get_clipboard_history` | Read the full buffer |\n| `hs_watch_urls` | Incoming `hammerspoon://mcp/...` URLs |\n| `hs_unwatch` | Stop a watcher |\n| `hs_list_watchers` | List active watchers |\n\n### 🧪 Escape Hatch (1)\n\n| Tool | Description |\n|------|-------------|\n| `hs_eval` | Execute arbitrary Lua in the Hammerspoon runtime |\n\nThis gives access to **all 200+ Hammerspoon modules** not covered by dedicated tools — USB details, serial ports, network interfaces, Canvas drawing, menubar items, etc.\n\n## Resources (Subscriptions)\n\nMCP clients can subscribe to resource URIs and receive push notifications when OS events fire:\n\n| URI | Triggers |\n|-----|----------|\n| `watcher://apps` | App lifecycle events |\n| `watcher://wifi` | Network changes |\n| `watcher://usb` | USB device events |\n| `watcher://battery` | Battery/power changes |\n| `watcher://screens` | Display reconfiguration |\n| `watcher://paths` | Filesystem changes |\n| `watcher://sleep` | Sleep/wake/lock events |\n| `watcher://clipboard` | Pasteboard changes |\n| `watcher://clipboard_history` | Clipboard ring buffer updates |\n| `watcher://urls` | Incoming URL events |\n| `watcher://audio` | Audio device changes |\n| `hammerspoon://system` | System info snapshot |\n\n### How event streaming works\n\n```\nAgent calls: hs_watch_wifi\n  → Server injects Lua watcher into Hammerspoon runtime\n  → macOS WiFi change fires\n  → Watcher writes JSON to named pipe (/tmp/hs-mcp-events.pipe)\n  → Node reader emits event\n  → Server calls sendResourceUpdated(\"watcher://wifi\")\n  → Client reads resource → gets { ssid: \"NewNetwork\", ... }\n```\n\n## Examples\n\n**\"Tile my editor and terminal side by side\"**\n```\nhs_window_layout({ preset: \"side-by-side\" })\n```\n\n**\"Switch my audio to AirPods\"**\n```\nhs_audio_default({ output: \"airpods\" })\n→ \"set default output: AirPods Pro\"\n```\n\n**\"Move Slack to Space 3\"**\n```\nhs_list_windows({ app: \"Slack\" })  → { id: 4523, ... }\nhs_spaces_move_window({ windowId: 4523, spaceIndex: 3 })\n```\n\n**\"Start a 25-minute focus session\"**\n```\nhs_focus_schedule({ minutes: 25 })\nhs_timer_start({ name: \"pomodoro\", minutes: 25, message: \"Break time!\" })\n```\n\n**\"Notify me when I switch away from my IDE\"**\n```\nhs_watch_apps()\n→ Events stream to watcher://apps whenever focus changes\n```\n\n**\"Connect my headphones and set volume to 40%\"**\n```\nhs_bluetooth_connect({ device: \"WH-1000XM5\" })\nhs_audio_volume({ device: \"WH-1000XM5\", volume: 40 })\n```\n\n## Compared to Alternatives\n\n| Capability | hammerspoon-mcp | mac-use-mcp | macos-automator-mcp |\n|------------|:-:|:-:|:-:|\n| Window management by ID | ✅ | ❌ | ❌ |\n| Virtual desktop (Spaces) control | ✅ | ❌ | ❌ |\n| Audio device switching | ✅ | ❌ | ❌ |\n| Bluetooth control | ✅ | ❌ | ❌ |\n| Focus / DND mode | ✅ | ❌ | ❌ |\n| Real-time OS event subscriptions | ✅ | ❌ | ❌ |\n| Clipboard history | ✅ | ✅ | ❌ |\n| Arbitrary OS scripting | ✅ (Lua) | ❌ | ✅ (AppleScript) |\n| Screenshot | ✅ | ✅ | ❌ |\n| Click / type / hotkeys | ✅ | ✅ | ❌ |\n| Accessibility tree queries | via `hs_eval` | ✅ | ❌ |\n| Zero-install (no prerequisites) | ❌ | ✅ | ✅ |\n| Total tools | **75** | 18 | ~10 |\n\n## Prerequisites\n\n| Requirement | Install |\n|---|---|\n| macOS 13+ | — |\n| Node.js 22+ | `brew install node` |\n| Hammerspoon | `brew install --cask hammerspoon` |\n| `hs.ipc` module | Add `require(\"hs.ipc\")` to `~/.hammerspoon/init.lua` |\n| blueutil (optional) | `brew install blueutil` (for Bluetooth tools) |\n\n> The `hs` CLI symlink (`hs.ipc.cliInstall()`) is optional — the server auto-discovers the binary inside the Hammerspoon app bundle.\n\n### Verify setup\n\n```bash\n# If hs is on your PATH:\nhs -c 'return \"ok\"'\n\n# Or directly via the app bundle:\n/Applications/Hammerspoon.app/Contents/Frameworks/hs/hs -c 'return \"ok\"'\n\n# Should print: ok\n```\n\nIf this works, `npx hammerspoon-mcp` will connect successfully.\n\n## Permissions\n\n| Permission | Required for | How to grant |\n|---|---|---|\n| **Accessibility** | Window, input, app control | System Settings → Privacy & Security → Accessibility → add your terminal |\n| **Screen Recording** | `hs_screenshot` only | System Settings → Privacy & Security → Screen Recording |\n\n## Project Structure\n\n```\nhammerspoon-mcp/\n├── src/\n│   ├── index.ts              MCP server entry point + resource registration\n│   ├── bridge.ts             hs CLI bridge (auto-discovers binary, eval, evalJson)\n│   ├── tools.ts              Core tools: window, app, input, screen, system, eval (23)\n│   ├── watcher-tools.ts      Watcher management tools (13)\n│   ├── watchers.ts           Named pipe event bridge\n│   ├── spaces-tools.ts       Spaces / virtual desktop tools (9)\n│   ├── audio-tools.ts        Audio device management (6)\n│   ├── focus-tools.ts        DND / Focus mode (6)\n│   ├── bluetooth-tools.ts    Bluetooth via blueutil (5)\n│   └── extra-tools.ts        Brightness, timers, system info, Finder, utils (13)\n├── package.json\n├── tsconfig.json\n├── LICENSE                   MIT\n└── README.md\n```\n\n## Development\n\n```bash\ngit clone <repo>\ncd hammerspoon-mcp\nnpm install\nnpm run dev     # Run with tsx (hot reload)\nnpm run build   # Compile to dist/\nnpm start       # Run compiled version\n```\n\n## How It Works\n\n1. **Startup** — the bridge auto-discovers the `hs` binary (checks `~/.local/bin`, `/usr/local/bin`, `/opt/homebrew/bin`, app bundle) and validates connectivity\n2. **Tools** execute synchronously via `hs -c '<lua>'` — typically 30–80ms per call. Stdout noise (`-- Loading extension: ...`) is stripped automatically\n3. **Watchers** inject Lua code that writes JSON lines to a FIFO pipe; a Node reader process emits them as MCP resource updates\n4. **Resources** buffer the last 50 events per watcher; clients read on-demand after receiving update notifications\n5. **Graceful shutdown** stops all watchers and cleans up the pipe on SIGINT/SIGTERM\n\n## Known Limitations\n\n- Node.js 22+ is specified in `engines` but the server builds and runs fine on Node 20 (tested)\n- `hs_spaces_goto`, `addSpaceToScreen`, `removeSpace` briefly flash Mission Control (macOS limitation — enable \"Reduce motion\" to minimize)\n- Bluetooth tools require [blueutil](https://github.com/toy/blueutil) (`brew install blueutil`)\n- Focus/DND reading uses heuristics (no public Apple API exists)\n- `hs_screenshot` returns base64 — large for full-screen captures\n- Saved layouts persist only in Hammerspoon's runtime (lost on HS reload)\n- Named pipe path is `/tmp/hs-mcp-events.pipe` — only one server instance at a time\n\n## License\n\n[MIT](LICENSE) © 2026 Mohammed Babur\n",
  "bytes": 15264,
  "sha": "1913dceac71238d408c359c5d9f9b3f28ab8ec877784725711fcf59f46801ad2",
  "repo_slug": "mobabur94/hammerspoon-mcp",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_mobabur94_hammerspoon_8e05d695/readme"
}