@zzp123/mcp-zentao 1.9.0 → 1.10.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 +26 -0
- package/README.md +9 -9
- package/dist/api/zentaoApi.d.ts +2 -2
- package/dist/api/zentaoApi.js +44 -50
- package/dist/index.js +8 -6
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,32 @@ 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.10.0] - 2025-11-07
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
- **修复 Bug 获取工具的 API 端点和参数问题** 🔧
|
|
12
|
+
- 从错误的 `/bugs` 端点改为正确的 `/products/{productId}/bugs` 端点
|
|
13
|
+
- 移除了低效的客户端过滤逻辑,改用服务器端原生支持的 `assigntome` 状态
|
|
14
|
+
- 修复了 `onlyAssignedToMe` 参数无法正确工作的问题
|
|
15
|
+
|
|
16
|
+
### Added
|
|
17
|
+
- **添加排序参数支持** ✨
|
|
18
|
+
- 为 `getMyBugs` 和 `getProductBugs` 工具添加 `order` 参数
|
|
19
|
+
- 支持多种排序方式:`id_desc`(ID降序)、`id_asc`(ID升序)、`pri_desc`(优先级降序)等
|
|
20
|
+
- 默认使用 `id_desc` 排序,确保最新的 Bug 显示在前面
|
|
21
|
+
- 与禅道官方 API 完全一致的参数格式
|
|
22
|
+
|
|
23
|
+
### Improved
|
|
24
|
+
- **性能优化**
|
|
25
|
+
- 使用服务器端过滤替代客户端过滤,显著提升查询效率
|
|
26
|
+
- 减少不必要的数据传输,降低网络开销
|
|
27
|
+
- 更准确的分页信息和总数统计
|
|
28
|
+
|
|
29
|
+
### Changed
|
|
30
|
+
- 更新了 `getMyBugs` 方法签名,添加 `order` 参数
|
|
31
|
+
- 更新了 `getProductBugs` 方法签名,添加 `order` 参数
|
|
32
|
+
- 优化了查询参数的构建方式,使用 URLSearchParams 确保参数正确编码
|
|
33
|
+
|
|
8
34
|
## [1.9.0] - 2025-11-06
|
|
9
35
|
|
|
10
36
|
### Fixed
|
package/README.md
CHANGED
|
@@ -15,17 +15,17 @@ npm install @zzp123/mcp-zentao -g
|
|
|
15
15
|
|
|
16
16
|
查看完整的版本更新历史,请访问 [CHANGELOG.md](./CHANGELOG.md)
|
|
17
17
|
|
|
18
|
-
**最新版本**: v1.
|
|
19
|
-
- 修复
|
|
20
|
-
-
|
|
21
|
-
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
- v1.
|
|
18
|
+
**最新版本**: v1.9.0
|
|
19
|
+
- ✅ 修复 getMyBugs 工具的过滤逻辑问题
|
|
20
|
+
- ✅ 改为客户端过滤,正确获取当前用户的bug
|
|
21
|
+
- ✅ 支持 assignedTo 的对象和字符串两种格式
|
|
22
|
+
|
|
23
|
+
**近期版本**:
|
|
24
|
+
- v1.8.9 - 修复 resolveBug 工具自动设置默认 resolvedBuild
|
|
25
|
+
- v1.8.8 - 修复 getMyBugs 工具返回Bug数量为0的问题
|
|
26
|
+
- v1.6.1 - 修复 Windows 剪贴板图片读取问题
|
|
25
27
|
- v1.4.0 - 新增解决Bug接口
|
|
26
|
-
- v1.3.1 - 修复剪贴板图片上传核心问题
|
|
27
28
|
- v1.3.0 - 添加命令行脚本支持
|
|
28
|
-
- v1.2.0 - 添加文件上传/下载功能
|
|
29
29
|
|
|
30
30
|
## 使用方法
|
|
31
31
|
|
package/dist/api/zentaoApi.d.ts
CHANGED
|
@@ -9,13 +9,13 @@ 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, page?: number, limit?: number, onlyAssignedToMe?: boolean): Promise<{
|
|
12
|
+
getMyBugs(status?: BugStatus, productId?: number, page?: number, limit?: number, onlyAssignedToMe?: boolean, order?: string): Promise<{
|
|
13
13
|
page: number;
|
|
14
14
|
total: number;
|
|
15
15
|
limit: number;
|
|
16
16
|
bugs: Bug[];
|
|
17
17
|
}>;
|
|
18
|
-
getProductBugs(productId: number, page?: number, limit?: number, status?: BugStatus): Promise<{
|
|
18
|
+
getProductBugs(productId: number, page?: number, limit?: number, status?: BugStatus, order?: string): Promise<{
|
|
19
19
|
page: number;
|
|
20
20
|
total: number;
|
|
21
21
|
limit: number;
|
package/dist/api/zentaoApi.js
CHANGED
|
@@ -111,7 +111,7 @@ export class ZentaoAPI {
|
|
|
111
111
|
throw error;
|
|
112
112
|
}
|
|
113
113
|
}
|
|
114
|
-
async getMyBugs(status, productId, page, limit, onlyAssignedToMe = false) {
|
|
114
|
+
async getMyBugs(status, productId, page, limit, onlyAssignedToMe = false, order) {
|
|
115
115
|
if (!productId) {
|
|
116
116
|
// 如果没有提供产品ID,获取第二个可用的产品(索引为1)
|
|
117
117
|
const products = await this.getProducts();
|
|
@@ -125,73 +125,67 @@ export class ZentaoAPI {
|
|
|
125
125
|
// 默认每页20条,最多100条
|
|
126
126
|
const finalLimit = limit ? Math.min(limit, 100) : 20;
|
|
127
127
|
const finalPage = page || 1;
|
|
128
|
-
//
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
status
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
128
|
+
// 构建查询参数
|
|
129
|
+
const queryParams = new URLSearchParams();
|
|
130
|
+
// 如果需要只获取分配给我的Bug,使用 assigntome 状态
|
|
131
|
+
if (onlyAssignedToMe) {
|
|
132
|
+
queryParams.append('status', 'assigntome');
|
|
133
|
+
}
|
|
134
|
+
else if (status && status !== 'all') {
|
|
135
|
+
queryParams.append('status', status);
|
|
136
|
+
}
|
|
137
|
+
queryParams.append('page', finalPage.toString());
|
|
138
|
+
queryParams.append('limit', finalLimit.toString());
|
|
139
|
+
// 添加排序参数,默认按ID降序
|
|
140
|
+
const finalOrder = order || 'id_desc';
|
|
141
|
+
queryParams.append('order', finalOrder);
|
|
142
|
+
try {
|
|
143
|
+
console.log(`正在获取产品 ${productId} 的Bug列表,参数: status=${onlyAssignedToMe ? 'assigntome' : (status || 'all')}, page=${finalPage}, limit=${finalLimit}, order=${finalOrder}`);
|
|
144
|
+
const response = await this.request('GET', `/products/${productId}/bugs?${queryParams.toString()}`);
|
|
140
145
|
console.log(`Bug列表响应: 获取到 ${response.bugs?.length || 0} 条数据`);
|
|
141
|
-
let bugs = [];
|
|
142
146
|
if (Array.isArray(response)) {
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
147
|
+
// 如果返回的是数组,转换为标准格式
|
|
148
|
+
return {
|
|
149
|
+
page: finalPage,
|
|
150
|
+
total: response.length,
|
|
151
|
+
limit: finalLimit,
|
|
152
|
+
bugs: response
|
|
153
|
+
};
|
|
150
154
|
}
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
return bug.assignedTo === this.config.username;
|
|
161
|
-
}
|
|
162
|
-
return false;
|
|
163
|
-
});
|
|
164
|
-
console.log(`过滤后剩余 ${bugs.length} 个Bug`);
|
|
155
|
+
else if (response && typeof response === 'object') {
|
|
156
|
+
if (Array.isArray(response.bugs)) {
|
|
157
|
+
return {
|
|
158
|
+
page: response.page || finalPage,
|
|
159
|
+
total: response.total || response.bugs.length,
|
|
160
|
+
limit: response.limit || finalLimit,
|
|
161
|
+
bugs: response.bugs
|
|
162
|
+
};
|
|
163
|
+
}
|
|
165
164
|
}
|
|
166
|
-
|
|
167
|
-
const start = (finalPage - 1) * finalLimit;
|
|
168
|
-
const paginatedBugs = bugs.slice(start, start + finalLimit);
|
|
169
|
-
return {
|
|
170
|
-
page: finalPage,
|
|
171
|
-
total: bugs.length,
|
|
172
|
-
limit: finalLimit,
|
|
173
|
-
bugs: paginatedBugs
|
|
174
|
-
};
|
|
165
|
+
throw new Error(`获取Bug列表失败: 响应格式不正确 ${JSON.stringify(response)}`);
|
|
175
166
|
}
|
|
176
167
|
catch (error) {
|
|
177
|
-
if (error instanceof Error && error.message.includes('Need product id')) {
|
|
178
|
-
throw new Error('获取Bug列表失败: 请提供产品ID');
|
|
179
|
-
}
|
|
180
168
|
console.error('获取Bug列表失败:', error);
|
|
181
169
|
throw error;
|
|
182
170
|
}
|
|
183
171
|
}
|
|
184
|
-
async getProductBugs(productId, page, limit, status) {
|
|
172
|
+
async getProductBugs(productId, page, limit, status, order) {
|
|
185
173
|
try {
|
|
186
174
|
// 默认每页20条,最多100条
|
|
187
175
|
const finalLimit = limit ? Math.min(limit, 100) : 20;
|
|
188
176
|
const finalPage = page || 1;
|
|
189
|
-
|
|
177
|
+
const finalOrder = order || 'id_desc';
|
|
178
|
+
console.log(`正在获取产品 ${productId} 的Bug列表 (page=${finalPage}, limit=${finalLimit}, order=${finalOrder})...`);
|
|
190
179
|
// 构建查询参数
|
|
191
180
|
const queryParams = new URLSearchParams();
|
|
192
181
|
queryParams.append('page', finalPage.toString());
|
|
193
182
|
queryParams.append('limit', finalLimit.toString());
|
|
194
|
-
|
|
183
|
+
queryParams.append('order', finalOrder);
|
|
184
|
+
// 支持 assigntome 状态
|
|
185
|
+
if (status === 'assigntome') {
|
|
186
|
+
queryParams.append('status', 'assigntome');
|
|
187
|
+
}
|
|
188
|
+
else if (status && status !== 'all') {
|
|
195
189
|
queryParams.append('status', status);
|
|
196
190
|
}
|
|
197
191
|
const response = await this.request('GET', `/products/${productId}/bugs?${queryParams.toString()}`);
|
package/dist/index.js
CHANGED
|
@@ -92,11 +92,12 @@ server.tool("getMyBugs", "获取Bug列表 - 默认获取所有Bug,可选择只
|
|
|
92
92
|
productId: z.number().optional(),
|
|
93
93
|
page: z.number().optional().describe("页码,从1开始,默认为1"),
|
|
94
94
|
limit: z.number().optional().describe("每页数量,默认20,最大100"),
|
|
95
|
-
onlyAssignedToMe: z.boolean().optional().describe("是否只获取分配给我的Bug,默认false(获取所有Bug)")
|
|
96
|
-
|
|
95
|
+
onlyAssignedToMe: z.boolean().optional().describe("是否只获取分配给我的Bug,默认false(获取所有Bug)"),
|
|
96
|
+
order: z.string().optional().describe("排序方式,如 id_desc(ID降序), id_asc(ID升序), pri_desc(优先级降序)等,默认id_desc")
|
|
97
|
+
}, async ({ status, productId, page, limit, onlyAssignedToMe, order }) => {
|
|
97
98
|
if (!zentaoApi)
|
|
98
99
|
throw new Error("Please initialize Zentao API first");
|
|
99
|
-
const result = await zentaoApi.getMyBugs(status, productId, page, limit, onlyAssignedToMe || false);
|
|
100
|
+
const result = await zentaoApi.getMyBugs(status, productId, page, limit, onlyAssignedToMe || false, order);
|
|
100
101
|
// 返回分页信息和数据
|
|
101
102
|
const summary = {
|
|
102
103
|
summary: `当前第 ${result.page} 页,共 ${result.total} 个Bug,本页显示 ${result.bugs.length} 个`,
|
|
@@ -117,11 +118,12 @@ server.tool("getProductBugs", {
|
|
|
117
118
|
productId: z.number(),
|
|
118
119
|
page: z.number().optional().describe("页码,从1开始,默认为1"),
|
|
119
120
|
limit: z.number().optional().describe("每页数量,默认20,最大100"),
|
|
120
|
-
status: z.enum(['active', 'resolved', 'closed', 'all']).optional()
|
|
121
|
-
|
|
121
|
+
status: z.enum(['active', 'resolved', 'closed', 'all']).optional(),
|
|
122
|
+
order: z.string().optional().describe("排序方式,如 id_desc(ID降序), id_asc(ID升序), pri_desc(优先级降序)等,默认id_desc")
|
|
123
|
+
}, async ({ productId, page, limit, status, order }) => {
|
|
122
124
|
if (!zentaoApi)
|
|
123
125
|
throw new Error("Please initialize Zentao API first");
|
|
124
|
-
const result = await zentaoApi.getProductBugs(productId, page, limit, status);
|
|
126
|
+
const result = await zentaoApi.getProductBugs(productId, page, limit, status, order);
|
|
125
127
|
// 返回分页信息和数据
|
|
126
128
|
const summary = {
|
|
127
129
|
summary: `当前第 ${result.page} 页,共 ${result.total} 个Bug,本页显示 ${result.bugs.length} 个`,
|