npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@torsday/omnifocus-mcp

v1.5.3

Published

MCP server exposing the full OmniFocus surface to LLM agents — 80 typed tools spanning tasks, projects, tags, folders, perspectives, forecast, review, notes, attachments, and sync, with a strict typed-error taxonomy and per-tool circuit breakers, rate lim

Readme

omnifocus-mcp

npm version CI License: MIT Node: 24+ Platform: macOS 13+ Mutation tested: Stryker

Give any MCP-compatible AI assistant full, typed access to your OmniFocus. Read your inbox, create tasks, close projects, batch-update dozens of items, evaluate perspectives, trigger sync — all through natural language. omnifocus-mcp wires an 80-tool MCP server directly to OmniFocus on macOS via JXA and OmniJS, with circuit breakers, rate limits, and an agent-aware error hierarchy so the assistant knows exactly what to do next when something goes wrong.


Table of contents


Agent-native OmniFocus — beyond the app surface

A plain MCP wrapper would be a one-to-one mirror of the OmniFocus app. This server is more than that. It exposes a small set of capabilities that exist because an LLM is the caller — capabilities the app itself doesn't ship and probably never will, because they're only worth the effort when the consumer is an agent that can reason over structured input and act on the result.

These are the agent-native capabilities, framed in the user outcome they enable:

  • Stalled-project triageomnifocus://project-health returns granular signals (last activity, available task count, deferred-future tasks, review-overdue) so an agent can identify projects worth a status nudge without the user opening the app. Mechanical aggregation; the app could do it but doesn't.
  • Semantic dedupetask_find_similar does lexical similarity search across task names so an agent confirms intent ("is this a duplicate of X?") before creating a new task. Possible without an LLM, but only useful with one in the loop.
  • Taxonomy auditomnifocus://taxonomy-audit flags inconsistent tag/folder usage so an agent can propose cleanup grounded in the actual structure of the database. Mechanical.
  • NL perspective authoring (in development — #476) — describe a perspective in prose; the agent compiles a rule tree and writes it via perspective_create. Exists because of the agent — the rule tree is a non-trivial structure most users won't compose by hand.
  • Time-budget reconciliationforecast_pack takes a daily minute budget and packs the forecast into it, surfacing overloaded days. Asking "I have 90 minutes, what should I do?" gets a structured answer.
  • Retrospective resourceomnifocus://retrospective?from=…&to=… aggregates the closed-task surface so an agent can write the user's weekly review against real data instead of asking them to recap.
  • Project templatesproject_template_save / _instantiate capture and replay project structures with parameter substitution and date shifting. The agent fills the parameters from conversation context.
  • Inbox-triage prompt — the bundled inbox-triage MCP prompt sequences the tool calls for a full GTD-style processing sweep. Intentionally a prompt, not a tool — the value is in orchestrating the existing surface.
  • Calendar + agendaomnifocus://calendar and omnifocus://agenda merge macOS Calendar events with the OF forecast so an agent can answer "what does my day actually look like?" without the user holding two windows side by side.

How this is different from a plain wrapper. A wrapper exposes the app's verbs. This server adds verbs the app doesn't have, because LLMs change what's worth building. Some of the additions (project-health, taxonomy-audit) are mechanical aggregations the app could ship and never has — they sit unbuilt because no human wants to click through them. Others (NL perspective authoring, semantic dedupe, time-budget reconciliation) are only valuable with an LLM in the call path. Both kinds belong here. The split is honest: don't pretend the mechanical stuff is novel, and don't pretend the agent-only stuff is just sugar.


Why this exists

OmniFocus is a powerful GTD tool, but it's an island. Your tasks sit there while you context-switch between your AI assistant and your task manager, manually copy-pasting notes, updating projects, and trying to keep everything in sync with your actual work.

omnifocus-mcp removes that friction. With it connected, your AI assistant can:

  • Capture — turn a conversation into tasks directly in OmniFocus, with the right project, tags, due dates, and notes, without you touching the app
  • Review — pull today's overdue items, this week's forecast, or a full project breakdown into context so the assistant can reason about your workload alongside your work
  • Maintain — batch-defer a pile of overdue tasks, complete a sprint's worth of items, reorganize projects after a meeting debrief
  • Reflect — ask "what's in my inbox right now?" or "what projects haven't been reviewed in a month?" and get structured, actionable answers

The server is built to a single-user local-first standard: no network surface, no cloud sync, typed errors with agent-readable remediation hints, safe by default.

See docs/examples.md for concrete prompt-to-tool-call sequences and docs/prompts.md for the bundled MCP prompt templates (daily-review, weekly-review, capture-meeting, project-planning).


Quick start

Prerequisites: macOS 13 (Ventura) or later · OmniFocus 3.x or 4.x (4.x recommended; some tools require 4.x — see version compatibility) · Node 24+ (not required for Homebrew install)

  1. Install

    # Homebrew (no Node required)
    brew install torsday/tap/omnifocus-mcp
    
    # or npm
    npm install -g @torsday/omnifocus-mcp
  2. Configure your MCP client. Every client uses the same command + args + env shape — only the file path and serialization (JSON vs TOML) differ. The universal shape:

    command: omnifocus-mcp
    args:    (none)
    env:     OMNIFOCUS_LOG_LEVEL=info   # optional; "debug" is verbose

    Two common cases inline; full per-client guides at docs/clients/ (Claude Code, Claude Desktop, Codex, OpenCode, Pi, generic stdio).

    claude mcp add omnifocus omnifocus-mcp

    Detailed: docs/clients/claude-code.md

    {
      "mcpServers": {
        "omnifocus": {
          "command": "omnifocus-mcp",
          "args": [],
          "env": { "OMNIFOCUS_LOG_LEVEL": "info" }
        }
      }
    }

    Detailed: docs/clients/claude-desktop.md

  3. Grant macOS Automation permission on first use — the app running the MCP server will prompt to control OmniFocus; click OK. If denied by mistake: System Settings → Privacy & Security → Automation → [app] → OmniFocus

  4. Verify — ask your assistant: "Use the internal_status tool and tell me what it returns."

Stuck? See docs/troubleshooting.md.


Security & trust

omnifocus-mcp is a local-only Node.js process that drives a local OmniFocus app via Apple's osascript runtime. Installing this package does not introduce cloud connectivity, telemetry, or network egress that wasn't already on your machine.

OmniFocus DB (local) ─→ JXA / OmniJS via osascript (local) ─→ MCP server (local stdio)
                                                                    │
                                                                    ↓
                                                       MCP client (local)
                                                                    │
                                                                    ↓
                                                  LLM provider (only if your client uses one)

The LLM hop at the bottom is your client's choice, not this package's. If you run a local-only client (or a client configured to use a local model), nothing in this stack reaches the network.

Hard guarantees

Each guarantee is enforced by code, not by promise. Click through to verify.

  • No network I/O at the source level — a custom lint rule (no-network-import) bans import of node:http, node:https, node-fetch, axios, undici, and cross-fetch. CI fails on any new import that would enable network calls. See src/linting/customRules.ts Rule 4.
  • No stdout writes outside the MCP framing pathinstallStdoutGuard() proxies process.stdout.write at server boot and rejects any write that wouldn't corrupt MCP's JSON-RPC stream. The contract is pinned by src/server/stdoutGuard.test.ts.
  • No telemetry / analytics — production dependencies in package.json are six packages: @modelcontextprotocol/sdk, lru-cache, pino, ulid, zod, zod-to-json-schema. No analytics SDK; nothing phones home.
  • No postinstall / preinstall scriptspackage.json ships with one lifecycle script (prepublishOnly) and one dev hook (prepare for git hooks). Neither runs when a downstream consumer installs the package.
  • Config secrets redacted from logs — the boot-time server.started event runs config through redactConfig before logging; path-shaped values are sha256-hashed (12-char prefix) so even local stderr doesn't leak attachment-path layout.
  • Attachment paths are allowlist-bounded — every attachment operation passes through assertAttachmentPath, which resolves symlinks before checking against OMNIFOCUS_ATTACHMENT_PATHS (default: $HOME) to defeat symlink-escape, and hard-blocks /System, /Library, and their /private/* mirrors regardless of the allowlist.

Opt-in escape hatch

There is exactly one feature that's gated behind an environment variable because enabling it broadens the threat surface:

  • OMNIFOCUS_ALLOW_RAW_SCRIPT=1 — exposes run_jxa_script and run_omnijs_script, which run arbitrary JXA / OmniJS supplied by the agent. Off by default. When enabled, every invocation emits a raw_script.invoked audit event at info level (regardless of OMNIFOCUS_LOG_LEVEL) including the full script body and tool name. See ADR-0004 for the rationale.

Verify it yourself

Three recipes that take seconds; you don't have to take this README's word for any of the above.

  1. Audit the source. The repo at github.com/torsday/omnifocus-mcp is the canonical source. Each published artifact is built from its own tagged commit (v<version>); compare dist/index.js against the build output of the tag matching the version you installed.
  2. Verify the published artifact's provenance. npm publishes attestations via Sigstore:
    npm view @torsday/omnifocus-mcp dist.attestations
    The provenance URL points to the GitHub Actions run that built the artifact, signed with the workflow's OIDC identity.
  3. Inspect what's actually in the tarball. It should be five files — no more, no less, and no install scripts:
    curl -sL "$(npm view @torsday/omnifocus-mcp dist.tarball)" | tar -tzvf -
    Expected output (file count = 5):
    package/LICENSE
    package/dist/index.js
    package/package.json
    package/CHANGELOG.md
    package/README.md

Out of scope

The threat model deliberately excludes anything outside this codebase: vulnerabilities in OmniFocus itself, Apple's JXA / OmniJS / osascript runtimes, transitive npm-dependency CVEs (track and patch via npm audit / Dependabot, but not part of this project's guarantees), and any attacker with root-equivalent local access (who could replace osascript, the MCP server binary, or your shell). See SECURITY.md § Scope.

Full threat model: SECURITY.md, docs/design/security.md.


Architecture at a glance

flowchart LR
    Agent["LLM agent<br/>(any MCP client)"] --> SDK["MCP stdio<br/>transport"]
    SDK --> Tools["Tool &<br/>Resource handlers"]
    Tools --> Services["Service layer"]
    Services --> Cache[(30s LRU<br/>read cache)]
    Cache --> Adapter{OmniFocus<br/>Adapter}
    Adapter --> Router[Transport<br/>Router]
    Router -->|CRUD, forecast, search| Jxa[JxaTransport]
    Router -->|Perspectives, plug-ins,<br/>reorder, reparent| OmniJs[OmniJsTransport]
    Jxa --> OF[(OmniFocus)]
    OmniJs --> OF

    classDef boundary stroke-dasharray: 5 5
    class Adapter boundary

Key design points:

  • Adapter seam — services never see osascript or URL schemes; OmniFocusAdapter is the only OS boundary. Tests swap in an InMemoryAdapter.
  • Dual transport — JXA via osascript for CRUD; OmniJS via evaluateJavascript() for custom perspectives, plug-ins, reorder, and reparent. A TransportRouter picks per operation.
  • Read pool + write queue — concurrent JXA reads from a configurable pool; mutations serialized through a write queue; OmniJS operations through a separate queue.
  • 30s LRU read cache — invalidated on every write. Mutations are never served stale.
  • Middleware stack — every registered tool runs through: assertNotShuttingDowncircuitBreakerrateLimitMetaloopDetection.

The full layered diagram with queues, circuit breakers, and the test adapter lives in docs/design/architecture.md.


Status and roadmap

The package is published on npm; see the latest release for the current version and notes. The phase table below records the milestone work that shipped in v1.0.0; the live backlog and future enhancements track on the Project board, and the unreleased section of the CHANGELOG lists what's already merged toward the next release.

| Phase | Milestone | Status | |---|---|---| | M0 | Foundation + both transports | ✅ Done | | M1 | Core task & project surface | ✅ Done | | M2 | Metadata + perspectives (OmniJS) | ✅ Done | | M3 | Advanced (repeat, notes, review, batch, DSL) | ✅ Done | | M4 | Long tail (attachments, OPML, sync, plug-ins, raw scripts) | ✅ Done | | M5 | Polish & release (observability, E2E, CI, docs, npm) | ✅ Done |

Track open issues and future enhancements on the GitHub Project board.


Reference docs

| Doc | What | |---|---| | docs/tools.md | Auto-generated reference for every tool — input schemas, examples, responses | | src/tools/INDEX.md | One-line-per-tool index grouped by domain (cheaper than grepping) | | docs/examples.md | Concrete prompt → tool-call sequences | | docs/prompts.md | Bundled MCP prompt templates (daily-review, weekly-review, capture-meeting, project-planning) | | AGENTS.md | Agent-facing guide — engineering conventions for contributors AND calling conventions for clients (IDs, error codes, dates, idempotency, _links, response envelope, meta.warnings, rate limits) | | docs/clients/ | Per-client setup guides (Claude Code, Claude Desktop, Codex, OpenCode, Pi, generic stdio) | | docs/troubleshooting.md | OmniFocus not running, Automation permission, slow startup, raw-script gating, sync staleness | | docs/domain-reference.md | OmniFocus glossary, canonical schemas, lossiness matrix for export/import | | docs/security.md | Attack surface, mitigations, test coverage | | SECURITY.md | Vulnerability reporting, scope | | SPEC.md | Functional scope and resolved v1 decisions | | DESIGN.md | Index of the per-area design files under docs/design/ — architecture, envelope, IDs/dates, security, testing, observability, configuration, distribution, example tool, resources | | docs/adr/ | Architecture Decision Records — every load-bearing choice (TypeScript+Node 24, dual transport, namespacing, raw-script gating, scripts-as-files, LRU cache, ISO-8601 dates, branded IDs, pool+queue, stdio transport, semver, npx distribution, response envelope, E2E adapter switch, NL envelope, webhooks, Stryker mutation gate, EventKit calendar bridge, cross-transport ID interop, JXA helper inlining, reactive runtime spike, envelope text/structured split) | | CHANGELOG.md | Release history per Keep a Changelog |

For the full environment-variable surface with override semantics see docs/design/configuration.md; the load-bearing knobs are OMNIFOCUS_LOG_LEVEL, OMNIFOCUS_CACHE_TTL_MS, OMNIFOCUS_ALLOW_RAW_SCRIPT, and OMNIFOCUS_ATTACHMENT_PATHS.


Contributing

This is a single-developer project; external contributions are not currently solicited. The design, ADRs, and task backlog are public so the work is inspectable and forkable. See CONTRIBUTING.md for the patterns any contribution would need to follow.


License

MIT — see LICENSE.