mcpField
V
Vikunja MCP Server

Vikunja MCP Server

本地服务
github.com/democratize-technology生产力11336 周前更新

关于 Vikunja MCP Server

Vikunja MCP Server 是面向 Vikunja 任务管理实例的 MCP 服务,让 AI 助手能够读取和操作任务、项目、标签、团队、用户及 Webhook。项目采用 TypeScript 编写,并使用 Zod 进行输入校验。

主要功能

  1. 01按子命令组织的工具
  2. 02基于会话的身份验证与令牌管理
  3. 03项目、标签与团队管理
  4. 04Webhook 管理
  5. 05从 CSV 或 JSON 文件批量导入任务
  6. 06基于 opossum 的断路器重试机制

适用场景

  1. 01让 AI 助手查询和创建 Vikunja 任务
  2. 02通过 Webhook 为项目配置自动化
  3. 03批量导入任务数据

MCP 相关

该项目是一个 MCP 服务,向 MCP 客户端暴露 Vikunja 的任务、项目、标签等操作,工具以 vikunja_tasks、vikunja_projects 等子命令形式提供。

配置

本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。

mcp.json · 通用配置
{
"mcpServers": {
"vikunja": {
"command": "npx",
"args": [
"-y",
"@democratize-technology/vikunja-mcp"
],
"env": {
"VIKUNJA_URL": "<VIKUNJA_URL>",
"VIKUNJA_API_TOKEN": "<VIKUNJA_API_TOKEN>"
}
}
}
}
启动命令npx -y @democratize-technology/vikunja-mcp
需要配置的环境变量
VIKUNJA_URL
VIKUNJA_API_TOKEN密钥

本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。

安装包
npm@democratize-technology/vikunja-mcp

工具

9

收录的工具列表 · 更新于 3 周前

参数类型必填说明
subcommandenum是connect | status | refresh | disconnect
apiUrlstring否
apiTokenstring否

详细介绍

Vikunja MCP 服务

一个模型上下文协议(MCP)服务,可让 AI 助手与 Vikunja 任务管理实例交互。

功能

  • 基于子命令的工具,让 AI 交互更直观
  • 基于会话的身份验证,自动管理令牌
  • 已实现完整的任务管理操作
  • 完整的项目管理,支持 CRUD 操作
  • 标签管理,用于整理任务
  • 团队操作,支持协作(受 API 限制,仅支持获取、更新和成员管理)
  • 用户管理,支持设置和搜索
  • Webhook 管理,用于项目自动化
  • 批量导入,可从 CSV 或 JSON 文件导入任务
  • 输入验证,适用于日期、ID 和十六进制颜色
  • 高效的差异式更新,用于受指派人
  • 采用严格模式的 TypeScript,确保类型安全
  • 全面的错误处理,包含类型化错误和集中式工具
  • 适用于生产环境的重试逻辑,使用 opossum 熔断器增强可靠性
  • 增强的安全性,采用基于 Zod 的输入验证并防范 DoS
  • 速率限制保护,通过可配置限制抵御 DoS 攻击
  • 内存保护,包含分页限制和使用情况监控
  • 简化的架构,代码量减少 90%,更易维护

🚀 重大架构改进(v0.2.0)

此版本进行了大幅架构简化,消除技术债务,同时提升安全性和可靠性:

存储架构重构(代码量减少 90%)
  • 之前:33 个文件,9,803 行过度设计的存储系统代码
  • 之后:4 个文件,仅保留必要功能
  • 已移除:复杂的协调器、健康监控器、统计跟踪和迁移系统
  • 结果:外部 API 保持不变,可维护性大幅提升
基于 Zod 的筛选器系统(移除 850 多行代码)
  • 之前:自定义分词器、解析器和验证器,存在安全漏洞
  • 之后:通过安全的 Zod 模式验证实现适用于生产环境的解析
  • 增强:DoS 防护、输入清理和全面的错误处理
  • 结果:解析速度更快、安全性更高,并达到企业级可靠性
适用于生产环境的重试系统(替换 580 多行代码)
  • 之前:自定义重试逻辑,维护成本高
  • 之后:经过实战验证的 opossum 熔断器库
  • 功能:共享熔断器状态、自动恢复和全面监控
  • 结果:采用经过实战验证的模式,增强生产环境的可靠性
零破坏性变更

所有改进在增强可靠性和安全性的同时,均与现有实现保持100% 向后兼容。

要求

  • Node.js 20+(仅限 LTS 版本)
  • 具有 API 访问权限的 Vikunja 实例
  • 用于身份验证的 API 令牌(以 tk_ 开头)或 JWT 令牌

安装

选项 1:从 NPM 安装(推荐)

使用 vikunja-mcp 最简单的方式是在 Claude Desktop 或其他兼容 MCP 的客户端配置中通过 npx 调用:

{
  "vikunja": {
    "command": "npx",
    "args": ["-y", "@democratize-technology/vikunja-mcp"],
    "env": {
      "VIKUNJA_URL": "https://your-vikunja-instance.com/api/v1",
      "VIKUNJA_API_TOKEN": "your-api-token"
    }
  }
}
选项 2:本地开发

用于开发或自定义:

git clone https://github.com/democratize-technology/vikunja-mcp.git
cd vikunja-mcp
npm install
npm run build

然后配置 MCP 客户端:

