Lean, identifier-native MCP server for Vikunja: compact task payloads and project keys like INFRA-41 instead of opaque global IDs.
Strong tool naming (verb_noun pattern throughout), comprehensive parameter descriptions, and well-documented schemas. All 28 tools have clear descriptions (50-300 chars, within baseline). Input schemas are explicit with types and descriptions. Output schemas are documented in descriptions. Error handling is present but could be more prescriptive. Tool composition is excellent, one operation per tool, enabling fine-grained permission control. Main gaps: some parameter descriptions lack format/constraint details (e.g., RFC3339 format stated but not validated), and error recovery guidance is minimal.
Assigns one or more users to a task, keeping whoever is already assigned. Name the task by key (INFRA-41) and each user by username, exactly as vikunja_list_members reports it; a numeric id is the escape hatch for a username two accounts share. A user already assigned is skipped rather than re-assigned, and a name that matches no member — or more than one — fails the whole call before anything is written. A user the server itself refuses (no access to the project) can only be found out mid-call: the error then names which assignments already landed, and they stay. Answers with the task, listing everyone now assigned to it.
Set the same values of done, priority and/or due across many tasks in one write. Name the tasks by key (INFRA-41), or by global id as the escape hatch; a task named twice is written once. Every name is resolved before anything is written, so an unknown key changes nothing, and the write itself is one transaction — the server either moves the whole set or none of it. Fields not passed keep their current values, as do titles, descriptions and labels. Tasks that carry assignees or reminders, or that you have favourited, are refused by name: Vikunja's bulk endpoint destroys all three whatever it is told to write, so change those with vikunja_update_task instead. A repeating task is refused by name when this call would complete it — the bulk endpoint does not make a completion stick on one, leaving it open with its dates unrolled and a done_at stamp on it; complete those with vikunja_complete_task. Only that case is refused: setting priority or due on a repeating task, or reopening one, goes through like any other task. Answers with the tasks as the server holds them afterwards, read back rather than echoed.
Add a comment to a task. The comment body is markdown.
STDIO transport only, not remotely accessible. Hosted MCP clients cannot connect. Limits deployment to local/containerized scenarios.
Error handling lacks recovery guidance. Tools throw errors but do not suggest next steps (e.g., 'User not found. Try vikunja_list_members() to find valid usernames.').
Destructive tools (delete_task, delete_comment, delete_label) lack confirmation/dry-run pattern. Agents can delete without explicit user approval.
Parameter descriptions omit validation rules. E.g., priority accepts 0-5 but description does not state the range; due date format is RFC3339 but not validated in description.
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-23 | D | 54 | 2026-07-28+ | v2 |
Mark a task done. A repeating task does not end up done: Vikunja moves it to its next occurrence and answers with it still open, so calling this twice advances it twice — check the answer rather than retrying. Use vikunja_update_task to reopen a task or to change anything else.
Create a label, instance-wide — labels in Vikunja are not scoped to a project. Answers with the label's id, which is how it is addressed when its title turns out to be shared. A title another label already holds is refused: Vikunja would allow the duplicate, but then neither label could be named by its title anywhere. To put an existing label on a task use vikunja_label_task.
Create a task in a project. Answers with the task as stored.
Create multiple tasks in a project in one call. Answers with the tasks as stored.
Delete a comment. Answers with the comment as it was before deletion.
Delete a label. Answers with the label as it was before deletion.
Delete a task. Answers with the task as it was before deletion.
Fetch a project's kanban board: its buckets and the tasks in each, in the order Vikunja holds them.
Fetch one comment by id.
Fetch one task by key (INFRA-41) or by global id.
Attach a label to a task. The label must already exist; use vikunja_create_label to bring one into existence. A label already on the task is skipped rather than re-attached, and a name that matches no label — or more than one — fails the whole call before anything is written. Answers with the task, listing every label now on it.
List every comment on a task.
List every label on the instance.
List everyone Vikunja considers a member of a project: its owner, its direct user shares, the members of every team it is shared with, and everything inherited by walking up the parent chain.
List all projects, including archived ones.
List tasks across projects, optionally filtered by project, completion state, label, or custom filter expression.
Move a task to a different bucket on a kanban board.
Create a relation between two tasks. The relation kind is one of the kinds Vikunja knows: relates_to, duplicates, duplicated_by, blocks, blocked_by, relates_to, subtask_of, parent_task_of. Answers with the relation as stored.
Set the labels on a task to an exact list, removing any not named. Every name is resolved before the first write, so an unknown label changes nothing. Answers with the task, listing every label now on it.
Remove one or more users from a task. Name the task by key (INFRA-41) and each user by username, exactly as vikunja_list_members reports it; a numeric id is the escape hatch for a username two accounts share. A user not assigned is skipped rather than errored, and a name that matches no member — or more than one — fails the whole call before anything is written. Answers with the task, listing everyone still assigned to it.
Delete a relation between two tasks.
Update a comment's body. The body is markdown.
Update a label's title and/or colour. Fields not passed keep their current values. Answers with the label as stored.
Update a task's title, description, priority, or due date. Fields not passed keep their current values. Answers with the task as stored.
Update the same field values across many tasks in one call. Fields not passed keep their current values.
Pagination not visible in list tools. vikunja_list_tasks, vikunja_list_labels, vikunja_list_comments lack limit/offset/page parameters and total count in responses.