launchd-audit
MCP server for macOS that lists launchd jobs, cron entries, and brew services in plain English, flags the ones that are failing or stale, and can act on them without rm or sudo.
Ask an agent: what's running in the background on this Mac, and is anything broken?
totals: {"jobs": 31, "running": 8, "disabled": 0, "failing": 1, "stale": 1}
[failing] com.example.drift-check | exit=1
[stale] com.example.backup | daily at 03:00, no evidence for 41 days
macOS only. Python 3.11+. MIT.
Why
A machine that has been alive for a while accumulates agents from apps you uninstalled, cron lines from a test, brew services that log megabytes a day. launchctl list is a wall of PIDs. Plists are XML. Answering "is anything wrong?" means reading a dozen files by hand.
Four of the five tools are read-only. Mutation is one tool, dry-run by default. Design notes live in PHILOSOPHY.md. Tool contracts live in SPEC.md.
Tools
| Tool | Role |
|---|---|
job_health |
The audit. Failing, stale, or writing huge logs, with totals and reasons. Start here. |
list_scheduled_jobs |
Every launchd job, cron entry, and brew service. Schedules as "every 15 minutes", "daily at 03:00", "keep alive (respawn)". |
job_detail |
One job: plist (secrets masked), live launchctl state, log tails. |
search_job_logs |
Regex search in that job's output files, optional since date. |
job_action |
The only mutating tool. Dry-run unless confirm=true. |
Sources scanned: ~/Library/LaunchAgents, /Library/LaunchAgents, /Library/LaunchDaemons, crontab -l plus /etc/cron.d and /etc/crontab. Brew services are recognised by their homebrew.mxcl.* plists in ~/Library/LaunchAgents; brew itself is never called. /System/Library is skipped on purpose.
Safety
Guardrails are in the server, not in a companion skill or prompt.
- Dry-run by default. Without
confirm=true,job_actiononly reports what it would run: commands, files, warnings, and an undo recipe. - System daemons are listable and never modified. Anything in
/Library/LaunchDaemonsis refused, whatever it is called. - Nothing is deleted.
removeunloads the plist and moves it to Trash. Every applied action returns an undo recipe. - No sudo. A plist the current user cannot move is refused up front, not half-removed.
- Secrets are masked at parse time.
EnvironmentVariablesvalues never leave the process. Secret-lookingProgramArguments(--token=…,API_KEY=…,--password …) are redacted too. Masking is a heuristic: a secret hidden in a plain positional argument or inside a script the job runs is not covered. - Every applied action is appended to
~/.launchd-audit/actions.jsonl.
Install
Requires macOS, Python 3.11+, and uv. No clone needed: uvx fetches and runs the server straight from GitHub.
Claude Code
claude mcp add launchd-audit -- uvx --from git+https://github.com/aka-kika/launchd-audit launchd-audit
Claude Desktop, Cursor, and other JSON-configured clients
{
"mcpServers": {
"launchd-audit": {
"command": "uvx",
"args": ["--from", "git+https://github.com/aka-kika/launchd-audit", "launchd-audit"]
}
}
}
Pin a release with git+https://github.com/aka-kika/launchd-audit@v0.3.0.
From a local checkout
git clone https://github.com/aka-kika/launchd-audit.git
cd launchd-audit
uv sync
uv run pytest -q
uv run python scripts/smoke.py # spawns the server over stdio and calls the read-only tools
Then point the client at the checkout:
{
"mcpServers": {
"launchd-audit": {
"command": "uv",
"args": ["--directory", "/path/to/launchd-audit", "run", "launchd-audit"]
}
}
}
Use
Ask the client something like:
- "What scheduled jobs are on this Mac?"
- "Is anything quietly failing?"
- "Did the backup run yesterday?"
- "Preview disabling
com.vendor.sync."
Worked conversations are in EXAMPLES.md.
Limitations
/System/Librarylaunchd items are not scanned.- Full
system/domain runtime state needs root. Refused by design, so system daemon state shows asnot-loadedandlast_exitis unknown. - Stale detection uses log-file mtimes. Jobs with no declared output logs are not flagged stale.
last_exitin listings comes fromlaunchctl list, which reports 0 for a job that has never run.job_detailuseslaunchctl printand reportsnullin that case.- Cron entries have no live state (macOS cron does not expose one). Only
removeis supported for them. - Not Windows Task Scheduler. Not Linux systemd.
Docs
| File | Contents |
|---|---|
| PHILOSOPHY.md | Why it exists |
| SPEC.md | Inputs, outputs, safety rules |
| EXAMPLES.md | Five example sessions |
| CHANGELOG.md | Version history |
License
MIT.
Metadata
Release files for launchd-audit 0.3.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| launchd_audit-0.3.0.tar.gz | 76.3 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| launchd_audit-0.3.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 98.2 kB
Release files / launchd_audit-0.3.0.tar.gz
| Download URL | launchd_audit-0.3.0.tar.gz |
|---|---|
| Size | 76.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
4ed70d66e0b3e5b659efb5bbe98a1336b4da5ba6349270cbaa1402165880ea82
|
|
BLAKE2b-256 checksum How to use checksums |
1b8f47e2753c395beda2bfe606e03e62b48e2fbfb93d4fa52c09ec0c62c740c2
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 9, 2026.
Transparency logRelease files / launchd_audit-0.3.0-py3-none-any.whl
| Download URL | launchd_audit-0.3.0-py3-none-any.whl |
|---|---|
| Size | 22.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
2cad168e1d81cb767a041529f445b72ef2e4f9e46860bee700446764a1cd6236
|
|
BLAKE2b-256 checksum How to use checksums |
b2217afdb6673ac1ec1dcac956c72542b4983c8dd12177197963156c53d2a492
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 9, 2026.
Transparency logSecurity for launchd-audit 0.3.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for launchd_audit-0.3.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 9, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.