{
  "vikunja": {
    "command": "node",
    "args": ["/path/to/vikunja-mcp/dist/index.js"],
    "env": {
      "VIKUNJA_URL": "https://your-vikunja-instance.com/api/v1",
      "VIKUNJA_API_TOKEN": "your-api-token"
    }
  }
}

配置

日志配置

服务包含结构化日志系统。通过环境变量进行配置:

# Enable debug logging (default: false)
DEBUG=true

# Set specific log level (error, warn, info, debug)
# If not set, defaults to 'info' (or 'debug' if DEBUG=true)
LOG_LEVEL=debug

日志输出包含时间戳和日志级别:

[2025-05-25T17:00:00.000Z] [INFO] Vikunja MCP server started
[2025-05-25T17:00:00.100Z] [DEBUG] Executing tasks tool { subcommand: 'list', args: {...} }

所有日志均写入 stderr,以便将 stdout 保留用于 MCP 协议通信。

身份验证方式

Vikunja MCP 服务支持两种身份验证方式,各自具备不同的能力:

API 令牌身份验证(默认)

API 令牌是 Vikunja 的标准身份验证方式:

  • 获取方式: 前往 Vikunja「设置」→「API 令牌」→「创建新令牌」
  • 令牌格式: 以 tk_ 开头(例如,tk_abc123def456)
  • 功能: 可完整访问任务、项目、标签、团队和 Webhook
  • 限制: 无法访问用户专属端点(用户个人资料、设置、导出)
  • 适用场景: 自动化、CI/CD 和常规任务管理
JWT 身份验证(高级)

JWT(JSON Web Token)身份验证可完整访问 Vikunja 的所有端点:

  • 获取方式: 从浏览器会话中提取(见下方说明)
  • 令牌格式: 以 eyJ 开头的长字符串(标准 JWT 格式)
  • 功能: 可完整访问所有端点,包括用户管理和导出
  • 限制: 令牌会过期(通常在 24 小时后)
  • 适用场景: 用户管理、数据导出以及需要用户上下文的操作
如何提取 JWT 令牌
  1. 在网页浏览器中登录 Vikunja
  2. 打开开发者工具(F12 或右键单击 → 检查)
  3. 前往 Application/Storage 选项卡
  4. 找到 JWT 令牌:
    • 在 Local Storage → 你的 Vikunja 域名下查找
    • 找到名为 token 或类似名称的键
    • 其值就是你的 JWT 令牌
  5. 复制完整的令牌值(令牌会很长)
使用 JWT 身份验证
// Connect with JWT token - automatically detected!
vikunja_auth.connect({
  apiUrl: "https://your-vikunja-instance.com/api/v1",
  apiToken: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
})

重要说明:

  • JWT 令牌会过期;过期后需要重新提取
  • 系统会根据格式自动检测令牌类型(无需设置标志)
  • 部分工具(用户、导出)仅支持 JWT 身份验证

快速入门

  1. 设置身份验证(如果不使用环境变量):

    vikunja_auth.connect({
      apiUrl: "https://your-vikunja-instance.com/api/v1",
      apiToken: "your-api-token"
    })
    
  2. 创建你的第一个任务:

    vikunja_tasks.create({
      projectId: 1,
      title: "My first task via MCP!"
    })
    
  3. 列出你的所有任务:

    vikunja_tasks.list({ allProjects: true })
    

用法

MCP 服务通过子命令提供工具。所有操作都需要先进行身份验证(通过环境变量或手动连接)。

身份验证
// Connect with API token (automatically detected)
vikunja_auth.connect({
  apiUrl: "https://your-vikunja-instance.com/api/v1",
  apiToken: "tk_your-api-token"
})

// Connect with JWT token (automatically detected, enables additional tools: users, export)
vikunja_auth.connect({
  apiUrl: "https://your-vikunja-instance.com/api/v1",
  apiToken: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
})

// Check authentication status
vikunja_auth.status()

// Disconnect and clean up resources
vikunja_auth.disconnect()
任务管理示例
// List all tasks across all projects
vikunja_tasks.list({ allProjects: true })

// List tasks for a specific project with pagination
vikunja_tasks.list({
  projectId: 1,
  page: 1,
  perPage: 20,
  sort: "due_date"
})

// List tasks with filters (high priority, not done)
vikunja_tasks.list({
  filter: "(priority >= 4 && done = false)"
})

// List tasks with simple filter
vikunja_tasks.list({
  filter: "priority >= 3"
})

// List tasks with complex filter conditions
vikunja_tasks.list({
  filter: "(priority >= 3 && priority <= 5) || (done = true && updated > '2024-01-01')"
})

// Combine filter with search
vikunja_tasks.list({
  filter: "priority >= 4",
  search: "urgent"
})

// Create a new task with labels and assignees
vikunja_tasks.create({
  projectId: 1,
  title: "Complete documentation",
  description: "Update README with examples",
  dueDate: "2024-12-31T23:59:59Z",
  priority: 3,
  labels: [1, 2],      // Label IDs
  assignees: [1, 3]    // User IDs
})

// Create a recurring task (repeats every week)
vikunja_tasks.create({
  projectId: 1,
  title: "Weekly team meeting",
  description: "Sync up with the team",
  dueDate: "2024-12-01T10:00:00Z",
  repeatAfter: 7,      // Number of units
  repeatMode: "day"    // Unit: "day", "week", "month", or "year"
})

// Create a monthly recurring task
vikunja_tasks.create({
  projectId: 1,
  title: "Monthly report",
  repeatAfter: 1,
  repeatMode: "month"
})

