PluginWorld
Su

superproductivity-mcp

MCP

Enables AI assistants to read and write Super Productivity tasks through the Nextcloud sync file, preserving local-first data and safely handling concurrent edits from other devices.

@daxrpm · v1.0.0 · MIT · updated today

SECURITY

B

SCORE

68

STARS

0

PLUG IN

git clone https://github.com/daxrpm/superproductivity-mcp-offline.git

See the README to configure this MCP server

README

Super Productivity MCP

Give your AI assistant real access to your tasks — without giving up local-first.

An MCP server that reads and writes your Super Productivity tasks through the sync file already sitting in your Nextcloud. No plugin, no fork, no second app to keep in step.

┌─────────────────┐         ┌──────────────────┐         ┌─────────────────┐
│  Super          │  sync   │    Nextcloud     │  sync   │  This MCP       │
│  Productivity   │ ──────► │  sync-data.json  │ ◄────── │  server         │
│  desktop/mobile │ ◄────── │                  │ ──────► │                 │
└─────────────────┘         └──────────────────┘         └────────┬────────┘
                                                                  │ MCP
                                                         ┌────────▼────────┐
                                                         │  OpenClaw,      │
                                                         │  Claude, …      │
                                                         └─────────────────┘

Why this exists

Super Productivity is deliberately local-first. There is no remote API, no outgoing webhook, and the desktop app's local REST API binds to 127.0.0.1 — unreachable from anywhere else by design. The CalDAV plugin exports only tasks that already have a date, and exports them as calendar events, not tasks.

The sync file is different. It holds everything: every project, every tag, the whole undated backlog, subtasks, estimates, time tracking. It is already on your server. It is the complete picture, and nothing else is.

So this server talks to that.

What makes it safe

The naive version of this idea — download the JSON, edit it, upload it — will eventually destroy your task history. Super Productivity is not a file with tasks in it; it is an operation log with vector clocks, and your devices merge changes by replaying operations, not by comparing files.

This server participates in that protocol properly. It behaves as one more device on your account:

Naive file editor This server
Concurrent edit on your phone Silently overwritten Detected, re-applied on top
Other devices see the change As a mysterious whole-file replacement As a normal operation, like any device
Identity in the sync protocol None — masquerades as your desktop Its own client id, its own vector clock entry
A field it doesn't understand Dropped Preserved byte-for-byte
Interrupted write Corrupt file Previous version still in .bak
Sync file format changed Breaks Detected and handled

Concretely, every write:

  1. Reads with a strong ETag. Nextcloud's OC-ETag, which survives reverse proxies rewriting the plain ETag.
  2. Applies the change through a faithful port of Super Productivity's own reducers — so TODAY stays a virtual tag, dueDay and dueWithTime stay mutually exclusive, and completing a task never invents a due date.
  3. Emits the matching operation with this server's client id and an incremented vector clock, so your other devices accept it as causally newer rather than flagging it as a conflict.
  4. Refreshes the .bak before touching the primary file.
  5. Writes conditionally, on the revision it read. Never unconditionally. If another device wrote first, the whole mutation re-runs against the fresh state — their change survives, yours applies on top.

Verified against a live Nextcloud: If-Match is genuinely enforced, and after a full create/schedule/complete/delete cycle the remote is still a valid schema-4 envelope with archives and untouched state byte-identical.


Quick start

Requirements: Node 20.11+, a Nextcloud with Super Productivity already syncing to it.

git clone <this-repo> superproductivity-mcp
cd superproductivity-mcp
npm install
cp .env.example .env

Fill in .env:

SP_NEXTCLOUD_URL=https://cloud.example.com
SP_NEXTCLOUD_USER=yourname
SP_NEXTCLOUD_PASSWORD=xxxxx-xxxxx-xxxxx-xxxxx-xxxxx
SP_SYNC_FOLDER=super-productivity

Use an app password, not your login password: Settings → Security → Devices & sessions → Create new app password. It is scoped, revocable, and it is the only thing that works when two-factor authentication is on.

Check everything before wiring it up to anything:

npm run doctor
Super Productivity MCP — doctor (v1.0.0)

  ok   configuration                https://cloud.example.com as yourname
  ok   sync folder                  super-productivity
  ok   mode                         read-write
  ok   client id                    M_TSrPmbHAoq
  ok   encryption                   not configured (the sync file must be plaintext)
  ok   reachable                    Nextcloud answered and the credentials were accepted
  ok   sync file                    SINGLE_FILE (sync-data.json)
  ok   decoded                      syncVersion 114, schema 4
  ok   conditional writes           the server returns a strong ETag, so concurrent writes are safe
  ok   devices                      B_UjDTUW, A_rmkezu
  ok   operation log                354 of 2000 retained
  ok   contents                     12 open tasks, 3 projects, 4 tags

