Skip to main content
Glama
sunblob
by sunblob
README.md
# @fswap/mcp-vikunja

An [MCP](https://modelcontextprotocol.io) server for [Vikunja](https://vikunja.io) that lets Claude Desktop, Claude Code, Cursor and other MCP clients list, create, update and complete your tasks.

Runs locally over stdio. No install step — clients launch it with `npx`.

## Quick start

1. Create an API token in Vikunja under **Settings → API Tokens** (scope it to projects, tasks and labels).
2. Run the interactive setup once:

   ```bash
   npx -y @fswap/mcp-vikunja@latest setup
   ```

   It asks for your Vikunja URL and token, verifies them against `/api/v1/user`, lets you pick a default project, and stores the answers in your OS config directory (mode `0600`).

3. Add the server to your client. Every value asked in `setup` can be skipped with Enter; anything you skip goes into the `env` block shown below instead. `setup --print` shows these snippets again at any time.

   **Claude Desktop** (`claude_desktop_config.json`) and **Cursor** (`~/.cursor/mcp.json` or `<project>/.cursor/mcp.json`):

   ```json
   {
     "mcpServers": {
       "vikunja": {
         "command": "npx",
         "args": ["-y", "@fswap/mcp-vikunja@latest"]
       }
     }
   }
   ```

   **Codex** (`~/.codex/config.toml`):

   ```toml
   [mcp_servers.vikunja]
   command = "npx"
   args = ["-y", "@fswap/mcp-vikunja@latest"]
   ```

   **Claude Code**:

   ```bash
   claude mcp add vikunja -- npx -y @fswap/mcp-vikunja@latest
   ```

### Without `setup` (environment variables)

Environment variables take precedence over the config file, so you can skip `setup` entirely (or skip individual values in it and set them here):

```json
{
  "mcpServers": {
    "vikunja": {
      "command": "npx",
      "args": ["-y", "@fswap/mcp-vikunja@latest"],
      "env": {
        "VIKUNJA_URL": "https://try.vikunja.io",
        "VIKUNJA_API_TOKEN": "tk_..."
      }
    }
  }
}
```

Codex equivalent:

```toml
[mcp_servers.vikunja]
command = "npx"
args = ["-y", "@fswap/mcp-vikunja@latest"]
[mcp_servers.vikunja.env]
VIKUNJA_URL = "https://try.vikunja.io"
VIKUNJA_API_TOKEN = "tk_..."
```

| Variable | Purpose |
|---|---|
| `VIKUNJA_URL` | Vikunja base URL (with or without `/api/v1`) |
| `VIKUNJA_API_TOKEN` | API token or login JWT |
| `VIKUNJA_ALLOW_DELETE` | `true` to expose `delete_task` |
| `VIKUNJA_DEFAULT_PROJECT_ID` | Project used by `create_task` when `projectId` is omitted |

## Tools

| Tool | Method + endpoint | Notes |
|---|---|---|
| `list_projects` | `GET /projects` | id, title, description, parent |
| `get_project` | `GET /projects/{id}` | |
| `create_project` | `PUT /projects` | optional parent project |
| `list_tasks` | `GET /tasks` or `GET /projects/{id}/tasks` | open tasks by default; `assignedToMe`, `filter`, `sortBy`, pagination |
| `get_task` | `GET /tasks/{id}` | full task incl. description, labels, assignees |
| `create_task` | `PUT /projects/{id}/tasks` | title, description, dates, priority, labels |
| `update_task` | `POST /tasks/{id}` | merges your changes onto the current task; `labelIds` replaces labels |
| `complete_task` | `POST /tasks/{id}` with `done: true` | `done=false` reopens |
| `list_labels` | `GET /labels` | label ids for create/update |
| `create_label` | `PUT /labels` | |
| `delete_task` | `DELETE /tasks/{id}` | only when delete is allowed (setup answer or `VIKUNJA_ALLOW_DELETE=true`) |

Every task and project includes a `url` to its page in the Vikunja web UI (taken from `/info` `frontend_url`). Vikunja's zero date (`0001-01-01T00:00:00Z`) is normalised to `null` in every response. API errors are returned to the model as `isError` results rather than crashing the server.

## Development

TypeScript source in `src/`, bundled to `dist/` with [tsdown](https://tsdown.dev). Only `dist/` is published.

```bash
npm install
npm run build        # tsdown → dist/index.js
npm run lint         # eslint (typescript-eslint)
npm run typecheck    # tsc --noEmit
npm test             # builds, then spawns the server and checks the tool list
npm run check        # all of the above (also runs on prepublishOnly)
VIKUNJA_URL=... VIKUNJA_API_TOKEN=... npm run inspect   # MCP Inspector UI against dist/
```

Never write to stdout from server code — it is the protocol channel. Use `console.error`.

## Reset

```bash
npx -y @fswap/mcp-vikunja@latest setup --reset
```

## License

MIT

TDQS

B3.2/5.0

Scored across 10 tools

Disambiguation4/5

Tools are clearly separated by resource (projects, tasks, labels) and action (list, get, create, update, complete). The only minor overlap is update_task vs complete_task for marking a task done, but the description explicitly redirects that use case.

Naming Consistency5/5

All tool names follow a consistent snake_case verb_noun pattern (list_projects, get_task, create_label, etc.). There are no deviations or mixed conventions.

Tool Count5/5

With 10 tools, the set is well within the ideal 3-15 range and each tool maps to a distinct operation. It is focused without unnecessary redundancy.

Completeness3/5

Task management has create/get/update/complete but lacks delete_task. Projects and labels lack update and delete operations entirely, which are notable gaps for a complete task management surface.

Maintenance

ActivityMaintained
ResponsivenessNo issues