Skip to main content
Glama
OmniFocus MCP Server

OmniFocus MCP Server

by estrenuo

OmniFocus MCP Server

A Model Context Protocol (MCP) server that enables AI assistants to interact with OmniFocus on macOS via JXA (JavaScript for Automation).

Features

This MCP server provides access to OmniFocus functionality:

Task Management

  • List inbox tasks - View and filter tasks in your inbox (including multi-tag filtering)

  • Create tasks - Add new tasks with full property support (due dates, planned dates, tags, notes, subtasks, recurrence)

  • Update tasks - Change name, note, dates, flag, estimate, recurrence, or move a task to another project

  • Complete/Drop tasks - Mark tasks as done or dropped, individually or in batch

  • Delete tasks - Remove a task permanently

  • Update task notes - Replace, clear, or append to a task's note

  • Get due tasks - Find tasks due within a timeframe

  • Get planned tasks - Find tasks planned within a timeframe

  • Get flagged tasks - List all flagged items

  • Add/remove tags from tasks - Manage task tags, individually or in batch

Project Management

  • List projects - View projects with status filtering

  • Get project tasks - List all tasks belonging to a project

  • Create projects - New projects with folder placement, status, dates, sequential mode, review interval

  • Update projects - Change name, note, status, flag, dates, sequential mode, review interval

  • Delete projects - Remove a project and its tasks

  • Update project notes - Replace, clear, or append to a project's note

  • Get projects for review - Find projects needing review, optionally with their incomplete tasks

  • Mark project reviewed - Update a project's review status and next review date

  • Batch mark reviewed - Efficiently review multiple projects at once

Organization

  • List folders - View folder hierarchy

  • Create/rename/delete folders - Manage the folder tree (including nested folders)

  • List tags - View all tags

  • List perspectives - View built-in and custom perspectives

  • Get perspective tasks - List tasks shown in a specific perspective

  • Universal search - Search across tasks, projects, folders, and tags

Safety properties

  • No silent winner on duplicate names. OmniFocus allows two projects (or tasks) to share a name. Name lookups collect every match and fail with the matching IDs when there is more than one, so a rename, move or delete can never hit the wrong item while reporting success.

  • Mutations are verified. Operations that JXA can fail silently at (notably moving a task between projects) read the result back inside the same script, so a failed move is reported as an error rather than as success.

Related MCP server: OmniFocus MCP Server

Requirements

  • macOS (OmniFocus is macOS/iOS only, and this server uses JXA)

  • OmniFocus 3+ installed

  • Node.js 18+

  • Automation permissions enabled for your terminal/client app

Installation

  1. Clone or download this repository:

    cd omnifocus-mcp-server
  2. Install dependencies:

    npm install
  3. Build the TypeScript:

    npm run build
  4. Configure your MCP client to use the server (see Configuration below)

Configuration

Claude Desktop

Add to your Claude Desktop configuration file (~/Library/Application Support/Claude/claude_desktop_config.json):

{
  "mcpServers": {
    "omnifocus": {
      "command": "/opt/homebrew/bin/node",
      "args": ["/path/to/omnifocus-mcp-server/dist/index.js"]
    }
  }
}

Use an absolute path to the node binary. A bare "node" resolves against the GUI session's PATH, which does not include Homebrew or a version manager's shims, so the server fails to start with a "server unreachable"-style error even though it runs fine from your terminal. Find yours with which node.

Other MCP Clients

The server uses stdio transport by default, so configure your client to spawn:

node /path/to/omnifocus-mcp-server/dist/index.js

Remote access (HTTP transport)

For remote clients — most importantly claude.ai custom connectors, which is how the Claude iOS app reaches MCP servers — the server can run as a Streamable HTTP endpoint:

MCP_TRANSPORT=http \
MCP_AUTH_TOKEN="$(openssl rand -hex 32)" \
node /path/to/omnifocus-mcp-server/dist/index.js

Environment variables:

Variable

Default

Purpose

MCP_TRANSPORT

stdio

Set to http to enable the HTTP transport

MCP_HTTP_PORT

3000

Port to listen on

MCP_HTTP_HOST

127.0.0.1

Bind address (keep loopback; expose via a tunnel)

MCP_AUTH_TOKEN

—

Required shared secret; the server refuses to start without it

MCP_PUBLIC_URL

—