// Get detailed information about a task
vikunja_tasks.get({ id: 123 })

// Update a task (partial updates supported)
vikunja_tasks.update({
  id: 123,
  done: true,
  priority: 5
})

// Update recurring settings on an existing task
vikunja_tasks.update({
  id: 123,
  repeatAfter: 14,     // Change to bi-weekly
  repeatMode: "day"
})

// Update task assignees (uses efficient diff-based approach)
vikunja_tasks.update({
  id: 123,
  assignees: [1, 2, 4]  // Only adds/removes differences
})

// Delete a task
vikunja_tasks.delete({ id: 123 })

// Bulk assign users to a task
vikunja_tasks.assign({
  id: 123,
  assignees: [2, 3, 4]
})

// Remove users from a task
vikunja_tasks.unassign({
  id: 123,
  assignees: [2, 4]  // Removes only these users
})

// List all assignees for a task
vikunja_tasks.list-assignees({ id: 123 })

// Add a comment to a task
vikunja_tasks.comment({
  id: 123,
  comment: "This task is now complete!"
})

// List all comments on a task
vikunja_tasks.comment({ id: 123 })

// Create a task relation (e.g., subtask, blocking, related)
vikunja_tasks.relate({
  id: 123,
  otherTaskId: 124,
  relationKind: "subtask"  // 124 is a subtask of 123
})

// Available relation kinds:
// - subtask: Other task is a subtask of this task
// - parenttask: Other task is the parent of this task
// - related: Tasks are related
// - duplicateof: This task is a duplicate of the other
// - duplicates: Other task is a duplicate of this one
// - blocking: This task blocks the other
// - blocked: This task is blocked by the other
// - precedes: This task precedes the other
// - follows: This task follows the other
// - copiedfrom: This task was copied from the other
// - copiedto: Other task was copied from this one

// Remove a task relation
vikunja_tasks.unrelate({
  id: 123,
  otherTaskId: 124,
  relationKind: "subtask"
})

// Get all relations for a task
vikunja_tasks.relations({ id: 123 })

// Add a reminder to a task
vikunja_tasks.add-reminder({
  id: 123,
  reminderDate: "2024-12-25T10:00:00Z"
})

// List all reminders for a task
vikunja_tasks.list-reminders({ id: 123 })

// Remove a specific reminder from a task
vikunja_tasks.remove-reminder({
  id: 123,
  reminderId: 1
})

// Bulk create multiple tasks at once (max 100)
vikunja_tasks.bulk-create({
  projectId: 1,
  tasks: [
    {
      title: "Task 1",
      description: "First task",
      priority: 3,
      labels: [1, 2]
    },
    {
      title: "Task 2", 
      dueDate: "2024-12-31T23:59:59Z",
      assignees: [1]
    },
    {
      title: "Weekly standup",
      repeatAfter: 7,
      repeatMode: "day"
    }
  ]
})

// Bulk update multiple tasks with the same field value
vikunja_tasks.bulk-update({
  taskIds: [123, 124, 125],
  field: "done",          // Field to update
  value: true             // New value for all tasks
})

// Other bulk update examples
vikunja_tasks.bulk-update({
  taskIds: [123, 124, 125],
  field: "priority",
  value: 5
})

vikunja_tasks.bulk-update({
  taskIds: [123, 124],
  field: "project_id",
  value: 2               // Move tasks to different project
})

vikunja_tasks.bulk-update({
  taskIds: [123, 124, 125],
  field: "labels",
  value: [1, 3, 5]       // Set same labels on all tasks
})

// Bulk delete multiple tasks (max 100)
vikunja_tasks.bulk-delete({
  taskIds: [123, 124, 125]
})

// Batch import tasks from CSV or JSON
vikunja_batch_import({
  projectId: 1,
  format: "json",
  data: JSON.stringify([
    {
      title: "Task 1",
      description: "First imported task",
      priority: 3,
      dueDate: "2024-12-31T23:59:59Z"
    },
    {
      title: "Task 2",
      labels: ["bug", "urgent"],  // Will look up label IDs by name
      assignees: ["john.doe"]      // Will look up user IDs by username
    }
  ])
})

// Import from CSV with headers
vikunja_batch_import({
  projectId: 1,
  format: "csv",
  data: `title,description,priority,dueDate,labels,assignees
"Task 1","Description with, comma",3,2024-12-31T23:59:59Z,"bug;feature","john.doe"
"Task 2","Another task",5,,"urgent","john.doe;jane.smith"`
})

// Dry run to validate without creating tasks
vikunja_batch_import({
  projectId: 1,
  format: "json",
  data: JSON.stringify([...]),
  dryRun: true  // Only validates, doesn't create tasks
})

// Continue on errors instead of stopping
vikunja_batch_import({
  projectId: 1,
  format: "csv",
  data: csvData,
  skipErrors: true  // Skip invalid tasks and continue with valid ones
})
数据导出示例
// Export a project with all its data
vikunja_export_project({
  projectId: 1,
  includeChildren: false  // Only export the specified project
})

// Export a project including all child projects
vikunja_export_project({
  projectId: 1,
  includeChildren: true  // Recursively export child projects
})

// The export returns JSON data with the following structure:
// {
//   project: { ... },        // Project details
//   tasks: [ ... ],          // All tasks in the project
//   labels: [ ... ],         // All labels used in tasks
//   child_projects: [ ... ], // Nested child project exports (if includeChildren: true)
//   exported_at: "...",      // ISO timestamp of export
//   version: "1.0.0"         // Export format version
// }

