hammerspoon-mcp

hammerspoon-mcp

Give AI agents deep control over macOS — windows, audio, Bluetooth, Spaces, Focus mode, and 200+ OS APIs — through one MCP server.

Category
Visit Server

README

hammerspoon-mcp

Give AI agents deep control over macOS — windows, audio, Bluetooth, Spaces, Focus mode, and 200+ OS APIs — through one MCP server.

macOS 13+ Node 22+ MIT License TypeScript

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.

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-mcp

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.

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

<details> <summary><strong>Claude Desktop</strong></summary>

Add to ~/Library/Application Support/Claude/claude_desktop_config.json:

{
  "mcpServers": {
    "hammerspoon": {
      "command": "npx",
      "args": ["hammerspoon-mcp"]
    }
  }
}

</details>

<details> <summary><strong>Claude Code</strong></summary>

claude mcp add hammerspoon -- npx hammerspoon-mcp

</details>

<details> <summary><strong>Cursor / VS Code / Copilot</strong></summary>

Add to .vscode/mcp.json:

{
  "servers": {
    "hammerspoon": {
      "command": "npx",
      "args": ["hammerspoon-mcp"]
    }
  }
}

</details>

<details> <summary><strong>Kiro</strong></summary>

Add to ~/.aws/amazonq/mcp.json:

{
  "mcpServers": {
    "hammerspoon": {
      "command": "npx",
      "args": ["hammerspoon-mcp"]
    }
  }
}

</details>

Tools

🪟 Window Management (7)

Tool Description
hs_list_windows All visible windows with app, title, frame, id
hs_focus_window Bring window to front by id or title match
hs_move_window Move/resize window by id
hs_window_layout 16 presets (halves, thirds, quarters, grid, cascade, center…) + custom fractional layouts
hs_save_layout Snapshot all window positions as a named layout
hs_restore_layout Restore a saved layout (matches by app + title)
hs_list_layouts List saved layout names

🖥️ Spaces / Virtual Desktops (9)

Tool Description
hs_spaces_list All spaces per screen with active markers
hs_spaces_active Currently focused space + screen
hs_spaces_goto Switch by ID or index
hs_spaces_move_window Move window to another space (optionally follow)
hs_spaces_add Create a new space
hs_spaces_remove Remove a space (safety checks)
hs_spaces_windows Windows on a specific space
hs_spaces_window_spaces Which space(s) a window is on
hs_spaces_mission_control Toggle Mission Control / Exposé / Launchpad / Show Desktop

📱 Application Control (3)

Tool Description
hs_list_apps Running applications with bundle ID, PID, hidden state
hs_launch_app Launch or activate an app by name
hs_kill_app Quit an app

⌨️ Input (3)

Tool Description
hs_click Click at coordinates with button + modifier options
hs_type Type text (Unicode, CJK, emoji)
hs_hotkey Press key combo (e.g. "cmd+shift+4")

🖼️ Screen (3)

Tool Description
hs_screenshot Capture full screen or region (base64 PNG)
hs_screens Display info (resolution, name, rotation)
hs_mouse_position Current cursor coordinates

🔊 Audio Devices (6)

Tool Description
hs_audio_devices List all input/output with volume, mute, transport type
hs_audio_default Get/set default devices (fuzzy name match)
hs_audio_volume Volume on any specific device (0–100)
hs_audio_mute Mute/unmute/toggle any device
hs_audio_datasources List/switch data sources (speakers vs headphones)
hs_audio_watch Watch for device changes

🔵 Bluetooth (5)

Tool Description
hs_bluetooth_status Power state and discoverability
hs_bluetooth_power On / off / toggle
hs_bluetooth_devices List paired or connected devices
hs_bluetooth_connect Connect by name (fuzzy) or MAC address
hs_bluetooth_disconnect Disconnect a device

Requires blueutil: brew install blueutil

🌙 Focus / Do Not Disturb (6)

Tool Description
hs_focus_status Check if DND/Focus is active + profile name
hs_focus_toggle Toggle via Control Center (no setup needed)
hs_focus_shortcut Enable/disable via macOS Shortcuts (most reliable)
hs_focus_install_shortcut One-time setup guide
hs_focus_schedule DND for N minutes with auto-disable
hs_focus_cancel_timer Cancel scheduled disable

🔋 System State (6)

