omnifocus-cli
Independent OmniFocus 4 automation with a production CLI, a secure HTTPS API, and a container-first MCP server.
omnifocus-cli syncs directly with an OmniFocus WebDAV endpoint, reads encrypted .ofocus
bundles, exposes a clean of command-line interface, runs as an MCP server for
Claude-compatible hosts, and can serve a private HTTPS JSON API for n8n and other automation
clients.
It does not require OmniFocus.app, macOS, AppleScript, or Omni Automation. If a host can
reach your OmniFocus WebDAV bundle and provide the right credentials, omnifocus-cli can run
there headlessly.
Why This Project
- Native-feeling CLI for day-to-day task management
- MCP server over stdio for LLM tooling and local assistants
- HTTPS JSON API for n8n-style automations, protected by mandatory TLS 1.3+ and Bearer auth
- Direct WebDAV sync without AppleScript or Omni Automation glue
- No dependency on OmniFocus.app or macOS at runtime
- Headless/container-friendly operation on servers, CI runners, and agent boxes
- Hermetic tests, strict typing, and 100% coverage enforcement
- Podman-friendly runtime image with a non-root default user
No OmniFocus App Required
omnifocus-cli talks to the sync bundle directly.
- Required: WebDAV access to the
.ofocusbundle and, when enabled, the encryption passphrase - Not required: OmniFocus.app installed locally
- Not required: macOS
- Not required: System Automation permissions
- Supported deployment shape: local workstation, remote Linux box, CI job, or long-lived MCP host
Quick Start
Build the runtime image
podman build --target runtime -t of .
Create a persistent cache
mkdir -p .of-cache
Run CLI commands
# Runs anywhere with network access to the WebDAV bundle
podman run --rm \
-v "$PWD/.of-cache":/cache \
-e OF_CACHE_DIR=/cache \
-e OF_WEBDAV_URL \
-e OF_WEBDAV_USER \
-e OF_WEBDAV_PASS \
of sync
podman run --rm \
-v "$PWD/.of-cache":/cache \
-e OF_CACHE_DIR=/cache \
-e OF_WEBDAV_URL \
-e OF_WEBDAV_USER \
-e OF_WEBDAV_PASS \
of tasks --inbox
Run as an MCP host process
podman run --rm -i \
-v "$PWD/.of-cache":/cache \
-e OF_CACHE_DIR=/cache \
-e OF_WEBDAV_URL \
-e OF_WEBDAV_USER \
-e OF_WEBDAV_PASS \
of
Run as a private HTTPS API
podman run --rm \
-p 127.0.0.1:8443:8443 \
-v "$PWD/certs":/tls:ro \
-e OF_HTTP_API_KEY=replace-me \
-e OF_HTTP_TLS_CERT_FILE=/tls/cert.pem \
-e OF_HTTP_TLS_KEY_FILE=/tls/key.pem \
of http
Inspect the CLI surface
podman run --rm --pull=always of --help
podman run --rm --pull=always of --version
Documentation
- CLI reference for the exact
ofcommand surface - HTTPS API reference for operator guidance, including a copy-paste local TLS and Hermes MCP setup
- ASVS 5.0 security mapping for implemented controls and known gaps
- MCP reference for the stdio tool catalog and input contracts
- Container and runtime notes for image behavior and deployment patterns
Command Model
The project exposes three native Python entrypoints:
offor CLI usageof-mcpfor direct MCP usageof-httpfor direct HTTPS API usage
The container image ships with a launcher:
podman run --rm of syncRuns the CLIpodman run --rm of add "Task name"Runs the CLIpodman run --rm -i ofStarts the MCP server over stdiopodman run --rm -i of mcpStarts the MCP server explicitlypodman run --rm of httpStarts the HTTPS API with mandatory TLS and Bearer auth
CLI Usage
The full CLI surface lives in docs/cli.md.
High-level command groups:
- tasks
- projects
- folders
- tags
- sync
Current Surface
Today the project supports:
- direct WebDAV sync of OmniFocus bundles
- tasks, projects, folders, and tags
- project review workflow over MCP
- HTTPS JSON API for tasks, projects, folders, tags, review, and sync
- MCP over stdio
- headless/container-first deployment
Not yet implemented:
- perspectives
- statistics commands
- dedicated inbox subcommands
MCP Integration
The detailed MCP tool catalog lives in docs/mcp.md.
Container behavior
The runtime image starts the MCP server when you run it without a command:
podman run --rm -i of
That is the correct shape for MCP hosts that manage a long-lived stdio subprocess.
If you track :latest, prefer podman pull or --pull=always before running so a cached local
image does not mask a newer published release.
Claude-compatible configuration
{
"mcpServers": {
"omnifocus": {
"command": "podman",
"args": [
"run",
"--rm",
"-i",
"-v",
"/absolute/path/to/repo/.of-cache:/cache",
"-e",
"OF_CACHE_DIR=/cache",
"-e",
"OF_WEBDAV_URL",
"-e",
"OF_WEBDAV_USER",
"-e",
"OF_WEBDAV_PASS",
"of:latest"
]
}
}
}
Set the variables in your MCP host's explicit environment configuration; do not put credential
values in command arguments or embed them in OF_WEBDAV_URL.
MCP deployment notes
- Mount a persistent cache directory for better warm-start performance
- Reuse a long-lived container when your MCP host supports it
- Use
podman run --rm -i of mcponly if you want an explicit MCP selector
Environment Variables
| Variable | Required | Description |
|---|---|---|
OF_WEBDAV_URL |
Yes | WebDAV bundle URL. Embedded credentials remain supported for compatibility but are not recommended. |
OF_WEBDAV_USER |
No | Explicit WebDAV username. Overrides URL-embedded credentials. |
OF_WEBDAV_PASS |
No | Explicit WebDAV password. Overrides URL-embedded credentials. |
OF_ENCRYPTION_PASSPHRASE |
No | Bundle decryption passphrase. Defaults to the WebDAV password. |
OF_CACHE_DIR |
No | Cache directory. Defaults to a repo-local .of-cache/ when detectable, otherwise /tmp/of-cache. |
Every sensitive variable also accepts a mutually exclusive _FILE form (for example,
OF_WEBDAV_PASS_FILE). The file is read as UTF-8 and is suitable for a container secret mount.
Prefer it to an environment variable or URL-embedded credentials.
Distribution
The project ships as:
- a Python package with
ofandof-mcp - a container image for CLI and MCP usage
- a GitHub Release with wheel and sdist artifacts
- a GHCR runtime image published by the release workflow
The runtime container is based on python:3.14-slim, runs as a non-root user, and is published
for both linux/amd64 and linux/arm64.
PyPI publishing is intentionally out of scope for the current release process.
Development
For local setup, quality checks, and release workflow details, see CONTRIBUTING.md.
Changelog
Release notes live in CHANGELOG.md.
Author
Maciej Szymczak
[email protected]
No comments yet
Be the first to share your take.