// Request a full user data export (sent via email)
vikunja_request_user_export({
  password: "your-password"  // Required for security
})

// Download a previously requested user data export
vikunja_download_user_export({
  password: "your-password"  // Required for security
})
项目管理示例
// List all projects
vikunja_projects.list()

// List projects with search and pagination
vikunja_projects.list({
  search: "frontend",
  page: 1,
  perPage: 10,
  isArchived: false
})

// Get a specific project
vikunja_projects.get({ id: 1 })

// Create a new project
vikunja_projects.create({
  title: "New Frontend Project",
  description: "React-based web application",
  hexColor: "#4287f5"
})

// Update a project
vikunja_projects.update({
  id: 1,
  title: "Updated Project Name",
  isArchived: true
})

// Archive a project
vikunja_projects.archive({ id: 1 })

// Unarchive a project
vikunja_projects.unarchive({ id: 1 })

// Delete a project
vikunja_projects.delete({ id: 1 })

// --- Project Hierarchy Management ---

// Create a child project
vikunja_projects.create({
  title: "Frontend Module",
  description: "React components",
  parentProjectId: 1,  // Will be a child of project 1
  hexColor: "#3498db"
})

// Get all direct children of a project
vikunja_projects.get-children({ id: 1 })
// Returns: Array of projects that have parentProjectId = 1

// Get complete project hierarchy as a tree
vikunja_projects.get-tree({ id: 1 })
// Returns: Project with nested children structure
// {
//   id: 1,
//   title: "Main Project",
//   children: [
//     {
//       id: 2,
//       title: "Frontend Module",
//       children: [
//         { id: 4, title: "Components", children: [] },
//         { id: 5, title: "Styles", children: [] }
//       ]
//     },
//     {
//       id: 3,
//       title: "Backend Module",
//       children: []
//     }
//   ]
// }

// Get breadcrumb path from root to a project
vikunja_projects.get-breadcrumb({ id: 5 })
// Returns: Array of projects from root to target
// [
//   { id: 1, title: "Main Project" },
//   { id: 2, title: "Frontend Module" },
//   { id: 5, title: "Styles" }
// ]
// Also includes a formatted path: "Main Project > Frontend Module > Styles"

// Move a project to a new parent
vikunja_projects.move({ 
  id: 5,              // Project to move
  parentProjectId: 3  // New parent
})
// Validates against circular references and depth limits

// Move a project to root level (no parent)
vikunja_projects.move({ 
  id: 5,
  parentProjectId: undefined
})

// --- Project Sharing ---

// Create a read-only share link
vikunja_projects.create-share({ 
  id: 1,
  right: 0,  // 0=Read, 1=Write, 2=Admin
  label: "Public read-only access"
})

// Create a password-protected share with write access
vikunja_projects.create-share({ 
  id: 1,
  right: 1,
  password: "securepassword123",
  label: "Team collaboration link"
})

// Create an expiring share link
vikunja_projects.create-share({ 
  id: 1,
  right: 0,
  expires: "2025-12-31T23:59:59Z",
  label: "Temporary access until year end"
})

// List all shares for a project
vikunja_projects.list-shares({ id: 1 })

// Get details of a specific share
vikunja_projects.get-share({ 
  id: 1, 
  shareId: 123 
})

// Delete a share link
vikunja_projects.delete-share({ 
  id: 1, 
  shareId: 123 
})

// Authenticate to access a shared project
vikunja_projects.auth-share({ 
  shareHash: "abc123def456" 
})

// Authenticate to a password-protected share
vikunja_projects.auth-share({ 
  shareHash: "abc123def456",
  password: "securepassword123"
})
标签管理示例
// List all labels
vikunja_labels.list()

// Search for labels
vikunja_labels.list({
  search: "bug",
  page: 1,
  perPage: 20
})

// Get a specific label
vikunja_labels.get({ id: 1 })

// Create a new label
vikunja_labels.create({
  title: "Critical",
  description: "Critical priority issues",
  hexColor: "#ff0000"
})

// Update a label
vikunja_labels.update({
  id: 1,
  title: "High Priority",
  hexColor: "#ff6600"
})

// Delete a label
vikunja_labels.delete({ id: 1 })

// --- Label Assignment to Tasks ---

// Apply multiple labels to a task
vikunja_tasks.apply-label({
  id: 123,
  labels: [1, 2, 3]  // Apply labels with IDs 1, 2, and 3
})

// Apply a single label
vikunja_tasks.apply-label({
  id: 123,
  labels: [1]  // Apply just the "research" label
})

// Remove specific labels from a task
vikunja_tasks.remove-label({
  id: 123,
  labels: [2, 3]  // Remove labels 2 and 3, keep others
})

// List all labels on a task
vikunja_tasks.list-labels({ id: 123 })
// Returns: Task info with detailed label data including colors and descriptions
团队管理示例
// List all teams
vikunja_teams.list()

// Search for teams
vikunja_teams.list({
  search: "frontend",
  page: 1,
  perPage: 10
})

// Create a new team
vikunja_teams.create({
  name: "Frontend Team",
  description: "Responsible for UI/UX development"
})

// Delete a team
vikunja_teams.delete({ id: 1 })

// Note: get, update, and members operations are not yet
// implemented in the node-vikunja library
用户管理示例

