Skip to content

Latest commit

 

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

launchd MCP Server

macOS Rust rmcp License MRTR gated

A governed rmcp 3.1.2 port of the complete 19-tool LaunchdServices surface. It discovers, inspects, creates, edits, starts, stops, and removes allow-listed launchd services while placing every mutation behind sealed MCP 2026 MRTR approval.

Architecture

launchd MCP architecture

The policy path is intentionally layered: tool-schema validation, manifest risk classification, MRTR approval, exact label resolution, canonical path allow-listing, plist validation/redaction, and only then launchctl or an atomic filesystem operation. System-owned plist roots remain read-only even when listed for discovery.

MCP protocol surface

  • All 19 tools advertise object output schemas, readable titles, and standard read-only, destructive, idempotent, and closed-world annotations.
  • Existing JSON text is preserved for older clients and mirrored into MCP structuredContent; rejected or failed operations return isError: true.
  • The server does not advertise Tasks because launchd operations are bounded direct calls. The static tool catalog carries a 24-hour public cache hint.
  • Annotations are advisory. Sealed MRTR, caller binding, exact-label resolution, plist validation, and canonical path allow-lists remain the enforcement boundary.

Governance posture

  • All 12 lifecycle, plist, and GUI mutations require MRTR approval.
  • Seven inspection tools are read-only.
  • The default plist allow-list is only ~/Library/LaunchAgents.
  • Labels resolve by exact match before an operation is executed.
  • System launchd plists can be discovered but cannot be overwritten or deleted.
  • Global mutations use non-interactive privilege escalation and fail closed when unavailable.
  • Secret-bearing environment values and command arguments are redacted from plist reads.

Tools (19)

Tool Purpose Risk Approval
launchd_list Discover allow-listed services with optional status/filter Read-only No
launchd_status Return normalized loaded/running/error state Read-only No
launchd_start Start or bootstrap a service Identity/security action Required
launchd_stop Send SIGTERM or SIGKILL through launchctl Identity/security action Required
launchd_restart Restart, bootstrapping when unloaded Identity/security action Required
launchd_load Bootstrap a service in its domain Identity/security action Required
launchd_unload Boot out a service Identity/security action Required
launchd_enable Enable in launchd's override database Identity/security action Required
launchd_disable Disable in launchd's override database Identity/security action Required
launchd_logs Read bounded stdout/stderr tails Read-only No
launchd_info Return bounded raw launchctl print output Read-only No
launchd_create Validate, write, and bootstrap a new plist Identity/security action Required
launchd_delete Boot out and remove a non-system plist Identity/security action Required
launchd_plist_read Read XML with secret redaction Read-only No
launchd_plist_write Validate and atomically replace plist XML Identity/security action Required
launchd_force_reload Boot out then bootstrap Identity/security action Required
launchd_print_disabled Read a domain's override database Read-only No
launchd_override_status Compare plist Disabled with override state Read-only No
launchd_open_gui Open the optional ZLaunchManager app External write Required

The complete risk declarations live in mcp-server.toml.

Domains and allow-lists

Supported logical domains are user, global-agent, and global-daemon. Read/list calls may use all where documented.

export MCP_LAUNCHD_PLIST_ALLOWLIST="$HOME/Library/LaunchAgents:/Library/LaunchAgents:/Library/LaunchDaemons"
export MCP_LAUNCHD_LOG_ALLOWLIST="$HOME/Library/Logs:/var/log:/private/tmp"

Entries are colon-separated canonical roots, not glob patterns. Adding a system root permits discovery, not modification of system-owned plist files.

Requirements

  • macOS with /bin/launchctl.
  • Rust 1.94.1 or newer.
  • A 32-byte-or-longer MCP_REQUEST_STATE_KEY for mutations.
  • Optional narrow non-interactive privilege for global agents/daemons.
  • Optional ZLaunchManager.app for launchd_open_gui.

Install and run

git clone https://github.com/zavora-ai/mcp-launchd.git
cd mcp-launchd
cargo +1.94.1 build --release --locked
./target/release/mcp-launchd

MCP client configuration

{
  "mcpServers": {
    "launchd": {
      "command": "/absolute/path/to/mcp-launchd/target/release/mcp-launchd",
      "env": {
        "MCP_REQUEST_STATE_KEY": "replace-with-a-random-secret-of-at-least-32-bytes",
        "MCP_LAUNCHD_PLIST_ALLOWLIST": "/Users/me/Library/LaunchAgents",
        "MCP_LAUNCHD_LOG_ALLOWLIST": "/Users/me/Library/Logs:/private/tmp"
      }
    }
  }
}

Do not commit the request-state key. The manifest is embedded, so the binary is independent of its working directory.

Create example

{
  "name": "launchd_create",
  "arguments": {
    "label": "com.example.heartbeat",
    "program": "/usr/bin/logger",
    "domain": "user",
    "arguments": ["heartbeat"],
    "run_at_load": true,
    "start_interval": 300,
    "stdout_path": "/Users/me/Library/Logs/heartbeat.log"
  }
}

The program must be absolute, present, and executable. Working and log directories must exist and remain inside their configured allow-lists. The MCP client must complete MRTR before creation occurs.

Plist safety

  • Plists are capped at 10 MiB and parsed before writes.
  • Label, executable paths, arguments, working directory, log paths, interval, booleans, and environment dictionaries are validated.
  • New and replacement files are written atomically.
  • KeepAlive booleans and dictionaries are recognized.
  • Environment values and secret-like arguments are redacted on reads.
  • Paths are canonicalized or normalized without following a write outside an allowed root.
  • Bounded command execution prevents a stuck launchctl process from hanging the MCP server.

Privileged deployment

User LaunchAgents need no elevation. Global agent/daemon filesystem writes may require a root helper or narrowly scoped sudoers rule. docs/sudoers.example is a starting point only; narrow it to the exact domains and commands required by the host.

Verification

cargo +1.94.1 fmt --check
cargo +1.94.1 test --locked --all-targets --all-features
cargo +1.94.1 clippy --locked --all-targets --all-features -- -D warnings
cargo +1.94.1 build --release --locked

The non-mutating suite checks all 19 manifest entries, both MCP protocol versions, path policy, plist validation/redaction, and a sealed MRTR round trip. The opt-in native lifecycle test creates a unique user agent, bootstraps it, checks status, unloads it, deletes it, and verifies cleanup:

cargo +1.94.1 test --locked --test lifecycle_smoke -- --ignored --nocapture

Compatibility

  • rmcp 3.1.2
  • adk-mcp-sdk 0.2
  • Rust 1.94.1
  • MCP 2025-11-25 and 2026-07-28
  • stdio transport; direct bounded calls (Tasks intentionally not advertised)

License

Apache-2.0. See LICENSE.

Part of the Zavora AI MCP server ecosystem.

About

Governed 19-tool launchd service-management MCP server with MRTR approval

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages