ziptask
ziptask
MCP task tracker for AI agents. A pure state layer — statuses, dependencies, leases, versioning — over SQLite, served to agents over MCP.
Install
One-liner — downloads a compiled binary, writes default config, prints client setup:
curl -LsS https://raw.githubusercontent.com/zumik3-del/ziptask/main/scripts/install.sh | shDefault install dir: ~/.ziptask/. Override with ZIPTASK_HOME=/some/path. Pin a version with ZIPTASK_VERSION=0.1.0. On systemd systems the installer provisions a background service on port 3005 (override with --port); skip it with --no-service.
Service mode
When systemd is detected and running, install.sh creates /etc/systemd/system/ziptask.service (Type=simple, Restart=on-failure, port 3005). Manage it with:
sudo systemctl start ziptask
sudo systemctl stop ziptask
sudo systemctl enable ziptask # auto-start on boot
journalctl -u ziptask -f # live logsRemove with bash ~/.ziptask/scripts/uninstall.sh (use --keep-data to preserve the DB and settings).
MCP client config (stdio)
// Claude / Cursor / opencode — ~/.config/claude/settings.json or equivalent
{
"mcpServers": {
"ziptask": {
"command": "~/.ziptask/bin/ziptask",
"args": ["--stdio"]
}
}
}Upgrade path is via bash ~/.ziptask/scripts/update.sh — it fetches the latest release, downloads the matching binary, and restarts the systemd service when active. To pin a version, pass --version <tag> (the script accepts tags with or without a v prefix):
bash ~/.ziptask/scripts/update.sh
bash ~/.ziptask/scripts/update.sh --version v0.2.0The script prompts y/N before overwriting. Before swapping the binary it creates an online SQLite backup of ZIPTASK_DB (resolved from env → settings.json dbPath → ~/.ziptask/data/ziptask.db) into ~/.ziptask/backups/ziptask-<timestamp>.db via sqlite3 .backup; missing sqlite3 or a missing DB are handled as warnings and the update continues. The backup path is printed so a failed upgrade is reversible. On non-systemd systems the binary is replaced but you must restart manually.
Schema guard on startup: a fresh DB auto-initialises; an older DB (V < L) auto-migrates forward; a DB newer than the binary (V > L, i.e. you downgraded) refuses to start with SCHEMA: database schema version <V> is newer than this binary supports (<L>); upgrade ziptask or restore the database from backup, exit 1. Fix by re-upgrading to the newer binary or restoring the backup that update.sh wrote. --version does not touch the DB.
Quick start (from source)
Requires Bun ≥ 1.4.
bun install
bun run start # HTTP server on an ephemeral port (MCP endpoint + /health)
bun run start:stdio # run over stdioBuild a binary
bun run build:bin # produces dist/ziptask
dist/ziptask --version # prints ziptask 0.1.0
dist/ziptask --stdio # runs as stdio MCP serverConfiguration
Variable | Default | Description |
|
| SQLite path (directory auto-created) |
|
| HTTP listen host |
|
| HTTP listen port |
|
| Claim lease length in minutes |
MCP tools
Tool | What it does |
| Enqueue a task; always |
| Read a task; |
| Filter by |
| Claim a queued task (auto-pick or by id); returns |
| Transition status with optimistic version lock. Epic close-guard: |
| Pipe lines |
| Pipe lines of dep-satisfied queued tasks (epics excluded) |
| Append a comment to a task |
| Merged audit log + comments feed. Epics show |
| Fetch a markdown template (task description, comments) |
| Aggregated stats: |
Statuses
Codes (STATUS_CODES): 0 not_found, 1 queued, 2 in_progress, 3 review, 4 done, 5 failed, 6 blocked.
Flow: queued → in_progress → review → done (or failed / blocked). done and failed are terminal; lease expiry returns to queued and increments attempts. Tasks with unsatisfied depends_on stay queued but are hidden from list_queue and auto-claim.
Epic → sub-task workflow
Epics are structural containers, not work. They cannot be claimed and their status is manual.
Declaring an epic
create_task {title: 'Release v2', reporter: 'orchestrator', epic: true}or attach the first sub-task to promote it automatically:
create_task {title: 'Sub-work', reporter: 'orchestrator', epic_id: <epic-id>}The target is auto-promoted to is_epic=1 (audit row promote_epic).
Attaching sub-tasks
Sub-tasks are ordinary tasks with epic_id pointing at the epic. Use depends_on for ordering:
create_task {title: 'Implement auth', reporter: 'orchestrator', epic_id: <epic-id>}
create_task {title: 'Write docs', reporter: 'orchestrator', epic_id: <epic-id>, depends_on: [<auth-id>]}Nested membership is rejected (epic cannot be a sub-task; sub-task cannot be an epic).
Roll-up and closure
get_task fields:["subtasks"]on the epic returns{total, open, done, failed}(open = queued+in_progress+review+blocked).list_tasks epic_id:<epic-id>returns only children.The epic timeline (
get_timeline) mirrors terminal sub-task events:subtask_add,subtask_done,subtask_failed.update_status(epic→done)is guarded: rejected withCHILDREN: N sub-tasks not terminalwhile any child is non-terminal.
Constraints
Rule | Error |
|
|
|
|
|
|
|
|
|
|
Explicit |
|
Notes
epic_idis immutable after creation (no re-parent/detach). Manual SQL is the escape hatch.blocked/failedon an epic are manual flags only; no cascade to children.Metrics (
metricstool) exclude epics — an epic sitting in one status for days would distortbottleneck.
Architecture
Layered by carrier, no framework beyond the MCP SDK:
Layer | Location | Responsibility |
MCP |
| Tool schemas, registration, response shaping |
Service |
| Domain rules: transitions, leases, deps, versioning |
Storage |
| SQL (bun:sqlite, WAL) + migrations |
Entry |
| Composition root, HTTP transport |
Development
bun test # unit tests (per-test temp DBs)
bunx tsc --noEmit # type check
bunx biome check src/ # lint
bun run src/smoke.ts # MCP end-to-end over HTTP (ephemeral port + temp DB)Docker
docker build -t ziptask .
docker run -d --name ziptask \
-p 3000:3000 \
-e ZIPTASK_HOST=0.0.0.0 \
-e ZIPTASK_PORT=3000 \
-v /host/data:/var/lib/ziptask \
-v /host/backups:/backups \
ziptaskThe container takes an online SQLite backup on a cron schedule; old archives are pruned.
Variable | Default | Description |
|
| Backup destination (mount a volume) |
|
| Backup file prefix |
|
| Backups to keep (oldest pruned) |
|
| Cron expression for the backup job |
License
MIT
Please note that this project is released with a Contributor Code of Conduct. By participating in this project you agree to abide by its terms.