Skip to main content
Glama

macctl

npm version license platform

Accessibility-first macOS desktop automation for Claude and other MCP clients.

macctl gives an MCP client first-class access to a Mac: it can see the screen, read the accessibility tree of any application to identify controls by role/title/identifier (not just pixels), drive the mouse and keyboard, manage windows, inspect processes and launchd services, work with files and the clipboard, and run shell commands — all gated behind explicit permission tiers, with a local audit log of every call. It is the macOS counterpart to winctl, Sitharaj's Windows equivalent.

Why macctl

  • Accessibility-tree-first, not screenshot-and-click. ax_snapshot and ax_find let a client locate "the Save button" or "the text field named Subject" by role/title/identifier via AXUIElement, instead of guessing pixel coordinates from a screenshot and hoping the layout doesn't shift.

  • Correct global-point coordinates. Every coordinate macctl reports or accepts is a global screen point in the CG coordinate space (top-left origin of the main display), with each display's backingScaleFactor reported alongside — no silent pixel/point confusion across Retina and external displays.

  • Reliable, paced input. Keystrokes are synthesized layout-independently; text longer than ~200 characters is delivered via a clipboard paste (with the prior clipboard contents restored afterward) instead of hundreds of individually racy synthetic keypresses.

  • Stable, opaque handles. windowHandle and elementHandle values are fingerprinted (pid + window/element identity) rather than raw pointers, so a handle from one call either still resolves correctly on the next call or fails cleanly — it never silently targets the wrong window.

  • Honest, verified failures. Window moves are read back and the verified final frame is returned (apps clamp geometry); TCC failures come back as a structured PERMISSION_MISSING error naming the exact permission and the exact app to grant it to, not a generic timeout.

  • Tiered permissions + audit log. Every tool belongs to one of five permission tiers. Tools in a disabled tier are never registered with the MCP client — it cannot see or attempt them, not just be told no. Every call is appended to a local, redacted JSONL audit log.

Related MCP server: computer-use

Requirements

  • macOS 14 (Sonoma) or later recommended; built and tested on macOS 26.

  • Apple Silicon or Intel — macctl ships a universal (arm64 + x86_64) helper binary, no compilation required on your machine.

  • Node.js 20+.

Grant permissions

macctl's native tools (window management, input, accessibility, screen capture) need macOS permissions granted through System Settings → Privacy & Security, not through macctl itself. This is the single most common source of confusion, so read this before anything else:

The permission grant attaches to whichever app launches macctl — Claude Desktop, Terminal, iTerm2, VS Code, or whatever process ultimately spawns node dist/index.jsnever to node or macctl themselves. If you grant Accessibility to the wrong app, or grant it and then keep running the old process, tools will keep failing with PERMISSION_MISSING.

Steps:

  1. Run the doctor tool to find out exactly which app to grant, and get a direct System Settings link for each permission:

    npx @sitharaj88/macctl doctor

    This prints the responsible app (identified by walking the process tree up to the nearest .app bundle), the current status of Accessibility, Screen Recording, and Full Disk Access, and a x-apple.systempreferences: deep link for each one that isn't granted yet.

  2. Open System Settings → Privacy & Security and grant:

    • Accessibility — required for window management, mouse/keyboard input, and the ax_* accessibility tools.

    • Screen Recording — required for capture, capture_region, capture_window, and list_capturable_windows.

    • Full Disk Access — only needed if you point MACCTL_ALLOWED_PATHS outside your user-visible home folders.

  3. Restart the host app (Claude Desktop, your terminal, etc.) after granting. macOS does not apply a fresh TCC grant to an already-running process.

You can also trigger the native grant dialogs directly instead of only getting deep links:

npx @sitharaj88/macctl doctor --prompt

Tools that hit a missing permission at runtime return a structured PERMISSION_MISSING error with the same "which app, which permission, which link" detail — call system_doctor again after granting to confirm.

Installation

Download the latest macctl.mcpb from GitHub Releases and double-click it. The bundled helper binary is Developer ID signed and notarized, so Gatekeeper accepts it without extra steps on a fresh download.

Claude Code

claude mcp add macctl -- npx -y @sitharaj88/macctl

Manual MCP client configuration

