mcpField
T
taiga-mcp-server

taiga-mcp-server

本地服务
github.com生产力01 天前更新

关于 taiga-mcp-server

taiga-mcp-server 让 AI Agent 读取和管理 Taiga 项目工作,包括用户故事、任务、问题、分配和迭代。它按项目内的名称解析状态、成员和迭代,并提供基于时间线的活动视图。

主要功能

  1. 01读取已分配工作和项目数据
  2. 02创建和更新用户故事、任务、问题及史诗
  3. 03管理分配、估算、评论和迭代
  4. 04按名称解析目录值
  5. 05汇总指定时间段的项目活动

适用场景

  1. 01查看分配给团队成员的事项
  2. 02变更工作事项的项目状态
  3. 03创建并分配 Taiga 工作事项
  4. 04查看项目上周的变更

MCP 相关

此仓库包含 taiga-api-mcp-server,可将 Taiga 项目数据和管理操作作为工具提供给 MCP 客户端。它使用 Taiga API 客户端,并遵循账户权限。

配置

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

mcp.json · 通用配置
{
"mcpServers": {
"xhisusx-taiga": {
"command": "npx",
"args": [
"-y",
"taiga-api-mcp-server"
],
"env": {
"TAIGA_URL": "<TAIGA_URL>",
"TAIGA_USERNAME": "<TAIGA_USERNAME>",
"TAIGA_PASSWORD": "<TAIGA_PASSWORD>",
"TAIGA_APP_TOKEN": "<TAIGA_APP_TOKEN>",
"TAIGA_PROJECT": "<TAIGA_PROJECT>",
"TAIGA_MCP_READ_ONLY": "<TAIGA_MCP_READ_ONLY>",
"TAIGA_MCP_ALLOW_DELETE": "<TAIGA_MCP_ALLOW_DELETE>"
}
}
}
}
启动命令npx -y taiga-api-mcp-server
需要配置的环境变量
TAIGA_URLTaiga instance root, without /api/v1. Taiga Cloud: https://api.taiga.io
TAIGA_USERNAMETaiga username or email, used together with TAIGA_PASSWORD
TAIGA_PASSWORD密钥Password for TAIGA_USERNAME
TAIGA_APP_TOKEN密钥Long-lived Application token, instead of a username and password
TAIGA_PROJECTDefault project (id, slug or name) for tool calls that omit one
TAIGA_MCP_READ_ONLYSet to 1 to register the read tools only
TAIGA_MCP_ALLOW_DELETESet to 1 to also register taiga_delete_item

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

安装包
npmtaiga-api-mcp-server0.5.0

工具

还没有获取到工具列表

本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。

详细介绍

taiga-api-client

npm version npm downloads bundle size types license CI GitHub stars

🇬🇧 English version — you are here · 🇷🇺 Русская версия

Taiga REST API 的 TypeScript 客户端。零运行时依赖,同时支持 ESM + CJS,可在 Node ≥18、Bun ≥1.0 及任何提供全局 fetch 的环境中运行。

  • 🔐 Bearer 身份验证,支持自动刷新令牌并对并发刷新请求去重
  • 📦 类型完整:为每种资源、负载和参数提供类型
  • 🚫 零运行时依赖
  • 🔄 用于分页的异步迭代器
  • ⚠️ 类型化错误(TaigaApiError、TaigaAuthError、TaigaRateLimitError、TaigaNetworkError)
  • 🚀 同时构建 ESM + CJS,并提供 .d.ts / .d.cts 类型映射

安装

npm install taiga-api-client
# or
bun add taiga-api-client
# or
pnpm add taiga-api-client

快速入门

import { TaigaClient } from "taiga-api-client";

// baseUrl is the only required option.
const client = new TaigaClient({ baseUrl: "https://api.taiga.io" });

// Log in (the `username` field accepts both username and email).
await client.auth.login({ username: "[email protected]", password: "secret" });

// Fetch projects you are a member of.
const me = await client.users.me();
const projects = await client.projects.list({ member: me.id });

// Create a user story.
const story = await client.userStories.create({
  project: projects[0].id,
  subject: "Add OAuth login",
  description: "Support GitHub OAuth in addition to password login",
});

// Iterate over every issue in the project (auto-pagination).
for await (const issue of client.issues.paginate({ project: projects[0].id })) {
  console.log(`#${issue.ref} ${issue.subject}`);
}

