Skip to main content
Glama
README.md
# zentao-mcp-server(自建)

## 目标
- 提供一个可运行的 MCP Server(stdio),连接你的禅道 RESTful API v1。
- 最小功能:自动获取/缓存 Token + 常用 bug 工具。

## 非目标
- 不在仓库内存任何密钥/Token。
- 不暴露无边界的通用 REST 写接口。
- 不保证覆盖你禅道的全部 API;优先支持高频 bug 流程,再按你的流程补工具。

## 依赖
- Node.js 18+(需要内置 `fetch`)

## License
MIT - 详见 [LICENSE](./LICENSE) 文件

## 配置
复制 `.env.example` 为 `.env` 并填写:
- `ZENTAO_BASE_URL`
- `ZENTAO_ACCOUNT`
- `ZENTAO_PASSWORD`
- (可选)`ZENTAO_PRODUCT_ID`:你的禅道实例若报 `Need product id` 时设置
- (可选)`ZENTAO_PROJECT_ID`:按项目查 bug 时设置(直接 `/projects/{id}/bugs`)
- (可选)`ZENTAO_PROJECT_SET_ID`:你的 bug 若在项目集视角,设置项目集 ID
- (可选)`ZENTAO_DEFAULT_RESOLVED_BUILD`:解决 bug 时的默认「解决版本」,默认 `trunk`(很多实例必填)
- (可选)`ZENTAO_MY_BUGS_PATH`:我的 bug 专用接口路径(如 `/my/bug`)
- (可选)`ZENTAO_BUGS_FALLBACK_PATHS`:bug 列表回退路径(逗号分隔)
- (可选)`ZENTAO_PROJECT_SET_BUGS_PATHS`:项目集 bug 路径模板(支持 `{projectSetId}`)

> 注意:不同禅道版本/部署方式的 token 端点与返回结构可能不同;可通过 `ZENTAO_TOKEN_PATH`/`ZENTAO_API_PREFIX` 调整。
>
> 默认情况下不需要配置 `ZENTAO_API_PREFIX`(默认值是 `/api.php/v1`)。
>
> 安全约束:`ZENTAO_API_PREFIX` / `ZENTAO_TOKEN_PATH` 只支持相对路径;默认要求 `ZENTAO_BASE_URL` 使用 HTTPS。`ZENTAO_BASE_URL` 可为根路径或子目录部署地址(如 `https://zentao.example.com/zentao`)。

## 安装与运行
```bash
npm i
cp .env.example .env
npm start
```

## npm 安装后的运行
发布到 npm 后,推荐用 `npx` 启动(适合 MCP 客户端配置):
```bash
npx -y @aipper/zentao-mcp-server
```

## 验证(不依赖 MCP 客户端)
```bash
npm i
cp .env.example .env
set -a; source .env; set +a
npm run smoke
```
期望结果:
- 输出一行 `token: xxxx…yyyy source: ...`
- 输出 `GET /projects status: 200`(或你的禅道实际返回码)

## Claude Desktop / Cursor 示例(stdio)
优先使用 `npx`(npm 发布版):
```json
{
  "mcpServers": {
    "zentao": {
      "command": "npx",
      "args": ["-y", "@aipper/zentao-mcp-server"],
      "env": {
        "ZENTAO_BASE_URL": "https://zentao.example.com",
        "ZENTAO_ACCOUNT": "your_account",
        "ZENTAO_PASSWORD": "your_password"
      }
    }
  }
}
```

本地源码调试可继续用 `node src/index.js`:

示例(Claude Desktop 的 `mcpServers` 风格,按你的客户端实际字段为准):
```json
{
  "mcpServers": {
    "zentao": {
      "command": "node",
      "args": ["src/index.js"],
      "cwd": "/ABS/PATH/TO/zentao",
      "env": {
        "ZENTAO_BASE_URL": "https://zentao.example.com",
        "ZENTAO_ACCOUNT": "your_account",
        "ZENTAO_PASSWORD": "your_password"
      }
    }
  }
}
```

