@zzp123/mcp-zentao 1.18.7 → 1.18.9
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.
- package/README.md +527 -463
- package/dist/index-dev.js +5 -4
- package/dist/index-pm.js +5 -4
- package/dist/index-qa.js +8 -4
- package/dist/index.js +5 -4
- package/dist/serverTransport.d.ts +20 -0
- package/dist/serverTransport.js +151 -0
- package/dist/transportConfig.d.ts +12 -0
- package/dist/transportConfig.js +23 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,463 +1,527 @@
|
|
|
1
|
-
# MCP-Zentao
|
|
2
|
-
|
|
3
|
-
[](https://www.npmjs.com/package/@zzp123/mcp-zentao)
|
|
4
|
-
[](https://opensource.org/licenses/MIT)
|
|
5
|
-
|
|
6
|
-
禅道项目管理系统的高级API集成包,提供任务管理、Bug跟踪等功能的完整封装,专为Cursor IDE设计的MCP扩展。**支持产品经理、测试工程师、开发工程师三种角色的精简版本,大幅减少上下文占用。**
|
|
7
|
-
|
|
8
|
-
## 📦 安装
|
|
9
|
-
|
|
10
|
-
```bash
|
|
11
|
-
npm install @zzp123/mcp-zentao -g
|
|
12
|
-
```
|
|
13
|
-
|
|
14
|
-
## 🎭 角色版本选择
|
|
15
|
-
|
|
16
|
-
从 v1.17.0 开始,提供了三个角色专用的精简版本,每个版本只包含对应角色常用的工具,**可显著减少 LLM 上下文占用 30-45%**:
|
|
17
|
-
|
|
18
|
-
| 版本 | 命令 | 工具数量 | 文件大小 | 适用角色 |
|
|
19
|
-
|------|------|---------|---------|---------|
|
|
20
|
-
| **完整版** | `zentao` | 91个 | 67 KB | 需要使用全部功能 |
|
|
21
|
-
| **产品经理版** | `zentao-pm` | 39个 | 39 KB (-57%) | 需求、产品、计划管理 |
|
|
22
|
-
| **测试工程师版** | `zentao-qa` | 26个 | 25 KB (-71%) | Bug、测试用例管理 |
|
|
23
|
-
| **开发工程师版** | `zentao-dev` | 38个 | 35 KB (-58%) | 任务、项目、Bug解决 |
|
|
24
|
-
|
|
25
|
-
### 如何选择版本?
|
|
26
|
-
|
|
27
|
-
在 Claude Desktop 配置文件中,将 `command` 字段改为对应的命令即可:
|
|
28
|
-
|
|
29
|
-
```json
|
|
30
|
-
{
|
|
31
|
-
"mcpServers": {
|
|
32
|
-
"zentao-pm": {
|
|
33
|
-
"command": "zentao-pm",
|
|
34
|
-
"args": []
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
```
|
|
39
|
-
|
|
40
|
-
### 各版本包含的功能
|
|
41
|
-
|
|
42
|
-
<details>
|
|
43
|
-
<summary><b>产品经理版 (zentao-pm)</b> - 点击展开</summary>
|
|
44
|
-
|
|
45
|
-
- ✅ 初始化配置
|
|
46
|
-
- ✅ 需求管理(创建、查看、修改、删除)
|
|
47
|
-
- ✅ 产品管理
|
|
48
|
-
- ✅ 计划管理
|
|
49
|
-
- ✅ 项目集管理
|
|
50
|
-
- ✅ Bug查看(只读)
|
|
51
|
-
- ✅ 任务查看(只读)
|
|
52
|
-
- ✅ 评论功能
|
|
53
|
-
- ✅ 文件上传
|
|
54
|
-
</details>
|
|
55
|
-
|
|
56
|
-
<details>
|
|
57
|
-
<summary><b>测试工程师版 (zentao-qa)</b> - 点击展开</summary>
|
|
58
|
-
|
|
59
|
-
- ✅ 初始化配置
|
|
60
|
-
- ✅ Bug管理(创建、查看、修改、解决、删除)
|
|
61
|
-
- ✅ 测试用例管理
|
|
62
|
-
- ✅ 需求查看(只读)
|
|
63
|
-
- ✅ 任务查看(只读)
|
|
64
|
-
- ✅ 评论功能
|
|
65
|
-
- ✅ 文件上传
|
|
66
|
-
</details>
|
|
67
|
-
|
|
68
|
-
<details>
|
|
69
|
-
<summary><b>开发工程师版 (zentao-dev)</b> - 点击展开</summary>
|
|
70
|
-
|
|
71
|
-
- ✅ 初始化配置
|
|
72
|
-
- ✅ 任务管理(创建、查看、更新、完成、删除)
|
|
73
|
-
- ✅ Bug解决(查看、解决、评论)
|
|
74
|
-
- ✅ 项目管理
|
|
75
|
-
- ✅ 执行管理
|
|
76
|
-
- ✅ 构建版本管理
|
|
77
|
-
- ✅ 需求查看(只读)
|
|
78
|
-
- ✅ 评论功能
|
|
79
|
-
- ✅ 文件上传
|
|
80
|
-
</details>
|
|
81
|
-
|
|
82
|
-
## 🎯 Claude Code Skills
|
|
83
|
-
|
|
84
|
-
**新功能!** 现在提供 Claude Code Skills 支持,让你通过自然语言更便捷地使用禅道功能。
|
|
85
|
-
|
|
86
|
-
### 什么是 Skills?
|
|
87
|
-
|
|
88
|
-
Skills 是 Claude Code 的功能扩展,通过它你可以:
|
|
89
|
-
- 用自然语言描述需求,无需记住命令
|
|
90
|
-
- 智能化的任务管理和状态更新
|
|
91
|
-
- 更友好的交互体验
|
|
92
|
-
|
|
93
|
-
### 可用的 Skills
|
|
94
|
-
|
|
95
|
-
目前提供以下 Skills:
|
|
96
|
-
|
|
97
|
-
| Skill | 适用角色 | 功能 | 文档 |
|
|
98
|
-
|-------|---------|-----|------|
|
|
99
|
-
| **zentao-dev** | 开发工程师 | 任务管理、Bug解决、项目管理 | [文档](./.claude/skills/zentao-dev/) |
|
|
100
|
-
|
|
101
|
-
### 如何使用 Skills?
|
|
102
|
-
|
|
103
|
-
1. **安装 MCP 服务器**(如果还没安装):
|
|
104
|
-
```bash
|
|
105
|
-
npm install -g @zzp123/mcp-zentao
|
|
106
|
-
```
|
|
107
|
-
|
|
108
|
-
2. **配置 Claude Desktop**(参考下方 MCP 配置)
|
|
109
|
-
|
|
110
|
-
3. **在 Claude Code 中使用**:
|
|
111
|
-
```
|
|
112
|
-
你:查看我今天的任务
|
|
113
|
-
AI:[显示任务列表]
|
|
114
|
-
|
|
115
|
-
你:开始处理任务 #123
|
|
116
|
-
AI:✓ 任务已更新为"进行中"状态
|
|
117
|
-
```
|
|
118
|
-
|
|
119
|
-
详细使用指南请查看 [Skills 文档](./.claude/skills/zentao-dev/README.md)
|
|
120
|
-
|
|
121
|
-
## 📋 版本历史
|
|
122
|
-
|
|
123
|
-
查看完整的版本更新历史,请访问 [CHANGELOG.md](./CHANGELOG.md)
|
|
124
|
-
|
|
125
|
-
**最新版本**: v1.17.6 - 2025-11-10
|
|
126
|
-
- 💬 评论工具完全统一(移除所有便捷工具,统一使用 addComment)
|
|
127
|
-
- 📊 完整版 91个工具,PM版 39个,QA版 26个,Dev版 38个
|
|
128
|
-
- 🎯 累计减少约 106行重复代码,完全统一评论API
|
|
129
|
-
|
|
130
|
-
**近期版本**:
|
|
131
|
-
- v1.17.5 - 评论工具简化(移除 addBugComment 和 addTaskComment)
|
|
132
|
-
- v1.17.4 - 产品经理版移除工单管理(42个工具)
|
|
133
|
-
- v1.17.3 - 测试工程师版移除构建和工单管理(29个工具)
|
|
134
|
-
|
|
135
|
-
## 使用方法
|
|
136
|
-
|
|
137
|
-
### 首次使用(配置禅道信息)
|
|
138
|
-
|
|
139
|
-
首次使用时,需要提供禅道的配置信息:
|
|
140
|
-
|
|
141
|
-
```bash
|
|
142
|
-
zentao '{"config":{"url":"https://your-zentao-url","username":"your-username","password":"your-password","apiVersion":"v1"},"name":"张三","age":25,"skills":["编程","设计"]}'
|
|
143
|
-
```
|
|
144
|
-
|
|
145
|
-
配置信息会被保存在用户目录下的 `.zentao/config.json` 文件中,后续使用时无需再次提供。
|
|
146
|
-
|
|
147
|
-
### 后续使用
|
|
148
|
-
|
|
149
|
-
配置完成后,只需要提供任务相关的信息即可:
|
|
150
|
-
|
|
151
|
-
```bash
|
|
152
|
-
zentao '{"name":"张三","age":25,"skills":["编程","设计"]}'
|
|
153
|
-
```
|
|
154
|
-
|
|
155
|
-
### 更新配置
|
|
156
|
-
|
|
157
|
-
如果需要更新禅道配置信息,只需要再次提供 config 参数即可:
|
|
158
|
-
|
|
159
|
-
```bash
|
|
160
|
-
zentao '{"config":{"url":"https://new-zentao-url","username":"new-username","password":"new-password","apiVersion":"v1"},"name":"张三","age":25,"skills":["编程","设计"]}'
|
|
161
|
-
```
|
|
162
|
-
|
|
163
|
-
## 配置文件位置
|
|
164
|
-
|
|
165
|
-
配置文件保存在用户目录下的 `.zentao/config.json` 文件中:
|
|
166
|
-
|
|
167
|
-
- Windows: `C:\Users\你的用户名\.zentao\config.json`
|
|
168
|
-
- macOS/Linux: `~/.zentao/config.json`
|
|
169
|
-
|
|
170
|
-
## 功能特性
|
|
171
|
-
|
|
172
|
-
- 支持配置信息的持久化存储
|
|
173
|
-
- 自动管理禅道API的认证信息
|
|
174
|
-
- 提供任务创建、更新、完成等功能
|
|
175
|
-
- 支持Bug跟踪和处理
|
|
176
|
-
- 完整的类型定义支持
|
|
177
|
-
|
|
178
|
-
## 注意事项
|
|
179
|
-
|
|
180
|
-
- 配置文件中包含敏感信息,请确保文件权限设置正确
|
|
181
|
-
- 建议定期更新密码,以确保安全性
|
|
182
|
-
- 如遇到问题,可以删除配置文件重新配置
|
|
183
|
-
|
|
184
|
-
## 许可证
|
|
185
|
-
|
|
186
|
-
MIT
|
|
187
|
-
|
|
188
|
-
## 特点
|
|
189
|
-
|
|
190
|
-
- 完整的禅道API封装
|
|
191
|
-
- 简单易用的接口设计
|
|
192
|
-
- 类型安全(TypeScript支持)
|
|
193
|
-
- 完善的错误处理
|
|
194
|
-
- 自动化的认证管理
|
|
195
|
-
|
|
196
|
-
## 与其他项目的区别
|
|
197
|
-
|
|
198
|
-
不同于通用的数据库操作工具(如 mcp-mysql-server),本项目专注于提供:
|
|
199
|
-
|
|
200
|
-
1. 禅道系统特定的业务功能
|
|
201
|
-
2. 高级别的API抽象
|
|
202
|
-
3. 完整的禅道工作流支持
|
|
203
|
-
4. 开箱即用的禅道集成方案
|
|
204
|
-
|
|
205
|
-
## 本地开发
|
|
206
|
-
|
|
207
|
-
1. 克隆仓库
|
|
208
|
-
```bash
|
|
209
|
-
git clone https://github.com/bigtian/mcp-zentao.git
|
|
210
|
-
cd mcp-zentao
|
|
211
|
-
```
|
|
212
|
-
|
|
213
|
-
2. 安装依赖
|
|
214
|
-
```bash
|
|
215
|
-
npm install
|
|
216
|
-
```
|
|
217
|
-
|
|
218
|
-
3. 运行测试
|
|
219
|
-
```bash
|
|
220
|
-
npm test
|
|
221
|
-
```
|
|
222
|
-
|
|
223
|
-
4. 构建项目
|
|
224
|
-
```bash
|
|
225
|
-
npm run build
|
|
226
|
-
```
|
|
227
|
-
|
|
228
|
-
## Docker 使用
|
|
229
|
-
|
|
230
|
-
### 使用 docker-compose(推荐)
|
|
231
|
-
|
|
232
|
-
1. 复制环境变量模板并修改配置
|
|
233
|
-
```bash
|
|
234
|
-
cp .env.example .env
|
|
235
|
-
# 编辑 .env 文件,填入你的禅道系统配置
|
|
236
|
-
```
|
|
237
|
-
|
|
238
|
-
2. 启动服务
|
|
239
|
-
```bash
|
|
240
|
-
docker-compose up -d
|
|
241
|
-
```
|
|
242
|
-
|
|
243
|
-
3. 查看日志
|
|
244
|
-
```bash
|
|
245
|
-
docker-compose logs -f
|
|
246
|
-
```
|
|
247
|
-
|
|
248
|
-
### 手动使用 Docker
|
|
249
|
-
|
|
250
|
-
1. 构建镜像
|
|
251
|
-
```bash
|
|
252
|
-
docker build -t mcp-zentao .
|
|
253
|
-
```
|
|
254
|
-
|
|
255
|
-
2. 运行容器
|
|
256
|
-
```bash
|
|
257
|
-
docker run -d \
|
|
258
|
-
--name mcp-zentao \
|
|
259
|
-
-p 3000:3000 \
|
|
260
|
-
-e ZENTAO_URL=your-zentao-url \
|
|
261
|
-
-e ZENTAO_USERNAME=your-username \
|
|
262
|
-
-e ZENTAO_PASSWORD=your-password \
|
|
263
|
-
-e ZENTAO_API_VERSION=v1 \
|
|
264
|
-
-v $(pwd)/logs:/app/logs \
|
|
265
|
-
mcp-zentao
|
|
266
|
-
```
|
|
267
|
-
|
|
268
|
-
### 在 Cursor IDE 中配置
|
|
269
|
-
|
|
270
|
-
在 Cursor IDE 的配置文件中添加以下配置:
|
|
271
|
-
|
|
272
|
-
```json
|
|
273
|
-
{
|
|
274
|
-
"mcpServers": {
|
|
275
|
-
"zentao": {
|
|
276
|
-
"url": "http://localhost:3000"
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
```
|
|
281
|
-
|
|
282
|
-
##
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
-
|
|
459
|
-
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
1
|
+
# MCP-Zentao
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@zzp123/mcp-zentao)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
|
|
6
|
+
禅道项目管理系统的高级API集成包,提供任务管理、Bug跟踪等功能的完整封装,专为Cursor IDE设计的MCP扩展。**支持产品经理、测试工程师、开发工程师三种角色的精简版本,大幅减少上下文占用。**
|
|
7
|
+
|
|
8
|
+
## 📦 安装
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npm install @zzp123/mcp-zentao -g
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## 🎭 角色版本选择
|
|
15
|
+
|
|
16
|
+
从 v1.17.0 开始,提供了三个角色专用的精简版本,每个版本只包含对应角色常用的工具,**可显著减少 LLM 上下文占用 30-45%**:
|
|
17
|
+
|
|
18
|
+
| 版本 | 命令 | 工具数量 | 文件大小 | 适用角色 |
|
|
19
|
+
|------|------|---------|---------|---------|
|
|
20
|
+
| **完整版** | `zentao` | 91个 | 67 KB | 需要使用全部功能 |
|
|
21
|
+
| **产品经理版** | `zentao-pm` | 39个 | 39 KB (-57%) | 需求、产品、计划管理 |
|
|
22
|
+
| **测试工程师版** | `zentao-qa` | 26个 | 25 KB (-71%) | Bug、测试用例管理 |
|
|
23
|
+
| **开发工程师版** | `zentao-dev` | 38个 | 35 KB (-58%) | 任务、项目、Bug解决 |
|
|
24
|
+
|
|
25
|
+
### 如何选择版本?
|
|
26
|
+
|
|
27
|
+
在 Claude Desktop 配置文件中,将 `command` 字段改为对应的命令即可:
|
|
28
|
+
|
|
29
|
+
```json
|
|
30
|
+
{
|
|
31
|
+
"mcpServers": {
|
|
32
|
+
"zentao-pm": {
|
|
33
|
+
"command": "zentao-pm",
|
|
34
|
+
"args": []
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
### 各版本包含的功能
|
|
41
|
+
|
|
42
|
+
<details>
|
|
43
|
+
<summary><b>产品经理版 (zentao-pm)</b> - 点击展开</summary>
|
|
44
|
+
|
|
45
|
+
- ✅ 初始化配置
|
|
46
|
+
- ✅ 需求管理(创建、查看、修改、删除)
|
|
47
|
+
- ✅ 产品管理
|
|
48
|
+
- ✅ 计划管理
|
|
49
|
+
- ✅ 项目集管理
|
|
50
|
+
- ✅ Bug查看(只读)
|
|
51
|
+
- ✅ 任务查看(只读)
|
|
52
|
+
- ✅ 评论功能
|
|
53
|
+
- ✅ 文件上传
|
|
54
|
+
</details>
|
|
55
|
+
|
|
56
|
+
<details>
|
|
57
|
+
<summary><b>测试工程师版 (zentao-qa)</b> - 点击展开</summary>
|
|
58
|
+
|
|
59
|
+
- ✅ 初始化配置
|
|
60
|
+
- ✅ Bug管理(创建、查看、修改、解决、删除)
|
|
61
|
+
- ✅ 测试用例管理
|
|
62
|
+
- ✅ 需求查看(只读)
|
|
63
|
+
- ✅ 任务查看(只读)
|
|
64
|
+
- ✅ 评论功能
|
|
65
|
+
- ✅ 文件上传
|
|
66
|
+
</details>
|
|
67
|
+
|
|
68
|
+
<details>
|
|
69
|
+
<summary><b>开发工程师版 (zentao-dev)</b> - 点击展开</summary>
|
|
70
|
+
|
|
71
|
+
- ✅ 初始化配置
|
|
72
|
+
- ✅ 任务管理(创建、查看、更新、完成、删除)
|
|
73
|
+
- ✅ Bug解决(查看、解决、评论)
|
|
74
|
+
- ✅ 项目管理
|
|
75
|
+
- ✅ 执行管理
|
|
76
|
+
- ✅ 构建版本管理
|
|
77
|
+
- ✅ 需求查看(只读)
|
|
78
|
+
- ✅ 评论功能
|
|
79
|
+
- ✅ 文件上传
|
|
80
|
+
</details>
|
|
81
|
+
|
|
82
|
+
## 🎯 Claude Code Skills
|
|
83
|
+
|
|
84
|
+
**新功能!** 现在提供 Claude Code Skills 支持,让你通过自然语言更便捷地使用禅道功能。
|
|
85
|
+
|
|
86
|
+
### 什么是 Skills?
|
|
87
|
+
|
|
88
|
+
Skills 是 Claude Code 的功能扩展,通过它你可以:
|
|
89
|
+
- 用自然语言描述需求,无需记住命令
|
|
90
|
+
- 智能化的任务管理和状态更新
|
|
91
|
+
- 更友好的交互体验
|
|
92
|
+
|
|
93
|
+
### 可用的 Skills
|
|
94
|
+
|
|
95
|
+
目前提供以下 Skills:
|
|
96
|
+
|
|
97
|
+
| Skill | 适用角色 | 功能 | 文档 |
|
|
98
|
+
|-------|---------|-----|------|
|
|
99
|
+
| **zentao-dev** | 开发工程师 | 任务管理、Bug解决、项目管理 | [文档](./.claude/skills/zentao-dev/) |
|
|
100
|
+
|
|
101
|
+
### 如何使用 Skills?
|
|
102
|
+
|
|
103
|
+
1. **安装 MCP 服务器**(如果还没安装):
|
|
104
|
+
```bash
|
|
105
|
+
npm install -g @zzp123/mcp-zentao
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
2. **配置 Claude Desktop**(参考下方 MCP 配置)
|
|
109
|
+
|
|
110
|
+
3. **在 Claude Code 中使用**:
|
|
111
|
+
```
|
|
112
|
+
你:查看我今天的任务
|
|
113
|
+
AI:[显示任务列表]
|
|
114
|
+
|
|
115
|
+
你:开始处理任务 #123
|
|
116
|
+
AI:✓ 任务已更新为"进行中"状态
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
详细使用指南请查看 [Skills 文档](./.claude/skills/zentao-dev/README.md)
|
|
120
|
+
|
|
121
|
+
## 📋 版本历史
|
|
122
|
+
|
|
123
|
+
查看完整的版本更新历史,请访问 [CHANGELOG.md](./CHANGELOG.md)
|
|
124
|
+
|
|
125
|
+
**最新版本**: v1.17.6 - 2025-11-10
|
|
126
|
+
- 💬 评论工具完全统一(移除所有便捷工具,统一使用 addComment)
|
|
127
|
+
- 📊 完整版 91个工具,PM版 39个,QA版 26个,Dev版 38个
|
|
128
|
+
- 🎯 累计减少约 106行重复代码,完全统一评论API
|
|
129
|
+
|
|
130
|
+
**近期版本**:
|
|
131
|
+
- v1.17.5 - 评论工具简化(移除 addBugComment 和 addTaskComment)
|
|
132
|
+
- v1.17.4 - 产品经理版移除工单管理(42个工具)
|
|
133
|
+
- v1.17.3 - 测试工程师版移除构建和工单管理(29个工具)
|
|
134
|
+
|
|
135
|
+
## 使用方法
|
|
136
|
+
|
|
137
|
+
### 首次使用(配置禅道信息)
|
|
138
|
+
|
|
139
|
+
首次使用时,需要提供禅道的配置信息:
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
zentao '{"config":{"url":"https://your-zentao-url","username":"your-username","password":"your-password","apiVersion":"v1"},"name":"张三","age":25,"skills":["编程","设计"]}'
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
配置信息会被保存在用户目录下的 `.zentao/config.json` 文件中,后续使用时无需再次提供。
|
|
146
|
+
|
|
147
|
+
### 后续使用
|
|
148
|
+
|
|
149
|
+
配置完成后,只需要提供任务相关的信息即可:
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
zentao '{"name":"张三","age":25,"skills":["编程","设计"]}'
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### 更新配置
|
|
156
|
+
|
|
157
|
+
如果需要更新禅道配置信息,只需要再次提供 config 参数即可:
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
zentao '{"config":{"url":"https://new-zentao-url","username":"new-username","password":"new-password","apiVersion":"v1"},"name":"张三","age":25,"skills":["编程","设计"]}'
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## 配置文件位置
|
|
164
|
+
|
|
165
|
+
配置文件保存在用户目录下的 `.zentao/config.json` 文件中:
|
|
166
|
+
|
|
167
|
+
- Windows: `C:\Users\你的用户名\.zentao\config.json`
|
|
168
|
+
- macOS/Linux: `~/.zentao/config.json`
|
|
169
|
+
|
|
170
|
+
## 功能特性
|
|
171
|
+
|
|
172
|
+
- 支持配置信息的持久化存储
|
|
173
|
+
- 自动管理禅道API的认证信息
|
|
174
|
+
- 提供任务创建、更新、完成等功能
|
|
175
|
+
- 支持Bug跟踪和处理
|
|
176
|
+
- 完整的类型定义支持
|
|
177
|
+
|
|
178
|
+
## 注意事项
|
|
179
|
+
|
|
180
|
+
- 配置文件中包含敏感信息,请确保文件权限设置正确
|
|
181
|
+
- 建议定期更新密码,以确保安全性
|
|
182
|
+
- 如遇到问题,可以删除配置文件重新配置
|
|
183
|
+
|
|
184
|
+
## 许可证
|
|
185
|
+
|
|
186
|
+
MIT
|
|
187
|
+
|
|
188
|
+
## 特点
|
|
189
|
+
|
|
190
|
+
- 完整的禅道API封装
|
|
191
|
+
- 简单易用的接口设计
|
|
192
|
+
- 类型安全(TypeScript支持)
|
|
193
|
+
- 完善的错误处理
|
|
194
|
+
- 自动化的认证管理
|
|
195
|
+
|
|
196
|
+
## 与其他项目的区别
|
|
197
|
+
|
|
198
|
+
不同于通用的数据库操作工具(如 mcp-mysql-server),本项目专注于提供:
|
|
199
|
+
|
|
200
|
+
1. 禅道系统特定的业务功能
|
|
201
|
+
2. 高级别的API抽象
|
|
202
|
+
3. 完整的禅道工作流支持
|
|
203
|
+
4. 开箱即用的禅道集成方案
|
|
204
|
+
|
|
205
|
+
## 本地开发
|
|
206
|
+
|
|
207
|
+
1. 克隆仓库
|
|
208
|
+
```bash
|
|
209
|
+
git clone https://github.com/bigtian/mcp-zentao.git
|
|
210
|
+
cd mcp-zentao
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
2. 安装依赖
|
|
214
|
+
```bash
|
|
215
|
+
npm install
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
3. 运行测试
|
|
219
|
+
```bash
|
|
220
|
+
npm test
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
4. 构建项目
|
|
224
|
+
```bash
|
|
225
|
+
npm run build
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
## Docker 使用
|
|
229
|
+
|
|
230
|
+
### 使用 docker-compose(推荐)
|
|
231
|
+
|
|
232
|
+
1. 复制环境变量模板并修改配置
|
|
233
|
+
```bash
|
|
234
|
+
cp .env.example .env
|
|
235
|
+
# 编辑 .env 文件,填入你的禅道系统配置
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
2. 启动服务
|
|
239
|
+
```bash
|
|
240
|
+
docker-compose up -d
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
3. 查看日志
|
|
244
|
+
```bash
|
|
245
|
+
docker-compose logs -f
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
### 手动使用 Docker
|
|
249
|
+
|
|
250
|
+
1. 构建镜像
|
|
251
|
+
```bash
|
|
252
|
+
docker build -t mcp-zentao .
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
2. 运行容器
|
|
256
|
+
```bash
|
|
257
|
+
docker run -d \
|
|
258
|
+
--name mcp-zentao \
|
|
259
|
+
-p 3000:3000 \
|
|
260
|
+
-e ZENTAO_URL=your-zentao-url \
|
|
261
|
+
-e ZENTAO_USERNAME=your-username \
|
|
262
|
+
-e ZENTAO_PASSWORD=your-password \
|
|
263
|
+
-e ZENTAO_API_VERSION=v1 \
|
|
264
|
+
-v $(pwd)/logs:/app/logs \
|
|
265
|
+
mcp-zentao
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
### 在 Cursor IDE 中配置
|
|
269
|
+
|
|
270
|
+
在 Cursor IDE 的配置文件中添加以下配置:
|
|
271
|
+
|
|
272
|
+
```json
|
|
273
|
+
{
|
|
274
|
+
"mcpServers": {
|
|
275
|
+
"zentao": {
|
|
276
|
+
"url": "http://localhost:3000"
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
## 🚦 传输方式:STDIO 与 HTTP 双支持
|
|
283
|
+
|
|
284
|
+
默认使用 STDIO 传输(适合本地/CLI 场景)。如果需要通过 URL 与 MCP 客户端通信,可启用 HTTP 传输。两种配置方式任选其一:
|
|
285
|
+
|
|
286
|
+
**方式 1:环境变量**
|
|
287
|
+
- `MCP_TRANSPORT=http` 启用 HTTP(默认 `stdio`)
|
|
288
|
+
- `MCP_HTTP_PORT`(或 `MCP_PORT`):端口,默认 3000
|
|
289
|
+
- `MCP_HTTP_HOST`(或 `MCP_HOST`):监听地址,默认 `0.0.0.0`
|
|
290
|
+
- `MCP_HTTP_PATH`:路由前缀,默认 `/mcp`
|
|
291
|
+
- 安全可选:`MCP_ALLOWED_HOSTS`、`MCP_ALLOWED_ORIGINS`(逗号分隔),`MCP_DNS_PROTECTION=true`
|
|
292
|
+
|
|
293
|
+
示例:
|
|
294
|
+
```bash
|
|
295
|
+
MCP_TRANSPORT=http MCP_HTTP_PORT=3000 MCP_HTTP_PATH=/mcp zentao --config "{\"url\":\"http://your-zentao\",\"username\":\"u\",\"password\":\"p\",\"apiVersion\":\"v1\"}"
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
**方式 2:CLI 参数**
|
|
299
|
+
- `--transport http`
|
|
300
|
+
- `--http-port 3000`(或 `--port`)
|
|
301
|
+
- `--http-host 0.0.0.0`(或 `--host`)
|
|
302
|
+
- `--http-path /mcp`
|
|
303
|
+
- 安全可选:`--allowed-hosts host1,host2`,`--allowed-origins http://a,http://b`,`--dns-protection true`
|
|
304
|
+
|
|
305
|
+
示例:
|
|
306
|
+
```bash
|
|
307
|
+
zentao --transport http --http-port 3000 --http-path /mcp --config "{\"url\":\"http://your-zentao\",\"username\":\"u\",\"password\":\"p\",\"apiVersion\":\"v1\"}"
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
HTTP 模式下客户端需要连接到 `http://<host>:<port><path>`(默认 `http://localhost:3000/mcp`)。
|
|
311
|
+
|
|
312
|
+
**方式 3:交互式初始化(最便捷)**
|
|
313
|
+
|
|
314
|
+
运行一次交互式向导,选择 STDIO/HTTP 并保存到 `~/.zentao/transport.json`,后续无需再带参数:
|
|
315
|
+
```bash
|
|
316
|
+
zentao --init-transport
|
|
317
|
+
# 或 zentao-dev / zentao-pm / zentao-qa 均可
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
交互式示例:
|
|
321
|
+
```
|
|
322
|
+
=== MCP 传输方式初始化 ===
|
|
323
|
+
选择传输方式 (1) stdio (2) http [1]: 2
|
|
324
|
+
HTTP 端口 [3000]: 4000
|
|
325
|
+
HTTP 路径前缀 (如 /mcp) [/mcp]: /zentao
|
|
326
|
+
监听地址 [0.0.0.0]: 127.0.0.1
|
|
327
|
+
允许的 Host 列表(逗号分隔,可留空): localhost:4000
|
|
328
|
+
允许的 Origin 列表(逗号分隔,可留空): https://example.com
|
|
329
|
+
启用 DNS Rebinding 防护? (y/N): y
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
最终配置会写入 `~/.zentao/transport.json`,示例:
|
|
333
|
+
```json
|
|
334
|
+
{
|
|
335
|
+
"transport": "http",
|
|
336
|
+
"host": "127.0.0.1",
|
|
337
|
+
"port": 4000,
|
|
338
|
+
"path": "/zentao",
|
|
339
|
+
"allowedHosts": ["localhost:4000"],
|
|
340
|
+
"allowedOrigins": ["https://example.com"],
|
|
341
|
+
"dnsProtection": true
|
|
342
|
+
}
|
|
343
|
+
```
|
|
344
|
+
该文件优先级低于环境变量/CLI 参数,因此仍可通过临时参数覆盖。
|
|
345
|
+
|
|
346
|
+
## 基本使用
|
|
347
|
+
|
|
348
|
+
```typescript
|
|
349
|
+
import { ZentaoAPI } from '@zzp123/mcp-zentao';
|
|
350
|
+
|
|
351
|
+
// 创建API实例
|
|
352
|
+
const api = new ZentaoAPI({
|
|
353
|
+
url: 'https://your-zentao-url', // 你的禅道系统URL
|
|
354
|
+
username: 'your-username', // 用户名
|
|
355
|
+
password: 'your-password', // 密码
|
|
356
|
+
apiVersion: 'v1' // API版本,默认为v1
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
// 获取我的任务列表
|
|
360
|
+
async function getMyTasks() {
|
|
361
|
+
try {
|
|
362
|
+
const tasks = await api.getMyTasks();
|
|
363
|
+
console.log('我的任务:', tasks);
|
|
364
|
+
} catch (error) {
|
|
365
|
+
console.error('获取任务失败:', error);
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
// 获取我的Bug列表
|
|
370
|
+
async function getMyBugs() {
|
|
371
|
+
try {
|
|
372
|
+
const bugs = await api.getMyBugs();
|
|
373
|
+
console.log('我的Bug:', bugs);
|
|
374
|
+
} catch (error) {
|
|
375
|
+
console.error('获取Bug失败:', error);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
// 完成任务
|
|
380
|
+
async function finishTask(taskId: number) {
|
|
381
|
+
try {
|
|
382
|
+
await api.finishTask(taskId);
|
|
383
|
+
console.log('任务已完成');
|
|
384
|
+
} catch (error) {
|
|
385
|
+
console.error('完成任务失败:', error);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
// 解决Bug (v1.4.0+)
|
|
390
|
+
async function resolveBug(bugId: number) {
|
|
391
|
+
try {
|
|
392
|
+
const result = await api.resolveBug(bugId, {
|
|
393
|
+
resolution: 'fixed',
|
|
394
|
+
resolvedBuild: 'trunk',
|
|
395
|
+
assignedTo: 'admin',
|
|
396
|
+
comment: '问题已修复'
|
|
397
|
+
});
|
|
398
|
+
console.log('Bug已解决:', result);
|
|
399
|
+
} catch (error) {
|
|
400
|
+
console.error('解决Bug失败:', error);
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// 上传剪贴板图片 (v1.3.1+)
|
|
405
|
+
async function uploadClipboardImage() {
|
|
406
|
+
try {
|
|
407
|
+
// 注意:需要先复制图片到系统剪贴板
|
|
408
|
+
const result = await api.uploadFile({
|
|
409
|
+
filename: 'screenshot.png',
|
|
410
|
+
uid: 'optional-uid'
|
|
411
|
+
});
|
|
412
|
+
console.log('图片已上传:', result);
|
|
413
|
+
} catch (error) {
|
|
414
|
+
console.error('上传失败:', error);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
## API文档
|
|
420
|
+
|
|
421
|
+
### ZentaoAPI 类
|
|
422
|
+
|
|
423
|
+
#### 构造函数
|
|
424
|
+
|
|
425
|
+
```typescript
|
|
426
|
+
constructor(config: {
|
|
427
|
+
url: string; // 禅道系统URL
|
|
428
|
+
username: string; // 用户名
|
|
429
|
+
password: string; // 密码
|
|
430
|
+
apiVersion?: string; // API版本,默认为v1
|
|
431
|
+
})
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
#### 方法
|
|
435
|
+
|
|
436
|
+
1. `getMyTasks(): Promise<Task[]>`
|
|
437
|
+
- 获取当前用户的任务列表
|
|
438
|
+
- 返回: Promise<Task[]>
|
|
439
|
+
|
|
440
|
+
2. `getMyBugs(): Promise<Bug[]>`
|
|
441
|
+
- 获取当前用户的Bug列表
|
|
442
|
+
- 返回: Promise<Bug[]>
|
|
443
|
+
|
|
444
|
+
3. `finishTask(taskId: number): Promise<void>`
|
|
445
|
+
- 完成指定ID的任务
|
|
446
|
+
- 参数: taskId - 任务ID
|
|
447
|
+
- 返回: Promise<void>
|
|
448
|
+
|
|
449
|
+
4. `resolveBug(bugId: number, resolution: ResolveBugRequest): Promise<Bug>` (v1.4.0+)
|
|
450
|
+
- 解决指定ID的Bug
|
|
451
|
+
- 参数:
|
|
452
|
+
- bugId - Bug ID
|
|
453
|
+
- resolution - Bug解决方案
|
|
454
|
+
- 返回: Promise<Bug> - 返回更新后的Bug对象
|
|
455
|
+
|
|
456
|
+
5. `uploadFile(request: UploadFileRequest): Promise<FileUploadResponse>` (v1.2.0+)
|
|
457
|
+
- 上传文件到禅道
|
|
458
|
+
- 参数: UploadFileRequest - 文件上传请求
|
|
459
|
+
- 返回: Promise<FileUploadResponse> - 包含文件ID和访问URL
|
|
460
|
+
|
|
461
|
+
6. `downloadFile(fileId: number): Promise<Buffer>` (v1.2.0+)
|
|
462
|
+
- 从禅道下载文件
|
|
463
|
+
- 参数: fileId - 文件ID
|
|
464
|
+
- 返回: Promise<Buffer> - 文件的二进制数据
|
|
465
|
+
|
|
466
|
+
### 类型定义
|
|
467
|
+
|
|
468
|
+
```typescript
|
|
469
|
+
interface Task {
|
|
470
|
+
id: number;
|
|
471
|
+
name: string;
|
|
472
|
+
status: string;
|
|
473
|
+
pri: number;
|
|
474
|
+
assignedTo: string;
|
|
475
|
+
deadline: string;
|
|
476
|
+
// ... 其他任务属性
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
interface Bug {
|
|
480
|
+
id: number;
|
|
481
|
+
title: string;
|
|
482
|
+
status: string;
|
|
483
|
+
severity: number;
|
|
484
|
+
resolvedBy: string;
|
|
485
|
+
resolution: string;
|
|
486
|
+
// ... 其他Bug属性
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
// v1.4.0+
|
|
490
|
+
type ResolutionType = 'fixed' | 'bydesign' | 'duplicate' | 'external' | 'notrepro' | 'postponed' | 'willnotfix';
|
|
491
|
+
|
|
492
|
+
interface ResolveBugRequest {
|
|
493
|
+
resolution: ResolutionType; // 解决方案(必填)
|
|
494
|
+
resolvedBuild?: string; // 解决版本(resolution='fixed'时必填)
|
|
495
|
+
assignedTo?: string; // 指派给
|
|
496
|
+
comment?: string; // 备注
|
|
497
|
+
duplicateBug?: number; // 重复Bug的ID(resolution='duplicate'时必填)
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
// v1.2.0+
|
|
501
|
+
interface UploadFileRequest {
|
|
502
|
+
file: Buffer; // 文件数据
|
|
503
|
+
filename: string; // 文件名
|
|
504
|
+
uid?: string; // 唯一标识符(可选)
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
interface FileUploadResponse {
|
|
508
|
+
id: number; // 文件ID
|
|
509
|
+
url: string; // 文件访问URL
|
|
510
|
+
}
|
|
511
|
+
```
|
|
512
|
+
|
|
513
|
+
## 注意事项
|
|
514
|
+
|
|
515
|
+
1. 确保提供正确的禅道系统URL和API版本
|
|
516
|
+
2. 用户名和密码需要有相应的API访问权限
|
|
517
|
+
3. 所有API调用都是异步的,需要使用async/await或Promise处理
|
|
518
|
+
4. 错误处理建议使用try/catch进行捕获
|
|
519
|
+
|
|
520
|
+
## 开发环境
|
|
521
|
+
|
|
522
|
+
- Node.js >= 14.0.0
|
|
523
|
+
- TypeScript >= 4.0.0
|
|
524
|
+
|
|
525
|
+
## 贡献
|
|
526
|
+
|
|
527
|
+
欢迎提交Issue和Pull Request!
|
package/dist/index-dev.js
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
-
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
3
|
import { z } from "zod";
|
|
5
4
|
import { ZentaoAPI } from './api/zentaoApi.js';
|
|
6
5
|
import { loadConfig, saveConfig } from './config.js';
|
|
6
|
+
import { interactiveInitTransport, startServerTransport } from './serverTransport.js';
|
|
7
7
|
// 解析命令行参数
|
|
8
8
|
const args = process.argv.slice(2);
|
|
9
9
|
let configData = null;
|
|
10
|
+
if (await interactiveInitTransport({ args })) {
|
|
11
|
+
process.exit(0);
|
|
12
|
+
}
|
|
10
13
|
// 查找 --config 参数
|
|
11
14
|
const configIndex = args.indexOf('--config');
|
|
12
15
|
if (configIndex !== -1 && configIndex + 1 < args.length) {
|
|
@@ -783,9 +786,7 @@ server.tool("deleteComment", "删除评论 - 只能删除自己的评论,管
|
|
|
783
786
|
}]
|
|
784
787
|
};
|
|
785
788
|
});
|
|
786
|
-
|
|
787
|
-
const transport = new StdioServerTransport();
|
|
788
|
-
await server.connect(transport).catch(err => {
|
|
789
|
+
await startServerTransport(server, { args }).catch(err => {
|
|
789
790
|
process.stderr.write('[FATAL] MCP server failed: ' + String(err) + '\n');
|
|
790
791
|
process.exit(1);
|
|
791
792
|
});
|
package/dist/index-pm.js
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
-
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
3
|
import { z } from "zod";
|
|
5
4
|
import { ZentaoAPI } from './api/zentaoApi.js';
|
|
6
5
|
import { loadConfig, saveConfig } from './config.js';
|
|
6
|
+
import { interactiveInitTransport, startServerTransport } from './serverTransport.js';
|
|
7
7
|
// 解析命令行参数
|
|
8
8
|
const args = process.argv.slice(2);
|
|
9
9
|
let configData = null;
|
|
10
|
+
if (await interactiveInitTransport({ args })) {
|
|
11
|
+
process.exit(0);
|
|
12
|
+
}
|
|
10
13
|
// 查找 --config 参数
|
|
11
14
|
const configIndex = args.indexOf('--config');
|
|
12
15
|
if (configIndex !== -1 && configIndex + 1 < args.length) {
|
|
@@ -908,9 +911,7 @@ server.tool("deleteComment", "删除评论 - 只能删除自己的评论,管
|
|
|
908
911
|
}]
|
|
909
912
|
};
|
|
910
913
|
});
|
|
911
|
-
|
|
912
|
-
const transport = new StdioServerTransport();
|
|
913
|
-
await server.connect(transport).catch(err => {
|
|
914
|
+
await startServerTransport(server, { args }).catch(err => {
|
|
914
915
|
process.stderr.write('[FATAL] MCP server failed: ' + String(err) + '\n');
|
|
915
916
|
process.exit(1);
|
|
916
917
|
});
|
package/dist/index-qa.js
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
-
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
3
|
import { z } from "zod";
|
|
5
4
|
import { ZentaoAPI } from './api/zentaoApi.js';
|
|
6
5
|
import { loadConfig, saveConfig } from './config.js';
|
|
6
|
+
import { interactiveInitTransport, startServerTransport } from './serverTransport.js';
|
|
7
7
|
// 解析命令行参数
|
|
8
8
|
const args = process.argv.slice(2);
|
|
9
9
|
let configData = null;
|
|
10
|
+
if (await interactiveInitTransport({ args })) {
|
|
11
|
+
process.exit(0);
|
|
12
|
+
}
|
|
10
13
|
// 查找 --config 参数
|
|
11
14
|
const configIndex = args.indexOf('--config');
|
|
12
15
|
if (configIndex !== -1 && configIndex + 1 < args.length) {
|
|
@@ -720,6 +723,7 @@ server.tool("deleteComment", "删除评论 - 只能删除自己的评论,管
|
|
|
720
723
|
}]
|
|
721
724
|
};
|
|
722
725
|
});
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
+
await startServerTransport(server, { args }).catch(err => {
|
|
727
|
+
process.stderr.write('[FATAL] MCP server failed: ' + String(err) + '\n');
|
|
728
|
+
process.exit(1);
|
|
729
|
+
});
|
package/dist/index.js
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
-
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
3
|
import { z } from "zod";
|
|
5
4
|
import { ZentaoAPI } from './api/zentaoApi.js';
|
|
6
5
|
import { loadConfig, saveConfig } from './config.js';
|
|
7
6
|
import { withApi } from './mcpHelpers.js';
|
|
7
|
+
import { interactiveInitTransport, startServerTransport } from './serverTransport.js';
|
|
8
8
|
// 解析命令行参数
|
|
9
9
|
const args = process.argv.slice(2);
|
|
10
10
|
let configData = null;
|
|
11
|
+
if (await interactiveInitTransport({ args })) {
|
|
12
|
+
process.exit(0);
|
|
13
|
+
}
|
|
11
14
|
// 查找 --config 参数
|
|
12
15
|
const configIndex = args.indexOf('--config');
|
|
13
16
|
if (configIndex !== -1 && configIndex + 1 < args.length) {
|
|
@@ -1540,9 +1543,7 @@ server.tool("deleteComment", "删除评论 - 只能删除自己的评论,管
|
|
|
1540
1543
|
}]
|
|
1541
1544
|
};
|
|
1542
1545
|
});
|
|
1543
|
-
|
|
1544
|
-
const transport = new StdioServerTransport();
|
|
1545
|
-
await server.connect(transport).catch(err => {
|
|
1546
|
+
await startServerTransport(server, { args }).catch(err => {
|
|
1546
1547
|
process.stderr.write('[FATAL] MCP server failed: ' + String(err) + '\n');
|
|
1547
1548
|
process.exit(1);
|
|
1548
1549
|
});
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
interface TransportLogger {
|
|
3
|
+
info?: (message: string) => void;
|
|
4
|
+
error?: (message: string) => void;
|
|
5
|
+
}
|
|
6
|
+
export interface StartTransportOptions {
|
|
7
|
+
args?: string[];
|
|
8
|
+
logger?: TransportLogger;
|
|
9
|
+
}
|
|
10
|
+
export interface InteractiveInitOptions {
|
|
11
|
+
args?: string[];
|
|
12
|
+
logger?: TransportLogger;
|
|
13
|
+
}
|
|
14
|
+
export declare function startServerTransport(server: McpServer, options?: StartTransportOptions): Promise<void>;
|
|
15
|
+
/**
|
|
16
|
+
* 交互式初始化:让用户选择 STDIO / HTTP 及 HTTP 参数,并将结果写入 ~/.zentao/transport.json
|
|
17
|
+
* 返回 true 表示已处理(应在入口处直接 exit)
|
|
18
|
+
*/
|
|
19
|
+
export declare function interactiveInitTransport(options?: InteractiveInitOptions): Promise<boolean>;
|
|
20
|
+
export {};
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { createServer } from 'node:http';
|
|
2
|
+
import { randomUUID } from 'node:crypto';
|
|
3
|
+
import readline from 'node:readline';
|
|
4
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
5
|
+
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
6
|
+
import { loadTransportConfig, saveTransportConfig } from './transportConfig.js';
|
|
7
|
+
const DEFAULT_HTTP_PORT = 3000;
|
|
8
|
+
const DEFAULT_HTTP_PATH = '/mcp';
|
|
9
|
+
const getArgValue = (args, flag) => {
|
|
10
|
+
for (let i = 0; i < args.length; i++) {
|
|
11
|
+
const arg = args[i];
|
|
12
|
+
if (arg === flag) {
|
|
13
|
+
return args[i + 1];
|
|
14
|
+
}
|
|
15
|
+
if (arg.startsWith(`${flag}=`)) {
|
|
16
|
+
return arg.substring(flag.length + 1);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return undefined;
|
|
20
|
+
};
|
|
21
|
+
const parsePort = (value) => {
|
|
22
|
+
if (!value)
|
|
23
|
+
return undefined;
|
|
24
|
+
const parsed = Number(value);
|
|
25
|
+
if (Number.isFinite(parsed) && parsed > 0 && parsed <= 65535) {
|
|
26
|
+
return parsed;
|
|
27
|
+
}
|
|
28
|
+
return undefined;
|
|
29
|
+
};
|
|
30
|
+
export async function startServerTransport(server, options = {}) {
|
|
31
|
+
const args = options.args ?? [];
|
|
32
|
+
const logger = options.logger ?? {
|
|
33
|
+
info: (msg) => process.stdout.write(msg + '\n'),
|
|
34
|
+
error: (msg) => process.stderr.write(msg + '\n')
|
|
35
|
+
};
|
|
36
|
+
const fileConfig = loadTransportConfig();
|
|
37
|
+
const mode = (process.env.MCP_TRANSPORT || getArgValue(args, '--transport') || fileConfig?.transport || 'stdio').toLowerCase();
|
|
38
|
+
if (mode !== 'http') {
|
|
39
|
+
const transport = new StdioServerTransport();
|
|
40
|
+
await server.connect(transport);
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
const portStr = process.env.MCP_HTTP_PORT
|
|
44
|
+
|| process.env.MCP_PORT
|
|
45
|
+
|| getArgValue(args, '--http-port')
|
|
46
|
+
|| getArgValue(args, '--port')
|
|
47
|
+
|| (fileConfig?.port ? String(fileConfig.port) : undefined);
|
|
48
|
+
const host = process.env.MCP_HTTP_HOST
|
|
49
|
+
|| process.env.MCP_HOST
|
|
50
|
+
|| getArgValue(args, '--http-host')
|
|
51
|
+
|| getArgValue(args, '--host')
|
|
52
|
+
|| fileConfig?.host
|
|
53
|
+
|| '0.0.0.0';
|
|
54
|
+
const pathSetting = process.env.MCP_HTTP_PATH
|
|
55
|
+
|| getArgValue(args, '--http-path')
|
|
56
|
+
|| fileConfig?.path
|
|
57
|
+
|| DEFAULT_HTTP_PATH;
|
|
58
|
+
const port = parsePort(portStr) ?? DEFAULT_HTTP_PORT;
|
|
59
|
+
const normalizedPath = pathSetting.startsWith('/') ? pathSetting : `/${pathSetting}`;
|
|
60
|
+
const allowedHosts = (process.env.MCP_ALLOWED_HOSTS || getArgValue(args, '--allowed-hosts'))?.split(',').map(h => h.trim()).filter(Boolean)
|
|
61
|
+
|| fileConfig?.allowedHosts;
|
|
62
|
+
const allowedOrigins = (process.env.MCP_ALLOWED_ORIGINS || getArgValue(args, '--allowed-origins'))?.split(',').map(o => o.trim()).filter(Boolean)
|
|
63
|
+
|| fileConfig?.allowedOrigins;
|
|
64
|
+
const enableDnsProtection = (process.env.MCP_DNS_PROTECTION || getArgValue(args, '--dns-protection')) === 'true'
|
|
65
|
+
|| Boolean(fileConfig?.dnsProtection);
|
|
66
|
+
const httpTransport = new StreamableHTTPServerTransport({
|
|
67
|
+
sessionIdGenerator: () => randomUUID(),
|
|
68
|
+
enableJsonResponse: true,
|
|
69
|
+
allowedHosts,
|
|
70
|
+
allowedOrigins,
|
|
71
|
+
enableDnsRebindingProtection: enableDnsProtection
|
|
72
|
+
});
|
|
73
|
+
await server.connect(httpTransport);
|
|
74
|
+
const httpServer = createServer(async (req, res) => {
|
|
75
|
+
const reqPath = new URL(req.url || '/', 'http://localhost').pathname;
|
|
76
|
+
if (reqPath !== normalizedPath) {
|
|
77
|
+
res.writeHead(404).end('Not Found');
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
try {
|
|
81
|
+
await httpTransport.handleRequest(req, res);
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
logger.error?.(`[ERROR] HTTP transport request failed: ${String(error)}`);
|
|
85
|
+
res.writeHead(500).end('Internal Server Error');
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
httpServer.listen(port, host, () => {
|
|
89
|
+
const displayHost = host === '0.0.0.0' ? 'localhost' : host;
|
|
90
|
+
logger.info?.(`[INFO] MCP HTTP server listening at http://${displayHost}:${port}${normalizedPath}`);
|
|
91
|
+
});
|
|
92
|
+
httpServer.on('error', (err) => {
|
|
93
|
+
logger.error?.(`[ERROR] HTTP server error: ${String(err)}`);
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* 交互式初始化:让用户选择 STDIO / HTTP 及 HTTP 参数,并将结果写入 ~/.zentao/transport.json
|
|
98
|
+
* 返回 true 表示已处理(应在入口处直接 exit)
|
|
99
|
+
*/
|
|
100
|
+
export async function interactiveInitTransport(options = {}) {
|
|
101
|
+
const args = options.args ?? [];
|
|
102
|
+
const logger = options.logger ?? {
|
|
103
|
+
info: (msg) => process.stdout.write(msg + '\n'),
|
|
104
|
+
error: (msg) => process.stderr.write(msg + '\n')
|
|
105
|
+
};
|
|
106
|
+
if (!args.includes('--init-transport')) {
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
const rl = readline.createInterface({
|
|
110
|
+
input: process.stdin,
|
|
111
|
+
output: process.stdout
|
|
112
|
+
});
|
|
113
|
+
const ask = (query) => new Promise(resolve => rl.question(query, resolve));
|
|
114
|
+
try {
|
|
115
|
+
logger.info?.('=== MCP 传输方式初始化 ===');
|
|
116
|
+
const modeInput = await ask('选择传输方式 (1) stdio (2) http [1]: ');
|
|
117
|
+
const chosenMode = modeInput.trim() === '2' ? 'http' : 'stdio';
|
|
118
|
+
if (chosenMode === 'stdio') {
|
|
119
|
+
saveTransportConfig({ transport: 'stdio' });
|
|
120
|
+
logger.info?.('已保存:使用 STDIO 传输');
|
|
121
|
+
return true;
|
|
122
|
+
}
|
|
123
|
+
const portInput = await ask('HTTP 端口 [3000]: ');
|
|
124
|
+
const pathInput = await ask('HTTP 路径前缀 (如 /mcp) [/mcp]: ');
|
|
125
|
+
const hostInput = await ask('监听地址 [0.0.0.0]: ');
|
|
126
|
+
const allowedHostsInput = await ask('允许的 Host 列表(逗号分隔,可留空): ');
|
|
127
|
+
const allowedOriginsInput = await ask('允许的 Origin 列表(逗号分隔,可留空): ');
|
|
128
|
+
const dnsInput = await ask('启用 DNS Rebinding 防护? (y/N): ');
|
|
129
|
+
const port = parsePort(portInput?.trim()) ?? DEFAULT_HTTP_PORT;
|
|
130
|
+
const pathSetting = pathInput?.trim() || '/mcp';
|
|
131
|
+
const host = hostInput?.trim() || '0.0.0.0';
|
|
132
|
+
const allowedHosts = allowedHostsInput.split(',').map(s => s.trim()).filter(Boolean);
|
|
133
|
+
const allowedOrigins = allowedOriginsInput.split(',').map(s => s.trim()).filter(Boolean);
|
|
134
|
+
const dnsProtection = dnsInput.trim().toLowerCase() === 'y';
|
|
135
|
+
const config = {
|
|
136
|
+
transport: 'http',
|
|
137
|
+
host,
|
|
138
|
+
port,
|
|
139
|
+
path: pathSetting.startsWith('/') ? pathSetting : `/${pathSetting}`,
|
|
140
|
+
allowedHosts: allowedHosts.length ? allowedHosts : undefined,
|
|
141
|
+
allowedOrigins: allowedOrigins.length ? allowedOrigins : undefined,
|
|
142
|
+
dnsProtection
|
|
143
|
+
};
|
|
144
|
+
saveTransportConfig(config);
|
|
145
|
+
logger.info?.(`已保存:HTTP 模式 http://${host}:${port}${config.path}`);
|
|
146
|
+
return true;
|
|
147
|
+
}
|
|
148
|
+
finally {
|
|
149
|
+
rl.close();
|
|
150
|
+
}
|
|
151
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export type TransportMode = 'stdio' | 'http';
|
|
2
|
+
export interface TransportConfig {
|
|
3
|
+
transport: TransportMode;
|
|
4
|
+
host?: string;
|
|
5
|
+
port?: number;
|
|
6
|
+
path?: string;
|
|
7
|
+
allowedHosts?: string[];
|
|
8
|
+
allowedOrigins?: string[];
|
|
9
|
+
dnsProtection?: boolean;
|
|
10
|
+
}
|
|
11
|
+
export declare function saveTransportConfig(config: TransportConfig): void;
|
|
12
|
+
export declare function loadTransportConfig(): TransportConfig | null;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import os from 'os';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
const CONFIG_DIR = path.join(os.homedir(), '.zentao');
|
|
5
|
+
const TRANSPORT_FILE = path.join(CONFIG_DIR, 'transport.json');
|
|
6
|
+
export function saveTransportConfig(config) {
|
|
7
|
+
if (!fs.existsSync(CONFIG_DIR)) {
|
|
8
|
+
fs.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
9
|
+
}
|
|
10
|
+
fs.writeFileSync(TRANSPORT_FILE, JSON.stringify(config, null, 2));
|
|
11
|
+
}
|
|
12
|
+
export function loadTransportConfig() {
|
|
13
|
+
try {
|
|
14
|
+
if (fs.existsSync(TRANSPORT_FILE)) {
|
|
15
|
+
const config = JSON.parse(fs.readFileSync(TRANSPORT_FILE, 'utf-8'));
|
|
16
|
+
return config;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
// ignore parse errors, return null
|
|
21
|
+
}
|
|
22
|
+
return null;
|
|
23
|
+
}
|