🤖 MCP 服务:面向 AI Agent 的 Taiga

此仓库还包含 taiga-api-mcp-server——一个基于此客户端构建的模型上下文协议服务。 将 MCP 客户端(Claude Code、Claude Desktop、Cursor 等)连接到你的 Taiga 账户后,它便可以读取分配给你的事项、变更事项状态、创建用户故事、 任务、问题和史诗、分配人员、估算、评论和规划迭代——权限仅限于你自己的权限。

状态、成员和迭代按名称指定;服务会在项目自己的目录中查找对应项,因此 Agent 无须猜测数字 ID。

它还可以通过一次调用回答**“我们上周做了什么”**:taiga_activity 会遍历项目时间线,并针对时间范围内涉及的每张卡片返回实际发生的情况——状态变更、关闭(包括几天后才录入的关闭)、评论、迭代变更和附件。 Taiga 的列表端点没有日期筛选,因此这是获取某个时间段工作情况的唯一方式。 故事是否完成取决于其任务(tasks_done 和 tasks_total),是否关闭则取决于项目自己的状态——日期和关闭标记都需要手动填写,而且通常根本不会填写。 这两项都会分别按当前时间和时间范围结束时的情况回答,因为一张卡片三天后才移至 Done,就不属于上周的工作。

claude mcp add taiga   --env TAIGA_URL=https://api.taiga.io   --env [email protected]   --env TAIGA_PASSWORD=...   -- npx -y taiga-api-mcp-server

📖 设置说明、工具参考和示例:mcp/README.md

💡 查找目录 ID?使用 filters_data

大多数写入操作都需要项目特定目录值的数字 ID(状态、类型、严重程度、优先级、受派人等)。不要分别获取每个目录——每种资源都提供 filters_data 方法,可在一次请求中返回所有目录值,并限定在项目范围内。

const data = await client.issues.filtersData({ project: 42 });
const inProgress = (data.statuses as Array<{ id: number; name: string }>)
  .find((s) => s.name === "In progress");

await client.issues.create({ project: 42, subject: "...", status: inProgress!.id });

📖 阅读完整指南:docs/filters-data.md——了解缓存、各资源可用的端点,以及哪些情况下它还不够。

创建客户端

最低配置:

const client = new TaigaClient({ baseUrl: "https://api.taiga.io" });

baseUrl 是唯一必填的选项。其他选项均为可选。

完整示例:

const client = new TaigaClient({
  baseUrl: "https://api.taiga.io", // ← required

  // Authentication — pick one (or none, if you will call client.auth.login later):
  token: "...", // a previously obtained bearer token (session restore)
  refreshToken: "...", // its paired refresh token
  applicationToken: "...", // a static Application token, used instead of bearer

  // Behavior:
  autoRefresh: true, // auto-refresh the bearer token on 401 (default true)
  timeoutMs: 30000, // request timeout in ms (default 30000)
  userAgent: "my-app/1.0",
  acceptLanguage: "en",
  apiPrefix: "/api/v1", // change only for self-hosted with a non-default prefix
  fetch: customFetch, // dependency-injected fetch (proxies, mocks)

  // Token persistence hook:
  onTokenChange: async (tokens) => {
    if (tokens) await db.saveTokens(tokens);
    else await db.clearTokens();
  },
});
TaigaClientOptions
Option是否必填TypeDefaultDescription
baseUrl✅ 是string—Taiga 实例的根 URL(不包含 /api/v1)。对于 Taiga Cloud:https://api.taiga.io。
token否string | nullnullBearer 令牌。传入该令牌可恢复现有会话。
refreshToken否string | nullnull配对的刷新令牌。
applicationToken否string | nullnull静态应用令牌(与 Bearer 令牌互斥)。
autoRefresh否booleantrue收到 401 时自动刷新 Bearer 令牌。
timeoutMs否number30000每个请求的超时时间,单位为毫秒。
userAgent否string—User-Agent 请求头。
acceptLanguage否string—Accept-Language 请求头(响应字符串的国际化设置)。
apiPrefix否string/api/v1API 路径前缀。仅在非标准的可自托管配置中更改。
fetch否typeof fetchglobal自定义 fetch 实现(代理、模拟、MSW)。
onTokenChange否(tokens | null) => void | Promise—在 login 后以及自动刷新成功后触发的回调。可用它持久化令牌。

身份验证

支持三种模式:

Mode使用场景Header
Bearer + refresh标准交互式登录Authorization: Bearer X
会话恢复进程重启时——将已保存的令牌传给构造函数Authorization: Bearer X
应用令牌集成 / 机器人,长期有效的静态令牌Authorization: Application X
自动刷新令牌

启用 autoRefresh: true(默认值)后,客户端会透明地拦截 401 响应,调用 POST /auth/refresh,并仅重试一次原始请求。并发的 401 会被合并去重——即使 10 个请求同时失败,也只会调用一次 /auth/refresh。如果刷新本身失败,令牌会被清除,并抛出 TaigaAuthError。

在进程重启后保留令牌
const client = new TaigaClient({
  baseUrl: "https://api.taiga.io",
  token: await db.loadToken(),
  refreshToken: await db.loadRefreshToken(),
  onTokenChange: async (tokens) => {
    if (tokens) await db.saveTokens(tokens);
    else await db.clearTokens();
  },
});

以下情况会调用 onTokenChange:

  • client.auth.login(...) 后——传入新的令牌对;
  • 自动刷新成功后——传入新的令牌对;
  • client.auth.logout() 后——传入 null。

重试请求前会等待返回的 Promise,因此可以确保持久化操作在下一个请求发出前完成。

资源和方法

每个资源都继承自 BaseResource,该类提供标准 CRUD 操作。各资源会在此基础上添加自己的方法。

BaseResource<T> — 通用 CRUD
MethodHTTPReturnsDescription
list(params?)GET /resourceT[]第一页(或在 disablePagination 时返回完整响应)。
paginate(params?)GET /resource?page=NAsyncIterable<T>延迟异步迭代器,遍历 x-pagination-next 链接。
listPage(params?, page?)GET /resource?page=N{ data, pagination }指定页面及分页元数据。
get(id)GET /resource/:idT按 ID 获取单个资源。
create(payload)POST /resourceT创建。
update(id, payload)PUT /resource/:idT完全替换。必须提供所有必填字段。
patch(id, payload, version?)PATCH /resource/:idT部分更新。version 用于启用乐观并发控制。
delete(id)DELETE /resource/:idvoid删除。
client.auth — 身份验证
MethodReturnsDescription
login({ username, password })Promise<AuthResponse>POST /auth。username 可接受用户名或电子邮件地址。
refresh()Promise<string>手动刷新。通常会在收到 401 时自动调用。
logout()Promise<void>在本地清除令牌并调用 onTokenChange(null)。
setTokens(tokens | null)Promise<void>手动设置令牌(例如,从数据库恢复后)。
getTokens()AuthTokens | null当前内存中的令牌。
getCurrentUser()AuthUser | null上次成功调用 login() 后的用户资料。
onTokenChange(handler)void在运行时替换持久化回调。
const auth = await client.auth.login({ username: "u", password: "p" });
console.log(auth.auth_token, auth.refresh, auth.full_name);
client.users — 用户

基础 CRUD 以及:

MethodHTTPDescription
me()GET /users/me当前已通过身份验证的用户。
updateMe(payload)PUT /users/:id (own id)update(me.id, …) 的便捷快捷方式。
changePassword(currentPassword, newPassword)POST /users/change_password更改密码。
stats(id)GET /users/:id/stats用户统计信息。

⚠️ users.create() 会有意抛出异常——注册使用专用端点(不在 v0.1 范围内)。

const me = await client.users.me();
await client.users.updateMe({ bio: "TypeScript fan", lang: "en" });
await client.users.changePassword("oldPwd!", "newPwd!");
client.projects — 项目

基础 CRUD,另包括:

方法HTTP描述
getBySlug(slug)GET /projects/by_slug?slug=...根据 slug 而非 id 获取项目。
stats(id)GET /projects/:id/stats项目统计信息(点数、里程碑)。
modulesConfig(id)GET /projects/:id/modules集成配置(GitHub、GitLab、Bitbucket、Gogs)。
setModulesConfig(id, config)PATCH /projects/:id/modules更新集成配置。
const proj = await client.projects.create({ name: "Demo", description: "" });
const sameProj = await client.projects.getBySlug(proj.slug);
const updated = await client.projects.patch(proj.id, { description: "Updated" }, /* version */ 1);
const stats = await client.projects.stats(proj.id);
await client.projects.delete(proj.id);
client.memberships — 项目成员关系