## 常见错误(`-32000`)
`-32000` 通常是客户端侧“通用 MCP 调用失败”映射码,优先检查:
- `env` 是否完整传入(尤其是 `ZENTAO_BASE_URL`/`ZENTAO_ACCOUNT`/`ZENTAO_PASSWORD`)。
- 默认要求 HTTPS;如果你的实例只能走 HTTP,必须显式设置 `ZENTAO_ALLOW_INSECURE_HTTP=true`,且只建议临时内网调试使用。
- 若报 `Need product id`,请设置 `ZENTAO_PRODUCT_ID`,或在 `get_my_bugs` 传 `productId`。
- 若你的 bug 在“项目集/我的视角”而非产品,建议设置 `ZENTAO_PROJECT_SET_ID`,并配置 `ZENTAO_MY_BUGS_PATH=/my/bug`。
- 项目集场景下,优先直接调用 `get_my_bugs` 并传 `projectSetId`,必要时再显式传 `path="/my/bug"`;不要先依赖 `list_my_projects` 找项目。
- 有些项目集本身没有创建实际项目,但仍然存在“我的 bug”;这类数据可能不会出现在 `list_my_projects` 结果里。
- `get_my_bugs` 会按候选路径回退(包含项目集路径);即使首个路径返回空列表也会继续尝试,并会把多端点结果合并去重。
- `get_my_bugs.total` 表示最终“我的 bug”总数;若需排查底层一共扫描了多少条,可看 `raw.scannedTotal`。
- 排查时看工具返回里的 `raw.triedPaths` / `raw.paths`,可确认每条路径的返回码与命中数量。
- `ZENTAO_API_PREFIX`/`ZENTAO_TOKEN_PATH` 是否和你的禅道实例一致,且使用相对路径。
- MCP 客户端是否真的在执行 `npx -y @aipper/zentao-mcp-server`(而不是旧的本地命令)。
- 客户端日志中是否有启动报错(如找不到命令、401、超时)。

## 已实现工具
- `get_token`:获取/刷新 token(始终只回显脱敏后的 token 摘要)
- `list_my_projects`:示例:列出“我参与的项目”(字段匹配基于常见返回结构,可能需按你的实例微调;不适合作为项目集 bug 的唯一发现入口)
- `get_my_bugs`:获取“指派给我”的 bug(支持 `status`/`keyword`/`limit`/`page`/`productId`/`projectSetId`,默认路径 `/bugs`;`path` 仅允许 `/bugs`、`/my/bug`、`/my/bugs`)
- `get_bug_detail`:按 `id` 获取 bug 详情(固定读取 `/bugs/{id}`;返回安全裁剪后的 bug 摘要与同源图片链接,不直接透传外部图片地址或原始附件外链)。当 bug 被激活过时,额外返回 `activation` 摘要 `{count, lastActor, lastDate, lastComment}`,其中 `lastComment` 是最近一次激活时测试人员填写的评论(已去除 HTML)。bug 摘要现包含 `activatedCount`/`activatedDate`。
- `get_bug_actions`:按 `id` 获取 bug 的动作历史(激活/解决/关闭/评论等)。同一 `/bugs/{id}` 端点,返回 `actions:[{id, actor, action, date, comment, history:[{field, old, new}]}]`(`comment` 已去 HTML)和 `activation` 摘要。当 bug 被激活、需要拿到激活评论/字段变更明细时使用;`get_bug_detail` 的 `activation.lastComment` 已覆盖"只看最近一次激活评论"的常见场景,需完整历史再用本工具。
- `resolve_bug`:按 `id` 调用真正的解决接口 `POST /bugs/{id}/resolve`(默认 `resolution=fixed`;`resolvedBuild` 默认取 `ZENTAO_DEFAULT_RESOLVED_BUILD`/`trunk`;优先 `solutionModules`,兼容纯文本 `solution`)
- `batch_resolve_my_bugs`:批量解决“我的 bug”(同上 resolve API;默认筛选 `status=active`,支持 `productId`/`projectSetId`/`resolvedBuild`,默认遇错即停,`maxItems` 上限 100;优先共享 `solutionModules`)
- `close_bug`:按 `id` 关闭 bug
- `verify_bug`:验证结果处理(`pass`=关闭,`fail`=激活)
- `comment_bug`:按 `id` 添加备注

### 结构化解决说明 `solutionModules`(推荐)

字段:
- `rootCause`:【根因】触发条件 + 错误行为
- `fixApproach`:【修复思路】策略说明
- `logicChange`:【改动逻辑】关键分支/校验/流程变化
- `impact`:【影响范围】受影响场景 + 结果型回归点

服务端会格式化为多行文本写入禅道,例如:

```text
【根因】
...

【修复思路】
...

【改动逻辑】
...

【影响范围】
...
```

优先级:`solutionModules`(任一非空字段)> 纯文本 `solution` > `comment` 兜底。

**解决版本 `resolvedBuild`**:很多禅道实例校验「解决版本不能为空」。请传工具参数 `resolvedBuild`,或配置 `ZENTAO_DEFAULT_RESOLVED_BUILD`(默认 `trunk`)。无版本列表时 `trunk` 通常可用。

**不要用 HTTP 编辑冒充解决**:`PUT /bugs/{id}` 或网页表单“编辑”只可能改字段/状态,**不会**走 resolve 动作,也**不会**把 MCP 的 `solutionModules` 模板写进解决说明。必须用 `resolve_bug` / `batch_resolve_my_bugs`。