Public HTTPS origin the server is reachable at (e.g. https://your-tunnel-host). Set this to enable the OAuth layer needed by claude.ai / Claude Desktop's "Connectors" UI — see below. Leave unset for direct/programmatic clients, which only need the static token.

OMNIFOCUS_SCRIPT_TIMEOUT_MS

60000

Kill a JXA script that hangs (applies to both transports)

The MCP endpoint is /mcp. Authentication accepts either an Authorization: Bearer <token> header, or the token as a path segment (/mcp/<token>) for clients that cannot send custom headers. GET /health is unauthenticated.

Reaching it from claude.ai / the iOS app. Custom connectors connect from Anthropic's cloud (not from your device), so the endpoint must be publicly reachable over HTTPS. A Cloudflare Tunnel or a Tailscale Funnel both work for this — either way, a local process makes an outbound connection, so no ports are opened. (Plain Tailscale, without Funnel, does not: that only reaches your own tailnet, and Anthropic's cloud isn't on it.) Optionally restrict access to Anthropic's outbound IP range (160.79.104.0/21) in a Cloudflare WAF rule.

claude.ai's and Claude Desktop's "Connectors" UI (as opposed to a raw MCP config / direct API client) always drives a full OAuth handshake before it will call a remote server — it does not accept the static token by itself, even embedded in the URL. Set MCP_PUBLIC_URL to your tunnel's public origin to turn on a self-issued OAuth layer that satisfies this while still gating access with the same static secret (see oauth.ts / CLAUDE.md for how). With that set, add the connector in Settings → Connectors using the path-token URL (https://your-tunnel-host/mcp/<token>) — the "Connect" step will complete the OAuth handshake automatically. If your tunnel also proxies / to a different local service, make sure /authorize, /token, /register, and /.well-known/* are mapped to this server too, or the OAuth requests never arrive here.

The Mac must stay awake with OmniFocus running (caffeinate -s or Amphetamine).

Session semantics — single client only. The HTTP transport serves one session at a time: a new initialize replaces the previous session. Every tool call is stateless, and a spec-compliant client re-initializes when it receives 404 for a session that no longer exists, so a single client calling sequentially works fine. That 404 also covers the case where the server holds no session at all, which is what happens to every client after the server restarts — they re-initialize instead of treating it as a dead endpoint.

Two clients at once do not. Reproduced by firing two concurrent initialize→tools/list sequences at the public endpoint: one consistently 404s. The underlying MCP SDK binds a single transport to the shared server instance, so the losing session is evicted and its in-flight request either 404s or hangs. Fixing this requires a per-session McpServer instance rather than the current registration-on-a-singleton pattern; it is not currently planned. See CLAUDE.md for the details.

Troubleshooting a "can't connect" client. Clients collapse every remote failure into one vague message, so read this server's log (StandardErrorPath of your launch agent) rather than the client's wording — the status code says which of three unrelated things went wrong:

What the log shows

Meaning

Fix

↳ path token rejected: got N chars …

The token in the client's URL is wrong or truncated

Re-copy <MCP_PUBLIC_URL>/mcp/<token> whole; never retype it

↳ authorize rejected: resource … !== …

Same cause, seen from the OAuth side. A /authorize → 302 with no /token after it always means this

As above

[…] → 404 then a fresh initialize

Normal recovery after a restart or a session takeover

Nothing; the client re-initializes itself

[initialize] → 200 — client: …

Working. The client name identifies which one

—

If nothing appears in the log at all, the request never reached this server: check the tunnel's path mappings, not this code.

Permissions

On first use, macOS will prompt you to allow automation access:

  1. Go to System Preferences → Security & Privacy → Privacy → Automation

  2. Enable permission for your terminal or Claude Desktop to control OmniFocus

Tool Reference

All 31 tools are listed below, grouped by area.

omnifocus_list_inbox

List tasks in the inbox, optionally filtered by tags.

{
  "includeCompleted": false,
  "limit": 50,
  "tags": ["Work", "Urgent"],
  "tagMatchMode": "all"
}

tagMatchMode is "all" (task has every listed tag, default), "any" (at least one), or "none" (none of them). It only applies when tags is given. The same two parameters work on omnifocus_get_due_tasks, omnifocus_get_flagged_tasks and omnifocus_get_planned_tasks.

omnifocus_list_projects

List projects with filtering.

{
  "status": "active",
  "folderName": "Work",
  "limit": 50
}

omnifocus_get_project_tasks

Get all tasks belonging to one project.

{
  "projectId": "abc123",
  "includeCompleted": false,
  "limit": 100
}

omnifocus_create_project

Create a project, optionally inside a folder.

{
  "name": "Website redesign",
  "note": "Q1 initiative",
  "folderName": "Work",
  "dueDate": "2024-03-31T17:00:00",
  "deferDate": "2024-01-15T09:00:00",
  "flagged": false,
  "sequential": false,
  "status": "active",
  "reviewIntervalDays": 7
}

status is "active" (default), "on hold", "done" or "dropped". sequential: false (default) makes a parallel project.

omnifocus_update_project

Update project properties. Identify by projectId or projectName (ID wins).

{
  "projectId": "abc123",
  "name": "Website redesign v2",
  "status": "on hold",
  "flagged": true,
  "dueDate": null,
  "sequential": true,
  "reviewIntervalDays": 14
}

Pass null for note, dueDate or deferDate to clear them. A project cannot be moved to another folder (JXA limitation).

omnifocus_delete_project

Delete a project and its tasks. Identify by projectId or projectName (ID wins).

{
  "projectId": "abc123"
}

omnifocus_list_folders

List all folders.

{
  "status": "active",
  "limit": 50
}

omnifocus_create_folder

Create a folder, top-level or nested.

{
  "name": "Clients",
  "parentFolderName": "Work"
}

omnifocus_update_folder

Rename a folder. Identify by folderId or folderName (ID wins). A folder cannot be moved into another folder (JXA limitation).

{
  "folderName": "Clients",
  "name": "Key clients"
}

omnifocus_delete_folder

Delete a folder and everything in it. Identify by folderId or folderName (ID wins).

{
  "folderId": "abc123"
}

omnifocus_list_tags

List all tags.

{
  "status": "active",
  "limit": 50
}

omnifocus_list_perspectives

List perspectives (built-in and custom).

{
  "limit": 50
}

omnifocus_get_perspective_tasks

Get tasks shown in a specific perspective.

{
  "perspectiveName": "Next",
  "limit": 50
}

omnifocus_create_task

Create a new task.

{
  "name": "Review quarterly report",
  "note": "Check all sections",
  "projectName": "Work",
  "dueDate": "2024-12-31T17:00:00",
  "deferDate": "2024-12-01T09:00:00",
  "plannedDate": "2024-12-15T09:00:00",
  "flagged": true,
  "estimatedMinutes": 60,
  "tagNames": ["Review", "Important"],
  "parentTaskId": "xyz789",
  "recurrence": {
    "frequency": "weekly",
    "interval": 1,
    "daysOfWeek": ["Monday", "Thursday"],
    "repeatFrom": "due-date"
  }
}

Planned Date vs Due Date:

  • dueDate: When the task must be completed (deadline)

  • plannedDate: When you intend to work on the task (planning)

  • This distinction is crucial for separating deadlines from scheduled work time

Recurrence: frequency is "daily", "weekly", "monthly" or "yearly". Use daysOfWeek for weekly, dayOfMonth (1-31) for monthly, monthOfYear (1-12) for yearly. repeatFrom is "due-date" (default) or "completion-date".

Subtasks: pass parentTaskId to create the task as a child of an existing task.

omnifocus_update_task

Update an existing task. Identify by taskId or taskName (ID wins).

{
  "taskId": "abc123",
  "name": "Review quarterly report (final)",
  "note": null,
  "dueDate": "2024-12-20T17:00:00",
  "flagged": true,
  "estimatedMinutes": 45,
  "projectName": "Work"
}
  • Pass null for note, dueDate, deferDate or plannedDate to clear them; estimatedMinutes: 0 clears the estimate.

  • projectId / projectName moves the task to that project (subtasks come along). The move is verified afterwards, so a failure is reported as an error instead of a false success.

  • recurrence takes the same object as create_task; recurrence: null or clearRecurrence: true turns repetition off.

omnifocus_delete_task

Delete a task. Identify by taskId or taskName (ID wins).

{
  "taskId": "abc123"
}

omnifocus_update_task_note

Replace, clear, or append to a task's note. Identify by taskId or taskName (ID wins).

{
  "taskId": "abc123",
  "note": "Added after the call.",
  "append": true
}

An empty note string clears the note.

omnifocus_complete_task

Mark a task as complete or dropped. You can identify the task by either ID or name.

{
  "taskId": "abc123",
  "action": "complete"
}

Or using task name:

{
  "taskName": "Write documentation",
  "action": "complete"
}

Action can be "complete" (default) or "drop". If both taskId and taskName are provided, taskId takes priority.

Dropping a repeating task clears its repetition rule first, so the series really stops instead of rolling forward to the next occurrence.

omnifocus_batch_complete_task

Complete or drop up to 100 tasks by ID in one call.

{
  "taskIds": ["id1", "id2", "id3"],
  "action": "complete"
}

omnifocus_add_tag_to_task

Add a tag to a task. You can identify the task by either ID or name.

{
  "taskId": "abc123",
  "tagName": "Urgent"
}

Or using task name:

{
  "taskName": "Write report",
  "tagName": "Urgent"
}

If both taskId and taskName are provided, taskId takes priority.

omnifocus_remove_tag_from_task

Remove a tag from a task. You can identify the task by either ID or name.

{
  "taskId": "abc123",
  "tagName": "Urgent"
}

Or using task name:

{
  "taskName": "Old task",
  "tagName": "Done"
}

If both taskId and taskName are provided, taskId takes priority.

omnifocus_batch_add_tag

Add one existing tag to up to 100 tasks by ID.

{
  "taskIds": ["id1", "id2", "id3"],
  "tagName": "Urgent"
}

omnifocus_batch_remove_tag

Remove one tag from up to 100 tasks by ID.

{
  "taskIds": ["id1", "id2", "id3"],
  "tagName": "Urgent"
}

omnifocus_update_project_note

Replace, clear, or append to a project's note. Identify by projectId or projectName (ID wins).

{
  "projectName": "Website redesign",
  "note": "Kickoff moved to March.",
  "append": false
}

Search across OmniFocus.

{
  "query": "report",
  "searchType": "all",
  "limit": 20
}

omnifocus_get_due_tasks

Get tasks due within a timeframe.

{
  "daysAhead": 7,
  "includeOverdue": true,
  "limit": 50
}

omnifocus_get_flagged_tasks

Get flagged tasks.

{
  "includeCompleted": false,
  "limit": 50
}

omnifocus_get_planned_tasks

Get tasks planned within a timeframe.

{
  "daysAhead": 7,
  "includeOverdue": true,
  "limit": 50
}

omnifocus_get_projects_for_review

Get projects that need review based on their next review date. Perfect for GTD practitioners following the review workflow.

{
  "daysAhead": 0,
  "status": "active",
  "limit": 50,
  "includeTasks": true,
  "taskLimit": 50
}

Parameters:

  • daysAhead: How many days ahead to look (0 = overdue reviews only)

  • status: Filter by project status ("active", "done", "dropped", "onHold", "all")

  • limit: Maximum number of projects to return (1-500)

  • includeTasks: Include each project's incomplete tasks in the result (default false) — this turns one review pass into a single call instead of one follow-up call per project

  • taskLimit: Maximum tasks per project when includeTasks is true (1-200, default 50)

Each project also returns reviewInterval and lastReviewDate.

omnifocus_mark_project_reviewed

Mark a project as reviewed and update its next review date. You can identify the project by either ID or name.

{
  "projectId": "abc123"
}

Or using project name:

{
  "projectName": "Weekly Review"
}

With custom review interval:

{
  "projectName": "Work Project",
  "reviewIntervalDays": 14
}

Parameters:

  • projectId or projectName: Identifies the project (ID takes priority)

  • reviewIntervalDays (optional): Custom review interval in days. If not provided, uses the project's existing review interval.

omnifocus_batch_mark_reviewed

Mark multiple projects as reviewed in one efficient operation.

{
  "projectIds": ["id1", "id2", "id3"]
}

With custom review interval for all:

{
  "projectIds": ["id1", "id2", "id3"],
  "reviewIntervalDays": 7
}

Parameters:

  • projectIds: Array of project IDs to mark as reviewed (1-100 projects)

  • reviewIntervalDays (optional): Custom review interval to apply to all projects

Returns a summary with:

  • Count of successful reviews

  • Count of failures

  • Full project data for successful reviews

  • Error details for any failures

Date Formats

All dates use ISO 8601 format: YYYY-MM-DDTHH:mm:ss

Examples:

  • 2024-12-31T17:00:00 - December 31, 2024 at 5:00 PM

  • 2024-06-15T09:00:00 - June 15, 2024 at 9:00 AM

Error Handling

The server provides clear error messages for common issues:

  • OmniFocus not running: Launch OmniFocus first

  • Permission denied: Enable automation permissions in System Preferences

  • Item not found: The specified ID doesn't exist

  • Invalid parameters: Check parameter format and values

Development

Build

npm run build

Watch mode

npm run dev

Tests

npm test              # All unit tests
npm run test:watch    # Watch mode
npm run test:coverage # Coverage report (thresholds enforced: 80% lines, 75% branches)

The integration tests in src/__tests__/integration.test.ts are skipped by default: they require a running OmniFocus and they modify your real database.

Test manually

After building, you can test with:

echo '{"jsonrpc":"2.0","method":"tools/list","id":1}' | node dist/index.js

Applying changes to a running server (important)

MCP clients fetch the tool list once when they connect and cache it for the session. npm run build alone does not update a client that is already connected — Node does not hot-reload, and the client will not re-fetch the schema. After changing tools/schemas you must both restart the server and make each client reconnect:

  1. Rebuild: npm run build

  2. Restart the server process so it loads the new dist/:

    • LaunchAgent (HTTP transport): launchctl kickstart -k gui/$(id -u)/com.sanderrobijns.omnifocus-mcp

    • Verify it serves the new schema: lsof -nP -iTCP:3000 -sTCP:LISTEN should show a freshly started PID.

  3. Reconnect each client so it re-fetches tools/list:

    • Claude Code / Cowork: start a new session (a running session keeps its cached schema for its whole lifetime).

    • Claude Desktop: quit and reopen the app (or toggle the server off/on).

    • claude.ai / Claude iOS custom connector: re-sync the connector in Settings → Connectors (it caches the tool list at the connector level).

Until the client reconnects it keeps showing the old schema, even though the server already serves the new one.

License

MIT

Credits

Built using:

Available Tools

31 tools
omnifocus_add_tag_to_taskAdd Tag to TaskA
Idempotent

Add a tag to a task in OmniFocus.

Use either the task ID or task name to identify the task.

Args:

  • taskId (string, optional): The task's ID. Takes priority if both taskId and taskName provided.

  • taskName (string, optional): The task's name to search for. At least one of taskId or taskName is required.

  • tagName (string): Name of the tag to add

Returns: The updated task object

Examples:

  • By ID: { taskId: "abc123", tagName: "Urgent" }

  • By name: { taskName: "Write report", tagName: "Urgent" }

Inputs: tagName (required), taskId, taskName.

omnifocus_batch_add_tagBatch Add Tag to TasksA
Idempotent

Add the same tag to multiple tasks in one operation.

The tag must already exist. Tasks that already have the tag are left unchanged.

Args:

  • taskIds (array): Array of task IDs to tag (1-100)

  • tagName (string): Name of the tag to add

Returns: Summary with counts and the updated tasks, plus any per-task failures

Examples:

  • Tag several tasks: { taskIds: ["id1", "id2"], tagName: "Urgent" }

Inputs: tagName (required), taskIds (required).

omnifocus_batch_complete_taskBatch Complete/Drop TasksA
Idempotent

Complete or drop multiple tasks in one operation.

Args:

  • taskIds (array): Array of task IDs to complete or drop (1-100)

  • action (string): 'complete' (default) or 'drop'

Returns: Summary with counts and the updated tasks, plus any per-task failures

Examples:

  • Complete several: { taskIds: ["id1", "id2", "id3"] }

  • Drop several: { taskIds: ["id1", "id2"], action: "drop" }

Inputs: taskIds (required), action.

omnifocus_batch_mark_reviewedBatch Mark Projects ReviewedA

Mark multiple projects as reviewed in one operation.

Efficiently updates the review status for multiple projects at once.

Args:

  • projectIds (array): Array of project IDs to mark as reviewed (1-100 projects)

  • reviewIntervalDays (number, optional): Custom review interval in days (1-3650) to apply to all projects

Returns: Summary with count of successfully reviewed projects and any errors

Examples:

  • Review multiple projects: { projectIds: ["id1", "id2", "id3"] }

  • With custom interval: { projectIds: ["id1", "id2"], reviewIntervalDays: 14 }

Inputs: projectIds (required), reviewIntervalDays.

omnifocus_batch_remove_tagBatch Remove Tag from TasksA
Idempotent

Remove the same tag from multiple tasks in one operation.

Tasks that do not have the tag are left unchanged.

Args:

  • taskIds (array): Array of task IDs to untag (1-100)

  • tagName (string): Name of the tag to remove

Returns: Summary with counts and the updated tasks, plus any per-task failures

Examples:

  • Untag several tasks: { taskIds: ["id1", "id2"], tagName: "Waiting" }

Inputs: tagName (required), taskIds (required).

omnifocus_complete_taskComplete or Drop TaskA
Idempotent

Mark a task as complete or dropped in OmniFocus.

Use either the task ID from list/search results, or the task name for natural language interactions.

Args:

  • taskId (string, optional): The task's ID (primaryKey). Takes priority if both taskId and taskName provided.

  • taskName (string, optional): The task's name to search for. At least one of taskId or taskName is required.

  • action (string): 'complete' (default) or 'drop'

Note: Completing a recurring task advances it to the next occurrence (normal repeat behavior). Dropping a recurring task cancels the whole series - its repetition rule is removed first so it does not roll forward to a new active instance.

Returns: The updated task object

Examples:

  • Complete by ID: { taskId: "abc123" }

  • Complete by name: { taskName: "Write documentation" }

  • Drop by name: { taskName: "Old task", action: "drop" }

Inputs: action, taskId, taskName.

omnifocus_create_folderCreate FolderA

Create a new folder in OmniFocus.

Creates a folder at the top level or nested inside an existing folder.

Args:

  • name (string): Folder name (required)

  • parentFolderName (string, optional): Parent folder to nest inside (top level if omitted)

Returns: The created folder object with id, name, and other properties

Examples:

  • Top-level folder: { name: "Work" }

  • Nested folder: { name: "Q1", parentFolderName: "Work" }

Inputs: name (required), parentFolderName.

omnifocus_create_projectCreate ProjectA

Create a new project in OmniFocus.

Creates a project at the top level or inside a specific folder. Optional properties like due date, defer date, flags, and sequential ordering can be set.

Args:

  • name (string): Project name (required)

  • note (string): Optional note/description

  • folderName (string): Folder to place the project in (top level if omitted)

  • dueDate (string): Due date in ISO 8601 format

  • deferDate (string): Defer/start date in ISO 8601 format

  • flagged (boolean): Flag the project (default: false)

  • sequential (boolean): Tasks must be done in order (default: false = parallel)

  • status (string): "active", "on hold", "done", or "dropped" (default: "active")

Returns: The created project object with id, name, and other properties

Examples:

  • Simple project: { name: "Launch website" }

  • In a folder: { name: "Q1 Planning", folderName: "Work" }

  • With details: { name: "Write book", dueDate: "2024-12-31T17:00:00", sequential: true, flagged: true }

Inputs: name (required), note, status, dueDate, +4 more.

omnifocus_create_taskCreate TaskA

Create a new task in OmniFocus.

Creates a task in the inbox or a specific project. Tags, due dates, planned dates and other properties can be set. Supports repeating/recurring tasks.

Args:

  • name (string): Task name/title (required)

  • note (string): Optional note/description

  • projectName (string): Project to add to (inbox if not specified)

  • parentTaskId (string): ID of a parent task to create this as a subtask of (takes priority over projectName)

  • dueDate (string): Due date in ISO 8601 format - when the task must be completed

  • deferDate (string): Defer/start date in ISO 8601 format

  • plannedDate (string): Planned date in ISO 8601 format - when you intend to work on the task

  • flagged (boolean): Flag the task (default: false)

  • estimatedMinutes (number): Time estimate in minutes

  • tagNames (array): Tag names to apply

  • recurrence (object): Optional recurrence pattern with:

    • frequency: "daily", "weekly", "monthly", or "yearly"

    • interval: Number of periods between repetitions (default: 1)

    • daysOfWeek: Array of days for weekly recurrence (e.g., ["Monday", "Friday"])

    • dayOfMonth: Day number for monthly recurrence (1-31)

    • monthOfYear: Month number for yearly recurrence (1-12)

    • repeatFrom: "due-date" or "completion-date" (default: "due-date")

Returns: The created task object with id, name, and other properties

Examples:

  • Simple task: { name: "Buy groceries" }

  • Task with details: { name: "Review report", projectName: "Work", dueDate: "2024-12-31T17:00:00", flagged: true }

  • Daily recurring: { name: "Daily standup", dueDate: "2024-01-01T09:00:00", recurrence: { frequency: "daily", interval: 1 } }

  • Weekly on Mon/Wed/Fri: { name: "Workout", dueDate: "2024-01-01T07:00:00", recurrence: { frequency: "weekly", daysOfWeek: ["Monday", "Wednesday", "Friday"] } }

  • Monthly on 1st and 15th: { name: "Pay bills", dueDate: "2024-01-01T12:00:00", recurrence: { frequency: "monthly", interval: 1, dayOfMonth: 1 } }

  • Repeat from completion: { name: "Review quarterly", recurrence: { frequency: "monthly", interval: 3, repeatFrom: "completion-date" } }

  • Task with planning: { name: "Write article", plannedDate: "2024-12-15T09:00:00", dueDate: "2024-12-31T17:00:00" }

  • Task with tags: { name: "Call John", tagNames: ["Calls", "Urgent"] }

Inputs: name (required), note, dueDate, flagged, +7 more.

omnifocus_delete_folderDelete FolderA
Destructive

Permanently delete a folder from OmniFocus, including any projects and folders it contains. This cannot be undone via MCP.

Use either the folder ID from list/search results, or the folder name.

Args:

  • folderId (string, optional): The folder's ID. Takes priority if both folderId and folderName provided.

  • folderName (string, optional): The folder's name to search for. At least one of folderId or folderName is required.

Returns: Confirmation message with the deleted folder's name

Examples:

  • Delete by ID: { folderId: "abc123" }

  • Delete by name: { folderName: "Old folder" }

Inputs: folderId, folderName.

omnifocus_delete_projectDelete ProjectA
Destructive

Permanently delete a project from OmniFocus, including its tasks. This cannot be undone via MCP.

Use either the project ID from list/search results, or the project name.

Args:

  • projectId (string, optional): The project's ID. Takes priority if both projectId and projectName provided.

  • projectName (string, optional): The project's name to search for. At least one of projectId or projectName is required.

Returns: Confirmation message with the deleted project's name

Examples:

  • Delete by ID: { projectId: "abc123" }

  • Delete by name: { projectName: "Old project" }

Inputs: projectId, projectName.

omnifocus_delete_taskDelete TaskA
Destructive

Permanently delete a task from OmniFocus. This cannot be undone via MCP.

Use either the task ID from list/search results, or the task name.

Args:

  • taskId (string, optional): The task's ID. Takes priority if both taskId and taskName provided.

  • taskName (string, optional): The task's name to search for. At least one of taskId or taskName is required.

Returns: Confirmation message with the deleted task's name

Examples:

  • Delete by ID: { taskId: "abc123" }

  • Delete by name: { taskName: "Old draft" }

Inputs: taskId, taskName.

omnifocus_get_due_tasksGet Due TasksA
Read-onlyIdempotent

Get tasks that are due within a specified timeframe.

Args:

  • daysAhead (number): Days to look ahead, 0-365 (default: 7)

  • includeOverdue (boolean): Include overdue tasks (default: true)

  • limit (number): Max tasks, 1-500 (default: 50)

  • tags (array, optional): Filter to tasks matching these tag names (max 20)

  • tagMatchMode (string): How to match tags - 'all', 'any', or 'none' (default 'all'). Only applied when tags is provided.

Returns: Array of due tasks sorted by due date

Examples:

  • Due this week: {}

  • Due today: { daysAhead: 0 }

  • Due in 30 days: { daysAhead: 30 }

Inputs: tags, limit, daysAhead, tagMatchMode, +1 more.

omnifocus_get_flagged_tasksGet Flagged TasksA
Read-onlyIdempotent

Get all flagged tasks in OmniFocus.

Args:

  • includeCompleted (boolean): Include completed tasks (default: false)

  • limit (number): Max tasks, 1-500 (default: 50)

  • tags (array, optional): Filter to tasks matching these tag names (max 20)

  • tagMatchMode (string): How to match tags - 'all', 'any', or 'none' (default 'all'). Only applied when tags is provided.

Returns: Array of flagged tasks

Examples:

  • Active flagged: {}

  • All flagged: { includeCompleted: true }

Inputs: tags, limit, tagMatchMode, includeCompleted.

omnifocus_get_perspective_tasksGet Perspective TasksA
Read-onlyIdempotent

Get tasks shown in a specific OmniFocus perspective.

Switches the front OmniFocus window to the named perspective, reads the tasks it displays, then restores the original perspective.

Args:

  • perspectiveName (string): Name of the perspective (use omnifocus_list_perspectives to find names)

  • limit (number): Maximum tasks to return, 1-500 (default: 50)

Returns: Array of task objects with: id, name, note, completed, flagged, dueDate, deferDate, projectName, tags, estimatedMinutes

Examples:

  • Get tasks from a perspective: { perspectiveName: "Next" }

  • With limit: { perspectiveName: "Forecast", limit: 10 }

Inputs: perspectiveName (required), limit.

omnifocus_get_planned_tasksGet Planned TasksA
Read-onlyIdempotent

Get tasks that are planned within a specified timeframe.

Planned dates represent when you intend to work on a task, separate from the due date.

Args:

  • daysAhead (number): Days to look ahead, 0-365 (default: 7)

  • includeOverdue (boolean): Include overdue planned tasks (default: true)

  • limit (number): Max tasks, 1-500 (default: 50)

  • tags (array, optional): Filter to tasks matching these tag names (max 20)

  • tagMatchMode (string): How to match tags - 'all', 'any', or 'none' (default 'all'). Only applied when tags is provided.

Returns: Array of planned tasks sorted by planned date

Examples:

  • Planned this week: {}

  • Planned today: { daysAhead: 0 }

  • Planned in 30 days: { daysAhead: 30 }

Inputs: tags, limit, daysAhead, tagMatchMode, +1 more.

omnifocus_get_projects_for_reviewGet Projects for ReviewA
Read-onlyIdempotent

Get projects that need review based on their next review date.

Returns projects whose next review date is on or before today (or within specified days ahead).

Args:

  • daysAhead (number): Days to look ahead, 0-365 (default: 0 = overdue only)

  • status (string): Filter by status - 'all', 'active', 'done', 'dropped', 'onHold' (default: 'active')

  • limit (number): Maximum projects to return, 1-500 (default: 50)

Returns: Array of projects needing review sorted by next review date

Examples:

  • Overdue reviews: {}

  • Reviews due within 7 days: { daysAhead: 7 }

  • All active projects due for review: { status: "active", daysAhead: 30 }

Inputs: limit, status, daysAhead.

omnifocus_get_project_tasksGet Project TasksA
Read-onlyIdempotent

Get all tasks belonging to a specific project in OmniFocus.

Returns the tasks within a project, including subtasks. Use omnifocus_list_projects to find project IDs first.

Args:

  • projectId (string): The ID of the project

  • includeCompleted (boolean): Include completed tasks (default: false)

  • limit (number): Maximum tasks to return, 1-500 (default: 100)

Returns: Array of task objects with: id, name, note, completed, flagged, dueDate, deferDate, estimatedMinutes, tags, parentTaskId, parentTaskName, hasChildren, childTaskCount

Examples:

  • Get tasks for a project: { projectId: "abc123" }

  • Include completed: { projectId: "abc123", includeCompleted: true }

Inputs: projectId (required), limit, includeCompleted.

omnifocus_list_foldersList FoldersA
Read-onlyIdempotent

List folders in OmniFocus.

Folders are used to organize projects hierarchically.

Args:

  • status (string): Filter by status - 'all', 'active', 'dropped' (default: 'active')

  • limit (number): Maximum folders to return, 1-200 (default: 50)

Returns: Array of folder objects with: id, name, status, projectCount, folderCount, parentName

Examples:

  • List active folders: {}

  • List all folders: { status: "all" }

Inputs: limit, status.

omnifocus_list_inboxList Inbox TasksA
Read-onlyIdempotent

List tasks in the OmniFocus inbox.

Returns tasks that haven't been assigned to a project yet. These are typically newly captured items awaiting processing.

Args:

  • includeCompleted (boolean): Include completed tasks (default: false)

  • limit (number): Maximum tasks to return, 1-500 (default: 50)

  • tags (array, optional): Filter to tasks matching these tag names (max 20)

  • tagMatchMode (string): How to match tags - 'all' (every tag), 'any' (at least one), 'none' (none of them). Default 'all'. Only applied when tags is provided.

Returns: Array of task objects with: id, name, note, completed, flagged, dueDate, deferDate, estimatedMinutes, tags

Examples:

  • List all inbox items: {}

  • Include completed: { includeCompleted: true }

  • Only untagged-by-Work items: { tags: ["Work"], tagMatchMode: "none" }

Inputs: tags, limit, tagMatchMode, includeCompleted.

omnifocus_list_perspectivesList PerspectivesA
Read-onlyIdempotent

List perspectives in OmniFocus.

Perspectives are saved views/filters that show specific subsets of tasks. Includes both built-in and custom perspectives.

Args:

  • limit (number): Maximum perspectives to return, 1-200 (default: 50)

Returns: Array of perspective objects with: id, name

Examples:

  • List perspectives: {}

  • Limit results: { limit: 10 }

Inputs: limit.

omnifocus_list_projectsList ProjectsA
Read-onlyIdempotent

List projects in OmniFocus.

Returns projects with their status, dates, and folder information.

Args:

  • status (string): Filter by status - 'all', 'active', 'done', 'dropped', 'onHold' (default: 'active')

  • folderName (string): Optional folder name filter (partial match)

  • limit (number): Maximum projects to return, 1-500 (default: 50)

Returns: Array of project objects with: id, name, note, status, completed, flagged, dueDate, deferDate, folderName, taskCount, sequential

Examples:

  • List active projects: {}

  • List all projects: { status: "all" }

  • Projects in a folder: { folderName: "Work" }

Inputs: limit, status, folderName.

omnifocus_list_tagsList TagsA
Read-onlyIdempotent

List tags in OmniFocus.

Tags (formerly contexts) are used to categorize tasks by context, person, tool, etc.

Args:

  • status (string): Filter by status - 'all', 'active', 'onHold', 'dropped' (default: 'active')

  • limit (number): Maximum tags to return, 1-200 (default: 50)

Returns: Array of tag objects with: id, name, status, taskCount, allowsNextAction, parentName

Examples:

  • List active tags: {}

  • List all tags: { status: "all" }

Inputs: limit, status.

omnifocus_mark_project_reviewedMark Project ReviewedA

Mark a project as reviewed and update its next review date.

Use either the project ID or project name to identify the project. The next review date will be set based on the project's review interval or a custom interval if provided.

Args:

  • projectId (string, optional): The project's ID. Takes priority if both projectId and projectName provided.

  • projectName (string, optional): The project's name to search for. At least one of projectId or projectName is required.

  • reviewIntervalDays (number, optional): Custom review interval in days (1-3650). If not provided, uses the project's current review interval.

Returns: The updated project object

Examples:

  • By ID: { projectId: "abc123" }

  • By name: { projectName: "Work Project" }

  • With custom interval: { projectName: "Weekly Project", reviewIntervalDays: 7 }

Inputs: projectId, projectName, reviewIntervalDays.

omnifocus_remove_tag_from_taskRemove Tag from TaskA
Idempotent

Remove a tag from a task in OmniFocus.

Use either the task ID or task name to identify the task.

Args:

  • taskId (string, optional): The task's ID. Takes priority if both taskId and taskName provided.

  • taskName (string, optional): The task's name to search for. At least one of taskId or taskName is required.

  • tagName (string): Name of the tag to remove

Returns: The updated task object

Examples:

  • By ID: { taskId: "abc123", tagName: "Urgent" }

  • By name: { taskName: "Old task", tagName: "Done" }

Inputs: tagName (required), taskId, taskName.

omnifocus_update_folderUpdate FolderA

Rename an existing folder in OmniFocus.

Args:

  • folderId (string, optional): The folder's ID. Takes priority if both folderId and folderName provided.

  • folderName (string, optional): The folder's name to search for. At least one of folderId or folderName is required.

  • name (string): New folder name

Note: moving a folder into another folder is not supported by OmniFocus's JXA layer (the move operation is rejected). Recreate the folder in the target location if you need to move it.

Returns: The updated folder object

Examples:

  • Rename by ID: { folderId: "abc123", name: "Archive" }

  • Rename by name: { folderName: "Q1", name: "Q1 2027" }

Inputs: name (required), folderId, folderName.

omnifocus_update_projectUpdate ProjectA

Update properties of an existing project in OmniFocus.

Only the fields you provide are changed. Use null to clear a date or note field.

Args:

  • projectId (string, optional): The project's ID. Takes priority if both projectId and projectName provided.

  • projectName (string, optional): The project's name to search for. At least one of projectId or projectName is required.

  • name (string, optional): New project name

  • note (string | null, optional): New note text. Pass null to clear.

  • status (string, optional): "active", "on hold", "done", or "dropped". Setting "done" completes the project and "dropped" drops it (setting "active" reactivates a completed/dropped project).

  • flagged (boolean, optional): Set flagged state

  • dueDate (string | null, optional): New due date in ISO 8601 format. Pass null to clear.

  • deferDate (string | null, optional): New defer date in ISO 8601 format. Pass null to clear.

  • sequential (boolean, optional): Tasks must be done in order (true) or parallel (false)

  • reviewIntervalDays (number, optional): Review interval in days (1-3650)

Note: moving a project between folders is not supported by OmniFocus's JXA layer (the move operation returns "Replacement not supported"). Recreate the project in the target folder if you need to move it.

Returns: The updated project object

Examples:

  • Rename: { projectId: "abc123", name: "New name" }

  • Put on hold: { projectId: "abc123", status: "on hold" }

  • Complete the project: { projectId: "abc123", status: "done" }

  • Drop the project: { projectId: "abc123", status: "dropped" }

  • Set review interval: { projectId: "abc123", reviewIntervalDays: 14 }

  • Clear due date: { projectId: "abc123", dueDate: null }

Inputs: name, note, status, dueDate, +6 more.

omnifocus_update_project_noteUpdate Project NoteA
Idempotent

Update the note/description on an existing project in OmniFocus.

Use either the project ID or project name to identify the project.

Args:

  • projectId (string, optional): The project's ID. Takes priority if both projectId and projectName provided.

  • projectName (string, optional): The project's name to search for. At least one of projectId or projectName is required.

  • note (string): The new note content. Use empty string to clear the note.

  • append (boolean): If true, append to existing note instead of replacing (default: false)

Returns: The updated project object

Examples:

  • Set note by ID: { projectId: "abc123", note: "Q1 deliverables" }

  • Set note by name: { projectName: "Work Project", note: "Started Jan 2024" }

  • Clear note: { projectId: "abc123", note: "" }

  • Append to note: { projectId: "abc123", note: "\nNew update", append: true }

Inputs: note (required), append, projectId, projectName.

omnifocus_update_taskUpdate TaskA

Update properties of an existing task in OmniFocus.

Only the fields you provide are changed. Use null to clear a date or note field.

Args:

  • taskId (string, optional): The task's ID. Takes priority if both taskId and taskName provided.

  • taskName (string, optional): The task's name to search for. At least one of taskId or taskName is required.

  • name (string, optional): New task name

  • note (string | null, optional): New note text. Pass null to clear.

  • dueDate (string | null, optional): New due date in ISO 8601 format. Pass null to clear.

  • deferDate (string | null, optional): New defer date in ISO 8601 format. Pass null to clear.

  • plannedDate (string | null, optional): New planned date in ISO 8601 format. Pass null to clear.

  • flagged (boolean, optional): Set flagged state

  • estimatedMinutes (number, optional): Time estimate in minutes. Pass 0 to clear.

  • projectId (string, optional): ID of the project to move the task to.

  • projectName (string, optional): Name of the project to move the task to. Ignored if projectId is provided.

  • recurrence (object | null, optional): Repetition pattern to make the task recurring (same shape as create_task: frequency, interval, daysOfWeek, dayOfMonth, monthOfYear, repeatFrom). Pass null to remove recurrence.

  • clearRecurrence (boolean, optional): Set true to remove the task's repetition rule (turn off recurring). Equivalent to recurrence: null.

Returns: The updated task object

Examples:

  • Rename: { taskId: "abc123", name: "New name" }

  • Set due date: { taskId: "abc123", dueDate: "2024-12-31T17:00:00" }

  • Clear due date: { taskId: "abc123", dueDate: null }

  • Make it recurring weekly: { taskId: "abc123", recurrence: { frequency: "weekly", daysOfWeek: ["Monday"] } }

  • Turn off recurring: { taskId: "abc123", clearRecurrence: true }

  • Flag and estimate: { taskId: "abc123", flagged: true, estimatedMinutes: 30 }

Inputs: name, note, taskId, dueDate, +9 more.

omnifocus_update_task_noteUpdate Task NoteA
Idempotent

Update the note/description on an existing task in OmniFocus.

Use either the task ID or task name to identify the task.

Args:

  • taskId (string, optional): The task's ID. Takes priority if both taskId and taskName provided.

  • taskName (string, optional): The task's name to search for. At least one of taskId or taskName is required.

  • note (string): The new note content. Use empty string to clear the note.

  • append (boolean): If true, append to existing note instead of replacing (default: false)

Returns: The updated task object

Examples:

  • Set note by ID: { taskId: "abc123", note: "Remember to include charts" }

  • Set note by name: { taskName: "Write report", note: "Draft due Friday" }

  • Clear note: { taskId: "abc123", note: "" }

  • Append to note: { taskId: "abc123", note: "\nAdditional info here", append: true }

Inputs: note (required), append, taskId, taskName.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    A4/5.0

    Scored across 31 tools

    Disambiguation4/5

    Most tools target distinct resource-action pairs, but there is overlap between general update tools and dedicated note update tools (e.g., omnifocus_update_task vs omnifocus_update_task_note), which could confuse an agent deciding which to use.

    Naming Consistency5/5

    All tools follow a consistent omnifocus_verb_noun pattern with clear verbs (list_, get_, create_, update_, delete_, etc.) and consistent prepositions (add_tag_to_task, remove_tag_from_task), making the naming predictable and easy to navigate.

    Tool Count4/5

    31 tools is on the higher side, but the number is justified by the comprehensive feature set of OmniFocus (tasks, projects, folders, tags, perspectives, reviews, batch operations). A small reduction by merging note update tools would improve conciseness.

    Completeness3/5

    The surface covers most CRUD operations for tasks, projects, and folders, including tagging and review. However, missing tag creation and a general 'list all tasks' tool are notable gaps that may cause agent failures.

    Maintenance

    ActivityNo data
    ResponsivenessSyncing

    Related MCP Connectors

    Related MCP Servers