A side-by-side look at the Kanboard MCP and Feishu User Plugin MCP servers: transport, auth, tools, quality signals, and the config to install either one in Claude, Cursor, Windsurf or VS Code.
Verdict: choose Kanboard MCP for workplace & productivity work as a local process; choose Feishu User Plugin for workplace & productivity as a local process. You can also run both side by side in one mcpServers block.
KANBOARD_URL, KANBOARD_USERNAME, KANBOARD_API_TOKEN.add_project_user, attach_file_to_task, close_task.LARK_COOKIE, LARK_APP_ID, LARK_APP_SECRET, LARK_USER_ACCESS_TOKEN, LARK_USER_REFRESH_TOKEN.manage_bitable_app, manage_bitable_table, manage_bitable_field.Rows where the two servers differ are marked.
| Spec | ||
|---|---|---|
| Category | Workplace & Productivity | Workplace & Productivity |
| Quality signal | 61/100 · Good | 63/100 · Good |
| Install command | Confirmed | Confirmed |
| Transport | Local subprocess (stdio) | Local subprocess (stdio) |
| Auth | API key required | Not declared |
| Pricing | Free | Not declared |
| Required env vars | KANBOARD_URLKANBOARD_USERNAMEKANBOARD_API_TOKEN | LARK_COOKIELARK_APP_IDLARK_APP_SECRETLARK_USER_ACCESS_TOKENLARK_USER_REFRESH_TOKEN |
| Verified owner | Community listing | Community listing |
| GitHub stars | 9 | 10 |
| Directory activity | 4 views 0 installs 0 upvotes | 1 views 0 installs 0 upvotes |
| Tools | 39 | 85 |
| Full listing | View Kanboard MCP | View Feishu User Plugin |
add_project_userAdd a user to a Kanboard project with the given role. Role defaults to 'project-member' if not specified. Use list_project_users to find user ids and list_projects to find project ids; to unlink a member use remove_project_user. Returns { user_id, project_id, role } on success.attach_file_to_taskUpload a file to a Kanboard task as an attachment. Provide either file_path (local file) or content_base64 (inline base64 content) — not both. project_id is resolved automatically from the task (no need to provide it). Maximum file size: 5 MB (5,242,880 bytes) — larger files return VALIDATION_ERROR before any HTTP request is made. Returns { file_id } on success.close_taskClose (archive) an active Kanboard task. The task is set inactive and leaves the active board but is preserved — this is NOT a delete. Reversible: restore it with reopen_task. To permanently remove a task instead, use delete_task. Returns { ok: true, task_id } on success.create_columnAdd a column to a Kanboard project board. Project resolved from explicit project_id/project_identifier or .kanboard.yaml. To reorder it use move_column, to rename or change its WIP limit use update_column, to remove it use delete_column. Returns { column_id } on success.create_commentCreate a comment on a Kanboard task. The comment author is automatically set to the authenticated user (via getMe() cache); do NOT pass user_id — it is injected server-side. To edit a comment's body use update_comment; to remove one use delete_comment. Returns { comment_id } on success.create_projectCreate a new Kanboard project. Requires a name (1–255 chars). Optionally provide a description, short identifier, owner user id, start_date / end_date (ISO 8601 string or epoch seconds), and email. After creating, adjust the board with create_column / create_swimlane, add members with add_project_user, and edit attributes with update_project. Returns { project_id } on success.create_subtaskCreate a subtask under an existing Kanboard task. Status: 0 = todo (default), 1 = in progress, 2 = done. To list a task's subtasks use list_subtasks; to edit one use update_subtask. Returns { subtask_id, task_id } on success.create_swimlaneAdd a swimlane to a Kanboard project. Project resolved from explicit project_id/project_identifier or .kanboard.yaml. To reorder it use move_swimlane, to rename it use update_swimlane, to remove it use delete_swimlane. Returns { swimlane_id } on success.create_taskCreate a new task in a Kanboard project. Project is resolved from explicit project_id or project_identifier, or from .kanboard.yaml. Optional fields (column_id, owner_id, category_id, swimlane_id) fall back to .kanboard.yaml defaults when not provided. To create many tasks at once use create_tasks_batch; to move the task afterward use move_task_position. Returns { task_id, project_id } on success.create_tasks_batchBulk-create tasks in a Kanboard project using a single JSON-RPC batch request. Accepts 1–100 tasks per call. Non-atomic: partial failure is possible — check failed[] for per-task errors. Optional fields (column_id, owner_id, category_id, swimlane_id) fall back to .kanboard.yaml defaults when not provided. Returns { created: [...], failed: [...] } — never throws on partial failure.delete_columnPermanently delete a Kanboard column from a project board. DESTRUCTIVE and irreversible — requires explicit `confirm: true`. To rename a column or change its WIP limit use update_column; to reorder it use move_column. Returns { ok: true, column_id } on success.delete_commentPermanently delete a Kanboard comment. DESTRUCTIVE and irreversible — requires explicit `confirm: true`. To edit a comment's text instead of deleting it use update_comment. Returns { ok: true, comment_id } on success.manage_bitable_app[Official API] Manage a Bitable app. action=create (new app, optional wiki_space_id to attach), copy (duplicate an existing app), get_meta (read app metadata).manage_bitable_table[Official API] Manage a table inside a Bitable app. action=list, create (with optional initial fields), update (rename), delete.manage_bitable_field[Official API] Manage fields (columns) inside a Bitable table. action=list, create, update (Feishu requires `type` even when only renaming), delete.manage_bitable_view[Official API] Manage views inside a Bitable table. action=list, create, delete. (Feishu open API does not expose view update — recreate with a new name to change.)manage_bitable_record[Official API] Manage records (rows) inside a Bitable table. action=search, get, create, update, delete. create/update/delete accept arrays — single record or up to 500.list_calendars[Official API + UAT] List the current user's calendars (primary + shared + subscribed). Requires UAT — app identity only sees calendars it was explicitly invited to. Requires `calendar:calendar:readonly` scope on the OAuth.list_calendar_events[Official API + UAT] List events in a calendar within an optional time range. Typical usage: first list_calendars to find calendar_id (primary calendar has type="primary"), then list events in e.g. [now, now+7d] (Unix seconds).get_calendar_event[Official API + UAT] Get full details of a single calendar event (summary, description, start/end, attendees, location, attachments, meeting link).create_calendar_event[Official API + UAT, v1.3.7] Create a new calendar event. Requires `calendar:calendar.event:create` scope (re-run `npx feishu-user-plugin oauth` after enabling). The current identity (UAT-first) must have writer or owner permission on the calendar.
Time fields: A time object: {timestamp:"<unix-seconds>", timezone?:"Asia/Shanghai"} OR {date:"YYYY-MM-DD"} for all-day events.update_calendar_event[Official API + UAT, v1.3.7] Patch fields on an existing calendar event. Pass only the fields you want to change. Requires `calendar:calendar.event:update` scope.delete_calendar_event[Official API + UAT, v1.3.7] Delete a calendar event. Requires `calendar:calendar.event:delete` scope.respond_calendar_event[Official API + UAT, v1.3.7] Respond to an event invitation. The current identity must be in the event's attendee list. Requires `calendar:calendar.event:reply` scope.Paste either block (or both) into your client config, e.g. claude_desktop_config.json or ~/.cursor/mcp.json. We only show a config when the install command is confirmed.
{
"mcpServers": {
"ernestocorona-kanboard-mcp": {
"command": "npx",
"args": [
"-y",
"@ernestocorona/kanboard-mcp"
],
"env": {
"KANBOARD_URL": "YOUR_KANBOARD_URL_HERE",
"KANBOARD_USERNAME": "YOUR_KANBOARD_USERNAME_HERE",
"KANBOARD_API_TOKEN": "YOUR_KANBOARD_API_TOKEN_HERE"
}
}
}
}
{
"mcpServers": {
"ethanqc-feishu-user-plugin": {
"command": "npx",
"args": [
"-y",
"feishu-user-plugin"
],
"env": {
"LARK_COOKIE": "YOUR_LARK_COOKIE_HERE",
"LARK_APP_ID": "YOUR_LARK_APP_ID_HERE",
"LARK_APP_SECRET": "YOUR_LARK_APP_SECRET_HERE",
"LARK_USER_ACCESS_TOKEN": "YOUR_LARK_USER_ACCESS_TOKEN_HERE",
"LARK_USER_REFRESH_TOKEN": "YOUR_LARK_USER_REFRESH_TOKEN_HERE"
}
}
}
}