⚠️ 已知问题:即使使用有效令牌,用户端点也可能因身份验证错误而失败。这是 Vikunja API 的已知问题。发生这种情况时,MCP 服务会提供有帮助的错误消息。如果遇到此问题,请联系你的 Vikunja 服务管理员。

// Get current user information
vikunja_users.current()

// Search for users
vikunja_users.search({
  search: "john"
})

// Get current user settings
vikunja_users.settings()

// Update user settings
vikunja_users.update-settings({
  name: "John Doe",
  language: "en",
  timezone: "America/New_York",
  weekStart: 1  // Monday
})

// Update notification preferences
vikunja_users.update-settings({
  emailRemindersEnabled: true,  // Enable email reminders for tasks
  overdueTasksRemindersEnabled: true,  // Enable daily overdue task emails
  overdueTasksRemindersTime: "09:00"  // Time to send overdue task summary
})
Webhook 管理示例
// List all available webhook events
vikunja_webhooks.list-events()
// Returns: ["task.created", "task.updated", "task.deleted", "task.assigned", ...]

// List webhooks for a project
vikunja_webhooks.list({ projectId: 1 })

// Create a webhook for task events
vikunja_webhooks.create({
  projectId: 1,
  targetUrl: "https://example.com/webhook",
  events: ["task.created", "task.updated"],
  secret: "my-secret-key"  // Optional, for HMAC signing
})

// Create a webhook without secret
vikunja_webhooks.create({
  projectId: 1,
  targetUrl: "https://example.com/notifications",
  events: ["task.assigned", "task.comment.created"]
})

// Get a specific webhook
vikunja_webhooks.get({
  projectId: 1,
  webhookId: 123
})

// Update webhook events
vikunja_webhooks.update({
  projectId: 1,
  webhookId: 123,
  events: ["task.created", "task.updated", "task.deleted"]
})

// Delete a webhook
vikunja_webhooks.delete({
  projectId: 1,
  webhookId: 123
})
高级筛选示例
// Create a saved filter for high priority tasks
vikunja_filters.create({
  name: "High Priority Tasks",
  description: "All undone tasks with priority 4 or 5",
  filter: "done = false && priority >= 4",
  isGlobal: true
})

// Alternative format using title and filters object
vikunja_filters.create({
  title: "🔥 High Priority Tasks",
  description: "All tasks with priority 4 or 5 that are not completed",
  filters: {
    filter_by: ["priority"],
    filter_value: ["5"],
    filter_comparator: [">="],
    filter_concat: ""
  },
  is_favorite: true
})

// Create a filter with multiple conditions
vikunja_filters.create({
  title: "Urgent & Incomplete",
  filters: {
    filter_by: ["priority", "done"],
    filter_value: ["3", "false"],
    filter_comparator: [">=", "="],
    filter_concat: "&&"
  }
})

// Create a project-specific filter
vikunja_filters.create({
  name: "This Week's Tasks",
  filter: "dueDate >= now && dueDate < now+7d",
  projectId: 1,
  isGlobal: false
})

// List all saved filters
vikunja_filters.list()

// List filters for a specific project
vikunja_filters.list({ projectId: 1 })

// Apply a saved filter to task listing
vikunja_tasks.list({
  filterId: "550e8400-e29b-41d4-a716-446655440000"
})

// Build a filter programmatically
vikunja_filters.build({
  conditions: [
    { field: "done", operator: "=", value: false },
    { field: "priority", operator: ">=", value: 3 },
    { field: "assignees", operator: "in", value: ["user1", "user2"] }
  ],
  groupOperator: "&&"
})
// Returns: { filter: "(done = false && priority >= 3 && assignees in user1, user2)", valid: true }

// Update an existing filter
vikunja_filters.update({
  id: "550e8400-e29b-41d4-a716-446655440000",
  name: "Critical Tasks",
  filter: "done = false && priority = 5"
})

// Validate a filter string
vikunja_filters.validate({
  filter: "done = false && priority >= 3"
})
// Returns: { valid: true, errors: [] }
筛选语法参考

Vikunja 筛选语法支持类似 SQL 的查询,包含以下内容:

字段:

  • done - 任务完成状态(布尔值)
  • priority - 任务优先级(1-5)
  • percentDone - 完成百分比(0-100)
  • dueDate - 任务截止日期
  • assignees - 任务受分配人
  • labels - 关联的标签
  • created - 任务创建时间
  • updated - 任务更新时间

运算符:

  • 比较:=, !=, >, >=, <, <=
  • 模式匹配:like(使用 % 通配符)
  • 列表匹配:in, not in

日期计算:

  • now - 当前时间
  • now+24h - 24 小时后
  • now-7d - 7 天前
  • now/d - 当天开始时间
  • 支持:s(秒)、m(分钟)、h(小时)、d(天)、w(周)、M(月)、y(年)

示例:

  • priority = 4
  • dueDate < now
  • done = false && priority >= 3
  • assignees in user1, user2
  • dueDate >= now && dueDate < now+7d
  • title like "%urgent%"

智能混合筛选: 此 MCP 服务采用智能混合筛选方式,结合服务端和客户端筛选,以实现最佳性能和可靠性:

  • 首选:优先尝试服务端筛选,以获得最高性能
  • 回退:如果服务端筛选失败或不可用,则回退到客户端筛选
  • 透明:无论使用哪种方式,筛选语法都相同
  • 优化:通过分页限制提供内存保护,避免无界加载
  • 元数据:响应包含所用的筛选方式(serverSideFiltering 或 clientSideFiltering)
  • 性能:服务端筛选可显著减少网络流量和处理时间