示例参数:
- `resolve_bug`(推荐):
```json
{
  "id": 123,
  "resolution": "fixed",
  "resolvedBuild": "trunk",
  "solutionModules": {
    "rootCause": "分页参数为空时仍进入查询构造,导致列表请求异常。",
    "fixApproach": "为空参数补默认值,并在构造前做兜底校验。",
    "logicChange": "page/size 为空时回落默认值;非法值改为明确错误提示,避免前端重复提交。",
    "impact": "列表查询与分页切换;重点回归空参进入与异常提示是否收敛。"
  }
}
```
- `resolve_bug`(兼容纯文本):`{"id":123,"resolution":"fixed","resolvedBuild":"trunk","solution":"补齐分页参数为空时的默认值分支,避免空值继续进入查询构造;同时收敛异常提示,防止前端重复触发提交"}`
- `batch_resolve_my_bugs`(推荐共享模块):
```json
{
  "status": "active",
  "maxItems": 20,
  "resolvedBuild": "trunk",
  "solutionModules": {
    "rootCause": "状态切换时旧数据判空顺序错误,触发空指针。",
    "fixApproach": "统一状态切换的判空与分支顺序。",
    "logicChange": "保存前增加兜底校验;异常改为明确提示而非静默失败。",
    "impact": "状态流转相关保存与切换场景。"
  }
}
```
- `get_my_bugs`(项目集):`{"status":"active","projectSetId":1001,"limit":50}`
- `get_my_bugs`(我的):`{"status":"active","path":"/my/bug","limit":50}`
- `get_my_bugs`(项目集 + 我的):`{"status":"active","projectSetId":1001,"path":"/my/bug","limit":50}`
- `get_my_bugs`(按产品):`{"status":"active","productId":1,"limit":50}`
- `close_bug`:`{"id":123,"comment":"验证通过,关闭"}`
- `verify_bug`:`{"id":123,"result":"pass","comment":"验证通过"}`
- `comment_bug`:`{"id":123,"comment":"已复现,正在定位根因"}`

`solutionModules` / `solution` / `comment` 不要写 `Evidence:`、`Verify:`、文件路径、编译命令或“已修复并自测”这类无法说明改动内容的表述。

使用建议:如果用户提到“项目集”“我的 bug”“项目列表里找不到但禅道里能看到 bug”,优先走项目集视角的 `get_my_bugs`,不要先让用户证明项目已创建。

## 安全建议
- **默认要求使用 HTTPS**:HTTP 会明文传输账号密码和数据,存在安全风险;如确需兼容老旧实例,需显式设置 `ZENTAO_ALLOW_INSECURE_HTTP=true`。
- 使用最小权限账号(仅需要的项目权限),避免使用管理员账号。
- `get_token` 不再支持回显完整 token。
- 调试日志会自动脱敏 `query`、`body`、`comment`、`solution` 等敏感字段,但仍建议仅在排查问题时临时开启。

## 调试
如需查看详细日志,可设置环境变量:
```bash
ZENTAO_DEBUG=true npx -y @aipper/zentao-mcp-server
```
日志会输出到 stderr,不影响 MCP 协议通信。

## 发布到 npm
脚本:`scripts/release-npm.sh`(参考 `aiws` 的发布流程,默认 dry-run)。

常用命令:
```bash
# dry-run:只检查 + npm pack --dry-run,不会发布
npm run release:npm

# 自动递增版本(patch/minor/major)+ commit + tag(不发布)
npm run release:npm -- --bump patch

# 发布(会二次确认;默认自动 patch 递增)
npm run release:npm -- --publish

# 版本对齐 + commit + tag(不发布)
npm run release:npm -- --release v0.1.0
```

注意:
- 若 `package.json` 为 `private: true`,发布前请改成 `false` 并确认包名可用。
- 可加 `--require-tag` 要求 HEAD 上有匹配版本的 tag。
- 若发布时报 `403`,通常是包名归属问题;建议改为 scoped 包名(如 `@yourname/zentao-mcp-server`)。

TDQS

A3.6/5.0

Scored across 11 tools

Disambiguation5/5

Each tool targets a distinct operation: listing projects, listing bugs, detail, resolve (single/batch), close, verify, comment, upload/download attachments, and token management. Even with overlapping operations like resolve vs batch resolve, the single vs batch scopes are clearly separated by descriptions.

Naming Consistency5/5

All tools follow a consistent verb_noun snake_case pattern (e.g., list_my_projects, resolve_bug, upload_attachment). The naming is predictable and clear, with no mixed conventions.

Tool Count5/5

With 11 tools, the server is well-scoped for a bug tracking domain. Each tool serves a specific purpose without redundancy, covering core operations like listing, resolving, commenting, and file attachments.

Completeness2/5

The tool set is incomplete for a full bug lifecycle. Missing essential tools: create_bug, update_bug (general-purpose), delete_bug, and search bugs beyond 'my bugs'. The list_my_projects tool is noted as unreliable, and there is no tool to list all bugs or manage other entities like users or products.

Maintenance

ActivitySlowing
ResponsivenessNo issues