@zentao-hub/mcp 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/.env.example +13 -0
  2. package/LICENSE +21 -0
  3. package/README.md +231 -0
  4. package/README.zh-CN.md +225 -0
  5. package/dist/index.d.ts +2 -0
  6. package/dist/index.js +61 -0
  7. package/dist/index.js.map +1 -0
  8. package/dist/tools/bugs.d.ts +2 -0
  9. package/dist/tools/bugs.js +387 -0
  10. package/dist/tools/bugs.js.map +1 -0
  11. package/dist/tools/builds.d.ts +2 -0
  12. package/dist/tools/builds.js +54 -0
  13. package/dist/tools/builds.js.map +1 -0
  14. package/dist/tools/cases.d.ts +2 -0
  15. package/dist/tools/cases.js +121 -0
  16. package/dist/tools/cases.js.map +1 -0
  17. package/dist/tools/docs.d.ts +2 -0
  18. package/dist/tools/docs.js +75 -0
  19. package/dist/tools/docs.js.map +1 -0
  20. package/dist/tools/efforts.d.ts +2 -0
  21. package/dist/tools/efforts.js +65 -0
  22. package/dist/tools/efforts.js.map +1 -0
  23. package/dist/tools/executions.d.ts +2 -0
  24. package/dist/tools/executions.js +118 -0
  25. package/dist/tools/executions.js.map +1 -0
  26. package/dist/tools/helpers.d.ts +36 -0
  27. package/dist/tools/helpers.js +255 -0
  28. package/dist/tools/helpers.js.map +1 -0
  29. package/dist/tools/index.d.ts +4 -0
  30. package/dist/tools/index.js +44 -0
  31. package/dist/tools/index.js.map +1 -0
  32. package/dist/tools/issues.d.ts +2 -0
  33. package/dist/tools/issues.js +89 -0
  34. package/dist/tools/issues.js.map +1 -0
  35. package/dist/tools/products.d.ts +2 -0
  36. package/dist/tools/products.js +110 -0
  37. package/dist/tools/products.js.map +1 -0
  38. package/dist/tools/programs.d.ts +2 -0
  39. package/dist/tools/programs.js +46 -0
  40. package/dist/tools/programs.js.map +1 -0
  41. package/dist/tools/projects.d.ts +2 -0
  42. package/dist/tools/projects.js +80 -0
  43. package/dist/tools/projects.js.map +1 -0
  44. package/dist/tools/releases.d.ts +2 -0
  45. package/dist/tools/releases.js +46 -0
  46. package/dist/tools/releases.js.map +1 -0
  47. package/dist/tools/repos.d.ts +2 -0
  48. package/dist/tools/repos.js +42 -0
  49. package/dist/tools/repos.js.map +1 -0
  50. package/dist/tools/request.d.ts +2 -0
  51. package/dist/tools/request.js +41 -0
  52. package/dist/tools/request.js.map +1 -0
  53. package/dist/tools/risks.d.ts +2 -0
  54. package/dist/tools/risks.js +69 -0
  55. package/dist/tools/risks.js.map +1 -0
  56. package/dist/tools/stories.d.ts +2 -0
  57. package/dist/tools/stories.js +153 -0
  58. package/dist/tools/stories.js.map +1 -0
  59. package/dist/tools/tasks.d.ts +2 -0
  60. package/dist/tools/tasks.js +205 -0
  61. package/dist/tools/tasks.js.map +1 -0
  62. package/dist/tools/testreports.d.ts +2 -0
  63. package/dist/tools/testreports.js +47 -0
  64. package/dist/tools/testreports.js.map +1 -0
  65. package/dist/tools/todos.d.ts +2 -0
  66. package/dist/tools/todos.js +70 -0
  67. package/dist/tools/todos.js.map +1 -0
  68. package/dist/tools/users.d.ts +2 -0
  69. package/dist/tools/users.js +59 -0
  70. package/dist/tools/users.js.map +1 -0
  71. package/package.json +59 -0