基础 CRUD,另包括:

方法HTTP描述
bulkCreate(payload)POST /memberships/bulk_create一次调用邀请多位用户。
resendInvitation(id)POST /memberships/:id/resend_invitation重新发送邀请邮件。
await client.memberships.bulkCreate({
  project_id: proj.id,
  bulk_memberships: [
    { role_id: 1, username: "[email protected]" },
    { role_id: 2, username: "[email protected]" },
  ],
  invitation_extra_text: "Welcome to the team",
});
client.userStories — 用户故事

基础 CRUD,另包括:

方法HTTP描述
bulkCreate(payload)POST /userstories/bulk_create从以换行符分隔的字符串创建多个故事。
bulkUpdateBacklogOrder(payload)POST /userstories/bulk_update_backlog_order重新排列待办列表中的故事。
bulkUpdateKanbanOrder(payload)POST /userstories/bulk_update_kanban_order重新排列看板上的故事。
bulkUpdateSprintOrder(payload)POST /userstories/bulk_update_sprint_order重新排列冲刺中的故事。
filtersData({ project })GET /userstories/filters_data?project=...UI 可用筛选条件:状态、标签、受派人。
const story = await client.userStories.create({
  project: proj.id,
  subject: "User can log in via GitHub",
  description: "OAuth flow",
});

// Bulk create from a multi-line string (the way the Taiga UI does it):
await client.userStories.bulkCreate({
  project_id: proj.id,
  bulk_stories: "Story 1\nStory 2\nStory 3",
});

// Iterate over every story in the project:
for await (const s of client.userStories.paginate({ project: proj.id })) {
  console.log(s.subject);
}
client.tasks — 任务

基础 CRUD,另包括:

方法HTTP描述
bulkCreate(payload)POST /tasks/bulk_create一次创建多个任务。
filtersData({ project })GET /tasks/filters_data?project=...可用筛选条件。
const task = await client.tasks.create({
  project: proj.id,
  subject: "Implement OAuth callback",
  user_story: story.id, // link to a user story
});
client.issues — 问题

基础 CRUD,以及:

方法HTTP描述
filtersData({ project })GET /issues/filters_data?project=...可用筛选条件(类型、严重程度、优先级)。
voters(id)GET /issues/:id/voters对问题投过赞的用户。
upvote(id)POST /issues/:id/upvote为问题点赞。
downvote(id)POST /issues/:id/downvote取消赞。
watchers(id)GET /issues/:id/watchers关注该问题的用户。
watch(id)POST /issues/:id/watch订阅通知。
unwatch(id)POST /issues/:id/unwatch取消订阅。
const issue = await client.issues.create({
  project: proj.id,
  subject: "Login button doesn't work in Safari",
  description: "Reproduces on macOS Safari 17.x",
});
await client.issues.upvote(issue.id);
await client.issues.watch(issue.id);
client.epics — 史诗

基础 CRUD,以及:

方法HTTP描述
bulkCreate(payload)POST /epics/bulk_create根据以换行符分隔的字符串创建多个史诗。
filtersData({ project })GET /epics/filters_data?project=...UI 的可用筛选条件。
relatedUserStories(id)GET /epics/:id/related_userstories与该史诗关联的用户故事。
addRelatedUserStory(id, { user_story })POST /epics/:id/related_userstories将现有用户故事关联到该史诗。
removeRelatedUserStory(id, usId)DELETE /epics/:id/related_userstories/:usId取消用户故事与该史诗的关联。
voters(id)GET /epics/:id/voters对该史诗投过赞的用户。
upvote(id)POST /epics/:id/upvote为史诗点赞。
downvote(id)POST /epics/:id/downvote取消赞。
watchers(id)GET /epics/:id/watchers关注该史诗的用户。
watch(id)POST /epics/:id/watch订阅通知。
unwatch(id)POST /epics/:id/unwatch取消订阅。
const epic = await client.epics.create({
  project: proj.id,
  subject: "Q1 roadmap",
  color: "#ff8800",
});
await client.epics.addRelatedUserStory(epic.id, { user_story: story.id });
for (const link of await client.epics.relatedUserStories(epic.id)) {
  console.log(link.user_story);
}

ℹ️ 项目必须启用史诗(is_epics_activated: true),这些端点才能正常工作。

client.milestones — 冲刺

基础 CRUD,以及:

