@zzp123/mcp-zentao 1.18.8 → 1.18.10

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.
@@ -55,7 +55,9 @@ export class ZentaoAPI {
55
55
  }
56
56
  catch (error) {
57
57
  if (axios.isAxiosError(error)) {
58
- throw new Error(`请求失败: ${error.response?.data?.message || error.message}`);
58
+ const errData = error.response?.data;
59
+ const errMsg = typeof errData === 'object' ? JSON.stringify(errData) : (errData || error.message);
60
+ throw new Error(`请求失败: ${error.response?.status} - ${errMsg}`);
59
61
  }
60
62
  throw error;
61
63
  }
@@ -477,18 +479,18 @@ export class ZentaoAPI {
477
479
  }
478
480
  async getProductPlans(productId, branch, begin, end, status, parent) {
479
481
  try {
480
- const data = {};
482
+ const params = {};
481
483
  if (branch !== undefined)
482
- data.branch = branch;
484
+ params.branch = branch;
483
485
  if (begin)
484
- data.begin = begin;
486
+ params.begin = begin;
485
487
  if (end)
486
- data.end = end;
488
+ params.end = end;
487
489
  if (status)
488
- data.status = status;
490
+ params.status = status;
489
491
  if (parent !== undefined)
490
- data.parent = parent;
491
- const response = await this.request('POST', `/products/${productId}/plans`, undefined, data);
492
+ params.parent = parent;
493
+ const response = await this.request('GET', `/products/${productId}/plans`, params);
492
494
  if (Array.isArray(response)) {
493
495
  return response;
494
496
  }
@@ -505,7 +507,12 @@ export class ZentaoAPI {
505
507
  }
506
508
  async createStory(story) {
507
509
  try {
508
- const response = await this.request('POST', '/stories', undefined, story);
510
+ // 禅道API要求:如果没有指定reviewer,需要设置needNotReview=true跳过评审
511
+ const requestData = {
512
+ ...story,
513
+ needNotReview: true
514
+ };
515
+ const response = await this.request('POST', '/stories', undefined, requestData);
509
516
  return response;
510
517
  }
511
518
  catch (error) {
@@ -532,7 +539,7 @@ export class ZentaoAPI {
532
539
  }
533
540
  async deletePlan(planId) {
534
541
  try {
535
- const response = await this.request('DELETE', `/products/plans/${planId}`);
542
+ const response = await this.request('DELETE', `/productplans/${planId}`);
536
543
  return response;
537
544
  }
538
545
  catch (error) {
@@ -705,14 +712,8 @@ export class ZentaoAPI {
705
712
  */
706
713
  async changeStory(storyId, update) {
707
714
  try {
708
- // 自动处理评审人问题
709
- const updateData = { ...update };
710
- // 如果用户没有设置评审人,也没有指定无需评审,则自动跳过评审
711
- if (!updateData.reviewer && updateData.needNotReview !== true) {
712
- updateData.needNotReview = true;
713
- }
714
- // 使用 PUT /stories/:id 接口
715
- const response = await this.request('PUT', `/stories/${storyId}`, undefined, updateData);
715
+ // 使用 POST /stories/:id/change 接口(变更需求专用接口)
716
+ const response = await this.request('POST', `/stories/${storyId}/change`, undefined, update);
716
717
  return response;
717
718
  }
718
719
  catch (error) {
package/dist/index-dev.js CHANGED
@@ -3,6 +3,7 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
3
3
  import { z } from "zod";
4
4
  import { ZentaoAPI } from './api/zentaoApi.js';
5
5
  import { loadConfig, saveConfig } from './config.js';
6
+ import { withApi } from './mcpHelpers.js';
6
7
  import { interactiveInitTransport, startServerTransport } from './serverTransport.js';
7
8
  // 解析命令行参数
8
9
  const args = process.argv.slice(2);
@@ -33,6 +34,7 @@ const server = new McpServer({
33
34
  });
34
35
  // Initialize ZentaoAPI instance
35
36
  let zentaoApi = null;
37
+ const getApi = () => zentaoApi;
36
38
  export default async function main(params) {
37
39
  // 如果传入了配置信息,就保存它
38
40
  if (params.config) {
@@ -55,24 +57,20 @@ server.tool("initZentao", {}, async ({}) => {
55
57
  });
56
58
  server.tool("getMyTasks", {
57
59
  status: z.enum(['wait', 'doing', 'done', 'all']).optional()
58
- }, async ({ status }) => {
59
- if (!zentaoApi)
60
- throw new Error("Please initialize Zentao API first");
61
- const tasks = await zentaoApi.getMyTasks(status);
60
+ }, withApi(getApi, async (api, { status }) => {
61
+ const tasks = await api.getMyTasks(status);
62
62
  return {
63
63
  content: [{ type: "text", text: JSON.stringify(tasks, null, 2) }]
64
64
  };
65
- });
65
+ }));
66
66
  server.tool("getTaskDetail", {
67
67
  taskId: z.number()
68
- }, async ({ taskId }) => {
69
- if (!zentaoApi)
70
- throw new Error("Please initialize Zentao API first");
71
- const task = await zentaoApi.getTaskDetail(taskId);
68
+ }, withApi(getApi, async (api, { taskId }) => {
69
+ const task = await api.getTaskDetail(taskId);
72
70
  return {
73
71
  content: [{ type: "text", text: JSON.stringify(task, null, 2) }]
74
72
  };
75
- });
73
+ }));
76
74
  server.tool("getMyBugs", "获取我的Bug列表 - 固定查询指派给我的Bug(status='assigntome'),固定每页20条", {
77
75
  productId: z.number().optional().describe("产品ID,默认使用第二个产品"),
78
76
  page: z.number().optional().describe("页码,从1开始,默认为1"),
@@ -100,11 +98,11 @@ server.tool("getMyBugs", "获取我的Bug列表 - 固定查询指派给我的Bug
100
98
  });
101
99
  server.tool("getBugDetail", {
102
100
  bugId: z.number(),
103
- fields: z.enum(['basic', 'detail', 'full']).optional().describe(`
104
- 字段级别:
105
- - basic: 基本信息(id, title, status, severity, pri等核心字段)
106
- - detail: 详细信息(包含steps步骤,但不包含files/cases/linkBugs等关联数据)
107
- - full: 完整信息(包含所有字段,默认值)
101
+ fields: z.enum(['basic', 'detail', 'full']).optional().describe(`
102
+ 字段级别:
103
+ - basic: 基本信息(id, title, status, severity, pri等核心字段)
104
+ - detail: 详细信息(包含steps步骤,但不包含files/cases/linkBugs等关联数据)
105
+ - full: 完整信息(包含所有字段,默认值)
108
106
  `.trim())
109
107
  }, async ({ bugId, fields }) => {
110
108
  if (!zentaoApi)
@@ -225,29 +223,26 @@ server.tool("getProjectReleases", {
225
223
  };
226
224
  });
227
225
  server.tool("getStoryDetail", "获取需求详情 - 支持软件需求(story)和用户需求(requirement),自动识别类型", {
228
- storyId: z.number().describe("需求ID(可以是story或requirement类型)"),
229
- fields: z.enum(['basic', 'detail', 'full']).optional().describe(`
230
- 字段级别:
231
- - basic: 基本信息(id, title, status, stage, pri等核心字段)
232
- - detail: 详细信息(包含spec和verify,但不包含executions/tasks/stages/children等关联数据)
233
- - full: 完整信息(包含所有字段,默认值)
234
- `.trim())
235
- }, async ({ storyId, fields }) => {
226
+ storyId: z.number().describe("需求ID(可以是story或requirement类型)")
227
+ }, async ({ storyId }) => {
236
228
  if (!zentaoApi)
237
229
  throw new Error("Please initialize Zentao API first");
238
- const story = await zentaoApi.getStoryDetail(storyId, fields);
239
- // 添加说明信息
230
+ const story = await zentaoApi.getStoryDetail(storyId, 'basic');
231
+ // 只返回基本信息
240
232
  const result = {
241
- fieldLevel: fields || 'full',
242
- story: story
233
+ id: story.id,
234
+ title: story.title,
235
+ status: story.status,
236
+ stage: story.stage,
237
+ pri: story.pri,
238
+ type: story.type,
239
+ category: story.category,
240
+ product: story.product,
241
+ productName: story.productName,
242
+ openedBy: story.openedBy,
243
+ openedDate: story.openedDate,
244
+ assignedTo: story.assignedTo
243
245
  };
244
- // 根据字段级别添加提示
245
- if (fields === 'basic') {
246
- result.note = '仅显示基本信息,如需完整内容请使用 fields=detail 或 fields=full';
247
- }
248
- else if (fields === 'detail') {
249
- result.note = '显示详细信息但不包含关联数据(executions/tasks/stages/children),如需完整内容请使用 fields=full';
250
- }
251
246
  return {
252
247
  content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
253
248
  };
@@ -258,8 +253,17 @@ server.tool("getProjectExecutions", {
258
253
  if (!zentaoApi)
259
254
  throw new Error("Please initialize Zentao API first");
260
255
  const executions = await zentaoApi.getProjectExecutions(projectId);
256
+ // 只返回基本字段
257
+ const simplified = executions.map((e) => ({
258
+ id: e.id,
259
+ name: e.name,
260
+ status: e.status,
261
+ begin: e.begin,
262
+ end: e.end,
263
+ progress: e.progress
264
+ }));
261
265
  return {
262
- content: [{ type: "text", text: JSON.stringify(executions, null, 2) }]
266
+ content: [{ type: "text", text: JSON.stringify(simplified, null, 2) }]
263
267
  };
264
268
  });
265
269
  server.tool("getProjects", {
@@ -269,8 +273,10 @@ server.tool("getProjects", {
269
273
  if (!zentaoApi)
270
274
  throw new Error("Please initialize Zentao API first");
271
275
  const projects = await zentaoApi.getProjects(page, limit);
276
+ // 只返回 id 和 name 字段
277
+ const simplified = projects.map((p) => ({ id: p.id, name: p.name }));
272
278
  return {
273
- content: [{ type: "text", text: JSON.stringify(projects, null, 2) }]
279
+ content: [{ type: "text", text: JSON.stringify(simplified, null, 2) }]
274
280
  };
275
281
  });
276
282
  server.tool("deleteTask", {
@@ -341,6 +347,7 @@ server.tool("deleteProject", { projectId: z.number() }, async ({ projectId }) =>
341
347
  server.tool("createExecution", {
342
348
  projectId: z.number(), project: z.number(), name: z.string(), code: z.string(),
343
349
  begin: z.string(), end: z.string(), days: z.number().optional(), lifetime: z.string().optional(),
350
+ products: z.array(z.number()).describe("关联产品ID数组"),
344
351
  PO: z.string().optional(), PM: z.string().optional(), QD: z.string().optional(),
345
352
  RD: z.string().optional(), teamMembers: z.array(z.string()).optional(),
346
353
  desc: z.string().optional(), acl: z.string().optional(), whitelist: z.array(z.string()).optional()