响应格式

Vikunja MCP 服务中的所有操作均遵循标准化的响应格式,以确保一致性和可预测性:

interface StandardResponse {
  success: boolean;
  operation: string;      // The operation performed (e.g., 'create', 'update', 'list')
  message?: string;       // Human-readable description of the result
  data?: any;            // The primary data returned (task, project, label, etc.)
  metadata?: {
    timestamp: string;    // ISO 8601 timestamp of the operation
    [key: string]: any;  // Additional operation-specific metadata
  };
}
响应示例

成功:

{
  "success": true,
  "operation": "create",
  "message": "Task created successfully",
  "data": { "id": 123, "title": "Complete documentation" },
  "metadata": { "timestamp": "2025-05-25T12:00:00Z" }
}

错误:

{
  "success": false,
  "operation": "update",
  "message": "Task not found",
  "error": { "code": "TASK_NOT_FOUND", "details": "No task exists with ID 999" }
}

此标准化格式可确保:

  • 一致性:所有工具均以相同结构返回响应
  • 可预测性:客户端始终清楚会有哪些字段
  • 调试:元数据提供故障排查所需的上下文
  • 错误处理:提供包含代码和消息的清晰错误信息

可用工具

身份验证
  • vikunja_auth - 身份验证管理
    • connect - 使用 API 令牌初始化连接
    • status - 检查身份验证状态
    • refresh - 刷新身份验证令牌
任务管理 ✅
  • vikunja_tasks - 任务操作(已完整实现)
    • list - 按筛选条件列出任务
      • 按项目筛选或获取所有任务
      • 支持分页、搜索和排序
      • 按完成状态筛选
      • 使用 filterId 参数应用已保存的筛选条件
    • create - 创建新任务
      • 必填:title、projectId
      • 可选:description、dueDate、priority、labels、assignees
      • 验证日期格式(ISO 8601)和 ID
    • get - 按 ID 获取任务详情
    • update - 更新现有任务
      • 支持部分更新
      • 可更新 title、description、dueDate、priority 和 done 状态
      • 可更新标签和受分配人(使用高效的差异比对方式)
    • delete - 按 ID 删除任务
    • assign - 批量将用户分配给任务
    • unassign - 从任务中移除用户
    • comment - 列出或添加任务评论
    • bulk-update - 一次更新多个任务
      • 必填:taskIds 数组、字段名称、值
      • 支持的字段:done、priority、due_date、project_id、assignees、labels
      • 验证字段类型和值
      • ⚠️ 性能:会调用 API 获取每个已更新的任务
    • bulk-delete - 一次删除多个任务
      • 必填:taskIds 数组
      • 返回已删除任务的详情以供确认
      • 妥善处理部分失败
      • ⚠️ 性能:会为每个任务单独调用删除接口
      • 建议:每批处理 20 个或更少的任务
    • attach - 尚未实现(MCP 中不提供文件处理功能)
批量导入 ✅
  • vikunja_batch_import - 从 CSV 或 JSON 导入多个任务(已完整实现)
    • 必填:projectId、format('csv' 或 'json')、data
    • 可选:skipErrors(出错时继续)、dryRun(仅验证)
    • 批量大小限制:每次导入最多 100 个任务
    • CSV 格式:
      • 需要包含字段名称的标题行
      • 支持带引号的值和转义引号
      • 字段:title、description、priority、dueDate、labels、assignees
      • labels 和 assignees 使用分号分隔值(使用分号以避免与 CSV 逗号冲突)
    • JSON 格式:
      • 任务对象数组
      • 字段与 CSV 相同,并直接支持数组
    • 功能:
      • 按名称自动查找标签
      • 按用户名自动查找用户
      • 创建前进行验证
      • 详细的错误报告
      • 用于测试的试运行模式
      • 支持跳过错误,以便部分导入
