@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 CHANGED
@@ -1,463 +1,527 @@
1
- # MCP-Zentao
2
-
3
- [![npm version](https://badge.fury.io/js/%40zzp123%2Fmcp-zentao.svg)](https://www.npmjs.com/package/@zzp123/mcp-zentao)
4
- [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](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
- ```typescript
285
- import { ZentaoAPI } from '@zzp123/mcp-zentao';
286
-
287
- // 创建API实例
288
- const api = new ZentaoAPI({
289
- url: 'https://your-zentao-url', // 你的禅道系统URL
290
- username: 'your-username', // 用户名
291
- password: 'your-password', // 密码
292
- apiVersion: 'v1' // API版本,默认为v1
293
- });
294
-
295
- // 获取我的任务列表
296
- async function getMyTasks() {
297
- try {
298
- const tasks = await api.getMyTasks();
299
- console.log('我的任务:', tasks);
300
- } catch (error) {
301
- console.error('获取任务失败:', error);
302
- }
303
- }
304
-
305
- // 获取我的Bug列表
306
- async function getMyBugs() {
307
- try {
308
- const bugs = await api.getMyBugs();
309
- console.log('我的Bug:', bugs);
310
- } catch (error) {
311
- console.error('获取Bug失败:', error);
312
- }
313
- }
314
-
315
- // 完成任务
316
- async function finishTask(taskId: number) {
317
- try {
318
- await api.finishTask(taskId);
319
- console.log('任务已完成');
320
- } catch (error) {
321
- console.error('完成任务失败:', error);
322
- }
323
- }
324
-
325
- // 解决Bug (v1.4.0+)
326
- async function resolveBug(bugId: number) {
327
- try {
328
- const result = await api.resolveBug(bugId, {
329
- resolution: 'fixed',
330
- resolvedBuild: 'trunk',
331
- assignedTo: 'admin',
332
- comment: '问题已修复'
333
- });
334
- console.log('Bug已解决:', result);
335
- } catch (error) {
336
- console.error('解决Bug失败:', error);
337
- }
338
- }
339
-
340
- // 上传剪贴板图片 (v1.3.1+)
341
- async function uploadClipboardImage() {
342
- try {
343
- // 注意:需要先复制图片到系统剪贴板
344
- const result = await api.uploadFile({
345
- filename: 'screenshot.png',
346
- uid: 'optional-uid'
347
- });
348
- console.log('图片已上传:', result);
349
- } catch (error) {
350
- console.error('上传失败:', error);
351
- }
352
- }
353
- ```
354
-
355
- ## API文档
356
-
357
- ### ZentaoAPI 类
358
-
359
- #### 构造函数
360
-
361
- ```typescript
362
- constructor(config: {
363
- url: string; // 禅道系统URL
364
- username: string; // 用户名
365
- password: string; // 密码
366
- apiVersion?: string; // API版本,默认为v1
367
- })
368
- ```
369
-
370
- #### 方法
371
-
372
- 1. `getMyTasks(): Promise<Task[]>`
373
- - 获取当前用户的任务列表
374
- - 返回: Promise<Task[]>
375
-
376
- 2. `getMyBugs(): Promise<Bug[]>`
377
- - 获取当前用户的Bug列表
378
- - 返回: Promise<Bug[]>
379
-
380
- 3. `finishTask(taskId: number): Promise<void>`
381
- - 完成指定ID的任务
382
- - 参数: taskId - 任务ID
383
- - 返回: Promise<void>
384
-
385
- 4. `resolveBug(bugId: number, resolution: ResolveBugRequest): Promise<Bug>` (v1.4.0+)
386
- - 解决指定ID的Bug
387
- - 参数:
388
- - bugId - Bug ID
389
- - resolution - Bug解决方案
390
- - 返回: Promise<Bug> - 返回更新后的Bug对象
391
-
392
- 5. `uploadFile(request: UploadFileRequest): Promise<FileUploadResponse>` (v1.2.0+)
393
- - 上传文件到禅道
394
- - 参数: UploadFileRequest - 文件上传请求
395
- - 返回: Promise<FileUploadResponse> - 包含文件ID和访问URL
396
-
397
- 6. `downloadFile(fileId: number): Promise<Buffer>` (v1.2.0+)
398
- - 从禅道下载文件
399
- - 参数: fileId - 文件ID
400
- - 返回: Promise<Buffer> - 文件的二进制数据
401
-
402
- ### 类型定义
403
-
404
- ```typescript
405
- interface Task {
406
- id: number;
407
- name: string;
408
- status: string;
409
- pri: number;
410
- assignedTo: string;
411
- deadline: string;
412
- // ... 其他任务属性
413
- }
414
-
415
- interface Bug {
416
- id: number;
417
- title: string;
418
- status: string;
419
- severity: number;
420
- resolvedBy: string;
421
- resolution: string;
422
- // ... 其他Bug属性
423
- }
424
-
425
- // v1.4.0+
426
- type ResolutionType = 'fixed' | 'bydesign' | 'duplicate' | 'external' | 'notrepro' | 'postponed' | 'willnotfix';
427
-
428
- interface ResolveBugRequest {
429
- resolution: ResolutionType; // 解决方案(必填)
430
- resolvedBuild?: string; // 解决版本(resolution='fixed'时必填)
431
- assignedTo?: string; // 指派给
432
- comment?: string; // 备注
433
- duplicateBug?: number; // 重复Bug的ID(resolution='duplicate'时必填)
434
- }
435
-
436
- // v1.2.0+
437
- interface UploadFileRequest {
438
- file: Buffer; // 文件数据
439
- filename: string; // 文件名
440
- uid?: string; // 唯一标识符(可选)
441
- }
442
-
443
- interface FileUploadResponse {
444
- id: number; // 文件ID
445
- url: string; // 文件访问URL
446
- }
447
- ```
448
-
449
- ## 注意事项
450
-
451
- 1. 确保提供正确的禅道系统URL和API版本
452
- 2. 用户名和密码需要有相应的API访问权限
453
- 3. 所有API调用都是异步的,需要使用async/await或Promise处理
454
- 4. 错误处理建议使用try/catch进行捕获
455
-
456
- ## 开发环境
457
-
458
- - Node.js >= 14.0.0
459
- - TypeScript >= 4.0.0
460
-
461
- ## 贡献
462
-
463
- 欢迎提交Issue和Pull Request!
1
+ # MCP-Zentao
2
+
3
+ [![npm version](https://badge.fury.io/js/%40zzp123%2Fmcp-zentao.svg)](https://www.npmjs.com/package/@zzp123/mcp-zentao)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](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
- // Start receiving messages on stdin and sending messages on stdout
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
- // Start receiving messages on stdin and sending messages on stdout
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
- // Start receiving messages on stdin and sending messages on stdout
724
- const transport = new StdioServerTransport();
725
- await server.connect(transport).catch(err => { process.stderr.write('[FATAL] MCP server failed: ' + String(err) + '\n'); process.exit(1); });
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
- // Start receiving messages on stdin and sending messages on stdout
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
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zzp123/mcp-zentao",
3
- "version": "1.18.7",
3
+ "version": "1.18.9",
4
4
  "description": "禅道项目管理系统的高级API集成包 - 完整版,包含所有94个工具。另有产品经理、测试工程师、开发工程师专用精简版本可选",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",