Add an entry to your client's MCP server config (Claude Desktop's claude_desktop_config.json, or the equivalent for your client):

{
  "mcpServers": {
    "macctl": {
      "command": "npx",
      "args": ["-y", "@sitharaj88/macctl"]
    }
  }
}

Then follow Grant permissions above and restart the client.

Permission tiers & profiles

Every tool belongs to exactly one tier. A tool in a disabled tier is never registered with the MCP client — this is an allowlist enforced at tools/list time, not a runtime check the model could talk its way around.

Tier

Covers

observe

Read-only: screenshots, window/AX inspection, system/process info, file reads.

interact

Mouse/keyboard input, window focus/move/close, AX actions, clipboard.

filesystem

Writing, deleting, moving, and creating files/directories.

manage

Process start/kill, launchd service control, notifications.

shell

Arbitrary shell commands via shell_run — unsandboxed, runs with the host process's own OS permissions.

Profiles bundle tiers together:

Profile

Tiers enabled

readonly

observe

standard (default)

observe, interact, filesystem

full

observe, interact, filesystem, manage, shell

system_doctor is always registered regardless of profile — it's how you diagnose everything else.

Set the profile via MACCTL_PROFILE, or bypass profiles entirely with an explicit tier list via MACCTL_TIERS (e.g. MACCTL_TIERS=observe,interact), which overrides the profile's tier set completely rather than adding to it.

MACCTL_PROFILE=readonly npx @sitharaj88/macctl        # observation only
MACCTL_PROFILE=full npx @sitharaj88/macctl             # everything, incl. shell
MACCTL_TIERS=observe,interact npx @sitharaj88/macctl   # explicit override

Configuration

All configuration is via environment variables, read once at startup:

Variable

Default

Description

MACCTL_PROFILE

standard

readonly | standard | full. See profiles above.

MACCTL_TIERS

(unset)

Comma-separated tier list (observe,interact,filesystem,manage,shell) that, if set, replaces the profile's tier set entirely.

MACCTL_ALLOWED_PATHS

(unset → home dir, /tmp, /private/tmp, /Volumes)

Colon-separated (:) list of directories file/exec-path tools are confined to.

MACCTL_DENIED_PATHS

(unset)

Colon-separated (:) list of additional directories to deny, layered on top of the built-in denylist (~/Library/Keychains, ~/.ssh, ~/Library/Application Support/com.apple.TCC, ~/Library/Containers, ~/Library/Group Containers, ~/Library/Cookies, ~/Library/Safari, ~/Library/Mail, /private/var/db). Denied always wins over allowed.

MACCTL_CONFIRM_DESTRUCTIVE

true

If true, destructive tools require an explicit confirm: true argument. Set to 0/false/no/off to disable.

MACCTL_AUDIT_LOG

~/Library/Logs/macctl/audit.jsonl

Path to the audit log file.

MACCTL_AUDIT_DISABLED

false

Disable audit logging entirely.

MACCTL_MAX_IMAGE_WIDTH

1600

Screenshots wider than this (in pixels) are downscaled by the helper before being returned.

MACCTL_COMMAND_TIMEOUT_MS

60000

Default timeout for shell_run, overridable per call.

MACCTL_HELPER_PATH

(unset → auto-resolved next to the installed package, native/bin/macctl-helper)

Explicit path to the native helper binary — mainly for local development (scripts/build-helper.sh --dev).

Complete tool reference

41 tools total. Tier is shown per tool; destructive tools additionally require confirm: true when MACCTL_CONFIRM_DESTRUCTIVE is enabled (the default).

Doctor (always registered, any profile)

Tool

Tier

Description

system_doctor

observe

TCC permission status, responsible app, native helper reachability, active profile/tiers, audit log location, version info.

Screen (5)

Tool

Tier

Description

list_monitors

observe

List connected displays with frame, visible frame, backing scale factor, and which is main.

capture

observe

Screenshot a full display (main by default).

capture_region

observe

Screenshot a rectangular region in global screen points.

capture_window

observe

Screenshot a single window by windowHandle.

list_capturable_windows

observe

List on-screen windows available for capture_window, via ScreenCaptureKit.

Windows (7)

Tool

Tier

Description

window_list

observe

List all on-screen windows with title, owning app, pid, bounds, layer, minimized state, and a windowHandle.