All checks passed. The MCP server should work.

The doctor never writes. If a step fails, it says which one and why — which is the whole point of having it, because every failure mode here otherwise shows up inside your AI client as an unhelpful "the tool didn't work".

Then build:

npm run build

Connecting it

OpenClaw

Add to your MCP server configuration:

{
  "mcpServers": {
    "superproductivity": {
      "command": "node",
      "args": ["/absolute/path/to/superproductivity-mcp/dist/main.js"],
      "env": {
        "SP_NEXTCLOUD_URL": "https://cloud.example.com",
        "SP_NEXTCLOUD_USER": "yourname",
        "SP_NEXTCLOUD_PASSWORD": "xxxxx-xxxxx-xxxxx-xxxxx-xxxxx",
        "SP_SYNC_FOLDER": "super-productivity"
      }
    }
  }
}

You can leave env out entirely and let it read the .env file instead — set SP_ENV_FILE to an absolute path if the working directory won't be the project root.

Claude Desktop

Same shape, in claude_desktop_config.json (~/Library/Application Support/Claude/ on macOS, %APPDATA%\Claude\ on Windows).

Claude Code
claude mcp add superproductivity -- node /absolute/path/to/dist/main.js
Any other MCP host

It is a standard stdio MCP server. Run node dist/main.js; it speaks JSON-RPC on stdout and logs to stderr only.


Tools

Reading

Tool What it answers
sp_overview "Where do things stand?" Counts, today's tasks, overdue work, all projects and tags with their ids. Start here.
sp_list_tasks Filter by project, tag, status, schedule or text. Ordered the way you'd work through them: overdue → today → by date → backlog → done.
sp_get_task One task in full, with notes and subtasks.
sp_list_projects Projects with task counts.
sp_list_tags Tags with ids.

Writing

Tool Notes
sp_create_task Title is the only requirement. Pass parentId to create a subtask.
sp_update_task Patches only the fields you send, so concurrent edits survive.
sp_complete_task Complete, or reopen with isDone: false.
sp_delete_task Deletes the task and its subtasks. Marked destructive so your host can confirm first.
sp_schedule_task dueDay for all-day, dueAt for a specific time, clear: true to unschedule.
sp_plan_for_today / sp_remove_from_today The right tools for "do this today".
sp_create_project / sp_update_project Create, rename, archive, toggle backlog.
sp_create_tag / sp_update_tag Duplicate names are refused.

Diagnostics

Tool Notes
sp_sync_status Layout, sync version, which devices have been writing, and any warnings.
sp_recent_activity The operation log in plain language — "did that actually save?"

In SP_MODE=read-only the write tools are not advertised at all, rather than advertised and refused. A tool a model can see is a tool it will try.

Two things worth knowing

"Today" is not a tag you apply. A task is in Today because its due date is today. sp_plan_for_today is the way to put it there; the TODAY tag's task list only stores ordering.

Durations are in minutes. Super Productivity stores milliseconds; this server converts at the boundary so nothing is ever off by a factor of sixty.


Configuration

Variable Default Notes
SP_NEXTCLOUD_URL required Base URL, no path. http:// is upgraded if your server redirects.
SP_NEXTCLOUD_USER required The username your files live under.
SP_NEXTCLOUD_PASSWORD required App password strongly preferred.
SP_NEXTCLOUD_LOGIN_NAME Only if your instance logs in by email but stores files under a different username.
SP_SYNC_FOLDER super-productivity Folder inside your Nextcloud.
SP_ENCRYPTION_PASSWORD Only if you enabled encryption in Super Productivity's sync settings. Must match exactly.
SP_MODE read-write read-only hides every write tool.
SP_CLIENT_ID derived This server's identity in the vector clock. Derived stably from machine + target; set it only if you need to pin it.
SP_CACHE_TTL_SECONDS 20 How long a read may be served from cache. Writes always refetch.
SP_LOG_LEVEL info silent | error | warn | info | debug. Always stderr.
SP_REQUEST_TIMEOUT_MS 30000 Per-request timeout.
SP_ENV_FILE ./.env Where to read the env file from.

Legacy nextcloud_user / nextcloud_password names are also accepted, so an existing .env needs no renaming.

Why the .env file is parsed rather than sourced: app passwords routinely begin with $, and source .env in a shell expands $Nyd0… to the empty string. The resulting failure looks exactly like a wrong password. This server reads the file literally and sidesteps the whole class of confusion.


How it works

