vikunja-mcp
This server provides a workflow-driven task management interface for Vikunja, guiding an AI agent through a fixed pipeline (Backlog → Queue → Design → Build → Review → Done).
next_task()— Retrieve your active task in Design/Build, or the top-priority free task from the Queue. Never surfaces Backlog or blocked tasks; only one task at a time.claim(task_id)— Claim a task from the Queue, assigning it to yourself and moving it to Design. Includes race-condition protection.get_task(task_id)— Fetch full details of a task: description, current stage, assignees, labels, and complete comment thread.comment(task_id, text)— Add a progress note or decision log to a task's comment thread.advance(task_id, to, spec, worklog, evidence)— Move your task forward: Design → Build (requires aspec), or Build → Review (requiresworklog+evidence). Advancing directly to Done is blocked — only humans can sign off.call_human(task_id, question)— Escalate to a human for a decision or input. Posts your question as a comment and moves the task to "Call to Human" while preserving your assignment.return_task(task_id, reason)— Return a task due to an external blocker (missing access, broken dependency, etc.). Unassigns you, adds ablockedlabel, and moves it back to Backlog for human re-triage.decompose(task_id, subtasks)— Break a large task into 2+ subtasks (each with a title, optional description, and priority). Subtasks are created in Queue with a parent relation; the parent is labeledepicand moved to Backlog.
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@vikunja-mcpGet me the next task I should work on."
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
vikunja-mcp
A workflow-level MCP server for Vikunja — not a generic task CRUD wrapper. It exposes a small set of tools that push an agent through a fixed pipeline, and the gates (what's allowed from which stage, what evidence is required) live in the tools themselves, not in prompts:
Backlog → Queue → Design → Build → Review → Done
↕ ↕
Your CallBacklogandDoneare human territory (triage in, sign-off out) — agents never move a task intoDonethemselves.Queue → Design → Build → Reviewis the agent loop: claim, spec, build, hand off for review.Your Call(abbreviatedYC) is a side branch reachable fromDesign/Buildwhen an agent needs a decision or input; the human answers and moves it back.
Run that loop under Claude Code's /loop with no interval — that lets the model
self-pace. The agent drains the queue one task at a time and schedules its own
wake-ups when the queue is empty, so it keeps working without a human
re-prompting it each turn.
Install
uvx --from git+https://github.com/ufna/vikunja-mcp@v0.1.0 vikunja-mcpRegister it with Claude Code via .mcp.json:
{
"mcpServers": {
"tracker": {
"command": "uvx",
"args": ["--refresh-package", "vikunja-mcp", "--from", "git+https://github.com/ufna/vikunja-mcp@stable", "vikunja-mcp"]
}
}
}Or register it with opencode via opencode.json
(repo root, or ~/.config/opencode/opencode.json globally; .jsonc also
works). MCP servers live under a top-level mcp key, and a local (stdio)
server takes the command and its arguments as one command array
(docs):
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"tracker": {
"type": "local",
"command": ["uvx", "--refresh-package", "vikunja-mcp", "--from", "git+https://github.com/ufna/vikunja-mcp@stable", "vikunja-mcp"],
"enabled": true
}
}
}As with .mcp.json, no token goes in this file — the server reads it from the
same four config layers below (VIKUNJA_TOKEN, .vikunja-mcp.env, or the user
env file). This repo commits exactly such an opencode.json at its root to
dogfood itself against the stable channel.
To hand an opencode agent the tracker process rules (queue discipline, stage
gates, call_human vs return_task), run vikunja-mcp install-skill — it
installs the packaged SKILL.md for both Claude Code and opencode and prints an
instructions line to add to your opencode.json:
{
"instructions": ["/home/you/.config/opencode/skills/tracker/SKILL.md"]
}opencode also auto-loads AGENTS.md from the repo root (falling back to
CLAUDE.md), so the rules can live there instead — see
opencode rules.
Related MCP server: Accordo
Configuration
Config is resolved from four layers, in priority order:
Environment variables —
VIKUNJA_URL,VIKUNJA_TOKEN,VIKUNJA_PROJECT_IDRepo-local env file
.vikunja-mcp.env(KEY=VALUElines, same directory as.vikunja-mcp.toml, found by the same walk-up) — per-project token for machines that work across multiple repos, without touching the user env file. Never commit it — add it to the consuming repo's.gitignoreRepo file
.vikunja-mcp.toml(found by walking up from the cwd) —urlandproject_id, safe to commit (no secret)User env file
~/.config/vikunja-mcp/env(KEY=VALUElines,chmod 600) — the usual place forVIKUNJA_TOKEN
# .vikunja-mcp.toml (commit this)
[tracker]
url = "https://vikunja.example.com"
project_id = 12
project = "My Project" # informational label; not used for lookup# .vikunja-mcp.env (same directory as .vikunja-mcp.toml — gitignore it, never commit)
VIKUNJA_TOKEN=tk_xxxxxxxxxxxx# ~/.config/vikunja-mcp/env (chmod 600, keep out of git)
VIKUNJA_URL=https://vikunja.example.com
VIKUNJA_TOKEN=tk_xxxxxxxxxxxx
VIKUNJA_PROJECT_ID=12Note: the token is never read from .vikunja-mcp.toml — only from
VIKUNJA_TOKEN (env), .vikunja-mcp.env, or the user env file — so
.vikunja-mcp.toml has nothing secret in it and is safe to commit.
.vikunja-mcp.env uses the same KEY=VALUE parsing rules as the user env
file (quoted values, trailing # comment stripping on unquoted ones), and
all three keys (VIKUNJA_URL/VIKUNJA_TOKEN/VIKUNJA_PROJECT_ID) may
appear in it.
Tools
Tool | Gate / behavior |
| Returns your active task in Design/Build first (incl. one bounced back from Your Call), else the top-priority free task in Queue. Never returns Backlog or |
| Queue → Design only. Assign-then-verify: assigns you, re-reads the task, and backs off if someone else was assigned in the same window (race lost — call |
| Dossier: description, stage, assignees, labels, full comment thread. |
| Adds a progress note to the task's comment log. |
|
|
| Design/Build → |
| For external blockers (no access, missing dependency, someone else's service down). Unassigns you, adds a |
| Requires ≥2 subtasks (each needs a |
| Files an out-of-scope finding (a bug or bit of tech-debt spotted mid-work) into Backlog for human triage — not Queue. Stamps a |
Project setup
VIKUNJA_TOKEN=<admin token> vikunja-mcp setup --project NAME [--share user:read|write|admin ...] --url URLCreates the project if it doesn't exist (matched by title), then
creates/reconciles the seven canonical buckets in order, migrates known
default-Vikunja buckets (Todo/To-Do/To-do → Queue, Doing → Build,
moving their tasks), removes empty non-canonical buckets (leaves non-empty
unknown ones alone with a warning), sets Backlog as the default bucket and
Done as the done bucket, applies any --share grants, and prints ready-to-
commit .vikunja-mcp.toml + .mcp.json + opencode.json snippets.
vikunja-mcp install-skillCopies the packaged tracker skill (queue discipline, comment-trail
expectations, call_human vs return_task) to both
~/.claude/skills/tracker/SKILL.md (Claude Code) and
~/.config/opencode/skills/tracker/SKILL.md (opencode), and prints the
instructions line to wire the latter into an opencode.json.
For Claude Code it also auto-provisions a conditional SessionStart hook
(a small ~/.claude/hooks/vikunja-tracker-orchestrator.sh registered under
hooks.SessionStart in ~/.claude/settings.json) so you don't have to paste an
orchestrator redirect into each project's CLAUDE.md. On every session start the
hook walks up from the cwd for a .vikunja-mcp.toml; only inside a
tracker-configured project it injects a short standing-context that redirects a
bare /loop to the tracker orchestrator (drain the Queue: next_task → claim
→ dispatch a per-task agent) instead of Claude Code's generic autonomous-loop
default — outside a tracker project it emits nothing, so it never affects your
other repos. It's dependency-free (POSIX sh, no jq), idempotent (re-running
never duplicates the entry and preserves your other hooks and settings), and
takes effect after you restart Claude Code. The full playbook still lives in the
tracker skill; the hook just points /loop at it.
Releases: the stable channel
Consumers subscribe to the moving stable branch with --refresh-package
in .mcp.json — every MCP server start re-resolves the channel, so releases
roll out to all repos automatically (no per-consumer bumps, no PR bots).
Immutable vX.Y.Z tags remain the release history and rollback points.
Admin one-offs (setup, install-skill) may use @main.
Patch releases are automatic: every green push to main triggers a CI job
that bumps the patch version in both files, tags vX.Y.Z, and moves stable
onto it — so stable always tracks the latest green main with no manual step.
Manual steps remain for minor/major bumps and rollback:
bump
versioninpyproject.tomland__version__insrc/vikunja_mcp/__init__.py(only for a minor/major — patches are automated)commit, wait for CI green
git tag -a vX.Y.Z -m "vX.Y.Z" && git push origin vX.Y.Zgit branch -f stable vX.Y.Z && git push -f origin stable# rollout (rollback: same, older tag)
Development
uv sync
uv run ruff check .
uv run pytest tests/unit -qIntegration tests exercise a real Vikunja instance and are skipped unless
VIKUNJA_TEST_URL is set:
docker run -d --name vikunja -p 3456:3456 \
-e VIKUNJA_DATABASE_TYPE=sqlite -e VIKUNJA_DATABASE_PATH=/tmp/vikunja.db \
-e VIKUNJA_FILES_BASEPATH=/tmp/files \
-e VIKUNJA_SERVICE_JWTSECRET=ci-secret \
-e VIKUNJA_SERVICE_PUBLICURL=http://localhost:3456/ \
-e VIKUNJA_SERVICE_ENABLEREGISTRATION=true \
vikunja/vikunja:2.3.0
timeout 60 bash -c 'until curl -sf http://localhost:3456/api/v1/info; do sleep 1; done'
VIKUNJA_TEST_URL=http://localhost:3456 uv run pytest tests/integration -qVikunja rate-limits /login (10 requests/60s shared with /register); the
integration conftest retries on HTTP 429 with backoff (up to ~150s worst
case across a full run) — expected, not a bug.
License
MIT — see LICENSE.
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Latest Blog Posts
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/ufna/vikunja-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server