window_get_active

observe

Get the frontmost app and its focused window.

window_get_desktop_info

observe

Get screen layout, light/dark appearance, and cursor position.

window_focus

interact

Activate a window's owning app and raise the window.

window_set_state

interact

Minimize, restore, maximize, fullscreen, hide, or show a window.

window_move

interact

Move and/or resize a window; returns the verified final frame.

window_close

interact, destructive

Close a window by pressing its AXCloseButton.

Input (7)

Tool

Tier

Description

input_move_mouse

interact

Move the mouse cursor to a global screen point.

input_click

interact

Click at a global screen point (left/right/middle, single/double/triple).

input_drag

interact

Press, drag through interpolated points, and release.

input_scroll

interact

Post a scroll-wheel event, optionally moving the cursor first.

input_type

interact

Type Unicode text at the current keyboard focus (layout-independent; paste for long text).

input_press_keys

interact

Press a keyboard chord, e.g. "cmd+shift+4".

input_key_hold

interact

Hold a single key down or release it (auto-releases after 30s).

Accessibility (4)

Tool

Tier

Description

ax_snapshot

observe

Walk the AXUIElement tree of an app or window (role/title/value/description/identifier/enabled/frame per node).

ax_find

observe

Bounded search over an app's/window's tree by role, title/value substring, and/or identifier.

ax_invoke

interact

Perform an accessibility action on an element (default AXPress).

ax_set_value

interact

Set an element's value directly, or via focus + Cmd+A + type fallback, with verified read-back.

System & Processes (8)

Tool

Tier

Description

system_info

observe

CPU, memory, disk, network, battery, graphics, macOS version, hardware model.

list_services

observe

List launchd services: running (launchctl list) joined with installed LaunchAgents/LaunchDaemons.

list_installed_apps

observe

List installed applications: name, bundle id, version, path.

process_list

observe

List running processes (pid, ppid, cpu%, mem%, rss, elapsed, command).

control_service

manage, destructive

Restart/stop/start/enable/disable a launchd LaunchAgent. LaunchDaemons (system domain) refuse with PRIVILEGE_REQUIRED.

notify

manage

Show a macOS notification banner.

process_start

manage

Launch an app (open -a/open -b), open a document/URL, or spawn a bare executable.

process_kill

manage, destructive

Terminate a process by pid (SIGTERM, optionally escalating to SIGKILL).

Files, Clipboard & Shell (9)

Tool

Tier

Description

file_known_folders

observe

List well-known macOS folders (home, Desktop, Documents, Downloads, iCloud Drive, etc.) with existence/allow-list status.

file_list

observe

List a directory's entries, optionally recursive with a depth cap.

file_read

observe

Read a file as utf8 text or base64, capped at maxBytes.

file_search

observe

Search a directory tree by name glob and/or content regex, or accelerated via mdfind.

file_write

filesystem, destructive

Write, append to, or create a text/base64 file.

file_manage

filesystem, destructive

Copy, move, delete a file/directory, or mkdir -p.

clipboard_read

interact

Read clipboard text, file references, and image presence.

clipboard_write

interact

Replace clipboard contents with text or file references.

shell_run

shell

Run a command via /bin/zsh -c, capturing stdout/stderr/exit code.

Example workflow

A realistic multi-tool sequence — opening TextEdit, finding its text area via the accessibility tree (not coordinates), typing, and verifying visually:

1. process_start        { app: "TextEdit" }
2. window_get_active     → windowHandle for the new TextEdit window
3. ax_find                { windowHandle, role: "AXTextArea" }
                          → elementHandle for the document's text area
4. ax_invoke               { elementHandle }              # focus/click it
5. input_type              { text: "Meeting notes...\n\n- Discuss Q3 roadmap" }
6. ax_snapshot              { windowHandle, maxDepth: 5 }  # read the value back, verify it landed
7. capture_window           { windowHandle }               # visual confirmation
8. window_move               { windowHandle, x: 100, y: 100 }
9. input_press_keys           { keys: "cmd+s" }              # save
10. window_close                { windowHandle, confirm: true }  # destructive — needs confirm

Each step uses a real tool name and an opaque handle produced by an earlier step — never a guessed coordinate or a raw pointer.