src/
├── domain/          Pure. No I/O, no framework, no network.
│   ├── model/         Super Productivity's state, as we read it
│   ├── reducers/      Faithful ports of upstream's own reducers
│   ├── sync/          Vector clocks, the compact operation format
│   ├── errors.ts      One taxonomy, split by what the caller should do
│   └── ports.ts       The boundary: FileStore, Clock, IdGenerator, Logger
├── application/     Use cases and projections
│   ├── workspace.ts   Read-modify-write with optimistic concurrency
│   ├── read-models.ts Raw state → something a model can act on
│   └── services/      Task, organiser and diagnostics use cases
├── infrastructure/  Everything that touches the outside world
│   ├── codec/         The pf_ prefix, gzip, Argon2id + AES-GCM
│   ├── webdav/        Conditional writes, strong validators
│   ├── sync/          Layout detection, operation replay
│   └── config/        Env loading and validation
├── presentation/    The MCP tool surface
└── composition-root.ts  The only place a concrete dependency is chosen

The dependency rule points inward: domain knows nothing about WebDAV or MCP. That is not decoration — it is why the integration suite can run the entire stack, including the conditional-write retry logic, against an in-memory store with a frozen clock, offline, in under two seconds.

Both sync layouts, detected automatically

Super Productivity has shipped two remote layouts, and can migrate a folder at any time:

  • Single filesync-data.json holds the snapshot, archives and log together. The default.
  • Splitsync-ops.json is the commit point, sync-state.json the snapshot. Opt-in ("Surgical sync").

The layout is detected on every read, never configured. In the split layout the snapshot is rewritten only on compaction, so it can lag by up to 2000 operations; this server replays the pending log to close the gap, and reports anything it could not replay instead of quietly showing you an incomplete picture.

Encryption

If you enabled encryption in Super Productivity, set SP_ENCRYPTION_PASSWORD to the same password. The pipeline — JSON → gzip → Argon2id-derived AES-256-GCM — matches upstream exactly, including the legacy PBKDF2 format for files written by older clients.

A plaintext file is refused when encryption is configured. The encryption flag lives outside the authenticated envelope, so anyone who can write to your remote could strip it and serve you their own data; local intent wins over the remote's self-declaration.


Development

npm test              # unit + integration, no network, ~2s
npm run test:unit
npm run test:integration
npm run test:coverage
npm run test:e2e      # real Nextcloud — see below
npm run verify        # format + lint + typecheck + test
npm run dev           # run from source

284 tests. The integration suite runs the whole stack against an in-memory store that evaluates If-Match for real, covering the situations that are otherwise near-impossible to stage: a rival device committing inside the read-write gap, a server with no usable ETags, a failed backup write, a tombstoned folder, a corrupt remote.

The test fixture is a real sync file — same envelope, same 344-operation log, same schema version — with every title and note replaced by synthetic text.

End-to-end

npm run test:e2e runs against a real Nextcloud, in a separate sandbox folder seeded from a copy of your sync file and deleted afterwards. Your real sync folder is never written to by the suite. It skips itself when no credentials are configured.

Set SP_E2E_FOLDER in .env (default super-productivity-mcp-e2e). It must differ from SP_SYNC_FOLDER; the suite creates, overwrites and deletes files inside it.


Limitations

Stated plainly, because the alternative is discovering them later:

  • Sync is not instant. Changes land in the sync file immediately; your desktop and phone pick them up on their next sync.
  • Archived tasks are read-only. This server reads your archives but never writes to them. Complete tasks instead of archiving them.
  • The split layout is append-only. When its operation log fills up, the server refuses further writes and tells you to open Super Productivity once so it can compact. Compacting would mean publishing a partially-replayed snapshot as authoritative, which could silently discard whatever it failed to replay.
  • No time tracking or Pomodoro. Those are local, live features; there is nothing sensible to write from here.
  • Notes and repeating tasks are read-through, not writable.
  • One level of subtask nesting, matching Super Productivity itself.

Troubleshooting

Symptom Likely cause
Nextcloud rejected the credentials Login password with 2FA enabled — use an app password. Or a password starting with $ that a shell ate; this server parses .env literally, but your process manager may not.
Remote file not found: sync-data.json Wrong SP_SYNC_FOLDER. Check the folder name in Nextcloud's file browser.
Not a Super Productivity sync file Pointed at the wrong file, or encryption is on and SP_ENCRYPTION_PASSWORD is unset.
The remote sync file is plaintext but… SP_ENCRYPTION_PASSWORD is set but encryption is off in the app. Clear it.
no usable ETag in the doctor A proxy is stripping ETag headers. Writes will refuse rather than risk overwriting another device. Fix the proxy.
Changes not visible on your phone Open the app and let it sync. Check sp_sync_status for which devices have been writing.
another device kept writing first Something is syncing in a tight loop. Try again in a moment. Nothing was changed.

Credits

Built against Super Productivity by Johannes Millan. The operation-log format, vector-clock algorithms and reducer semantics here are ports of that project's own implementation — see docs/sync-and-op-log/ for the architecture this server had to learn to speak.

License

MIT

SIMILAR PLUGINS