Standalone MCP server for multi-tenant task and project management. SQLite + FTS5, no network deps, plug-and-play with any MCP-aware client (Claude Code, Claude Desktop, Cursor, Cline, …).
- Workspaces auto-created from an env var — one per chat / agent / project, isolated by default.
- Projects that can be shared across workspaces through a many-to-many join, so one project can live in multiple agents at once.
- Tasks with status, priority, due dates, tags, subtasks, and full-text search via SQLite FTS5.
- Server-side privilege model — structural ops (sharing, deletion) are gated by an env-var bit a trusted client controls.
pip install git+https://github.com/Ordinath/taskwing.git
# or, in this checkout:
pip install -e ".[dev]"Python 3.11+ required. The only runtime deps are mcp[cli] and pydantic>=2.
TASKWING_WORKSPACE=my-chat \
TASKWING_PRIVILEGED=true \
taskwing-mcpThe server speaks MCP over stdio. Stop it with Ctrl+C.
| var | meaning | default |
|---|---|---|
TASKWING_WORKSPACE |
workspace name. Auto-created on first run if absent. | default |
TASKWING_PRIVILEGED |
"true" (literal) ⇒ structural ops allowed; anything else ⇒ blocked. |
unset → unprivileged |
TASKWING_DB_PATH |
path to the SQLite file. | ~/.taskwing/taskwing.db |
TASKWING_PRIVILEGED is captured at workspace creation time and stored in the row. Flipping the env later does not retroactively elevate or downgrade an existing workspace — re-create it (workspace_delete then re-attach) to change the bit.
"taskwing": {
"transport": "stdio",
"command": "python3",
"args": ["-m", "taskwing.mcp_server"],
"env": {
"TASKWING_WORKSPACE": "${TASKWING_WORKSPACE}",
"TASKWING_PRIVILEGED": "${TASKWING_PRIVILEGED:-false}"
}
}Set TASKWING_WORKSPACE and TASKWING_PRIVILEGED per session/agent. The same DB file backs every workspace, so projects can be shared across them by name.
25 tools, grouped by entity:
| group | tool |
|---|---|
| workspace | workspace_current, workspace_list*, workspace_create*, workspace_rename*, workspace_delete* |
| project | project_list, project_create, project_get, project_update, project_archive, project_delete*, project_attach*, project_detach*, project_list_workspaces |
| task | task_create, task_get, task_update, task_complete, task_uncomplete, task_delete |
| search | task_search, task_today, task_overdue, task_upcoming, daily_digest |
Tools marked * are privileged-only.
Full descriptions, args, and return shapes are baked into the FastMCP tool registrations — your LLM client surfaces them as JSON schema automatically. See DESIGN.md for the full list with types and semantics.
Two roles, decided once per workspace at creation:
- Privileged workspace — can perform structural ops (share/unshare projects, delete projects, manage workspaces).
- Unprivileged workspace — can do everything else inside projects it can already see (full task R/W, project create/update/archive in its own scope), but can't reach across workspaces or hard-delete.
The bit is set from TASKWING_PRIVILEGED and frozen in the DB row. The trusted client (the thing that spawns the server) decides which workspaces get it — TaskWing itself is unopinionated.
If you run multiple agents/sessions/tenants against a shared TaskWing instance and only some of them should be allowed structural ops, set TASKWING_PRIVILEGED from a trusted source in your spawn pipeline — after reading any user-controlled config (.env, env-file, etc.) so the value cannot be overridden by the caller. Pseudocode:
// AFTER loading the agent's user config:
env.TASKWING_PRIVILEGED = isTrustedSession(agent) ? 'true' : 'false';A common shape is "host process / sandboxed container" — the host gets true, the container gets false, and the host always overwrites whatever the container's own config tried to set. Same pattern works for "admin chat vs shared room", "owner workspace vs guest", etc.
Single SQLite file at ~/.taskwing/taskwing.db (override with TASKWING_DB_PATH). All workspaces live in the same file. Tables: workspaces, projects, workspace_projects (m2m join), tasks. Full-text search uses an FTS5 virtual table kept in sync via triggers.
pip install -e ".[dev]"
pytest -q --cov=taskwingThe suite covers schema/migration, all CRUD paths, search filters, FTS, isolation between workspaces, privilege enforcement on every privileged op, R/W on shared projects, and the FastMCP server smoke flow. Coverage gate: ≥90%.
MIT.