@zzp123/mcp-zentao 1.15.0 → 1.16.0

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/CHANGELOG.md CHANGED
@@ -5,6 +5,34 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [1.16.0] - 2025-11-07
9
+
10
+ ### Fixed
11
+ - **修复评论功能 - 使用正确的 REST API v1 接口** 🔧
12
+ - 之前版本使用的是 HTML 接口,现已改用标准 REST API
13
+ - 修复了评论创建、更新、删除等功能
14
+
15
+ ### Added
16
+ - **完整的评论管理工具** 💬
17
+ - `getComments`: 获取指定对象的评论列表
18
+ - `getCommentDetail`: 获取单条评论详情
19
+ - `addComment`: 创建评论(支持11种对象类型)
20
+ - `updateComment`: 更新评论内容
21
+ - `deleteComment`: 删除评论
22
+ - 便捷工具:`addStoryComment`、`addTaskComment`、`addBugComment`
23
+
24
+ ### Changed
25
+ - **更新评论接口实现**
26
+ - 使用标准 REST API:`POST /comment`、`GET /comments/{type}/{id}`
27
+ - 支持更多对象类型:新增 execution、project 类型
28
+ - 返回完整的评论对象信息(包含 id、actor、date 等)
29
+
30
+ ### Technical
31
+ - 更新 `Comment` 接口定义
32
+ - 添加 `UpdateCommentRequest` 接口
33
+ - 添加 `CommentListResponse` 接口
34
+ - 所有评论方法使用统一的 `request()` 方法,保持 Token 认证
35
+
8
36
  ## [1.15.0] - 2025-11-07
9
37
 
10
38
  ### Added
@@ -1,4 +1,4 @@
1
- import { AssignFeedbackRequest, Bug, BugStatus, Build, CloseFeedbackRequest, CreateBuildRequest, CreateBugRequest, CreateExecutionRequest, CreateFeedbackRequest, CreatePlanRequest, CreateProductRequest, CreateProgramRequest, CreateProjectRequest, CreateStoryRequest, CreateTaskRequest, CreateTestCaseRequest, CreateTicketRequest, CreateUserRequest, Execution, Feedback, FileUploadResponse, ModulesResponse, ModuleType, Plan, Product, Program, Project, Release, ResolveBugRequest, Story, Task, TaskStatus, TaskUpdate, TestCase, Ticket, UpdateBuildRequest, UpdateBugRequest, UpdateExecutionRequest, UpdateFeedbackRequest, UpdatePlanRequest, UpdateProductRequest, UpdateProgramRequest, UpdateProjectRequest, ChangeStoryRequest, UpdateTestCaseRequest, UpdateTicketRequest, UpdateUserRequest, UploadFileRequest, UserDetail, ZentaoConfig } from '../types/zentao';
1
+ import { AddCommentRequest, AssignFeedbackRequest, Bug, BugStatus, Build, CloseFeedbackRequest, Comment, CommentObjectType, CreateBuildRequest, CreateBugRequest, CreateExecutionRequest, CreateFeedbackRequest, CreatePlanRequest, CreateProductRequest, CreateProgramRequest, CreateProjectRequest, CreateStoryRequest, CreateTaskRequest, CreateTestCaseRequest, CreateTicketRequest, CreateUserRequest, Execution, Feedback, FileUploadResponse, ModulesResponse, ModuleType, Plan, Product, Program, Project, Release, ResolveBugRequest, Story, Task, TaskStatus, TaskUpdate, TestCase, Ticket, UpdateBuildRequest, UpdateBugRequest, UpdateCommentRequest, UpdateExecutionRequest, UpdateFeedbackRequest, UpdatePlanRequest, UpdateProductRequest, UpdateProgramRequest, UpdateProjectRequest, ChangeStoryRequest, UpdateTestCaseRequest, UpdateTicketRequest, UpdateUserRequest, UploadFileRequest, UserDetail, ZentaoConfig } from '../types/zentao';
2
2
  export declare class ZentaoAPI {
3
3
  private config;
4
4
  private client;
@@ -239,6 +239,32 @@ export declare class ZentaoAPI {
239
239
  getModules(type: ModuleType, id: number, fields?: string): Promise<ModulesResponse>;
240
240
  uploadFile(uploadRequest: UploadFileRequest): Promise<FileUploadResponse>;
241
241
  downloadFile(fileId: number): Promise<Buffer>;
242
- addComment(objectType: string, objectID: number, comment: string): Promise<any>;
242
+ /**
243
+ * 获取评论列表
244
+ * GET /comments/{objectType}/{objectID}
245
+ */
246
+ getComments(objectType: CommentObjectType, objectID: number): Promise<Comment[]>;
247
+ /**
248
+ * 获取单条评论详情
249
+ * GET /comment/{id}
250
+ */
251
+ getCommentDetail(commentId: number): Promise<Comment>;
252
+ /**
253
+ * 创建评论
254
+ * POST /comment
255
+ */
256
+ addComment(request: AddCommentRequest): Promise<Comment>;
257
+ /**
258
+ * 更新评论
259
+ * PUT /comment/{id}
260
+ */
261
+ updateComment(commentId: number, update: UpdateCommentRequest): Promise<Comment>;
262
+ /**
263
+ * 删除评论
264
+ * DELETE /comment/{id}
265
+ */
266
+ deleteComment(commentId: number): Promise<{
267
+ message: string;
268
+ }>;
243
269
  getConfig(): ZentaoConfig;
244
270
  }
@@ -1534,63 +1534,87 @@ export class ZentaoAPI {
1534
1534
  throw error;
1535
1535
  }
1536
1536
  }
