@zzp123/mcp-zentao 1.12.0 → 1.13.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,29 @@ 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.13.0] - 2025-11-07
9
+
10
+ ### Added
11
+ - **产品和需求列表字段过滤功能** 📦
12
+ - 为 `getProducts` 添加 `fields` 参数支持
13
+ - 为 `getProductStories` 添加 `fields` 参数支持
14
+ - 支持三种字段级别:
15
+ - `basic`: 基本信息(产品:id, name, code, status;需求:id, title, assignedTo, openedBy)
16
+ - `default`: 默认信息(产品:包含 PO/QD/RD 等;需求:包含 status/stage/pri 等,**默认使用**)
17
+ - `full`: 完整信息(所有字段)
18
+
19
+ ### Changed
20
+ - **优化列表接口默认返回数据**
21
+ - 产品列表默认使用 `default` 字段级别,返回核心管理信息
22
+ - 需求列表默认使用 `basic` 字段级别,只返回最基本的4个字段
23
+ - 显著减少数据传输量,提升列表查询响应速度
24
+
25
+ ### Improved
26
+ - **性能优化**
27
+ - 列表接口数据量大幅减少,特别适合快速浏览和搜索场景
28
+ - 减少不必要的字段传输,降低网络开销
29
+ - 需要详细信息时可按需设置 `fields='full'`
30
+
8
31
  ## [1.12.0] - 2025-11-07
9
32
 
10
33
  ### Added