package/.env.example ADDED
@@ -0,0 +1,13 @@
1
+ # ZenTao instance base URL (no trailing slash). The /api.php/v1 path is appended automatically.
2
+ ZENTAO_URL=https://zentao.example.com
3
+
4
+ # Account used to authenticate. The MCP server exchanges these for a token at startup
5
+ # and silently re-logs in on 401.
6
+ ZENTAO_USERNAME=your-account
7
+ ZENTAO_PASSWORD=your-password
8
+
9
+ # Optional: pass a pre-issued token instead of username/password. Takes precedence when set.
10
+ # ZENTAO_TOKEN=
11
+
12
+ # Optional: HTTP request timeout in milliseconds (default 15000).
13
+ # ZENTAO_TIMEOUT_MS=15000
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Deven Liu
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,231 @@
1
+ # @zentao-hub/mcp
2
+
3
+ English | [简体中文](README.zh-CN.md)
4
+
5
+ A [Model Context Protocol](https://modelcontextprotocol.io) server for **ZenTao 20.x (open source edition)**. Covers the everyday entities of the ZenTao v1 REST API (products, projects, plans, executions, stories, tasks, bugs, test cases, builds, releases, repos, users, docs) plus a generic `zentao_request` escape hatch for endpoints that are not explicitly wrapped.
6
+
7
+ > Looking for the hub directory, slash commands, and commit-msg hook? See the sister package [`@zentao-hub/cli`](../cli). This package is the MCP server itself, nothing more.
8
+
9
+ ## Tool overview (129 tools)
10
+
11
+ | Entity | Tools |
12
+ | ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
13
+ | **Users / Depts** | `whoami` · `list_users` · `get_user` · `list_departments` · `list_department_users` |
14
+ | **Products** | `list_products` · `get_product` · `create_product` · `update_product` · `delete_product` · `list_product_branches` · `list_product_modules` · `list_product_plans` · `list_product_executions` |
15
+ | **Projects** | `list_projects` · `get_project` · `create_project` · `update_project` · `delete_project` · `list_project_executions` · `list_project_products` |
16
+ | **Programs** | `list_programs` · `get_program` · `create_program` · `delete_program` |
17
+ | **Executions** | `list_executions` · `get_execution` · `create_execution` · `update_execution` · `delete_execution` · `start_execution` · `close_execution` · `list_execution_tasks` · `list_execution_stories` · `list_execution_team` |
18
+ | **Stories** | `list_product_stories` · `get_story` · `create_story` · `update_story` · `delete_story` · `change_story` · `review_story` · `close_story` · `activate_story` · `assign_story` · `add_story_comment` |
19
+ | **Tasks** | `get_task` · `list_my_tasks` · `create_task` · `update_task` · `delete_task` · `start_task` · `finish_task` · `pause_task` · `activate_task` · `close_task` · `cancel_task` · `assign_task` · `add_task_comment` |
20
+ | **Bugs** | `list_my_bugs` · `list_product_bugs` · `list_execution_bugs` · `get_bug` · `create_bug` · `update_bug` · `delete_bug` · `resolve_bug` · `close_bug` · `confirm_bug` · `activate_bug` · `assign_bug` · `add_bug_comment` · `get_bug_history` · `download_bug_attachments` |
21
+ | **Test cases** | `list_product_cases` · `get_case` · `create_case` · `update_case` · `delete_case` · `create_case_result` · `list_case_results` · `list_product_testsuites` · `list_product_testtasks` |
22
+ | **Test reports** | `list_product_testreports` · `get_testreport` · `create_testreport` · `delete_testreport` |
23
+ | **Builds** | `list_product_builds` · `list_execution_builds` · `get_build` · `create_build` · `delete_build` |
24
+ | **Releases** | `list_product_releases` · `get_release` · `create_release` · `delete_release` |
25
+ | **Repos** | `list_repos` · `get_repo` · `list_repo_branches` · `list_repo_commits` |
26
+ | **Docs** | `list_doclibs` · `list_doclib_docs` · `get_doc` · `create_doc` · `update_doc` · `delete_doc` |
27
+ | **Effort** | `log_effort` · `list_efforts` · `update_effort` · `delete_effort` |
28
+ | **Todos** | `list_todos` · `get_todo` · `create_todo` · `update_todo` · `finish_todo` · `delete_todo` |
29
+ | **Issues** | `list_project_issues` · `get_issue` · `create_issue` · `update_issue` · `resolve_issue` · `close_issue` · `delete_issue` |
30
+ | **Risks** | `list_project_risks` · `get_risk` · `create_risk` · `update_risk` · `delete_risk` |
31
+ | **Escape hatch** | `zentao_request` — arbitrary `GET / POST / PUT / DELETE` against `/api.php/v1/...`, for endpoints with no explicit wrapper |
32
+
33
+ **On the product scope.** Most ZenTao v1 REST endpoints for bugs / stories / cases / builds / releases are product-scoped. The usual first step is `list_products` to grab a productId.
34
+
35
+ ## Requirements
36
+
37
+ - Node.js ≥ 18
38
+ - A ZenTao 20.x instance with the v1 REST API enabled, reachable from this machine
39
+ - A developer account (username + password), or a pre-issued token
40
+
41
+ ## Install
42
+
43
+ ```bash
44
+ npm install -g @zentao-hub/mcp
45
+ # Or, inside this monorepo: `pnpm install && pnpm run build` from the repo root.
46
+ ```
47
+
48
+ Executable entry point: `zentao-hub-mcp` (i.e. `dist/index.js`).
49
+
50
+ ## Configuration
51
+
52
+ The recommended path is to log in once via the sister CLI
53
+ [`@zentao-hub/cli`](../cli) and let the MCP server pick the credentials
54
+ up automatically:
55
+
56
+ ```bash
57
+ npm install -g @zentao-hub/cli @zentao-hub/mcp
58
+ zentao-hub login # default profile
59
+ zentao-hub login --profile personal # additional ZenTao instance
60
+ zentao-hub profiles # list and pick a default
61
+ ```
62
+
63
+ The CLI stores the token + URL + username in `~/.zentao-hub/credentials.json`
64
+ (mode 0600) and caches the password in the OS keychain (macOS Keychain /
65
+ libsecret on Linux / Credential Manager on Windows). The MCP server reads
66
+ that file when the env vars below are not set and silently refreshes
67
+ expired tokens using the cached password.
68
+
69
+ ### Environment variables
70
+
71
+ Env vars take precedence over the credentials file, so CI/CD setups stay
72
+ explicit.
73
+
74
+ | Env var | When to use | Description |
75
+ | ----------------------- | -------------------------------------------- | ---------------------------------------------------------------------------- |
76
+ | `ZENTAO_PROFILE` | Multiple profiles configured | Which profile to use. Defaults to the file's `default`, or to the sole profile if only one is configured. |
77
+ | `ZENTAO_URL` | Skipping the credentials file | ZenTao base URL, e.g. `https://zentao.example.com` (no trailing `/`). |
78
+ | `ZENTAO_TOKEN` | One of: token, or username+password | Pre-issued token. Auto-refresh disabled in this mode. |
79
+ | `ZENTAO_USERNAME` | Together with password | Account; exchanged for a token at startup, silently re-logged in on 401. |
80
+ | `ZENTAO_PASSWORD` | Together with username | Password. |
81
+ | `ZENTAO_TIMEOUT_MS` | Optional | Per-request timeout in milliseconds, default `15000`. |
82
+ | `ZENTAO_HUB_CREDENTIALS`| Optional | Override credentials file location (default: `~/.zentao-hub/credentials.json`). |
83
+
84
+ ## Wiring it up to an MCP client
85
+
86
+ `@zentao-hub/mcp` is a vanilla stdio MCP server, so it drops into any
87
+ MCP-capable client. The sister CLI knows the config file format and location
88
+ for the major agents and writes the entry for you:
89
+
90
+ ```bash
91
+ npm install -g @zentao-hub/cli
92
+ zentao-hub login # cache credentials once (shared)
93
+ zentao-hub install mcp --agent claude # claude | copilot | copilot-cli | codex
94
+ ```
95
+
96
+ | `--agent` | Tool | Writes to |
97
+ | ------------- | --------------------------- | ---------------------------------------------------------------------------------------- |
98
+ | `claude` | Claude Code / Desktop | `~/.claude.json` (user) or `<repo>/.mcp.json` (workspace) |
99
+ | `copilot` | GitHub Copilot (VS Code) | `User/mcp.json` (user) or `<repo>/.vscode/mcp.json` (workspace) |
100
+ | `copilot-cli` | GitHub Copilot CLI | `~/.copilot/mcp-config.json` (user) or `<repo>/.mcp.json` (workspace, shared with Claude)|
101
+ | `codex` | OpenAI Codex CLI | `~/.codex/config.toml` (user-only) |
102
+
103
+ For everything else (Cline, Continue, custom clients) hand-edit the config —
104
+ the shape is `command: "zentao-hub-mcp"` with no required args, see the
105
+ Claude Code example below.
106
+
107
+ ### Claude Code / Claude Desktop — using `zentao-hub login`
108
+
109
+ After `zentao-hub login` the env block can be omitted entirely:
110
+
111
+ ```json
112
+ {
113
+ "mcpServers": {
114
+ "zentao-hub": {
115
+ "command": "zentao-hub-mcp"
116
+ }
117
+ }
118
+ }
119
+ ```
120
+
121
+ With multiple profiles, pin one per MCP server entry:
122
+
123
+ ```json
124
+ {
125
+ "mcpServers": {
126
+ "zentao-hub": { "command": "zentao-hub-mcp" },
127
+ "zentao-hub-personal": { "command": "zentao-hub-mcp", "env": { "ZENTAO_PROFILE": "personal" } }
128
+ }
129
+ }
130
+ ```
131
+
132
+ ### Claude Code / Claude Desktop — env-only (no CLI, e.g. for CI)
133
+
134
+ ```json
135
+ {
136
+ "mcpServers": {
137
+ "zentao-hub": {
138
+ "command": "zentao-hub-mcp",
139
+ "env": {
140
+ "ZENTAO_URL": "https://zentao.example.com",
141
+ "ZENTAO_USERNAME": "your-account",
142
+ "ZENTAO_PASSWORD": "your-password"
143
+ }
144
+ }
145
+ }
146
+ }
147
+ ```
148
+
149
+ Claude Code CLI (env-only example):
150
+
151
+ ```bash
152
+ claude mcp add zentao-hub \
153
+ --env ZENTAO_URL=https://zentao.example.com \
154
+ --env ZENTAO_USERNAME=your-account \
155
+ --env ZENTAO_PASSWORD=your-password \
156
+ -- zentao-hub-mcp
157
+ ```
158
+
159
+ ## Usage conventions
160
+
161
+ - **List tools default to `limit=20`**, max 200. `page` is 1-based.
162
+ - **`list_my_bugs` / `list_my_tasks`** filter on the server side by `assignedTo.account == <current account>`, bypassing ZenTao v1's unreliable query parameters. The scan cap is `maxScan` (default 1000); the result includes a `truncated` flag.
163
+ - **State transitions use dedicated action tools** (`resolve_bug`, `finish_task`, `close_story`, ...) instead of `update_*`. ZenTao relies on `/{entity}/{id}/{action}` endpoints to drive its state machine and history records. `update_*` only changes fields and does not trigger state transitions.
164
+ - **`zentao_request` is the escape hatch.** Any endpoint without a typical wrapper (custom workflows, third-party extensions, endpoints added in newer versions) can be called directly:
165
+ ```json
166
+ {"method": "GET", "path": "/products/27/testreports", "query": {"limit": 10}}
167
+ ```
168
+
169
+ ## Behavior details
170
+
171
+ - **Automatic token refresh.** Any tool that gets a 401 clears the cached token, re-logs in once, and retries. In `ZENTAO_TOKEN`-only mode (no credentials to refresh with), the 401 is re-thrown as-is.
172
+ - **Compatible list shapes.** List tools auto-adapt to ZenTao's various wrappers (`{bugs:[...]}`, `{products:[...]}`, `{data:[...]}`, `{list:[...]}`, or a bare array) and try to extract `total`.
173
+ - **Summary field trimming.** List tools return only commonly used fields by default (id / title / status / assignedTo / openedDate / ...). Use the corresponding `get_*` for full details.
174
+ - **`download_bug_attachments`** additionally parses `<img src>` tags inside `bug.steps` to download inline screenshots into `destDir/inline/`. A single failure does not abort the rest.
175
+
176
+ ## Development
177
+
178
+ ```bash
179
+ # From the monorepo root (uses pnpm)
180
+ corepack enable
181
+ pnpm install
182
+ pnpm run build # build everything
183
+ pnpm run dev # watch everything
184
+
185
+ # Only this package
186
+ pnpm --filter @zentao-hub/mcp run build
187
+ ```
188
+
189
+ Smoke test (no real ZenTao instance required):
190
+
191
+ ```bash
192
+ ZENTAO_URL=http://example.invalid ZENTAO_TOKEN=fake \
193
+ node dist/index.js
194
+ # Send JSON-RPC `initialize` + `tools/list` over stdin to verify.
195
+ ```
196
+
197
+ ## Source layout
198
+
199
+ ```
200
+ src/
201
+ ├── index.ts entry point + stdio transport + tool registration
202
+ ├── zentao-client.ts HTTP client: login, token refresh, timeout, download
203
+ └── tools/
204
+ ├── index.ts aggregator: buildTools(client)
205
+ ├── helpers.ts shared schema fragments, summary-key sets, JSON extraction
206
+ ├── request.ts zentao_request escape hatch
207
+ ├── users.ts whoami / users / departments
208
+ ├── products.ts products / branches / modules / plans
209
+ ├── projects.ts projects / project executions / project products
210
+ ├── programs.ts programs
211
+ ├── executions.ts executions + execution tasks/stories/team
212
+ ├── stories.ts stories CRUD + change/review/close/activate/assign/comment
213
+ ├── tasks.ts tasks CRUD + start/finish/pause/activate/close/cancel/assign/comment
214
+ ├── bugs.ts bugs CRUD + resolve/close/confirm/activate/assign/comment/history/attachments
215
+ ├── cases.ts cases / test results / testsuites / testtasks
216
+ ├── testreports.ts test reports
217
+ ├── builds.ts product/execution builds
218
+ ├── releases.ts product releases
219
+ ├── repos.ts code repos + branches + commits
220
+ ├── docs.ts doclibs / docs
221
+ ├── efforts.ts effort (work-hour) logs
222
+ ├── todos.ts personal todos
223
+ ├── issues.ts project issues
224
+ └── risks.ts project risks
225
+ ```
226
+
227
+ Adding a new tool: drop a `defineTool({ description, schema, handler })` into the matching entity module — it gets picked up automatically by the `build*Tools(ctx)` calls in `tools/index.ts`.
228
+
229
+ ## License
230
+
231
+ [MIT](../../LICENSE) © Deven Liu
@@ -0,0 +1,225 @@
1
+ # @zentao-hub/mcp
2
+
3
+ [English](README.md) | 简体中文
4
+
5
+ 面向 **禅道 20.x 开源版** 的 [Model Context Protocol](https://modelcontextprotocol.io) 服务器,覆盖禅道 v1 REST API 的常用实体(产品 / 项目 / 计划 / 迭代 / 需求 / 任务 / Bug / 测试用例 / 构建 / 发布 / 代码库 / 用户 / 文档),加一个通用的 `zentao_request` 逃生口兜底未包装的端点。
6
+
7
+ > 想要 hub 目录、slash command、commit-msg hook 这些自动化协作工件,看姐妹包 [`@zentao-hub/cli`](../cli)。本包只做 MCP server 本身。
8
+
9
+ ## 工具总览(129 个)
10
+
11
+ | 实体 | 工具 |
12
+ | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
13
+ | **用户/部门** | `whoami` · `list_users` · `get_user` · `list_departments` · `list_department_users` |
14
+ | **产品** | `list_products` · `get_product` · `create_product` · `update_product` · `delete_product` · `list_product_branches` · `list_product_modules` · `list_product_plans` · `list_product_executions` |
15
+ | **项目** | `list_projects` · `get_project` · `create_project` · `update_project` · `delete_project` · `list_project_executions` · `list_project_products` |
16
+ | **项目集** | `list_programs` · `get_program` · `create_program` · `delete_program` |
17
+ | **执行/迭代** | `list_executions` · `get_execution` · `create_execution` · `update_execution` · `delete_execution` · `start_execution` · `close_execution` · `list_execution_tasks` · `list_execution_stories` · `list_execution_team` |
18
+ | **需求** | `list_product_stories` · `get_story` · `create_story` · `update_story` · `delete_story` · `change_story` · `review_story` · `close_story` · `activate_story` · `assign_story` · `add_story_comment` |
19
+ | **任务** | `get_task` · `list_my_tasks` · `create_task` · `update_task` · `delete_task` · `start_task` · `finish_task` · `pause_task` · `activate_task` · `close_task` · `cancel_task` · `assign_task` · `add_task_comment` |
20
+ | **Bug** | `list_my_bugs` · `list_product_bugs` · `list_execution_bugs` · `get_bug` · `create_bug` · `update_bug` · `delete_bug` · `resolve_bug` · `close_bug` · `confirm_bug` · `activate_bug` · `assign_bug` · `add_bug_comment` · `get_bug_history` · `download_bug_attachments` |
21
+ | **测试用例** | `list_product_cases` · `get_case` · `create_case` · `update_case` · `delete_case` · `create_case_result` · `list_case_results` · `list_product_testsuites` · `list_product_testtasks` |
22
+ | **测试报告** | `list_product_testreports` · `get_testreport` · `create_testreport` · `delete_testreport` |
23
+ | **构建** | `list_product_builds` · `list_execution_builds` · `get_build` · `create_build` · `delete_build` |
24
+ | **发布** | `list_product_releases` · `get_release` · `create_release` · `delete_release` |
25
+ | **代码库** | `list_repos` · `get_repo` · `list_repo_branches` · `list_repo_commits` |
26
+ | **文档** | `list_doclibs` · `list_doclib_docs` · `get_doc` · `create_doc` · `update_doc` · `delete_doc` |
27
+ | **工时(effort)** | `log_effort` · `list_efforts` · `update_effort` · `delete_effort` |
28
+ | **待办** | `list_todos` · `get_todo` · `create_todo` · `update_todo` · `finish_todo` · `delete_todo` |
29
+ | **问题** | `list_project_issues` · `get_issue` · `create_issue` · `update_issue` · `resolve_issue` · `close_issue` · `delete_issue` |
30
+ | **风险** | `list_project_risks` · `get_risk` · `create_risk` · `update_risk` · `delete_risk` |
31
+ | **逃生口** | `zentao_request` — 任意 `GET / POST / PUT / DELETE` 到 `/api.php/v1/...`,覆盖没显式包装的端点 |
32
+
33
+ **关于 product 作用域**:禅道 v1 REST API 大多 bug / story / case / build / release 接口都按产品作用域。第一步通常先 `list_products` 拿 productId。
34
+
35
+ ## 环境要求
36
+
37
+ - Node.js ≥ 18
38
+ - 一台开启了 v1 REST API 的禅道 20.x 实例,并能从本机访问
39
+ - 一个开发者账号(账号 / 密码),或一个已签发的 token
40
+
41
+ ## 安装
42
+
43
+ ```bash
44
+ npm install -g @zentao-hub/mcp
45
+ # 或在 monorepo 内:从根目录 `pnpm install && pnpm run build`
46
+ ```
47
+
48
+ 可执行入口:`zentao-hub-mcp`(即 `dist/index.js`)。
49
+
50
+ ## 配置
51
+
52
+ 推荐做法:通过姊妹 CLI [`@zentao-hub/cli`](../cli) 登录一次,MCP 服务自动读取:
53
+
54
+ ```bash
55
+ npm install -g @zentao-hub/cli @zentao-hub/mcp
56
+ zentao-hub login # 默认 profile
57
+ zentao-hub login --profile personal # 多个禅道实例时再加一个
58
+ zentao-hub profiles # 列出并切换默认 profile
59
+ ```
60
+
61
+ CLI 把 URL + 用户名 + token 存到 `~/.zentao-hub/credentials.json`(权限 0600),
62
+ 密码缓存在系统 keychain(macOS Keychain / Linux libsecret / Windows
63
+ Credential Manager)。MCP server 在环境变量未设置时自动读取,token 过期时
64
+ 用缓存密码静默续期。
65
+
66
+ ### 环境变量
67
+
68
+ 环境变量优先级高于凭据文件,CI/CD 场景仍可显式注入。
69
+
70
+ | 环境变量 | 何时使用 | 说明 |
71
+ | ----------------------- | ------------------------------------- | -------------------------------------------------------------------- |
72
+ | `ZENTAO_PROFILE` | 配置了多个 profile | 选用哪个 profile。默认使用文件中的 `default`,若只有 1 个 profile 直接用它。 |
73
+ | `ZENTAO_URL` | 不使用凭据文件时 | 禅道地址,例如 `https://zentao.example.com`(结尾不要 `/`)。 |
74
+ | `ZENTAO_TOKEN` | 与(用户名+密码)二选一 | 已签发的 token。此模式下自动续期关闭。 |
75
+ | `ZENTAO_USERNAME` | 与密码同时提供 | 账号;启动时换取 token,401 时自动重新登录。 |
76
+ | `ZENTAO_PASSWORD` | 与账号同时提供 | 密码。 |
77
+ | `ZENTAO_TIMEOUT_MS` | 可选 | 单次请求超时,毫秒,默认 `15000`。 |
78
+ | `ZENTAO_HUB_CREDENTIALS`| 可选 | 覆盖凭据文件路径(默认 `~/.zentao-hub/credentials.json`)。 |
79
+
80
+ ## 接入 MCP 客户端
81
+
82
+ `@zentao-hub/mcp` 是一个标准的 stdio MCP server,可接入任何 MCP-capable client。
83
+ 姊妹 CLI 内置了主流 agent 的配置格式与位置,可一条命令注册:
84
+
85
+ ```bash
86
+ npm install -g @zentao-hub/cli
87
+ zentao-hub login # 凭据缓存一次,所有 agent 共用
88
+ zentao-hub install mcp --agent claude # claude | copilot | copilot-cli | codex
89
+ ```
90
+
91
+ | `--agent` | 对应工具 | 写入文件 |
92
+ | ------------- | ------------------------- | ------------------------------------------------------------------------------------- |
93
+ | `claude` | Claude Code / Desktop | `~/.claude.json`(user)或 `<repo>/.mcp.json`(workspace) |
94
+ | `copilot` | GitHub Copilot(VS Code) | `User/mcp.json`(user)或 `<repo>/.vscode/mcp.json`(workspace) |
95
+ | `copilot-cli` | GitHub Copilot CLI | `~/.copilot/mcp-config.json`(user)或 `<repo>/.mcp.json`(workspace,与 Claude 共用)|
96
+ | `codex` | OpenAI Codex CLI | `~/.codex/config.toml`(仅 user) |
97
+
98
+ 其他 client(Cline、Continue、自研客户端等)手工编辑 config 即可——形如
99
+ `command: "zentao-hub-mcp"`,无需必填参数,参考下面的 Claude Code 示例。
100
+
101
+ ### Claude Code / Claude Desktop — 使用 `zentao-hub login`
102
+
103
+ `zentao-hub login` 完成后 env 块完全可省略:
104
+
105
+ ```json
106
+ {
107
+ "mcpServers": {
108
+ "zentao-hub": {
109
+ "command": "zentao-hub-mcp"
110
+ }
111
+ }
112
+ }
113
+ ```
114
+
115
+ 配置了多个 profile 时,给每个 MCP server 条目指定一个:
116
+
117
+ ```json
118
+ {
119
+ "mcpServers": {
120
+ "zentao-hub": { "command": "zentao-hub-mcp" },
121
+ "zentao-hub-personal": { "command": "zentao-hub-mcp", "env": { "ZENTAO_PROFILE": "personal" } }
122
+ }
123
+ }
124
+ ```
125
+
126
+ ### Claude Code / Claude Desktop — 纯环境变量(不依赖 CLI,例如 CI)
127
+
128
+ ```json
129
+ {
130
+ "mcpServers": {
131
+ "zentao-hub": {
132
+ "command": "zentao-hub-mcp",
133
+ "env": {
134
+ "ZENTAO_URL": "https://zentao.example.com",
135
+ "ZENTAO_USERNAME": "你的账号",
136
+ "ZENTAO_PASSWORD": "你的密码"
137
+ }
138
+ }
139
+ }
140
+ }
141
+ ```
142
+
143
+ Claude Code CLI(纯环境变量示例):
144
+
145
+ ```bash
146
+ claude mcp add zentao-hub \
147
+ --env ZENTAO_URL=https://zentao.example.com \
148
+ --env ZENTAO_USERNAME=你的账号 \
149
+ --env ZENTAO_PASSWORD=你的密码 \
150
+ -- zentao-hub-mcp
151
+ ```
152
+
153
+ ## 用法约定
154
+
155
+ - **列表工具默认 `limit=20`**,最大 200。`page` 1-based。
156
+ - **list_my_bugs / list_my_tasks** 在服务端按 `assignedTo.account == <当前账号>` 过滤,绕开禅道 v1 不可靠的查询参数;扫描上限由 `maxScan`(默认 1000)控制,结果中 `truncated` 标记是否截断。
157
+ - **状态转移用专用动作工具**(`resolve_bug`、`finish_task`、`close_story` …),而不是 `update_*` —— 禅道靠 `/{entity}/{id}/{action}` 端点驱动状态机和历史记录。`update_*` 只改字段,不触发状态变更。
158
+ - **`zentao_request` 是逃生口**:任何没典型 wrapper 的端点(自定义工作流、第三方扩展、新版本里新增端点)都可以直接调,例如:
159
+ ```json
160
+ {"method": "GET", "path": "/products/27/testreports", "query": {"limit": 10}}
161
+ ```
162
+
163
+ ## 行为细节
164
+
165
+ - **Token 自动刷新**:任何工具收到 401 都会清掉缓存 token、重登一次、重试。仅 `ZENTAO_TOKEN` 模式(没账号密码可刷新)下 401 会原样抛回。
166
+ - **列表结构兼容**:list 工具自动适配 `{bugs:[...]}` / `{products:[...]}` / `{data:[...]}` / `{list:[...]}` / 裸数组等多种禅道返回包装,并尽量提取 `total`。
167
+ - **summary 字段裁剪**:list 工具默认只回常用字段(id / title / status / assignedTo / openedDate …)。要全字段就用对应的 `get_*`。
168
+ - **`download_bug_attachments`** 还会解析 bug.steps 里的 `<img src>` 把内联截图也下载到 `destDir/inline/`,单个失败不中断。
169
+
170
+ ## 开发
171
+
172
+ ```bash
173
+ # 在 monorepo 根目录(使用 pnpm)
174
+ corepack enable
175
+ pnpm install
176
+ pnpm run build # 全部构建
177
+ pnpm run dev # 全部 watch
178
+
179
+ # 仅本包
180
+ pnpm --filter @zentao-hub/mcp run build
181
+ ```
182
+
183
+ 冒烟测试(不需要真实禅道实例):
184
+
185
+ ```bash
186
+ ZENTAO_URL=http://example.invalid ZENTAO_TOKEN=fake \
187
+ node dist/index.js
188
+ # 通过 stdin 发 JSON-RPC `initialize` + `tools/list` 验证。
189
+ ```
190
+
191
+ ## 源码组织
192
+
193
+ ```
194
+ src/
195
+ ├── index.ts 入口 + stdio transport + 工具注册
196
+ ├── zentao-client.ts HTTP 客户端:登录、token 刷新、超时、下载
197
+ └── tools/
198
+ ├── index.ts 聚合 buildTools(client)
199
+ ├── helpers.ts 共用 schema 片段、summary key 集合、JSON 提取
200
+ ├── request.ts zentao_request 逃生口
201
+ ├── users.ts whoami / users / departments
202
+ ├── products.ts products / branches / modules / plans
203
+ ├── projects.ts projects / project executions / project products
204
+ ├── programs.ts programs
205
+ ├── executions.ts executions + execution tasks/stories/team
206
+ ├── stories.ts stories CRUD + change/review/close/activate/assign/comment
207
+ ├── tasks.ts tasks CRUD + start/finish/pause/activate/close/cancel/assign/comment
208
+ ├── bugs.ts bugs CRUD + resolve/close/confirm/activate/assign/comment/history/attachments
209
+ ├── cases.ts cases / test results / testsuites / testtasks
210
+ ├── testreports.ts test reports
211
+ ├── builds.ts product/execution builds
212
+ ├── releases.ts product releases
213
+ ├── repos.ts code repos + branches + commits
214
+ ├── docs.ts doclibs / docs
215
+ ├── efforts.ts effort (work-hour) logs
216
+ ├── todos.ts personal todos
217
+ ├── issues.ts project issues
218
+ └── risks.ts project risks
219
+ ```
220
+
221
+ 加新工具:在对应实体模块里加一个 `defineTool({ description, schema, handler })`,然后会被 `tools/index.ts` 的 `build*Tools(ctx)` 自动带进来。
222
+
223
+ ## License
224
+
225
+ [MIT](../../LICENSE) © Deven Liu
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,61 @@
1
+ #!/usr/bin/env node
2
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
4
+ import { ZentaoApiError, createClientFromConfig, loadZentaoConfig, } from "@zentao-hub/sdk";
5
+ import { buildTools } from "./tools/index.js";
6
+ function toToolResult(value) {
7
+ return {
8
+ content: [
9
+ {
10
+ type: "text",
11
+ text: typeof value === "string" ? value : JSON.stringify(value, null, 2),
12
+ },
13
+ ],
14
+ };
15
+ }
16
+ function toErrorResult(err) {
17
+ const msg = err instanceof ZentaoApiError
18
+ ? `${err.message}${err.body ? `\n${JSON.stringify(err.body, null, 2).slice(0, 1000)}` : ""}`
19
+ : err instanceof Error
20
+ ? err.message
21
+ : String(err);
22
+ return {
23
+ isError: true,
24
+ content: [{ type: "text", text: msg }],
25
+ };
26
+ }
27
+ async function main() {
28
+ const config = loadZentaoConfig();
29
+ const client = createClientFromConfig(config);
30
+ const tools = buildTools(client);
31
+ const server = new McpServer({
32
+ name: "zentao-hub",
33
+ version: "0.3.0",
34
+ });
35
+ for (const [name, def] of Object.entries(tools)) {
36
+ server.registerTool(name, {
37
+ description: def.description,
38
+ inputSchema: def.schema.shape,
39
+ }, async (args) => {
40
+ try {
41
+ const parsed = def.schema.parse(args);
42
+ // The handler signatures are tied to each tool's own schema; the cast
43
+ // bridges the heterogeneous union back to the per-tool argument type.
44
+ const result = await def.handler(parsed);
45
+ return toToolResult(result);
46
+ }
47
+ catch (err) {
48
+ return toErrorResult(err);
49
+ }
50
+ });
51
+ }
52
+ const transport = new StdioServerTransport();
53
+ await server.connect(transport);
54
+ // stdio transport keeps the process alive via stdin; nothing else to do here.
55
+ }
56
+ main().catch((err) => {
57
+ // stderr is safe — stdio transport reserves stdout for the JSON-RPC channel.
58
+ console.error("zentao-hub-mcp failed to start:", err);
59
+ process.exit(1);
60
+ });
61
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import { type ToolDef, type ToolFactoryCtx } from "./helpers.js";
2
+ export declare function buildBugTools(ctx: ToolFactoryCtx): Record<string, ToolDef>;