Skip to main content
Glama
mobabur94

hammerspoon-mcp

by mobabur94

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.

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

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

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

Add 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

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

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    A
    maintenance
    A lightweight MCP server that bridges AI agents and macOS, enabling automation of file navigation, application control, UI interaction, browser automation, and system operations.
    176
    MIT
  • A
    license
    Not graded
    quality
    C
    maintenance
    Standalone MCP server that gives AI agents full GUI control over macOS — screenshots, mouse, keyboard, apps, clipboard, and multi-display — with zero private dependencies.
    19
    MIT
  • A
    license
    B
    quality
    D
    maintenance
    MCP server that enables AI to fully control macOS — mouse, keyboard, terminal, screenshots, window management, UI element detection, and provides AI-optimized information reporting.
    36
    41 npm
    MIT