@@ -8,7 +8,24 @@ export declare class ZentaoAPI {
8
8
  private request;
9
9
  getMyTasks(status?: TaskStatus): Promise<Task[]>;
10
10
  getTaskDetail(taskId: number): Promise<Task>;
11
- getProducts(): Promise<Product[]>;
11
+ /**
12
+ * 过滤产品字段
13
+ * @param product 原始产品对象
14
+ * @param fields 字段级别
15
+ */
16
+ private filterProductFields;
17
+ /**
18
+ * 过滤需求字段
19
+ * @param story 原始需求对象
20
+ * @param fields 字段级别
21
+ */
22
+ private filterStoryFields;
23
+ /**
24
+ * 获取产品列表
25
+ * @param fields 返回字段级别('basic'基本/'default'默认/'full'完整,默认'default')
26
+ * @returns Promise<产品列表>
27
+ */
28
+ getProducts(fields?: 'basic' | 'default' | 'full'): Promise<Product[]>;
12
29
  /**
13
30
  * 过滤 Bug 字段
14
31
  * @param bug 原始 Bug 对象
@@ -88,7 +105,15 @@ export declare class ZentaoAPI {
88
105
  deleteTask(taskId: number): Promise<{
89
106
  message: string;
90
107
  }>;
91
- getProductStories(productId: number, page?: number, limit?: number): Promise<{
108
+ /**
109
+ * 获取产品需求列表
110
+ * @param productId 产品ID
111
+ * @param page 页码(从1开始,默认1)
112
+ * @param limit 每页数量(默认20,最大100)
113
+ * @param fields 返回字段级别('basic'基本/'default'默认/'full'完整,默认'basic')
114
+ * @returns Promise<需求列表及分页信息>
115
+ */
116
+ getProductStories(productId: number, page?: number, limit?: number, fields?: 'basic' | 'default' | 'full'): Promise<{
92
117
  page: number;
93
118
  total: number;
94
119
  limit: number;
@@ -91,20 +91,95 @@ export class ZentaoAPI {
91
91
  throw error;
92
92
  }
93
93
  }
94
- async getProducts() {
94
+ /**
95
+ * 过滤产品字段
96
+ * @param product 原始产品对象
97
+ * @param fields 字段级别
98
+ */
99
+ filterProductFields(product, fields) {
100
+ if (fields === 'full') {
101
+ return product;
102
+ }
103
+ if (fields === 'basic') {
104
+ // 基本信息:只返回核心字段
105
+ return {
106
+ id: product.id,
107
+ name: product.name,
108
+ code: product.code,
109
+ status: product.status
110
+ };
111
+ }
112
+ // default: 默认字段(id, 名称, 状态, 负责人)
113
+ return {
114
+ id: product.id,
115
+ name: product.name,
116
+ code: product.code,
117
+ status: product.status,
118
+ type: product.type,
119
+ PO: product.PO,
120
+ QD: product.QD,
121
+ RD: product.RD,
122
+ createdBy: product.createdBy,
123
+ createdDate: product.createdDate
124
+ };
125
+ }
126
+ /**
127
+ * 过滤需求字段
128
+ * @param story 原始需求对象
129
+ * @param fields 字段级别
130
+ */
131
+ filterStoryFields(story, fields) {
132
+ if (fields === 'full') {
133
+ return story;
134
+ }
135
+ if (fields === 'basic') {
136
+ // 基本信息:只返回核心字段
137
+ return {
138
+ id: story.id,
139
+ title: story.title,
140
+ assignedTo: story.assignedTo,
141
+ openedBy: story.openedBy
142
+ };
143
+ }
144
+ // default: 默认字段(id, 标题, 状态, 指派人, 创建人)
145
+ return {
146
+ id: story.id,
147
+ title: story.title,
148
+ status: story.status,
149
+ stage: story.stage,
150
+ pri: story.pri,
151
+ assignedTo: story.assignedTo,
152
+ openedBy: story.openedBy,
153
+ openedDate: story.openedDate,
154
+ estimate: story.estimate,
155
+ product: story.product,
156
+ plan: story.plan
157
+ };
158
+ }
159
+ /**
160
+ * 获取产品列表
161
+ * @param fields 返回字段级别('basic'基本/'default'默认/'full'完整,默认'default')
162
+ * @returns Promise<产品列表>
163
+ */
164
+ async getProducts(fields) {
95
165
  try {
96
- console.log('正在获取产品列表...');
166
+ const finalFields = fields || 'default';
167
+ console.log(`正在获取产品列表 (fields=${finalFields})...`);
97
168
  const response = await this.request('GET', '/products');
98
169
  console.log('产品列表响应:', response);
170
+ let products = [];
99
171
  if (Array.isArray(response)) {
100
- return response;
172
+ products = response;
101
173
  }
102
- else if (response && typeof response === 'object') {
103
- if (Array.isArray(response.products)) {
104
- return response.products;
105
- }
174
+ else if (response && typeof response === 'object' && Array.isArray(response.products)) {
175
+ products = response.products;
176
+ }
177
+ else {
178
+ throw new Error(`获取产品列表失败: 响应格式不正确 ${JSON.stringify(response)}`);
106
179
  }
107
- throw new Error(`获取产品列表失败: 响应格式不正确 ${JSON.stringify(response)}`);
180
+ // 应用字段过滤
181
+ const filteredProducts = products.map(product => this.filterProductFields(product, finalFields));
182
+ return filteredProducts;
108
183
  }
109
184
  catch (error) {
110
185
  console.error('获取产品列表失败:', error);
@@ -746,38 +821,55 @@ export class ZentaoAPI {
746
821
  throw error;
747
822
  }
748
823
  }
749
- async getProductStories(productId, page, limit) {
824
+ /**
825
+ * 获取产品需求列表
826
+ * @param productId 产品ID
827
+ * @param page 页码(从1开始,默认1)
828
+ * @param limit 每页数量(默认20,最大100)
829
+ * @param fields 返回字段级别('basic'基本/'default'默认/'full'完整,默认'basic')
830
+ * @returns Promise<需求列表及分页信息>
831
+ */
832
+ async getProductStories(productId, page, limit, fields) {
750
833
  try {
751
834
  // 默认每页20条,最多100条
752
835
  const finalLimit = limit ? Math.min(limit, 100) : 20;
753
836
  const finalPage = page || 1;
754
- console.log(`正在获取产品 ${productId} 的需求列表 (page=${finalPage}, limit=${finalLimit})...`);
837
+ const finalFields = fields || 'basic'; // 默认只返回基本字段
838
+ console.log(`正在获取产品 ${productId} 的需求列表 (page=${finalPage}, limit=${finalLimit}, fields=${finalFields})...`);
755
839
  // 构建查询参数
756
840
  const queryParams = new URLSearchParams();
757
841
  queryParams.append('page', finalPage.toString());
758
842
  queryParams.append('limit', finalLimit.toString());
759
843
  const response = await this.request('GET', `/products/${productId}/stories?${queryParams.toString()}`);
760
844
  console.log(`产品需求列表响应: 获取到 ${response.stories?.length || 0} 条数据`);
845
+ let stories = [];
846
+ if (Array.isArray(response)) {
847
+ stories = response;
848
+ }
849
+ else if (response && typeof response === 'object' && Array.isArray(response.stories)) {
850
+ stories = response.stories;
851
+ }
852
+ else {
853
+ throw new Error(`获取产品需求列表失败: 响应格式不正确 ${JSON.stringify(response)}`);
854
+ }
855
+ // 应用字段过滤
856
+ const filteredStories = stories.map(story => this.filterStoryFields(story, finalFields));
761
857
  if (Array.isArray(response)) {
762
- // 如果返回的是数组,转换为标准格式
763
858
  return {
764
859
  page: finalPage,
765
- total: response.length,
860
+ total: filteredStories.length,
766
861
  limit: finalLimit,
767
- stories: response
862
+ stories: filteredStories
768
863
  };
769
864
  }
770
- else if (response && typeof response === 'object') {
771
- if (Array.isArray(response.stories)) {
772
- return {
773
- page: response.page || finalPage,
774
- total: response.total || response.stories.length,
775
- limit: response.limit || finalLimit,
776
- stories: response.stories
777
- };
778
- }
865
+ else {
866
+ return {
867
+ page: response.page || finalPage,
868
+ total: response.total || filteredStories.length,
869
+ limit: response.limit || finalLimit,
870
+ stories: filteredStories
871
+ };
779
872
  }
780
- throw new Error(`获取产品需求列表失败: 响应格式不正确 ${JSON.stringify(response)}`);
781
873
  }
782
874
  catch (error) {
783
875
  console.error('获取产品需求列表失败:', error);
package/dist/index.js CHANGED
@@ -78,10 +78,12 @@ server.tool("getTaskDetail", {
78
78
  };
79
79
  });
80
80
  // Add getProducts tool
81
- server.tool("getProducts", {}, async () => {
81
+ server.tool("getProducts", "获取产品列表 - 只返回核心字段(id, 名称, 负责人)", {
82
+ fields: z.enum(['basic', 'default', 'full']).optional().describe("返回字段级别:'basic'(id/名称/状态)、'default'(+负责人/创建人,默认)、'full'(完整字段)")
83
+ }, async ({ fields }) => {
82
84
  if (!zentaoApi)
83
85
  throw new Error("Please initialize Zentao API first");
84
- const products = await zentaoApi.getProducts();
86
+ const products = await zentaoApi.getProducts(fields);
85
87
  return {
86
88
  content: [{ type: "text", text: JSON.stringify(products, null, 2) }]
87
89
  };
@@ -669,14 +671,15 @@ server.tool("deleteTask", {
669
671
  };
670
672
  });
671
673
  // Add getProductStories tool
672
- server.tool("getProductStories", {
674
+ server.tool("getProductStories", "获取产品需求列表 - 默认只返回核心字段(id, 标题, 指派人, 创建人)", {
673
675
  productId: z.number(),
674
676
  page: z.number().optional().describe("页码,从1开始,默认为1"),
675
- limit: z.number().optional().describe("每页数量,默认20,最大100")
676
- }, async ({ productId, page, limit }) => {
677
+ limit: z.number().optional().describe("每页数量,默认20,最大100"),
678
+ fields: z.enum(['basic', 'default', 'full']).optional().describe("字段级别:\n- basic: 基本信息(id, title, assignedTo, openedBy)\n- default: 常用信息(包含basic + status, stage, pri等)\n- full: 完整信息(所有字段)")
679
+ }, async ({ productId, page, limit, fields }) => {
677
680
  if (!zentaoApi)
678
681
  throw new Error("Please initialize Zentao API first");
679
- const result = await zentaoApi.getProductStories(productId, page, limit);
682
+ const result = await zentaoApi.getProductStories(productId, page, limit, fields);
680
683
  // 返回分页信息和数据
681
684
  const summary = {
682
685
  summary: `当前第 ${result.page} 页,共 ${result.total} 条需求,本页显示 ${result.stories.length} 条`,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zzp123/mcp-zentao",
3
- "version": "1.12.0",
3
+ "version": "1.13.0",
4
4
  "description": "禅道项目管理系统的高级API集成包,提供任务管理、Bug跟踪等功能的完整封装,专为Cursor IDE设计的MCP扩展",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",