方法HTTP描述
stats(id)GET /milestones/:id/stats燃尽数据和以点数表示的完成进度。
const sprint = await client.milestones.create({
  project: proj.id,
  name: "Sprint 1",
  estimated_start: "2026-01-01",
  estimated_finish: "2026-01-14",
});
const burndown = await client.milestones.stats(sprint.id);

分页

list() 始终返回响应的第一页。遍历完整结果集有三种方式:

// 1. Async iterator (recommended): lazy, walks the next links.
for await (const story of client.userStories.paginate({ project: 42 })) {
  await processStory(story);
}

// 2. A specific page plus metadata.
const page = await client.userStories.listPage({ project: 42 }, 3);
console.log(page.data, page.pagination); // { count, perPage, current, next, prev }

// 3. Disable pagination entirely (returns the full set as one array).
const all = await client.http.get("/userstories", {
  query: { project: 42 },
  disablePagination: true,
});

⚠️ disablePagination 会发送 x-disable-pagination: True 标头。仅在小型集合上使用此选项——对于大型集合,Taiga 可能会返回错误或超时。

错误

所有错误都继承自抽象类 TaigaError:

类触发条件有用字段
TaigaApiError任何不成功的 4xx/5xx 响应status、code、detail、body
TaigaAuthError401 / 403+ TaigaApiError 的所有字段
TaigaRateLimitError429+ retryAfter(秒数,从 Retry-After 解析)
TaigaNetworkError网络故障、中止、超时cause(底层的 fetch / AbortError)
import {
  TaigaApiError,
  TaigaAuthError,
  TaigaRateLimitError,
  TaigaNetworkError,
} from "taiga-api-client";

try {
  await client.projects.get(99999);
} catch (err) {
  if (err instanceof TaigaRateLimitError) {
    await sleep((err.retryAfter ?? 1) * 1000);
  } else if (err instanceof TaigaAuthError) {
    // Session is dead — log in again
  } else if (err instanceof TaigaApiError) {
    console.error(`Taiga ${err.status}: ${err.detail ?? err.message}`);
  } else if (err instanceof TaigaNetworkError) {
    // Network / timeout
  }
}

乐观并发控制(OCC)

Taiga 通过 version 字段防止并发用户之间的更新丢失。调用 patch() 时,传入随对象收到的版本号——如果其他人先更新了该对象,Taiga 会返回 400 / 412,你便能得知发生了冲突。

const story = await client.userStories.get(42);
const updated = await client.userStories.patch(
  story.id,
  { subject: "Renamed" },
  story.version, // ← OCC
);

底层访问

对于尚未封装为资源的端点,可直接使用 client.http:

const result = await client.http.get<{ id: number }[]>("/wiki", {
  query: { project: 1 },
});
console.log(result.data, result.pagination, result.headers);

可用的方法包括 get、post、put、patch、delete。它们都接受相同的 RequestOptions:{ query, body, headers, signal, disablePagination, skipAuth }。

覆盖范围

已实现:

  • 身份验证:登录、刷新、注销、应用令牌、恢复会话、自动刷新并去重
  • 用户:个人资料、列表、更新、更改密码、统计信息
  • 项目:CRUD、按 slug 查询、统计信息、模块配置
  • 成员关系:CRUD、bulk_create、重新发送邀请
  • 用户故事:CRUD、bulk_create、对每个看板执行 bulk_update_order、filters_data
  • 任务:CRUD、bulk_create、filters_data
  • 问题:CRUD、filters_data、投票者、关注者、赞成投票/反对投票、关注/取消关注
  • 里程碑:CRUD、统计信息
  • 史诗 (v0.2 中新增):CRUD、bulk_create、filters_data、关联的用户故事(列出/添加/移除)、投票者、关注者、赞成投票/反对投票、关注/取消关注

尚未实现(计划在后续版本中推出): wiki、webhook、附件、自定义属性、历史记录/评论、导入器(Trello/Jira/GitHub)、项目模板、GitHub OAuth 流程。

如果你需要的端点不在覆盖范围内,请改用 client.http 和/或提交 issue。

兼容性

运行时最低版本备注
Node.js18全局 fetch 在 Node 18 中引入。
Bun1.0完全支持。
Deno1.x应可正常运行(提供全局 fetch)。
浏览器任意现代浏览器需要在 Taiga 端配置 CORS。

许可证

MIT

评论

支持 Markdown

第一次接触 MCP?

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

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