- TypeScript 94.8%
- JavaScript 5.2%
|
All checks were successful
CI / build (push) Successful in 16s
For users who only use kanban (Brad's case), having to track viewId
and bucketId for every move is needless ceremony. Two new shortcuts:
1. viewId is now optional on list-buckets and move-bucket. When
omitted, we GET /projects/{id}/views and pick the first one with
view_kind='kanban'. Errors clearly when no kanban view exists.
2. move-bucket accepts bucketName as an alternative to bucketId.
Case-insensitive title match against the project's buckets. On
miss, the error lists the available bucket titles so the agent
can recover.
Result: a kanban move is now just
{ taskId, projectId, bucketName: "Done" }
— no view discovery roundtrip, no ID tracking.
bucketId and viewId are still accepted for projects with multiple
kanban views or callers who already know the IDs.
Helpers live in src/tools/_kanban.ts; consumed by tasks.move-bucket
and projects.list-buckets. Verified end-to-end against the live
Vikunja: list-buckets without viewId returned the right buckets,
move-bucket by name moved the task, mistyped bucket name returned
"No bucket named 'X'. Available: To-Do, Doing, Done".
README and docs/API-NOTES.md updated.
|
||
|---|---|---|
| .forgejo/workflows | ||
| dist | ||
| docs | ||
| src | ||
| .env.example | ||
| .gitignore | ||
| .prettierrc.json | ||
| CLAUDE.md | ||
| eslint.config.mjs | ||
| foragent.md | ||
| LICENSE | ||
| package-lock.json | ||
| package.json | ||
| README.md | ||
| tsconfig.json | ||
vikunja-mcp
Model Context Protocol server for Vikunja — a thin wrapper over node-vikunja that lets MCP-aware AI assistants drive a Vikunja instance.
This is a hard fork of @democratize-technology/vikunja-mcp that strips the upstream's enterprise scaffolding (custom rate limiter, V8 memory estimator, regex log scrubber, filter parser, circuit breaker registry, multiple bulk implementations, ~12k LOC of duplicate tool surfaces) down to a small, readable wrapper.
Tools
| Tool | Subcommands | Auth |
|---|---|---|
vikunja_auth |
connect, status, disconnect |
— |
vikunja_tasks |
list, list-by-project, get, create, update, delete, set-status, assign, unassign, list-assignees, add-label, remove-label, list-labels, list-comments, add-comment, update-comment, delete-comment, create-relation, delete-relation, bulk-update, move-bucket |
API token or JWT |
vikunja_projects |
list, get, create, update, delete, duplicate, list-shares, get-share, create-share, delete-share, list-views, list-buckets, list-labels |
API token or JWT |
vikunja_labels |
list, get, create, update, delete |
API token or JWT |
vikunja_teams |
list, create, delete |
API token or JWT |
vikunja_filters |
get, create, update, delete |
API token or JWT |
vikunja_users |
current |
JWT only |
Authentication
Vikunja issues two token flavours:
- API tokens — start with
tk_. Created in Settings → API Keys. Cover most resources but cannot access/user*endpoints. - JWTs — start with
eyJ. Lifted from a browser session (DevTools → Local Storage). Full access.
vikunja_auth.connect auto-detects the type from the token prefix.
Configuration
| Env var | Description |
|---|---|
VIKUNJA_URL |
Base URL of your Vikunja instance (e.g. https://vikunja.example.com). Auto-connects on startup if both vars are set. |
VIKUNJA_API_TOKEN |
API token or JWT. |
LOG_LEVEL |
error | warn | info | debug (default info). |
Install / run
npm install
npm run build
node dist/index.js
For development:
npm run dev # tsx watch
npm run typecheck
npm run lint
Wiring into Claude Desktop
claude_desktop_config.json:
{
"mcpServers": {
"vikunja": {
"command": "node",
"args": ["/absolute/path/to/vikunja-mcp/dist/index.js"],
"env": {
"VIKUNJA_URL": "https://vikunja.example.com",
"VIKUNJA_API_TOKEN": "tk_..."
}
}
}
}
Filtering
The filter argument on vikunja_tasks.list / list-by-project is forwarded verbatim to Vikunja's filter parameter — Vikunja parses the DSL server-side. See Vikunja's filter docs.
Kanban operations
Kanban buckets technically live inside views, but if your project has only one kanban view (the common case) you can skip dealing with viewId entirely.
// 1. List buckets — auto-discovers the project's kanban view
{ "subcommand": "list-buckets", "projectId": 18 }
// → [{id: 64, title: "To-Do"}, {id: 65, title: "Doing"}, {id: 66, title: "Done"}]
// 2. Move a task by bucket name (case-insensitive)
{ "subcommand": "move-bucket", "taskId": 100, "projectId": 18, "bucketName": "Done" }
bucketId and viewId are still accepted if you have multiple kanban views or want to address a bucket by ID. If you misspell bucketName, the error lists the available titles.
vikunja_projects.list-labels { projectId } returns labels used by tasks in the project (Vikunja has no native per-project labels endpoint — we aggregate from up to 5 pages of tasks).
Creating tasks with labels and assignees
vikunja_tasks.create accepts top-level labels: number[] and assignees: number[] arrays alongside the nested task body. The MCP server creates the task, then attaches labels via the bulk endpoint and assigns users individually, returning the populated task.
{
"subcommand": "create",
"projectId": 1,
"task": { "title": "Foo", "priority": 3, "due_date": "2026-05-01T00:00:00Z" },
"labels": [4, 7],
"assignees": [2]
}
License
MIT — see LICENSE. Original copyright © 2025 Democratize Technology; fork modifications © 2026 Brad Wenner.