hammerspoon-mcp
Enables Bluetooth device management, including listing paired or connected devices, connecting/disconnecting by name or MAC address, and controlling power state.
Provides deep control over macOS, including window management, Spaces, audio devices, Bluetooth, Focus mode, system state, and access to 200+ OS-level APIs.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@hammerspoon-mcpMove the current window to the right half of the screen"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
hammerspoon-mcp
Give AI agents deep control over macOS — windows, audio, Bluetooth, Spaces, Focus mode, and 200+ OS APIs — through one MCP server.
What is this?
An MCP server that bridges AI agents to Hammerspoon — 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.
75 tools. 12 subscribable resources. Zero native dependencies.
Related MCP server: computer-use
Quick Start
# 1. Install Hammerspoon (if you haven't)
brew install --cask hammerspoon
# 2. Enable the IPC module (one-time setup)
# Add this line to ~/.hammerspoon/init.lua:
# require("hs.ipc")
# Then reload your Hammerspoon config (Cmd+Alt+Ctrl+R or restart the app)
# 3. (Optional) Install the hs CLI symlink for convenience:
# Run in Hammerspoon console: hs.ipc.cliInstall()
# Or manually: ln -sf /Applications/Hammerspoon.app/Contents/Frameworks/hs/hs ~/.local/bin/hs
# 4. Run the MCP server
npx hammerspoon-mcpNote: The server auto-discovers the
hsbinary — it checks~/.local/bin/hs,/usr/local/bin/hs,/opt/homebrew/bin/hs, and the app bundle path. You don't needhson your PATH if Hammerspoon is installed to/Applications.
Architecture
┌─────────────────────────────────────────────────────────┐
│ MCP Client (Claude, Cursor, MeshClaw, etc.) │
└──────────────────────────┬──────────────────────────────┘
│ stdio (JSON-RPC)
┌──────────────────────────▼──────────────────────────────┐
│ hammerspoon-mcp (Node.js) │
│ ├─ tools → execSync("hs -c '...'") (request/response)│
│ └─ watchers → named pipe reader (push events) │
└──────────────────────────┬──────────────────────────────┘
│ hs CLI / named pipe
┌──────────────────────────▼──────────────────────────────┐
│ Hammerspoon.app (Lua runtime + ObjC bridge) │
│ 200+ modules: window, screen, spaces, audio, wifi, │
│ bluetooth, battery, USB, filesystem, accessibility... │
└──────────────────────────┬──────────────────────────────┘
│
macOS APIs (CoreGraphics, IOKit, etc.)MCP Client Configuration
Add to ~/Library/Application Support/Claude/claude_desktop_config.json:
{
"mcpServers": {
"hammerspoon": {
"command": "npx",
"args": ["hammerspoon-mcp"]
}
}
}claude mcp add hammerspoon -- npx hammerspoon-mcpAdd to .vscode/mcp.json:
{
"servers": {
"hammerspoon": {
"command": "npx",
"args": ["hammerspoon-mcp"]
}
}
}Add to ~/.aws/amazonq/mcp.json:
{
"mcpServers": {
"hammerspoon": {
"command": "npx",
"args": ["hammerspoon-mcp"]
}
}
}Tools
🪟 Window Management (7)
Tool | Description |
| All visible windows with app, title, frame, id |
| Bring window to front by id or title match |
| Move/resize window by id |
| 16 presets (halves, thirds, quarters, grid, cascade, center…) + custom fractional layouts |
| Snapshot all window positions as a named layout |
| Restore a saved layout (matches by app + title) |
| List saved layout names |
🖥️ Spaces / Virtual Desktops (9)
Tool | Description |
| All spaces per screen with active markers |
| Currently focused space + screen |
| Switch by ID or index |
| Move window to another space (optionally follow) |
| Create a new space |
| Remove a space (safety checks) |
| Windows on a specific space |
| Which space(s) a window is on |
| Toggle Mission Control / Exposé / Launchpad / Show Desktop |
📱 Application Control (3)
Tool | Description |
| Running applications with bundle ID, PID, hidden state |
| Launch or activate an app by name |
| Quit an app |
⌨️ Input (3)
Tool | Description |
| Click at coordinates with button + modifier options |
| Type text (Unicode, CJK, emoji) |
| Press key combo (e.g. |
🖼️ Screen (3)
Tool | Description |
| Capture full screen or region (base64 PNG) |
| Display info (resolution, name, rotation) |
| Current cursor coordinates |
🔊 Audio Devices (6)
Tool | Description |
| List all input/output with volume, mute, transport type |
| Get/set default devices (fuzzy name match) |
| Volume on any specific device (0–100) |
| Mute/unmute/toggle any device |
| List/switch data sources (speakers vs headphones) |
| Watch for device changes |
🔵 Bluetooth (5)
Tool | Description |
| Power state and discoverability |
| On / off / toggle |
| List paired or connected devices |
| Connect by name (fuzzy) or MAC address |
| Disconnect a device |
Requires blueutil:
brew install blueutil
🌙 Focus / Do Not Disturb (6)
Tool | Description |
| Check if DND/Focus is active + profile name |
| Toggle via Control Center (no setup needed) |
| Enable/disable via macOS Shortcuts (most reliable) |
| One-time setup guide |
| DND for N minutes with auto-disable |
| Cancel scheduled disable |
🔋 System State (6)
Tool | Description |
| Charge %, power source, time remaining |
| Current SSID and interface details |
| System volume get/set |
| Get/toggle dark mode |
| Post a macOS notification |
| Read/write system pasteboard |
🔆 Display (1)
Tool | Description |
| Get/set screen brightness (0–100) |
⏱️ Timers (3)
Tool | Description |
| Named countdown with notification on fire |
| Cancel by name |
| List active timers |
📁 Finder / Files (4)
Tool | Description |
| Get currently selected Finder items |
| Open/reveal path in Finder |
| Move to Trash (reversible) |
| Quick Look preview |
🛠️ Utilities (5)
Tool | Description |
| CPU, memory, thermal state, uptime |
| Prevent sleep for N minutes |
| Run shell command (user environment) |
| Open URL in browser or specific app |
| HUD-style on-screen overlay message |
👁️ Watchers — Live OS Event Streams (13)
Tool | Events |
| App activated, launched, terminated, hidden |
| Network changes |
| Device connect/disconnect |
| Charge level, power source |
| Display configuration |
| Filesystem changes |
| Sleep/wake/lock/unlock |
| Every pasteboard change |
| Ring buffer with source app tracking |
| Read the full buffer |
| Incoming |
| Stop a watcher |
| List active watchers |
🧪 Escape Hatch (1)
Tool | Description |
| Execute arbitrary Lua in the Hammerspoon runtime |
This gives access to all 200+ Hammerspoon modules not covered by dedicated tools — USB details, serial ports, network interfaces, Canvas drawing, menubar items, etc.
Resources (Subscriptions)
MCP clients can subscribe to resource URIs and receive push notifications when OS events fire:
URI | Triggers |
| App lifecycle events |
| Network changes |
| USB device events |
| Battery/power changes |
| Display reconfiguration |
| Filesystem changes |
| Sleep/wake/lock events |
| Pasteboard changes |
| Clipboard ring buffer updates |
| Incoming URL events |
| Audio device changes |
| System info snapshot |
How event streaming works
Agent calls: hs_watch_wifi
→ Server injects Lua watcher into Hammerspoon runtime
→ macOS WiFi change fires
→ Watcher writes JSON to named pipe (/tmp/hs-mcp-events.pipe)
→ Node reader emits event
→ Server calls sendResourceUpdated("watcher://wifi")
→ Client reads resource → gets { ssid: "NewNetwork", ... }Examples
"Tile my editor and terminal side by side"
hs_window_layout({ preset: "side-by-side" })"Switch my audio to AirPods"
hs_audio_default({ output: "airpods" })
→ "set default output: AirPods Pro""Move Slack to Space 3"
hs_list_windows({ app: "Slack" }) → { id: 4523, ... }
hs_spaces_move_window({ windowId: 4523, spaceIndex: 3 })"Start a 25-minute focus session"
hs_focus_schedule({ minutes: 25 })
hs_timer_start({ name: "pomodoro", minutes: 25, message: "Break time!" })"Notify me when I switch away from my IDE"
hs_watch_apps()
→ Events stream to watcher://apps whenever focus changes"Connect my headphones and set volume to 40%"
hs_bluetooth_connect({ device: "WH-1000XM5" })
hs_audio_volume({ device: "WH-1000XM5", volume: 40 })Compared to Alternatives
Capability | hammerspoon-mcp | mac-use-mcp | macos-automator-mcp |
Window management by ID | ✅ | ❌ | ❌ |
Virtual desktop (Spaces) control | ✅ | ❌ | ❌ |
Audio device switching | ✅ | ❌ | ❌ |
Bluetooth control | ✅ | ❌ | ❌ |
Focus / DND mode | ✅ | ❌ | ❌ |
Real-time OS event subscriptions | ✅ | ❌ | ❌ |
Clipboard history | ✅ | ✅ | ❌ |
Arbitrary OS scripting | ✅ (Lua) | ❌ | ✅ (AppleScript) |
Screenshot | ✅ | ✅ | ❌ |
Click / type / hotkeys | ✅ | ✅ | ❌ |
Accessibility tree queries | via | ✅ | ❌ |
Zero-install (no prerequisites) | ❌ | ✅ | ✅ |
Total tools | 75 | 18 | ~10 |
Prerequisites
Requirement | Install |
macOS 13+ | — |
Node.js 22+ |
|
Hammerspoon |
|
| Add |
blueutil (optional) |
|
The
hsCLI symlink (hs.ipc.cliInstall()) is optional — the server auto-discovers the binary inside the Hammerspoon app bundle.
Verify setup
# If hs is on your PATH:
hs -c 'return "ok"'
# Or directly via the app bundle:
/Applications/Hammerspoon.app/Contents/Frameworks/hs/hs -c 'return "ok"'
# Should print: okIf this works, npx hammerspoon-mcp will connect successfully.
Permissions
Permission | Required for | How to grant |
Accessibility | Window, input, app control | System Settings → Privacy & Security → Accessibility → add your terminal |
Screen Recording |
| System Settings → Privacy & Security → Screen Recording |
Project Structure
hammerspoon-mcp/
├── src/
│ ├── index.ts MCP server entry point + resource registration
│ ├── bridge.ts hs CLI bridge (auto-discovers binary, eval, evalJson)
│ ├── tools.ts Core tools: window, app, input, screen, system, eval (23)
│ ├── watcher-tools.ts Watcher management tools (13)
│ ├── watchers.ts Named pipe event bridge
│ ├── spaces-tools.ts Spaces / virtual desktop tools (9)
│ ├── audio-tools.ts Audio device management (6)
│ ├── focus-tools.ts DND / Focus mode (6)
│ ├── bluetooth-tools.ts Bluetooth via blueutil (5)
│ └── extra-tools.ts Brightness, timers, system info, Finder, utils (13)
├── package.json
├── tsconfig.json
├── LICENSE MIT
└── README.mdDevelopment
git clone <repo>
cd hammerspoon-mcp
npm install
npm run dev # Run with tsx (hot reload)
npm run build # Compile to dist/
npm start # Run compiled versionHow It Works
Startup — the bridge auto-discovers the
hsbinary (checks~/.local/bin,/usr/local/bin,/opt/homebrew/bin, app bundle) and validates connectivityTools execute synchronously via
hs -c '<lua>'— typically 30–80ms per call. Stdout noise (-- Loading extension: ...) is stripped automaticallyWatchers inject Lua code that writes JSON lines to a FIFO pipe; a Node reader process emits them as MCP resource updates
Resources buffer the last 50 events per watcher; clients read on-demand after receiving update notifications
Graceful shutdown stops all watchers and cleans up the pipe on SIGINT/SIGTERM
Known Limitations
Node.js 22+ is specified in
enginesbut the server builds and runs fine on Node 20 (tested)hs_spaces_goto,addSpaceToScreen,removeSpacebriefly flash Mission Control (macOS limitation — enable "Reduce motion" to minimize)Bluetooth tools require blueutil (
brew install blueutil)Focus/DND reading uses heuristics (no public Apple API exists)
hs_screenshotreturns base64 — large for full-screen capturesSaved layouts persist only in Hammerspoon's runtime (lost on HS reload)
Named pipe path is
/tmp/hs-mcp-events.pipe— only one server instance at a time
License
MIT © 2026 Mohammed Babur
This server cannot be deployed
Maintenance
Related MCP Connectors
MCP server connecting AI agents to 100+ apps (Gmail, Slack, Notion, GitHub) via one-click OAuth.
Give your AI agent a memory and body on your iPhone: set alarms, ring your phone, over MCP.
Let ChatGPT, Claude & Cursor use your Mac: email, calendar, iMessage, Teams, files. Local, free.
Nifty's MCP server — exposes tasks, projects, messages, and files as tools for AI agents.
Related MCP Servers
- AlicenseNot gradedqualityAmaintenanceA lightweight MCP server that bridges AI agents and macOS, enabling automation of file navigation, application control, UI interaction, browser automation, and system operations.176MIT
- AlicenseNot gradedqualityCmaintenanceStandalone MCP server that gives AI agents full GUI control over macOS — screenshots, mouse, keyboard, apps, clipboard, and multi-display — with zero private dependencies.19MIT
- AlicenseNot gradedqualityAmaintenanceA local macOS MCP server for AI Agents that exposes safe endpoints for shell commands, files, processes, macOS automation, browser control, and more.60MIT
- AlicenseBqualityDmaintenanceMCP server that enables AI to fully control macOS — mouse, keyboard, terminal, screenshots, window management, UI element detection, and provides AI-optimized information reporting.3641 npmMIT