@zzp123/mcp-zentao 1.18.10 → 1.18.12
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/dist/index-pm.js +147 -0
- package/dist/roleConfig.js +18 -6
- package/package.json +1 -1
- package/scripts/generate-role-versions.cjs +6 -1
package/dist/index-pm.js
CHANGED
|
@@ -270,6 +270,153 @@ server.tool("deleteStory", "删除需求 - 支持软件需求(story)和用户需
|
|
|
270
270
|
throw new Error("Please initialize Zentao API first");
|
|
271
271
|
return { content: [{ type: "text", text: JSON.stringify(await zentaoApi.deleteStory(storyId), null, 2) }] };
|
|
272
272
|
});
|
|
273
|
+
server.tool("getMyProfile", {}, async () => {
|
|
274
|
+
if (!zentaoApi)
|
|
275
|
+
throw new Error("Please initialize Zentao API first");
|
|
276
|
+
return { content: [{ type: "text", text: JSON.stringify(await zentaoApi.getMyProfile(), null, 2) }] };
|
|
277
|
+
});
|
|
278
|
+
server.tool("getUsers", {
|
|
279
|
+
page: z.number().optional(),
|
|
280
|
+
limit: z.number().optional()
|
|
281
|
+
}, async (params) => {
|
|
282
|
+
if (!zentaoApi)
|
|
283
|
+
throw new Error("Please initialize Zentao API first");
|
|
284
|
+
return { content: [{ type: "text", text: JSON.stringify(await zentaoApi.getUsers(params), null, 2) }] };
|
|
285
|
+
});
|
|
286
|
+
server.tool("uploadFile", {
|
|
287
|
+
filePath: z.string().optional(),
|
|
288
|
+
base64Data: z.string().optional(),
|
|
289
|
+
filename: z.string().optional(),
|
|
290
|
+
uid: z.string().optional()
|
|
291
|
+
}, async ({ filePath, base64Data, filename, uid }) => {
|
|
292
|
+
if (!zentaoApi)
|
|
293
|
+
throw new Error("Please initialize Zentao API first");
|
|
294
|
+
const fs = await import('fs');
|
|
295
|
+
const path = await import('path');
|
|
296
|
+
let fileBuffer;
|
|
297
|
+
let finalFilename;
|
|
298
|
+
let savedPath;
|
|
299
|
+
// 如果提供了 base64 数据(复制的图片)
|
|
300
|
+
if (base64Data) {
|
|
301
|
+
// 创建 img 文件夹(如果不存在)
|
|
302
|
+
const imgDir = path.join(process.cwd(), 'img');
|
|
303
|
+
if (!fs.existsSync(imgDir)) {
|
|
304
|
+
fs.mkdirSync(imgDir, { recursive: true });
|
|
305
|
+
}
|
|
306
|
+
// 处理 base64 数据
|
|
307
|
+
const matches = base64Data.match(/^data:image\/(\w+);base64,(.+)$/);
|
|
308
|
+
if (matches) {
|
|
309
|
+
const ext = matches[1];
|
|
310
|
+
const data = matches[2];
|
|
311
|
+
fileBuffer = Buffer.from(data, 'base64');
|
|
312
|
+
finalFilename = filename || `image_${Date.now()}.${ext}`;
|
|
313
|
+
}
|
|
314
|
+
else {
|
|
315
|
+
// 直接的 base64 数据,没有 data URL 前缀
|
|
316
|
+
fileBuffer = Buffer.from(base64Data, 'base64');
|
|
317
|
+
finalFilename = filename || `image_${Date.now()}.png`;
|
|
318
|
+
}
|
|
319
|
+
// 保存到 img 文件夹
|
|
320
|
+
savedPath = path.join(imgDir, finalFilename);
|
|
321
|
+
fs.writeFileSync(savedPath, fileBuffer);
|
|
322
|
+
}
|
|
323
|
+
// 如果提供了文件路径
|
|
324
|
+
else if (filePath) {
|
|
325
|
+
fileBuffer = fs.readFileSync(filePath);
|
|
326
|
+
finalFilename = path.basename(filePath);
|
|
327
|
+
}
|
|
328
|
+
else {
|
|
329
|
+
throw new Error("必须提供 filePath 或 base64Data 参数");
|
|
330
|
+
}
|
|
331
|
+
const result = await zentaoApi.uploadFile({
|
|
332
|
+
file: fileBuffer,
|
|
333
|
+
filename: finalFilename,
|
|
334
|
+
uid
|
|
335
|
+
});
|
|
336
|
+
// 生成禅道需要的 HTML 格式
|
|
337
|
+
const fileId = result.id;
|
|
338
|
+
const baseUrl = zentaoApi.getConfig().url;
|
|
339
|
+
const imageUrl = `${baseUrl}/zentao/entao/api.php?m=file&f=read&t=png&fileID=${fileId}`;
|
|
340
|
+
const imageHtml = `<p><img onload="setImageSize(this,0)" src="${imageUrl}" alt="${finalFilename}" /></p>`;
|
|
341
|
+
const response = {
|
|
342
|
+
upload: result,
|
|
343
|
+
fileId: fileId,
|
|
344
|
+
imageUrl: imageUrl,
|
|
345
|
+
imageHtml: imageHtml,
|
|
346
|
+
tip: `更新 Bug 描述时,请使用 imageHtml 字段中的 HTML 代码。图片会被包裹在 <p> 标签中以确保正确显示。`
|
|
347
|
+
};
|
|
348
|
+
if (savedPath) {
|
|
349
|
+
response.savedPath = savedPath;
|
|
350
|
+
}
|
|
351
|
+
return { content: [{ type: "text", text: JSON.stringify(response, null, 2) }] };
|
|
352
|
+
});
|
|
353
|
+
server.tool("getComments", "获取评论列表 - 获取指定对象的所有评论", {
|
|
354
|
+
objectType: z.enum([
|
|
355
|
+
'story', // 软件需求
|
|
356
|
+
'requirement', // 用户需求
|
|
357
|
+
'task', // 任务
|
|
358
|
+
'bug', // Bug
|
|
359
|
+
'testcase', // 测试用例
|
|
360
|
+
'testtask', // 测试单
|
|
361
|
+
'todo', // 待办
|
|
362
|
+
'doc', // 文档
|
|
363
|
+
'execution', // 执行/迭代
|
|
364
|
+
'project', // 项目
|
|
365
|
+
'doctemplate' // 文档模板
|
|
366
|
+
]).describe("对象类型"),
|
|
367
|
+
objectID: z.number().describe("对象ID")
|
|
368
|
+
}, async ({ objectType, objectID }) => {
|
|
369
|
+
if (!zentaoApi)
|
|
370
|
+
throw new Error("Please initialize Zentao API first");
|
|
371
|
+
const comments = await zentaoApi.getComments(objectType, objectID);
|
|
372
|
+
return {
|
|
373
|
+
content: [{
|
|
374
|
+
type: "text",
|
|
375
|
+
text: JSON.stringify({
|
|
376
|
+
total: comments.length,
|
|
377
|
+
objectType,
|
|
378
|
+
objectID,
|
|
379
|
+
comments
|
|
380
|
+
}, null, 2)
|
|
381
|
+
}]
|
|
382
|
+
};
|
|
383
|
+
});
|
|
384
|
+
server.tool("addComment", "添加评论 - 为需求、任务、Bug等对象添加评论", {
|
|
385
|
+
objectType: z.enum([
|
|
386
|
+
'story', // 软件需求
|
|
387
|
+
'requirement', // 用户需求
|
|
388
|
+
'task', // 任务
|
|
389
|
+
'bug', // Bug
|
|
390
|
+
'testcase', // 测试用例
|
|
391
|
+
'testtask', // 测试单
|
|
392
|
+
'todo', // 待办
|
|
393
|
+
'doc', // 文档
|
|
394
|
+
'execution', // 执行/迭代
|
|
395
|
+
'project', // 项目
|
|
396
|
+
'doctemplate' // 文档模板
|
|
397
|
+
]).describe("对象类型"),
|
|
398
|
+
objectID: z.number().describe("对象ID"),
|
|
399
|
+
comment: z.string().describe("评论内容,支持HTML格式"),
|
|
400
|
+
uid: z.string().optional().describe("唯一标识符(可选)")
|
|
401
|
+
}, async ({ objectType, objectID, comment, uid }) => {
|
|
402
|
+
if (!zentaoApi)
|
|
403
|
+
throw new Error("Please initialize Zentao API first");
|
|
404
|
+
const result = await zentaoApi.addComment({
|
|
405
|
+
objectType,
|
|
406
|
+
objectID,
|
|
407
|
+
comment,
|
|
408
|
+
uid
|
|
409
|
+
});
|
|
410
|
+
return {
|
|
411
|
+
content: [{
|
|
412
|
+
type: "text",
|
|
413
|
+
text: JSON.stringify({
|
|
414
|
+
message: '评论添加成功',
|
|
415
|
+
comment: result
|
|
416
|
+
}, null, 2)
|
|
417
|
+
}]
|
|
418
|
+
};
|
|
419
|
+
});
|
|
273
420
|
await startServerTransport(server, { args }).catch(err => {
|
|
274
421
|
process.stderr.write('[FATAL] MCP server failed: ' + String(err) + '\n');
|
|
275
422
|
process.exit(1);
|
package/dist/roleConfig.js
CHANGED
|
@@ -82,7 +82,7 @@ export const TOOL_CATEGORIES = {
|
|
|
82
82
|
};
|
|
83
83
|
// 角色权限配置
|
|
84
84
|
export const ROLE_PERMISSIONS = {
|
|
85
|
-
//
|
|
85
|
+
// 产品经理:初始化+项目+需求+执行+计划+评论+用户+文件
|
|
86
86
|
pm: [
|
|
87
87
|
'initZentao',
|
|
88
88
|
'getProjects',
|
|
@@ -95,14 +95,26 @@ export const ROLE_PERMISSIONS = {
|
|
|
95
95
|
'linkStoriesToPlan',
|
|
96
96
|
'createExecution',
|
|
97
97
|
'deleteExecution',
|
|
98
|
-
'deletePlan'
|
|
99
|
-
|
|
100
|
-
|
|
98
|
+
'deletePlan',
|
|
99
|
+
'getComments',
|
|
100
|
+
'addComment',
|
|
101
|
+
'getUsers',
|
|
102
|
+
'getMyProfile',
|
|
103
|
+
'uploadFile'
|
|
104
|
+
],
|
|
105
|
+
// 测试工程师:初始化+任务查看+缺陷查看+测试用例完整管理+用户信息+文件
|
|
101
106
|
qa: [
|
|
102
107
|
'initZentao',
|
|
103
|
-
'
|
|
108
|
+
'getTaskDetail',
|
|
109
|
+
'getBugDetail',
|
|
110
|
+
'getProductTestCases',
|
|
111
|
+
'getTestCaseDetail',
|
|
104
112
|
'createTestCase',
|
|
105
|
-
'
|
|
113
|
+
'updateTestCase',
|
|
114
|
+
'deleteTestCase',
|
|
115
|
+
'getUsers',
|
|
116
|
+
'getMyProfile',
|
|
117
|
+
'uploadFile'
|
|
106
118
|
],
|
|
107
119
|
// 开发工程师:初始化+缺陷处理
|
|
108
120
|
dev: [
|
package/package.json
CHANGED
|
@@ -38,7 +38,12 @@ const ROLE_PERMISSIONS = {
|
|
|
38
38
|
'linkStoriesToPlan', // 产品计划关联需求
|
|
39
39
|
'createExecution', // 创建执行
|
|
40
40
|
'deleteExecution', // 删除执行
|
|
41
|
-
'deletePlan' // 删除产品计划
|
|
41
|
+
'deletePlan', // 删除产品计划
|
|
42
|
+
'getComments', // 查看评论
|
|
43
|
+
'addComment', // 添加评论
|
|
44
|
+
'getUsers', // 获取用户列表
|
|
45
|
+
'getMyProfile', // 获取我的个人信息
|
|
46
|
+
'uploadFile' // 上传文件
|
|
42
47
|
],
|
|
43
48
|
qa: [
|
|
44
49
|
...TOOL_CATEGORIES.init,
|