@zzp123/mcp-zentao 1.8.0 → 1.8.1

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.
@@ -9,8 +9,19 @@ export declare class ZentaoAPI {
9
9
  getMyTasks(status?: TaskStatus): Promise<Task[]>;
10
10
  getTaskDetail(taskId: number): Promise<Task>;
11
11
  getProducts(): Promise<Product[]>;
12
- getMyBugs(status?: BugStatus, productId?: number): Promise<Bug[]>;
13
- getBugDetail(bugId: number): Promise<Bug>;
12
+ getMyBugs(status?: BugStatus, productId?: number, page?: number, limit?: number): Promise<{
13
+ page: number;
14
+ total: number;
15
+ limit: number;
16
+ bugs: Bug[];
17
+ }>;
18
+ getProductBugs(productId: number, page?: number, limit?: number, status?: BugStatus): Promise<{
19
+ page: number;
20
+ total: number;
21
+ limit: number;
22
+ bugs: Bug[];
23
+ }>;
24
+ getBugDetail(bugId: number, fields?: 'basic' | 'detail' | 'full'): Promise<any>;
14
25
  resolveBug(bugId: number, resolution: ResolveBugRequest): Promise<Bug>;
15
26
  updateTask(taskId: number, update: TaskUpdate): Promise<Task>;
16
27
  finishTask(taskId: number, update?: TaskUpdate): Promise<Task>;
@@ -28,7 +39,7 @@ export declare class ZentaoAPI {
28
39
  linkBugsToPlan(planId: number, bugIds: number[]): Promise<Plan>;
29
40
  unlinkBugsFromPlan(planId: number, bugIds: number[]): Promise<Plan>;
30
41
  getProjectReleases(projectId: number): Promise<Release[]>;
31
- getStoryDetail(storyId: number): Promise<Story>;
42
+ getStoryDetail(storyId: number, fields?: 'basic' | 'detail' | 'full'): Promise<any>;
32
43
  createBug(productId: number, bug: CreateBugRequest): Promise<Bug>;
33
44
  getProjectExecutions(projectId: number): Promise<Execution[]>;
34
45
  createPlan(productId: number, plan: CreatePlanRequest): Promise<Plan>;
@@ -37,7 +48,12 @@ export declare class ZentaoAPI {
37
48
  deleteTask(taskId: number): Promise<{
38
49
  message: string;
39
50
  }>;
40
- getProductStories(productId: number): Promise<Story[]>;
51
+ getProductStories(productId: number, page?: number, limit?: number): Promise<{
52
+ page: number;
53
+ total: number;
54
+ limit: number;
55
+ stories: Story[];
56
+ }>;
41
57
  getProgramDetail(programId: number): Promise<Program>;
42
58
  createProgram(program: CreateProgramRequest): Promise<Program>;
43
59
  updateProgram(programId: number, update: UpdateProgramRequest): Promise<Program>;
@@ -111,7 +111,7 @@ export class ZentaoAPI {
111
111
  throw error;
112
112
  }
113
113
  }
114
- async getMyBugs(status, productId) {
114
+ async getMyBugs(status, productId, page, limit) {
115
115
  if (!productId) {
116
116
  // 如果没有提供产品ID,获取第一个可用的产品
117
117
  const products = await this.getProducts();
@@ -121,20 +121,36 @@ export class ZentaoAPI {
121
121
  productId = products[0].id;
122
122
  console.log(`使用第一个可用的产品ID: ${productId}`);
123
123
  }
124
+ // 默认每页20条,最多100条
125
+ const finalLimit = limit ? Math.min(limit, 100) : 20;
126
+ const finalPage = page || 1;
124
127
  const params = {
125
128
  assignedTo: this.config.username,
126
129
  status: status || 'all',
127
- product: productId
130
+ product: productId,
131
+ page: finalPage,
132
+ limit: finalLimit
128
133
  };
129
134
  try {
130
- console.log('正在获取Bug列表,参数:', params);
135
+ console.log(`正在获取Bug列表 (page=${finalPage}, limit=${finalLimit}),参数:`, params);
131
136
  const response = await this.request('GET', '/bugs', params);
132
- console.log('Bug列表响应:', response);
137
+ console.log(`Bug列表响应: 获取到 ${response.bugs?.length || 0} 条数据`);
133
138
  if (Array.isArray(response)) {
134
- return response;
139
+ // 如果返回的是数组,转换为标准格式
140
+ return {
141
+ page: finalPage,
142
+ total: response.length,
143
+ limit: finalLimit,
144
+ bugs: response
145
+ };
135
146
  }
136
147
  else if (response && typeof response === 'object' && Array.isArray(response.bugs)) {
137
- return response.bugs;
148
+ return {
149
+ page: response.page || finalPage,
150
+ total: response.total || response.bugs.length,
151
+ limit: response.limit || finalLimit,
152
+ bugs: response.bugs
153
+ };
138
154
  }
139
155
  throw new Error(`获取Bug列表失败: 响应格式不正确 ${JSON.stringify(response)}`);
140
156
  }
@@ -146,12 +162,80 @@ export class ZentaoAPI {
146
162
  throw error;
147
163
  }
148
164
  }
149
- async getBugDetail(bugId) {
165
+ async getProductBugs(productId, page, limit, status) {
166
+ try {
167
+ // 默认每页20条,最多100条
168
+ const finalLimit = limit ? Math.min(limit, 100) : 20;
169
+ const finalPage = page || 1;
170
+ console.log(`正在获取产品 ${productId} 的Bug列表 (page=${finalPage}, limit=${finalLimit})...`);
171
+ // 构建查询参数
172
+ const queryParams = new URLSearchParams();
173
+ queryParams.append('page', finalPage.toString());
174
+ queryParams.append('limit', finalLimit.toString());
175
+ if (status && status !== 'all') {
176
+ queryParams.append('status', status);
177
+ }
178
+ const response = await this.request('GET', `/products/${productId}/bugs?${queryParams.toString()}`);
179
+ console.log(`产品Bug列表响应: 获取到 ${response.bugs?.length || 0} 条数据`);
180
+ if (Array.isArray(response)) {
181
+ // 如果返回的是数组,转换为标准格式
182
+ return {
183
+ page: finalPage,
184
+ total: response.length,
185
+ limit: finalLimit,
186
+ bugs: response
187
+ };
188
+ }
189
+ else if (response && typeof response === 'object') {
190
+ if (Array.isArray(response.bugs)) {
191
+ return {
192
+ page: response.page || finalPage,
193
+ total: response.total || response.bugs.length,
194
+ limit: response.limit || finalLimit,
195
+ bugs: response.bugs
196
+ };
197
+ }
198
+ }
199
+ throw new Error(`获取产品Bug列表失败: 响应格式不正确 ${JSON.stringify(response)}`);
200
+ }
201
+ catch (error) {
202
+ console.error('获取产品Bug列表失败:', error);
203
+ throw error;
204
+ }
205
+ }
206
+ async getBugDetail(bugId, fields) {
150
207
  try {
151
- console.log(`正在获取Bug ${bugId} 的详情...`);
208
+ console.log(`正在获取Bug ${bugId} 的详情 (fields=${fields || 'full'})...`);
152
209
  const response = await this.request('GET', `/bugs/${bugId}`);
153
210
  console.log('Bug详情响应:', response);
154
- return response;
211
+ // 根据 fields 参数过滤返回的字段
212
+ const fieldLevel = fields || 'full';
213
+ if (fieldLevel === 'basic') {
214
+ // 基本信息:只返回核心字段
215
+ return {
216
+ id: response.id,
217
+ title: response.title,
218
+ product: response.product,
219
+ module: response.module,
220
+ status: response.status,
221
+ severity: response.severity,
222
+ pri: response.pri,
223
+ type: response.type,
224
+ assignedTo: response.assignedTo,
225
+ openedBy: response.openedBy,
226
+ openedDate: response.openedDate,
227
+ deadline: response.deadline
228
+ };
229
+ }
230
+ else if (fieldLevel === 'detail') {
231
+ // 详细信息:包含步骤描述,但排除不常用字段
232
+ const { files, cases, linkBugs, ...detailData } = response;
233
+ return detailData;
234
+ }
235
+ else {
236
+ // 完整信息:返回所有字段
237
+ return response;
238
+ }
155
239
  }
156
240
  catch (error) {
157
241
  console.error('获取Bug详情失败:', error);
@@ -414,12 +498,39 @@ export class ZentaoAPI {
414
498
  throw error;
415
499
  }
416
500
  }
417
- async getStoryDetail(storyId) {
501
+ async getStoryDetail(storyId, fields) {
418
502
  try {
419
- console.log(`正在获取需求 ${storyId} 的详情...`);
503
+ console.log(`正在获取需求 ${storyId} 的详情 (fields=${fields || 'full'})...`);
420
504
  const response = await this.request('GET', `/stories/${storyId}`);
421
505
  console.log('需求详情响应:', response);
422
- return response;
506
+ // 根据 fields 参数过滤返回的字段
507
+ const fieldLevel = fields || 'full';
508
+ if (fieldLevel === 'basic') {
509
+ // 基本信息:只返回核心字段
510
+ return {
511
+ id: response.id,
512
+ title: response.title,
513
+ product: response.product,
514
+ module: response.module,
515
+ status: response.status,
516
+ stage: response.stage,
517
+ pri: response.pri,
518
+ category: response.category,
519
+ estimate: response.estimate,
520
+ assignedTo: response.assignedTo,
521
+ openedBy: response.openedBy,
522
+ openedDate: response.openedDate
523
+ };
524
+ }
525
+ else if (fieldLevel === 'detail') {
526
+ // 详细信息:包含描述和验收标准,但不包含关联数据
527
+ const { executions, tasks, stages, children, ...detailData } = response;
528
+ return detailData;
529
+ }
530
+ else {
531
+ // 完整信息:返回所有字段
532
+ return response;
533
+ }
423
534
  }
424
535
  catch (error) {
425
536
  console.error('获取需求详情失败:', error);
@@ -526,17 +637,35 @@ export class ZentaoAPI {
526
637
  throw error;
527
638
  }
528
639
  }
529
- async getProductStories(productId) {
640
+ async getProductStories(productId, page, limit) {
530
641
  try {
531
- console.log(`正在获取产品 ${productId} 的需求列表...`);
532
- const response = await this.request('GET', `/products/${productId}/stories`);
533
- console.log('产品需求列表响应:', response);
642
+ // 默认每页20条,最多100条
643
+ const finalLimit = limit ? Math.min(limit, 100) : 20;
644
+ const finalPage = page || 1;
645
+ console.log(`正在获取产品 ${productId} 的需求列表 (page=${finalPage}, limit=${finalLimit})...`);
646
+ // 构建查询参数
647
+ const queryParams = new URLSearchParams();
648
+ queryParams.append('page', finalPage.toString());
649
+ queryParams.append('limit', finalLimit.toString());
650
+ const response = await this.request('GET', `/products/${productId}/stories?${queryParams.toString()}`);
651
+ console.log(`产品需求列表响应: 获取到 ${response.stories?.length || 0} 条数据`);
534
652
  if (Array.isArray(response)) {
535
- return response;
653
+ // 如果返回的是数组,转换为标准格式
654
+ return {
655
+ page: finalPage,
656
+ total: response.length,
657
+ limit: finalLimit,
658
+ stories: response
659
+ };
536
660
  }
537
661
  else if (response && typeof response === 'object') {
538
662
  if (Array.isArray(response.stories)) {
539
- return response.stories;
663
+ return {
664
+ page: response.page || finalPage,
665
+ total: response.total || response.stories.length,
666
+ limit: response.limit || finalLimit,
667
+ stories: response.stories
668
+ };
540
669
  }
541
670
  }
542
671
  throw new Error(`获取产品需求列表失败: 响应格式不正确 ${JSON.stringify(response)}`);
package/dist/index.js CHANGED
@@ -89,24 +89,80 @@ server.tool("getProducts", {}, async () => {
89
89
  // Add getMyBugs tool
90
90
  server.tool("getMyBugs", {
91
91
  status: z.enum(['active', 'resolved', 'closed', 'all']).optional(),
92
- productId: z.number().optional()
93
- }, async ({ status, productId }) => {
94
- if (!zentaoApi)
95
- throw new Error("Please initialize Zentao API first");
96
- const bugs = await zentaoApi.getMyBugs(status, productId);
92
+ productId: z.number().optional(),
93
+ page: z.number().optional().describe("页码,从1开始,默认为1"),
94
+ limit: z.number().optional().describe("每页数量,默认20,最大100")
95
+ }, async ({ status, productId, page, limit }) => {
96
+ if (!zentaoApi)
97
+ throw new Error("Please initialize Zentao API first");
98
+ const result = await zentaoApi.getMyBugs(status, productId, page, limit);
99
+ // 返回分页信息和数据
100
+ const summary = {
101
+ summary: `当前第 ${result.page} 页,共 ${result.total} 个Bug,本页显示 ${result.bugs.length} 个`,
102
+ pagination: {
103
+ page: result.page,
104
+ limit: result.limit,
105
+ total: result.total,
106
+ totalPages: Math.ceil(result.total / result.limit)
107
+ },
108
+ bugs: result.bugs
109
+ };
110
+ return {
111
+ content: [{ type: "text", text: JSON.stringify(summary, null, 2) }]
112
+ };
113
+ });
114
+ // Add getProductBugs tool
115
+ server.tool("getProductBugs", {
116
+ productId: z.number(),
117
+ page: z.number().optional().describe("页码,从1开始,默认为1"),
118
+ limit: z.number().optional().describe("每页数量,默认20,最大100"),
119
+ status: z.enum(['active', 'resolved', 'closed', 'all']).optional()
120
+ }, async ({ productId, page, limit, status }) => {
121
+ if (!zentaoApi)
122
+ throw new Error("Please initialize Zentao API first");
123
+ const result = await zentaoApi.getProductBugs(productId, page, limit, status);
124
+ // 返回分页信息和数据
125
+ const summary = {
126
+ summary: `当前第 ${result.page} 页,共 ${result.total} 个Bug,本页显示 ${result.bugs.length} 个`,
127
+ pagination: {
128
+ page: result.page,
129
+ limit: result.limit,
130
+ total: result.total,
131
+ totalPages: Math.ceil(result.total / result.limit)
132
+ },
133
+ bugs: result.bugs
134
+ };
97
135
  return {
98
- content: [{ type: "text", text: JSON.stringify(bugs, null, 2) }]
136
+ content: [{ type: "text", text: JSON.stringify(summary, null, 2) }]
99
137
  };
100
138
  });
101
139
  // Add getBugDetail tool
102
140
  server.tool("getBugDetail", {
103
- bugId: z.number()
104
- }, async ({ bugId }) => {
105
- if (!zentaoApi)
106
- throw new Error("Please initialize Zentao API first");
107
- const bug = await zentaoApi.getBugDetail(bugId);
141
+ bugId: z.number(),
142
+ fields: z.enum(['basic', 'detail', 'full']).optional().describe(`
143
+ 字段级别:
144
+ - basic: 基本信息(id, title, status, severity, pri等核心字段)
145
+ - detail: 详细信息(包含steps步骤,但不包含files/cases/linkBugs等关联数据)
146
+ - full: 完整信息(包含所有字段,默认值)
147
+ `.trim())
148
+ }, async ({ bugId, fields }) => {
149
+ if (!zentaoApi)
150
+ throw new Error("Please initialize Zentao API first");
151
+ const bug = await zentaoApi.getBugDetail(bugId, fields);
152
+ // 添加说明信息
153
+ const result = {
154
+ fieldLevel: fields || 'full',
155
+ bug: bug
156
+ };
157
+ // 根据字段级别添加提示
158
+ if (fields === 'basic') {
159
+ result.note = '仅显示基本信息,如需完整内容请使用 fields=detail 或 fields=full';
160
+ }
161
+ else if (fields === 'detail') {
162
+ result.note = '显示详细信息但不包含关联数据(files/cases/linkBugs),如需完整内容请使用 fields=full';
163
+ }
108
164
  return {
109
- content: [{ type: "text", text: JSON.stringify(bug, null, 2) }]
165
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
110
166
  };
111
167
  });
112
168
  // Add resolveBug tool
@@ -360,13 +416,31 @@ server.tool("getProjectReleases", {
360
416
  });
361
417
  // Add getStoryDetail tool
362
418
  server.tool("getStoryDetail", {
363
- storyId: z.number()
364
- }, async ({ storyId }) => {
365
- if (!zentaoApi)
366
- throw new Error("Please initialize Zentao API first");
367
- const story = await zentaoApi.getStoryDetail(storyId);
419
+ storyId: z.number(),
420
+ fields: z.enum(['basic', 'detail', 'full']).optional().describe(`
421
+ 字段级别:
422
+ - basic: 基本信息(id, title, status, stage, pri等核心字段)
423
+ - detail: 详细信息(包含spec和verify,但不包含executions/tasks/stages/children等关联数据)
424
+ - full: 完整信息(包含所有字段,默认值)
425
+ `.trim())
426
+ }, async ({ storyId, fields }) => {
427
+ if (!zentaoApi)
428
+ throw new Error("Please initialize Zentao API first");
429
+ const story = await zentaoApi.getStoryDetail(storyId, fields);
430
+ // 添加说明信息
431
+ const result = {
432
+ fieldLevel: fields || 'full',
433
+ story: story
434
+ };
435
+ // 根据字段级别添加提示
436
+ if (fields === 'basic') {
437
+ result.note = '仅显示基本信息,如需完整内容请使用 fields=detail 或 fields=full';
438
+ }
439
+ else if (fields === 'detail') {
440
+ result.note = '显示详细信息但不包含关联数据(executions/tasks/stages/children),如需完整内容请使用 fields=full';
441
+ }
368
442
  return {
369
- content: [{ type: "text", text: JSON.stringify(story, null, 2) }]
443
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
370
444
  };
371
445
  });
372
446
  // Add createBug tool
@@ -542,13 +616,26 @@ server.tool("deleteTask", {
542
616
  });
543
617
  // Add getProductStories tool
544
618
  server.tool("getProductStories", {
545
- productId: z.number()
546
- }, async ({ productId }) => {
547
- if (!zentaoApi)
548
- throw new Error("Please initialize Zentao API first");
549
- const stories = await zentaoApi.getProductStories(productId);
619
+ productId: z.number(),
620
+ page: z.number().optional().describe("页码,从1开始,默认为1"),
621
+ limit: z.number().optional().describe("每页数量,默认20,最大100")
622
+ }, async ({ productId, page, limit }) => {
623
+ if (!zentaoApi)
624
+ throw new Error("Please initialize Zentao API first");
625
+ const result = await zentaoApi.getProductStories(productId, page, limit);
626
+ // 返回分页信息和数据
627
+ const summary = {
628
+ summary: `当前第 ${result.page} 页,共 ${result.total} 条需求,本页显示 ${result.stories.length} 条`,
629
+ pagination: {
630
+ page: result.page,
631
+ limit: result.limit,
632
+ total: result.total,
633
+ totalPages: Math.ceil(result.total / result.limit)
634
+ },
635
+ stories: result.stories
636
+ };
550
637
  return {
551
- content: [{ type: "text", text: JSON.stringify(stories, null, 2) }]
638
+ content: [{ type: "text", text: JSON.stringify(summary, null, 2) }]
552
639
  };
553
640
  });
554
641
  // Program tools
@@ -5,8 +5,8 @@ export class BugService {
5
5
  this.api = api;
6
6
  }
7
7
  async getMyBugs(status) {
8
- const bugs = await this.api.getMyBugs(status);
9
- return bugs.map(bug => this.enrichBugData(bug));
8
+ const result = await this.api.getMyBugs(status);
9
+ return result.bugs.map((bug) => this.enrichBugData(bug));
10
10
  }
11
11
  async getBugDetail(bugId) {
12
12
  const bug = await this.api.getBugDetail(bugId);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zzp123/mcp-zentao",
3
- "version": "1.8.0",
3
+ "version": "1.8.1",
4
4
  "description": "禅道项目管理系统的高级API集成包,提供任务管理、Bug跟踪等功能的完整封装,专为Cursor IDE设计的MCP扩展",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",