项目管理 ✅
  • vikunja_projects - 项目操作(已完整实现)
    • list - 列出所有项目并支持筛选
      • 支持分页和搜索
      • 按归档状态筛选
    • get - 按 ID 获取项目详情
    • create - 创建新项目
      • 必填:title
      • 可选:description、parentProjectId、isArchived、hexColor(格式:#RRGGBB)
      • 验证父项目层级深度(最多 10 层)
    • update - 更新现有项目
      • 支持部分更新
      • 可更新所有项目字段,包括 hexColor(格式:#RRGGBB)
      • 更改父项目时验证父项目层级深度
    • delete - 按 ID 删除项目
    • archive - 归档项目
    • unarchive - 取消归档项目
    • 层级管理(新增!)
      • get-children - 列出项目的直接子项目
      • get-tree - 以树形结构获取完整的项目层级
      • get-breadcrumb - 获取从根项目到指定项目的路径
      • move - 将项目移至新的父项目下
        • 验证是否存在循环引用
        • 强制执行最多 10 层的深度限制
    • 项目共享
      • create-share - 创建具有相应权限的共享链接
      • list-shares - 列出项目的所有共享
      • get-share - 获取共享详情
      • delete-share - 移除共享链接
      • auth-share - 验证身份以访问共享项目
标签管理 ✅
  • vikunja_labels - 标签操作(已完整实现)
    • list - 列出所有标签并支持筛选
      • 支持分页和搜索
    • get - 按 ID 获取标签详情
    • create - 创建新标签
      • 必填:title
      • 可选:description、hexColor(格式:#RRGGBB)
    • update - 更新现有标签
      • 支持部分更新
      • 可更新 title、description、hexColor
    • delete - 按 ID 删除标签
    • apply-label - 将一个或多个标签应用到任务
      • 必填:task id、labels 数组
      • 支持批量应用标签
    • remove-label - 从任务中移除一个或多个标签
      • 必填:task id、labels 数组
      • 支持批量移除标签
    • list-labels - 列出分配给任务的所有标签
      • 必填:task id
      • 返回详细的标签信息
项目模板 ✅
  • vikunja_templates - 模板操作(已完整实现)
    • create - 根据现有项目创建模板
      • 必填:projectId、name
      • 可选:description、tags
      • 捕获所有项目设置和任务
    • list - 列出所有可用模板
      • 显示模板名称、标签和作者
    • get - 根据 ID 获取模板详情
    • update - 更新模板元数据
      • 可更新 name、description、tags
    • delete - 删除模板
    • instantiate - 根据模板创建新项目
      • 必填:id(模板 ID)、projectName
      • 可选:parentProjectId、variables
      • 支持变量替换:
        • {{PROJECT_NAME}} - 新项目名称
        • {{TODAY}} - 当前日期(YYYY-MM-DD)
        • {{NOW}} - 当前时间戳
        • 通过 variables 参数传入自定义变量
      • 使用模板中的标签创建所有任务
团队管理 ✅
  • vikunja_teams - 团队操作(部分实现)
    • list - 使用筛选条件列出所有团队
      • 支持分页和搜索
    • create - 创建新团队
      • 必填:name
      • 可选:description
    • delete - 根据 ID 删除团队(支持备用 API)
    • get - 尚未在 node-vikunja 中实现
    • update - 尚未在 node-vikunja 中实现
    • members - 尚未在 node-vikunja 中实现
用户管理 ✅
  • vikunja_users - 用户操作(已完整实现)[需要 JWT 身份验证]
    • current - 获取当前已通过身份验证的用户信息
    • search - 搜索用户
      • 可选:搜索查询、分页
    • settings - 获取当前用户设置
    • update-settings - 更新用户设置
      • 可选:name、language、timezone、weekStart、frontendSettings
    • 注意: 用户操作需要 JWT 身份验证。使用 API token 身份验证时,无法使用这些工具。
Webhook 管理 ✅
  • vikunja_webhooks - 用于项目自动化的 Webhook 操作(已完整实现)

    • list-events - 获取所有可用的 Webhook 事件类型
    • list - 列出项目的 Webhook
      • 必填:projectId
    • get - 获取指定的 Webhook
      • 必填:projectId、webhookId
    • create - 创建新的 Webhook
      • 必填:projectId、targetUrl、events(数组)
      • 可选:secret(用于 HMAC 签名)
      • 注意:会根据可用事件类型验证事件
    • update - 更新 Webhook 事件
      • 必填:projectId、webhookId、events(数组)
      • 注意:会根据可用事件类型验证事件
    • delete - 删除 Webhook
      • 必填:projectId、webhookId

    事件验证:创建或更新 Webhook 时,系统会自动根据 API 提供的可用事件列表验证所提供的事件。无效事件会导致清晰的错误消息,其中会指出哪些事件无效,并列出所有有效选项。有效事件会缓存 5 分钟,以提升性能。

筛选器管理 ✅
  • vikunja_filters - 任务高级筛选(已完整实现)

    • list - 列出已保存的筛选器
      • 可选:projectId(用于项目专属筛选器)、global 标记
    • get - 根据 ID 获取指定的筛选器
    • create - 创建新的已保存筛选器
      • 必填:name、filter(查询字符串)
      • 可选:description、projectId、isGlobal
    • update - 更新现有筛选器
      • 必填:id
      • 可选:name、description、filter、projectId、isGlobal
    • delete - 删除已保存的筛选器
    • build - 根据条件构建筛选器字符串
      • 必填:conditions 数组
      • 可选:groupOperator(&&、||)
    • validate - 验证筛选器字符串

    注意: 已保存的筛选器目前存储在内存中,MCP 服务重启时会丢失。用于生产环境时,请考虑实现持久化存储。

数据导出 ✅

⚠️ 警告:内存使用
导出操作会将整个项目层级结构加载到内存中。对于包含数千个任务或嵌套结构很深的大型项目,这可能会占用大量内存。建议逐个导出较小的项目。

  • vikunja_export_project - 导出项目数据 [需要 JWT 身份验证]

    • 参数:
      • projectId(必需)- 要导出的项目 ID
      • includeChildren(可选)- 递归包含子项目(默认值:false)
    • **返回:**完整的项目数据,包括任务、标签和元数据
    • 功能:
      • 导出所有任务及其完整详细信息
      • 包含项目中使用的所有标签
      • 可选包含完整的子项目层级结构
      • 检测嵌套项目中的循环引用
      • 导出元数据包含时间戳和版本
    • **注意:**导出操作需要 JWT 身份验证。使用 API 令牌身份验证时,此工具不可用。
  • vikunja_request_user_export - 请求导出完整的用户数据

    • 参数:
      • password(必需)- 用于安全验证的用户密码
    • **返回:**已请求导出的确认信息
    • **注意:**导出准备就绪后,您会收到一封电子邮件
  • vikunja_download_user_export - 下载之前请求的用户数据导出文件

    • 参数:
      • password(必需)- 用于安全验证的用户密码
    • **返回:**完整的用户数据导出文件
    • **注意:**必须先通过 vikunja_request_user_export 请求导出

已知限制

  1. 文件附件:由于 MCP 协议限制,尚未实现 attach 子命令
  2. 团队操作:由于 node-vikunja API 支持不完整,功能有限:
    • 无法按 ID 获取团队
    • 无法更新团队信息
    • 无法删除团队
    • 无法管理团队成员
  3. 分页:由于 API 限制,某些端点可能无法完全支持分页参数
  4. 身份验证问题:部分 Vikunja API 端点存在已知的身份验证问题:
    • 用户端点:即使令牌有效,也可能因令牌错误而失败(Vikunja API 的已知限制)
    • 批量操作:某些 Vikunja API 版本可能存在身份验证问题
    • 标签操作:某些服务器配置可能导致身份验证错误
    • 受派人操作:创建或更新包含受派人的任务时,可能出现身份验证错误
    • 出现这些问题时,服务会提供详细的错误消息,并建议解决方法

安全与性能功能

安全增强
  • Zod 模式验证:采用全面的类型检查,提供企业级输入验证
  • DoS 防护:输入清理、长度限制和字符允许列表
  • 凭据保护:自动屏蔽日志和错误消息中的敏感令牌与 URL
  • 实体解析服务:针对格式错误的 API 响应,提供稳健的标签和用户映射及防御性错误处理
  • 速率限制:可配置请求速率限制和负载大小限制,以防止 DoS 攻击
  • 内存保护:分页限制和内存使用监控,防止资源耗尽
  • 错误处理:采用结构化错误响应,避免泄露敏感系统信息
性能优化
  • 混合筛选:智能服务端筛选,并在必要时回退到客户端筛选,以实现最佳性能
  • 连接池:通过自动缓存客户端,高效管理会话
  • 请求批处理:通过基于差异的高效更新,优化批量操作
  • 内存管理:通过自动清理和分页,安全处理大型数据集
  • 线程安全的客户端管理:仅支持异步的 ClientContext API,可消除并发场景中的竞态条件
  • Opossum 熔断器:生产就绪的重试逻辑,支持自动检测故障并恢复
  • 简化存储:使用内存中的筛选器存储,复杂度和开销降低 90%

配置

环境变量

此服务支持通过环境变量配置多种选项:

基本配置
# Vikunja instance URL (required)
VIKUNJA_URL=https://your-vikunja-instance.com/api/v1

# Authentication token (required)
VIKUNJA_API_TOKEN=your-api-token

# Enable debug logging (default: false)
DEBUG=true

# Set log level (error, warn, info, debug)
LOG_LEVEL=debug
安全与性能配置
# Rate limiting (default: enabled)
RATE_LIMIT_ENABLED=true
RATE_LIMIT_PER_MINUTE=60        # Requests per minute (default: 60)
RATE_LIMIT_PER_HOUR=1000        # Requests per hour (default: 1000)

# Request size limits (default: 1MB)
MAX_REQUEST_SIZE=1048576        # Maximum request payload size in bytes
MAX_RESPONSE_SIZE=10485760      # Maximum response size in bytes (default: 10MB)

# Execution timeout (default: 30 seconds)
EXECUTION_TIMEOUT=30000         # Tool execution timeout in milliseconds

# Memory protection (default: enabled)
MEMORY_PROTECTION_ENABLED=true
MAX_TASKS_PER_REQUEST=1000      # Maximum tasks to load per request

# Circuit breaker configuration (opossum)
CIRCUIT_BREAKER_ENABLED=true    # Enable circuit breaker for API calls
CIRCUIT_BREAKER_TIMEOUT=60000   # Circuit breaker timeout in milliseconds (default: 60s)
CIRCUIT_BREAKER_ERRORS_THROTTLE=10 # Errors before opening circuit (default: 10)
CIRCUIT_BREAKER_RESET_TIMEOUT=30000 # Time to wait before trying half-open state (default: 30s)

# Filter security (Zod validation)
FILTER_MAX_LENGTH=1000          # Maximum filter string length (default: 1000)
FILTER_MAX_VALUE_LENGTH=200     # Maximum individual value length (default: 200)

详细的速率限制配置请参阅 docs/RATE_LIMITING.md。

路线图

  • ✅ 安全强化 - 已实现全面的漏洞修复
  • ✅ 性能优化 - 混合筛选和内存保护
  • ✅ 错误处理 - 集中式错误工具和结构化响应
  • ✅ 测试覆盖率 - 函数覆盖率达到 98.91%
  • ✅ 架构简化 - 代码量减少 90%,可维护性增强
  • ✅ 生产就绪的弹性 - Opossum 熔断器和 Zod 验证
  • 添加 Webhook 订阅以支持实时更新
  • 为频繁访问的数据添加缓存
  • 添加与真实 Vikunja 实例的集成测试
  • 为已保存的筛选器实现持久化存储(可选 - 内存存储效果良好)

贡献

开发指南和工作流请参阅 CONTRIBUTING.md。

许可证

MIT

评论

支持 Markdown

第一次接触 MCP?

从协议是什么,到在客户端里装上第一个服务,这几条回答覆盖了大多数入门问题。

一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。