1537
- // 评论模块 - 使用 HTML 接口
1538
- async addComment(objectType, objectID, comment) {
1537
+ // 评论模块 - 使用 REST API v1 接口
1538
+ /**
1539
+ * 获取评论列表
1540
+ * GET /comments/{objectType}/{objectID}
1541
+ */
1542
+ async getComments(objectType, objectID) {
1539
1543
  try {
1540
- console.log(`正在为 ${objectType} ${objectID} 添加评论...`);
1541
- const token = await this.getToken();
1542
- const htmlUrl = `${this.config.url}/zentao/action-comment-${objectType}-${objectID}.html`;
1543
- // 使用 form-urlencoded 格式发送数据
1544
- const formData = new URLSearchParams();
1545
- formData.append('actioncomment', comment);
1546
- const response = await axios.post(htmlUrl, formData.toString(), {
1547
- headers: {
1548
- 'Content-Type': 'application/x-www-form-urlencoded',
1549
- 'Token': token
1550
- },
1551
- timeout: 10000
1552
- });
1553
- console.log('添加评论响应:', response.data);
1554
- // 返回统一格式
1555
- if (response.data && typeof response.data === 'object') {
1556
- // 如果是 JSON 响应(API 模式)
1557
- if (response.data.status === 'success' || response.data.result === 'success') {
1558
- return {
1559
- status: 'success',
1560
- actionID: response.data.data || response.data.actionID,
1561
- message: '评论添加成功'
1562
- };
1563
- }
1564
- else if (response.data.result === 'fail' || response.data.status === 'fail') {
1565
- throw new Error(response.data.message || '评论添加失败');
1566
- }
1567
- }
1568
- // 如果返回的是 HTML 或其他格式,认为成功
1569
- return {
1570
- status: 'success',
1571
- message: '评论添加成功'
1572
- };
1544
+ console.log(`正在获取 ${objectType} ${objectID} 的评论列表...`);
1545
+ const response = await this.request('GET', `/comments/${objectType}/${objectID}`);
1546
+ console.log('获取评论列表响应:', response);
1547
+ return response;
1548
+ }
1549
+ catch (error) {
1550
+ console.error('获取评论列表失败:', error);
1551
+ throw error;
1552
+ }
1553
+ }
1554
+ /**
1555
+ * 获取单条评论详情
1556
+ * GET /comment/{id}
1557
+ */
1558
+ async getCommentDetail(commentId) {
1559
+ try {
1560
+ console.log(`正在获取评论 ${commentId} 的详情...`);
1561
+ const response = await this.request('GET', `/comment/${commentId}`);
1562
+ console.log('获取评论详情响应:', response);
1563
+ return response;
1564
+ }
1565
+ catch (error) {
1566
+ console.error('获取评论详情失败:', error);
1567
+ throw error;
1568
+ }
1569
+ }
1570
+ /**
1571
+ * 创建评论
1572
+ * POST /comment
1573
+ */
1574
+ async addComment(request) {
1575
+ try {
1576
+ console.log(`正在为 ${request.objectType} ${request.objectID} 添加评论...`);
1577
+ const response = await this.request('POST', '/comment', undefined, request);
1578
+ console.log('添加评论响应:', response);
1579
+ return response;
1573
1580
  }
1574
1581
  catch (error) {
1575
- if (axios.isAxiosError(error)) {
1576
- console.error('添加评论失败:', {
1577
- status: error.response?.status,
1578
- data: error.response?.data,
1579
- message: error.message
1580
- });
1581
- // 尝试解析错误消息
1582
- if (error.response?.data) {
1583
- const errorData = error.response.data;
1584
- if (typeof errorData === 'object' && errorData.message) {
1585
- throw new Error(`添加评论失败: ${errorData.message}`);
1586
- }
1587
- }
1588
- throw new Error(`添加评论失败: ${error.message}`);
1589
- }
1590
1582
  console.error('添加评论失败:', error);
1591
1583
  throw error;
1592
1584
  }
1593
1585
  }
1586
+ /**
1587
+ * 更新评论
1588
+ * PUT /comment/{id}
1589
+ */
1590
+ async updateComment(commentId, update) {
1591
+ try {
1592
+ console.log(`正在更新评论 ${commentId}...`);
1593
+ const response = await this.request('PUT', `/comment/${commentId}`, undefined, update);
1594
+ console.log('更新评论响应:', response);
1595
+ return response;
1596
+ }
1597
+ catch (error) {
1598
+ console.error('更新评论失败:', error);
1599
+ throw error;
1600
+ }
1601
+ }
1602
+ /**
1603
+ * 删除评论
1604
+ * DELETE /comment/{id}
1605
+ */
1606
+ async deleteComment(commentId) {
1607
+ try {
1608
+ console.log(`正在删除评论 ${commentId}...`);
1609
+ const response = await this.request('DELETE', `/comment/${commentId}`);
1610
+ console.log('删除评论响应:', response);
1611
+ return response;
1612
+ }
1613
+ catch (error) {
1614
+ console.error('删除评论失败:', error);
1615
+ throw error;
1616
+ }
1617
+ }
1594
1618
  // 获取配置信息(用于生成图片 URL)
1595
1619
  getConfig() {
1596
1620
  return this.config;
package/dist/index.js CHANGED
@@ -1504,7 +1504,51 @@ server.tool("downloadFile", {
1504
1504
  fs.writeFileSync(savePath, fileBuffer);
1505
1505
  return { content: [{ type: "text", text: `文件已下载到: ${savePath}` }] };
1506
1506
  });
1507
- // Comment tools (评论工具)
1507
+ // Comment tools (评论工具) - 使用 REST API v1
1508
+ server.tool("getComments", "获取评论列表 - 获取指定对象的所有评论", {
1509
+ objectType: z.enum([
1510
+ 'story', // 软件需求
1511
+ 'requirement', // 用户需求
1512
+ 'task', // 任务
1513
+ 'bug', // Bug
1514
+ 'testcase', // 测试用例
1515
+ 'testtask', // 测试单
1516
+ 'todo', // 待办
1517
+ 'doc', // 文档
1518
+ 'execution', // 执行/迭代
1519
+ 'project', // 项目
1520
+ 'doctemplate' // 文档模板
1521
+ ]).describe("对象类型"),
1522
+ objectID: z.number().describe("对象ID")
1523
+ }, async ({ objectType, objectID }) => {
1524
+ if (!zentaoApi)
1525
+ throw new Error("Please initialize Zentao API first");
1526
+ const comments = await zentaoApi.getComments(objectType, objectID);
1527
+ return {
1528
+ content: [{
1529
+ type: "text",
1530
+ text: JSON.stringify({
1531
+ total: comments.length,
1532
+ objectType,
1533
+ objectID,
1534
+ comments
1535
+ }, null, 2)
1536
+ }]
1537
+ };
1538
+ });
1539
+ server.tool("getCommentDetail", "获取单条评论详情", {
1540
+ commentId: z.number().describe("评论ID")
1541
+ }, async ({ commentId }) => {
1542
+ if (!zentaoApi)
1543
+ throw new Error("Please initialize Zentao API first");
1544
+ const comment = await zentaoApi.getCommentDetail(commentId);
1545
+ return {
1546
+ content: [{
1547
+ type: "text",
1548
+ text: JSON.stringify(comment, null, 2)
1549
+ }]
1550
+ };
1551
+ });
1508
1552
  server.tool("addComment", "添加评论 - 为需求、任务、Bug等对象添加评论", {
1509
1553
  objectType: z.enum([
1510
1554
  'story', // 软件需求
@@ -1515,42 +1559,87 @@ server.tool("addComment", "添加评论 - 为需求、任务、Bug等对象添
1515
1559
  'testtask', // 测试单
1516
1560
  'todo', // 待办
1517
1561
  'doc', // 文档
1562
+ 'execution', // 执行/迭代
1563
+ 'project', // 项目
1518
1564
  'doctemplate' // 文档模板
1519
1565
  ]).describe("对象类型"),
1520
1566
  objectID: z.number().describe("对象ID"),
1521
- comment: z.string().describe("评论内容,支持HTML格式")
1522
- }, async ({ objectType, objectID, comment }) => {
1567
+ comment: z.string().describe("评论内容,支持HTML格式"),
1568
+ uid: z.string().optional().describe("唯一标识符(可选)")
1569
+ }, async ({ objectType, objectID, comment, uid }) => {
1523
1570
  if (!zentaoApi)
1524
1571
  throw new Error("Please initialize Zentao API first");
1525
- const result = await zentaoApi.addComment(objectType, objectID, comment);
1572
+ const result = await zentaoApi.addComment({
1573
+ objectType,
1574
+ objectID,
1575
+ comment,
1576
+ uid
1577
+ });
1526
1578
  return {
1527
1579
  content: [{
1528
1580
  type: "text",
1529
1581
  text: JSON.stringify({
1530
- status: result.status,
1531
- message: result.message,
1532
- actionID: result.actionID,
1533
- details: `已为 ${objectType} #${objectID} 添加评论`
1582
+ message: '评论添加成功',
1583
+ comment: result
1534
1584
  }, null, 2)
1535
1585
  }]
1536
1586
  };
1537
1587
  });
1588
+ server.tool("updateComment", "更新评论 - 只能更新自己的评论", {
1589
+ commentId: z.number().describe("评论ID"),
1590
+ comment: z.string().describe("更新后的评论内容,支持HTML格式"),
1591
+ uid: z.string().optional().describe("唯一标识符(可选)")
1592
+ }, async ({ commentId, comment, uid }) => {
1593
+ if (!zentaoApi)
1594
+ throw new Error("Please initialize Zentao API first");
1595
+ const result = await zentaoApi.updateComment(commentId, {
1596
+ comment,
1597
+ uid
1598
+ });
1599
+ return {
1600
+ content: [{
1601
+ type: "text",
1602
+ text: JSON.stringify({
1603
+ message: '评论更新成功',
1604
+ comment: result
1605
+ }, null, 2)
1606
+ }]
1607
+ };
1608
+ });
1609
+ server.tool("deleteComment", "删除评论 - 只能删除自己的评论,管理员可以删除所有评论", {
1610
+ commentId: z.number().describe("评论ID")
1611
+ }, async ({ commentId }) => {
1612
+ if (!zentaoApi)
1613
+ throw new Error("Please initialize Zentao API first");
1614
+ const result = await zentaoApi.deleteComment(commentId);
1615
+ return {
1616
+ content: [{
1617
+ type: "text",
1618
+ text: JSON.stringify({
1619
+ status: 'success',
1620
+ message: result.message || '评论删除成功'
1621
+ }, null, 2)
1622
+ }]
1623
+ };
1624
+ });
1625
+ // 便捷工具 - 为特定类型对象添加评论
1538
1626
  server.tool("addStoryComment", "为需求添加评论 - 专用工具,支持软件需求(story)和用户需求(requirement)", {
1539
1627
  storyId: z.number().describe("需求ID"),
1540
1628
  comment: z.string().describe("评论内容,支持HTML格式")
1541
1629
  }, async ({ storyId, comment }) => {
1542
1630
  if (!zentaoApi)
1543
1631
  throw new Error("Please initialize Zentao API first");
1544
- // 使用 'story' 类型,API会自动识别是 story 还是 requirement
1545
- const result = await zentaoApi.addComment('story', storyId, comment);
1632
+ const result = await zentaoApi.addComment({
1633
+ objectType: 'story',
1634
+ objectID: storyId,
1635
+ comment
1636
+ });
1546
1637
  return {
1547
1638
  content: [{
1548
1639
  type: "text",
1549
1640
  text: JSON.stringify({
1550
- status: result.status,
1551
- message: result.message,
1552
- actionID: result.actionID,
1553
- details: `已为需求 #${storyId} 添加评论`
1641
+ message: `已为需求 #${storyId} 添加评论`,
1642
+ comment: result
1554
1643
  }, null, 2)
1555
1644
  }]
1556
1645
  };
@@ -1561,15 +1650,17 @@ server.tool("addTaskComment", "为任务添加评论", {
1561
1650
  }, async ({ taskId, comment }) => {
1562
1651
  if (!zentaoApi)
1563
1652
  throw new Error("Please initialize Zentao API first");
1564
- const result = await zentaoApi.addComment('task', taskId, comment);
1653
+ const result = await zentaoApi.addComment({
1654
+ objectType: 'task',
1655
+ objectID: taskId,
1656
+ comment
1657
+ });
1565
1658
  return {
1566
1659
  content: [{
1567
1660
  type: "text",
1568
1661
  text: JSON.stringify({
1569
- status: result.status,
1570
- message: result.message,
1571
- actionID: result.actionID,
1572
- details: `已为任务 #${taskId} 添加评论`
1662
+ message: `已为任务 #${taskId} 添加评论`,
1663
+ comment: result
1573
1664
  }, null, 2)
1574
1665
  }]
1575
1666
  };
@@ -1580,15 +1671,17 @@ server.tool("addBugComment", "为Bug添加评论", {
1580
1671
  }, async ({ bugId, comment }) => {
1581
1672
  if (!zentaoApi)
1582
1673
  throw new Error("Please initialize Zentao API first");
1583
- const result = await zentaoApi.addComment('bug', bugId, comment);
1674
+ const result = await zentaoApi.addComment({
1675
+ objectType: 'bug',
1676
+ objectID: bugId,
1677
+ comment
1678
+ });
1584
1679
  return {
1585
1680
  content: [{
1586
1681
  type: "text",
1587
1682
  text: JSON.stringify({
1588
- status: result.status,
1589
- message: result.message,
1590
- actionID: result.actionID,
1591
- details: `已为Bug #${bugId} 添加评论`
1683
+ message: `已为Bug #${bugId} 添加评论`,
1684
+ comment: result
1592
1685
  }, null, 2)
1593
1686
  }]
1594
1687
  };
@@ -717,14 +717,31 @@ export interface UploadFileRequest {
717
717
  filename: string;
718
718
  uid?: string;
719
719
  }
720
- export type CommentObjectType = 'story' | 'requirement' | 'task' | 'bug' | 'testcase' | 'testtask' | 'todo' | 'doc' | 'doctemplate';
720
+ export type CommentObjectType = 'story' | 'requirement' | 'task' | 'bug' | 'testcase' | 'testtask' | 'todo' | 'doc' | 'execution' | 'project' | 'doctemplate';
721
+ export interface Comment {
722
+ id: number;
723
+ objectType: string;
724
+ objectID: number;
725
+ action: string;
726
+ actor: string;
727
+ actorName?: string;
728
+ date: string;
729
+ comment: string;
730
+ extra?: string;
731
+ }
721
732
  export interface AddCommentRequest {
722
733
  objectType: CommentObjectType;
723
734
  objectID: number;
724
735
  comment: string;
736
+ uid?: string;
725
737
  }
726
- export interface CommentResponse {
727
- status: string;
728
- data?: number;
729
- message?: string;
738
+ export interface UpdateCommentRequest {
739
+ comment: string;
740
+ uid?: string;
741
+ }
742
+ export interface CommentListResponse {
743
+ page: number;
744
+ total: number;
745
+ limit: number;
746
+ comments: Comment[];
730
747
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zzp123/mcp-zentao",
3
- "version": "1.15.0",
3
+ "version": "1.16.0",
4
4
  "description": "禅道项目管理系统的高级API集成包,提供任务管理、Bug跟踪等功能的完整封装,专为Cursor IDE设计的MCP扩展",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",