Safety & audit

  • Destructive-action confirmation. window_close, file_write, file_manage, control_service, and process_kill are marked destructive. When MACCTL_CONFIRM_DESTRUCTIVE is enabled (the default), each call must include confirm: true or it's refused with CONFIRMATION_REQUIRED — no first-try accidental deletes.

  • Path containment. File and executable-path tools resolve the real (symlink-followed) path and check it against MACCTL_ALLOWED_PATHS / MACCTL_DENIED_PATHS, with a built-in denylist (Keychains, ~/.ssh, TCC store, app containers, Safari/Mail data, /private/var/db) that always wins over anything allowed.

  • No silent privilege escalation. macctl never shells out to sudo. Actions that would need elevated privileges (e.g. controlling a system-domain LaunchDaemon) fail honestly with PRIVILEGE_REQUIRED rather than prompting for or assuming root.

  • Audit log. Every tool call is appended as one JSON line to ~/Library/Logs/macctl/audit.jsonl (configurable, or disable with MACCTL_AUDIT_DISABLED=1): timestamp, tool name, outcome, duration, error code, and redacted arguments (long strings and base64-looking blobs are replaced with [redacted N chars] before being written).

Privacy

See PRIVACY.md for the full policy. In short: macctl runs entirely locally and has no telemetry. Screenshots, accessibility-tree contents, clipboard data, and file contents are returned only to the MCP client that requested them — nothing is sent anywhere by macctl itself.

Development

git clone https://github.com/sitharaj88/macctl.git
cd macctl
npm install
npm run build:all        # tsc + universal Swift helper build (native/bin/macctl-helper)

npm run build:helper (via scripts/build-helper.sh) builds the Swift helper as a universal (arm64 + x86_64) binary using Swift Package Manager; run it with --dev for a fast, current-arch-only build during local iteration.

npm run smoke             # spawns the server, exercises TCC-free tools across all 3 profiles
npm run smoke:full        # also exercises TCC-dependent tools (skips gracefully if ungranted)
node scripts/verify-interactive.mjs   # full TextEdit round-trip — needs a real GUI session + Accessibility/Screen Recording grants

verify-interactive.mjs drives an actual TextEdit window end-to-end (open → find the text area → type → read the value back → screenshot → move → close), so it must run in a real logged-in GUI session with permissions already granted to your terminal — it will not work over SSH or in CI.

Publishing

  • npm: npm publish --access public

  • .mcpb bundle: npx @anthropic-ai/mcpb pack (respects .mcpbignore; produces macctl.mcpb for GitHub Releases / Claude Desktop's drag-and-drop install).

  • MCP registry: mcp-publisher publish (using server.json).


👤 Author

Sitharaj Seenivasan

☕ Support

If this project helps you, consider buying me a coffee — it keeps the work going.

Buy Me A Coffee

📄 License

Licensed under the Apache License 2.0. © 2026 Sitharaj Seenivasan.

A
license - permissive license
-
quality - not tested
C
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Servers

  • A
    license
    -
    quality
    F
    maintenance
    An experimental MCP server providing full control over the macOS user interface through mouse, keyboard, and window management tools. It enables AI assistants to automate desktop tasks by utilizing native accessibility APIs and OCR for real-time screen comprehension.
    Last updated
    7
    Creative Commons Zero v1.0 Universal
  • A
    license
    -
    quality
    B
    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.
    Last updated
    16
    MIT
  • A
    license
    -
    quality
    A
    maintenance
    A local daemon for macOS that gives any MCP-capable AI client eyes, hands, and a face — screen capture, accessibility tree, mouse/keyboard actions, and an overlay — with a built-in security ceiling.
    Last updated
    1
    AGPL 3.0
  • A
    license
    -
    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.
    Last updated
    23
    MIT

View all related MCP servers

Related MCP Connectors

  • Let ChatGPT, Claude & Cursor use your Mac: email, calendar, iMessage, Teams, files. Local, free.

  • MCP connector that lets ChatGPT list, search, and run your Apple Shortcuts via a local Mac agent

  • Personal assistant MCP server with search, execute, packages, jobs, secrets, and integrations.

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/sitharaj88/macctl'

If you have feedback or need assistance with the MCP directory API, please join our Discord server