Tool Description
hs_battery Charge %, power source, time remaining
hs_wifi Current SSID and interface details
hs_volume System volume get/set
hs_dark_mode Get/toggle dark mode
hs_notify Post a macOS notification
hs_clipboard Read/write system pasteboard

🔆 Display (1)

Tool Description
hs_brightness Get/set screen brightness (0–100)

⏱️ Timers (3)

Tool Description
hs_timer_start Named countdown with notification on fire
hs_timer_cancel Cancel by name
hs_timer_list List active timers

📁 Finder / Files (4)

Tool Description
hs_finder_selection Get currently selected Finder items
hs_finder_open Open/reveal path in Finder
hs_trash Move to Trash (reversible)
hs_quicklook Quick Look preview

🛠️ Utilities (5)

Tool Description
hs_system_info CPU, memory, thermal state, uptime
hs_caffeinate Prevent sleep for N minutes
hs_execute Run shell command (user environment)
hs_open_url Open URL in browser or specific app
hs_alert HUD-style on-screen overlay message

👁️ Watchers — Live OS Event Streams (13)

Tool Events
hs_watch_apps App activated, launched, terminated, hidden
hs_watch_wifi Network changes
hs_watch_usb Device connect/disconnect
hs_watch_battery Charge level, power source
hs_watch_screens Display configuration
hs_watch_paths Filesystem changes
hs_watch_sleep Sleep/wake/lock/unlock
hs_watch_clipboard Every pasteboard change
hs_watch_clipboard_history Ring buffer with source app tracking
hs_get_clipboard_history Read the full buffer
hs_watch_urls Incoming hammerspoon://mcp/... URLs
hs_unwatch Stop a watcher
hs_list_watchers List active watchers

🧪 Escape Hatch (1)

Tool Description
hs_eval 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
watcher://apps App lifecycle events
watcher://wifi Network changes
watcher://usb USB device events
watcher://battery Battery/power changes
watcher://screens Display reconfiguration
watcher://paths Filesystem changes
watcher://sleep Sleep/wake/lock events
watcher://clipboard Pasteboard changes
watcher://clipboard_history Clipboard ring buffer updates
watcher://urls Incoming URL events
watcher://audio Audio device changes
hammerspoon://system 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 hs_eval ✅ ❌
Zero-install (no prerequisites) ❌ ✅ ✅
Total tools 75 18 ~10

Prerequisites

Requirement Install
macOS 13+ —
Node.js 22+ brew install node
Hammerspoon brew install --cask hammerspoon
hs.ipc module Add require("hs.ipc") to ~/.hammerspoon/init.lua
blueutil (optional) brew install blueutil (for Bluetooth tools)

The hs CLI 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: ok

If 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 hs_screenshot only 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.md

Development

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 version

How It Works

  1. Startup — the bridge auto-discovers the hs binary (checks ~/.local/bin, /usr/local/bin, /opt/homebrew/bin, app bundle) and validates connectivity
  2. Tools execute synchronously via hs -c '<lua>' — typically 30–80ms per call. Stdout noise (-- Loading extension: ...) is stripped automatically
  3. Watchers inject Lua code that writes JSON lines to a FIFO pipe; a Node reader process emits them as MCP resource updates
  4. Resources buffer the last 50 events per watcher; clients read on-demand after receiving update notifications
  5. Graceful shutdown stops all watchers and cleans up the pipe on SIGINT/SIGTERM

Known Limitations

  • Node.js 22+ is specified in engines but the server builds and runs fine on Node 20 (tested)
  • hs_spaces_goto, addSpaceToScreen, removeSpace briefly 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_screenshot returns base64 — large for full-screen captures
  • Saved 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

Recommended Servers

playwright-mcp

playwright-mcp

A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.

Official
Featured
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.

Official
Featured
Local
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.

Official
Featured
Local
TypeScript
VeyraX MCP

VeyraX MCP

Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.

Official
Featured
Local
graphlit-mcp-server

graphlit-mcp-server

The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.

Official
Featured
TypeScript
Kagi MCP Server

Kagi MCP Server

An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.

Official
Featured
Python
E2B

E2B

Using MCP to run code via e2b.

Official
Featured
Neon Database

Neon Database

MCP server for interacting with Neon Management API and databases

Official
Featured
Exa Search

Exa Search

A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.

Official
Featured
Qdrant Server

Qdrant Server

This repository is an example of how to create a MCP server for Qdrant, a vector search engine.

Official
Featured