Skip to main content
Glama

omnifocus-sync-mcp

A headless MCP server that reads and writes OmniFocus data by talking directly to the Omni Sync Server over WebDAV — no Mac, no running OmniFocus app. It implements OmniFocus's client-side end-to-end encryption in TypeScript, so it can run anywhere Node runs (a Linux NAS, a container, a cloud box).

Every other OmniFocus MCP server automates the local macOS app via AppleScript/JXA. This one speaks the cloud sync protocol instead, which is what lets it run headless.

⚠️ Alpha / use at your own risk. This talks to your real task database. The read and write paths have been validated end-to-end against a live Omni Sync Server account (add + delete transactions round-trip correctly), and the crypto and parsing have an offline test suite — but this hasn't been exercised across every OmniFocus version or edge case. Keep a backup (in OmniFocus: File → Back Up Database), and consider a throwaway account while evaluating. See Safety.

How it works

An OmniFocus sync store is a WebDAV collection (OmniFocus.ofocus/) containing:

  • a baseline transaction and a chain of delta transactions, each a .zip holding a contents.xml describing object creates/updates/deletes;

  • .client files registering each device and the transaction "tail" it has synced to;

  • an encrypted plist holding the wrapped document keys.

This server:

  1. Resolves the shardsync.omnigroup.com 302-redirects each account to a syncN.omnigroup.com host; the client follows it.

  2. Authenticates with HTTP Digest (what Omni Sync Server negotiates).

  3. Decrypts using the OmniFileStore format: PBKDF2 derives a wrapping key from your passphrase, RFC 3394 unwraps the document key slots, and each file is AES-128-CTR + HMAC-SHA256 in 64 KiB segments (encrypt-then-MAC).

  4. Replays the transaction chain into an in-memory object model.

  5. Writes by building a new contents.xml, zipping, encrypting, PUT-ting it as a new transaction descending from the current head tail, and refreshing its .client registration so other clients treat it as a real participant.

The crypto and format were reimplemented from Omni's own open-source frameworks (OmniGroup/OmniGroup, notably OmniFileStore/EncryptionFormat.md and DecryptionExample.py) plus community reverse-engineering (tomzx/ofocus-format, rubyfocus). See CREDITS.

Related MCP server: OmniFocus MCP Server

Install

git clone https://github.com/rosskukulinski/omnifocus-sync-mcp.git
cd omnifocus-sync-mcp
npm install
npm run build

Configure

Set credentials via environment variables (see .env.example):

Variable

Required

Description

OMNIFOCUS_SYNC_USERNAME

yes

Omni Sync Server account name

OMNIFOCUS_SYNC_PASSWORD

yes

Omni Sync Server account password

OMNIFOCUS_ENCRYPTION_PASSPHRASE

no

Defaults to the sync password (OmniFocus's default)

OMNIFOCUS_SYNC_URL

no

Defaults to https://sync.omnigroup.com

OMNIFOCUS_DATABASE

no

Defaults to OmniFocus.ofocus

OMNIFOCUS_CLIENT_NAME

no

Display name for this client's .client file

OMNIFOCUS_CLIENT_STATE_FILE

no

Where the stable client id is stored

OMNIFOCUS_READ_ONLY

no

1/true to refuse all writes

Try it without an MCP client

A diagnostic CLI exercises the sync path directly:

export OMNIFOCUS_SYNC_USERNAME=... OMNIFOCUS_SYNC_PASSWORD=...
npm run probe -- sync            # show database counts
npm run probe -- list flagged    # list flagged tasks
npm run probe -- projects        # list projects
npm run probe -- add "Buy milk"  # add an inbox task

Use as an MCP server

Run over stdio. Example Claude Desktop / Claude Code config:

{
  "mcpServers": {
    "omnifocus": {
      "command": "node",
      "args": ["/absolute/path/to/omnifocus-sync-mcp/dist/index.js"],
      "env": {
        "OMNIFOCUS_SYNC_USERNAME": "your-account",
        "OMNIFOCUS_SYNC_PASSWORD": "your-password"
      }
    }
  }
}

Tools

Tool

Description

of_sync

Reload the database from the server; returns counts

of_list_tasks

List tasks (remaining, available, flagged, inbox, due_soon, all), with project/tag/search filters

of_get_task

Full detail for one task by id

of_list_projects

List projects with folder path and status

of_add_task

Create a task (inbox or in a project)

of_complete_task

Mark a task complete

of_edit_task

Edit name/note/flag/due/defer; clear dates

of_delete_task

Delete a task by id

Safety

  • Back up first, and prefer a test account until you trust the write path.

  • Start with OMNIFOCUS_READ_ONLY=1 to explore safely.

  • Writes are append-only transactions; a bad write forks history rather than destroying data, and OmniFocus merges forks on its next sync — but that is a safety net, not a guarantee.

  • Secrets come from the environment only; nothing is written to this repo. The client identity file (client.json) contains no secrets.

Development

npm test          # offline test suite (crypto round-trips, parse/write, digest)
npm run dev -- ... # run the CLI via tsx without building

The offline tests cover the risky parts without a network or a real account: the OmniFileStore encrypt/decrypt round-trip at every segment boundary, MAC tamper detection, the passphrase→PBKDF2→key-unwrap pipeline, contents.xml build/parse round-trips, partial-update merge semantics, and the full zip→encrypt→decrypt→parse read path.

Credits

Built by reimplementing formats documented and open-sourced by others:

Not affiliated with or endorsed by The Omni Group. "OmniFocus" is their trademark.

License

MIT — see LICENSE.

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.

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/rosskukulinski/omnifocus-sync-mcp'

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