Wraps macOS system APIs in 13 tools that let Claude control your desktop directly. You get app and window management (launch, quit, focus, move, resize), audio routing via SwitchAudioSource, volume and appearance toggles, Focus mode control, and screenshot capture with optional base64 previews. It pulls system state like battery level, Wi-Fi SSID, and display count, and hooks into Finder for selection info and trash operations. Requires Accessibility for window manipulation, Screen Recording for window screenshots. Built for stdio on a local Mac, though it supports HTTP. Reach for this when you want Claude to orchestrate your desktop environment instead of just reading files.
Control macOS system settings, apps, windows, audio, displays, screenshots, and Focus mode via MCP. STDIO or Streamable HTTP.
macOS-only. This server controls the local macOS system — it requires the host machine to be running macOS. HTTP transport is supported for completeness, but the practical use case is stdio: run it locally and point your MCP client at it.
13 tools covering macOS system state, app and window management, audio routing, display control, screenshots, Finder integration, notifications, and Focus mode:
| Tool | Description |
|---|---|
macos_get_info | System snapshot: battery level and charging status, power source, Wi-Fi SSID, hostname, macOS version, uptime, and display count |
macos_check_permissions | Reports Accessibility, Screen Recording, Automation > Finder, and Notification status for the calling process |
macos_manage_apps | List, launch, quit, force-quit, hide, or show applications |
macos_manage_windows | List, focus, move, resize, move_resize, minimize, fullscreen, or close windows |
macos_control_volume | Get or set system output volume (0–100) and mute state |
macos_control_audio | List audio devices, get current defaults, or switch the default input/output device |
macos_control_appearance | Get or set dark/light mode |
macos_control_system | Lock the screen or put the display to sleep |
macos_take_screenshot | Capture full screen, display, named app window, or pixel region; saves PNG; optional base64 JPEG preview |
macos_manage_displays | List connected displays and apply named display layout presets |
macos_send_notification | Post a notification to macOS Notification Center |
macos_manage_focus | Get or set Do Not Disturb / Focus mode |
macos_manage_finder | Frontmost path, current selection, reveal, open with app, or move to Trash |
macos_get_infoReturns a live system snapshot with no prerequisites.
AC, Battery, UPS); null on desktops with no battery"15.1.0"), uptime in secondsmacos_check_permissionsReports permission status for each capability this server exercises. Run this first when debugging why a tool is failing.
move, resize, minimize, fullscreen, close), app hide/showmacos_take_screenshot with target=window)macos_manage_finder with action=get_selection"ghostty", "node") so you know which process to grant permissions formacos_manage_appsManage the lifecycle of user-facing applications.
list — all running user-facing apps with name, bundle ID, PID, visible, and frontmost flagsfrontmost — name, bundle ID, PID, and frontmost window title of the active applaunch — open or activate an app by name or bundle ID; hidden=true starts in the backgroundquit — graceful quit via AppleScript tell application … to quitforce_quit — SIGKILL without savinghide / show — toggle app visibility; requires Accessibilitymacos_manage_windowsWindow operations across all visible apps via System Events Accessibility.
list — all visible windows with app name, title, position, size, minimized state, and display index (0 = primary)focus — bring an app or window to the foreground (does not require Accessibility)move — reposition a window by top-left coordinateresize — change a window's width and heightmove_resize — set position and size in one callminimize — minimize to Dock or restore; minimized=true to minimize, false to restorefullscreen — toggle fullscreen via ⌃⌘F keystrokeclose — click the close button via Accessibilityapp_name, window_title, or both (window_title takes precedence)list and focus) require Accessibilitymacos_control_volumeget — returns current output volume (0–100) and mute stateset — accepts level (0–100), muted (true/false), or both; setting level=0 does not mutesetmacos_control_audioAudio device routing via SwitchAudioSource CLI (brew install switchaudio-osx).
list — all input and output devices, with is_default flag; filter by type=input|output|allcurrent — current default input and output device namesswitch_output / switch_input — change the default device; supports case-insensitive partial name matching ("MacBook" matches "MacBook Pro Microphone")macos_control_volume)macos_control_appearanceget — returns dark_mode: true/falseset with mode=dark|light|toggle — dark/light are idempotent; toggle flips on each callmacos_control_systemlock — locks the screen immediately via ⌃⌘Q (Accessibility); falls back to ScreenSaverEngine binary if Accessibility is not grantedsleep_display — puts all displays to sleep via pmset displaysleepnow; no permissions requiredmacos_take_screenshotSaves a full-resolution PNG to disk; optionally returns a downscaled JPEG preview as base64.
screen — full screen capture (all displays merged); no Screen Recording requireddisplay — a specific display by 0-based display_index; no Screen Recording requiredwindow — a named app window by app_name; requires Screen Recordingregion — a pixel rectangle { x, y, width, height }; no Screen Recording requiredpath — custom output path (must be within ~/Desktop, /tmp, or home dir); defaults to MACOS_SCREENSHOT_DIR/<timestamp>.png (falls back to ~/Desktop)include_data=true — adds preview (base64 JPEG, max 1024px wide, ~70% quality) + preview_width / preview_height to the response for agent visual analysismacos_manage_displaysRequires displayplacer CLI (brew install jakehilborn/jakehilborn/displayplacer).
list — connected display inventory: persistent ID, connection type, resolution, refresh rate, origin, rotation, scaling, enabled state; plus current_config (a displayplacer command string that reproduces the active arrangement)apply_layout — activates a named preset from MACOS_DISPLAY_LAYOUTS; layout names are pre-configured in the env var — raw displayplacer args are never accepted from the usermacos_send_notificationPosts to Notification Center via osascript. Does not require notification permission — osascript notifications bypass Do Not Disturb.
title (required), body, subtitle, sound=true (plays default notification sound)macos_manage_focusget — best-effort: reads ~/Library/DoNotDisturb/DB/Assertions.json when accessible; returns status: active|inactive|unknown; unknown is expected on macOS 13+ where the database is SIP-protectedset — requires the built-in "Set Focus" shortcut to exist in Shortcuts.app (present by default on macOS 12+); mode must match a configured Focus profile exactly (e.g. "Do Not Disturb", "Work"); enabled defaults to truemacos_manage_finderFinder integration via osascript and open.
frontmost_path — POSIX path of the active Finder window, or null when no window is open; no permissions requiredget_selection — POSIX paths of selected items; requires Automation > Finder permissionreveal — highlight a path in Finder (open -R path)open_with — open a path with a named app (open -a AppName path)trash — moves a path to the Trash (recoverable); not a permanent delete| Type | Name | Description |
|---|---|---|
| Resource | macos://system/info | Current macOS system snapshot: battery, power source, Wi-Fi SSID, hostname, version, uptime, display count |
| Resource | macos://audio/devices | All audio input and output devices, including which is the current default. Requires SwitchAudioSource CLI. |
| Resource | macos://displays | Connected display inventory including persistent IDs, type, resolution, origin, rotation, scaling, and enabled state. Requires displayplacer CLI. |
Resource data is also accessible via macos_get_info, macos_control_audio (action=list), and macos_manage_displays (action=list).
Built on @cyanheads/mcp-ts-core:
macOS-specific:
runJxa) and AppleScript (runAppleScript)macos_check_permissions tells you exactly which process needs which permission before you hit a Forbidden errorAgent-friendly output:
System Settings > Privacy & Security > [permission type])SwitchAudioSource, displayplacer) surface ServiceUnavailable with install instructions (brew install …)macos_manage_windows action=list includes display_index on every window so agents can reason about multi-monitor layoutsmacos_take_screenshot separates full-resolution disk write from optional base64 preview — keeps response size manageableThis server is local-only — it controls the macOS system it runs on. Use STDIO transport with your MCP client.
Add the following to your MCP client configuration file:
{
"mcpServers": {
"macos-mcp-server": {
"type": "stdio",
"command": "bunx",
"args": ["@cyanheads/macos-mcp-server@latest"],
"env": {
"MCP_TRANSPORT_TYPE": "stdio",
"MCP_LOG_LEVEL": "info"
}
}
}
}
Or with npx (no Bun required):
{
"mcpServers": {
"macos-mcp-server": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@cyanheads/macos-mcp-server@latest"],
"env": {
"MCP_TRANSPORT_TYPE": "stdio",
"MCP_LOG_LEVEL": "info"
}
}
}
}
brew install switchaudio-osx).brew install jakehilborn/jakehilborn/displayplacer).Some tools require macOS permissions granted to the terminal or MCP host app:
| Permission | Required by |
|---|---|
| Accessibility | macos_manage_windows (mutating actions), macos_manage_apps (hide/show), macos_control_system (lock) |
| Screen Recording | macos_take_screenshot with target=window |
| Automation > Finder | macos_manage_finder with action=get_selection |
Use macos_check_permissions to check current status before running permission-gated operations.
git clone https://github.com/cyanheads/macos-mcp-server.git
cd macos-mcp-server
bun install
cp .env.example .env
# edit .env if you want to set MACOS_SCREENSHOT_DIR or MACOS_DISPLAY_LAYOUTS
| Variable | Description | Default |
|---|---|---|
MACOS_SCREENSHOT_DIR | Default directory for screenshot files. | ~/Desktop |
MACOS_DISPLAY_LAYOUTS | JSON object mapping layout names to displayplacer argument strings. Used by macos_manage_displays action=apply_layout. | {} |
MCP_TRANSPORT_TYPE | Transport: stdio or http. | stdio |
MCP_HTTP_PORT | Port for HTTP server. | 3010 |
MCP_AUTH_MODE | Auth mode: none, jwt, or oauth. | none |
MCP_LOG_LEVEL | Log level. | info |
OTEL_ENABLED | Enable OpenTelemetry instrumentation. | false |
See .env.example for the full list of optional overrides.
Display layout example:
# Get the current displayplacer command for your setup:
displayplacer list
# Then configure named layouts in your env:
MACOS_DISPLAY_LAYOUTS='{"office":"id:1234 res:2560x1440 hz:60 color_depth:8 scaling:on origin:(0,0) degree:0 id:5678 res:1920x1080 hz:60 color_depth:8 scaling:on origin:(2560,0) degree:0"}'
# One-time build
bun run rebuild
# Run the built server
bun run start:stdio
# Run checks
bun run devcheck # Lint, format, typecheck, security, changelog sync
bun run test # Vitest test suite
bun run lint:mcp # Validate MCP definitions against spec
docker build -t macos-mcp-server .
docker run --rm -p 3010:3010 macos-mcp-server
The Dockerfile defaults to HTTP transport, stateless session mode, and logs to /var/log/macos-mcp-server. Note: the Docker image cannot exercise osascript or system CLI tools — it is provided for completeness but has limited utility for this server.
| Path | Purpose |
|---|---|
src/index.ts | createApp() entry — registers tools/resources and inits services |
src/config/server-config.ts | MACOS_SCREENSHOT_DIR and MACOS_DISPLAY_LAYOUTS env parsing |
src/mcp-server/tools/definitions/ | 13 tool definitions (macos-*.tool.ts) |
src/mcp-server/resources/definitions/ | 3 resource definitions (macos-*.resource.ts) |
src/services/osascript/ | osascript JXA + AppleScript runner with configurable timeout |
src/services/audio/ | SwitchAudioSource device listing and switching |
src/services/display/ | displayplacer list and apply-layout |
src/services/screencapture/ | screencapture + sips PNG capture and JPEG preview |
src/services/system-info/ | Battery, Wi-Fi, hostname, uptime via system_profiler/pmset |
tests/tools/ | Tool tests mirroring definitions |
See CLAUDE.md for development guidelines and architectural rules. The short version:
try/catch in tool logicctx.log for request-scoped logging; no console callsmacos_ and use snake_case; file names are macos-*.tool.tscreateApp() and accessed via get*Service() accessorsIssues and pull requests are welcome. Run checks and tests before submitting:
bun run devcheck
bun run test
Apache-2.0 — see LICENSE for details.
MACOS_SCREENSHOT_DIRDefault directory for screenshot files. Defaults to ~/Desktop when empty.
MACOS_DISPLAY_LAYOUTSdefault: {}JSON object mapping layout names to displayplacer argument strings for apply_layout.
MCP_LOG_LEVELdefault: infoMinimum log level for output: debug, info, notice, warning, error.
MCP_HTTP_HOSTdefault: 127.0.0.1The hostname for the HTTP server.
MCP_HTTP_PORTdefault: 3010The port to run the HTTP server on.
MCP_HTTP_ENDPOINT_PATHdefault: /mcpThe endpoint path for the MCP server.
MCP_AUTH_MODEdefault: noneAuthentication mode: none, jwt, or oauth.
io.github.socialapishub/social-media-api
io.github.xpaysh/social-media
com.thenextgennexus/youtube-media-mcp-server
io.github.ludmila-omlopes/youtube-video-analyzer
csoai-org/social-media-ai-mcp
com.ezbizservices/social-media