@leeguoo/zentao-mcp 0.3.0 → 0.3.2

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.
Files changed (3) hide show
  1. package/README.md +2 -1
  2. package/package.json +1 -1
  3. package/src/index.js +53 -14
package/README.md CHANGED
@@ -97,7 +97,7 @@ The MCP server provides four tools that can be triggered by natural language in
97
97
  - **`zentao_products_list`** - List all products
98
98
  - **`zentao_bugs_list`** - List bugs for a specific product
99
99
  - **`zentao_bugs_stats`** - Get bug statistics across products
100
- - **`zentao_bugs_mine`** - List my bugs by assignment or creator
100
+ - **`zentao_bugs_mine`** - List my bugs by assignment or creator (status filter supported)
101
101
 
102
102
  ### Usage Examples
103
103
 
@@ -157,6 +157,7 @@ The AI will automatically:
157
157
  **zentao_bugs_mine:**
158
158
  ```json
159
159
  {
160
+ "status": "active",
160
161
  "scope": "assigned",
161
162
  "includeZero": false,
162
163
  "includeDetails": true,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@leeguoo/zentao-mcp",
3
- "version": "0.3.0",
3
+ "version": "0.3.2",
4
4
  "description": "MCP server for ZenTao RESTful APIs",
5
5
  "keywords": [
6
6
  "zentao",
package/src/index.js CHANGED
@@ -50,10 +50,35 @@ function normalizeError(message, payload) {
50
50
  return { status: 0, msg: message || "error", result: payload ?? [] };
51
51
  }
52
52
 
53
- function normalizeAccount(value) {
53
+ function normalizeAccountValue(value) {
54
54
  return String(value || "").trim().toLowerCase();
55
55
  }
56
56
 
57
+ function extractAccounts(value) {
58
+ if (value === undefined || value === null) return [];
59
+ if (typeof value === "string" || typeof value === "number") {
60
+ const normalized = normalizeAccountValue(value);
61
+ return normalized ? [normalized] : [];
62
+ }
63
+ if (Array.isArray(value)) {
64
+ return value.flatMap((item) => extractAccounts(item));
65
+ }
66
+ if (typeof value === "object") {
67
+ const candidates = [];
68
+ if (value.account) candidates.push(...extractAccounts(value.account));
69
+ if (value.realname) candidates.push(...extractAccounts(value.realname));
70
+ if (value.name) candidates.push(...extractAccounts(value.name));
71
+ if (value.user) candidates.push(...extractAccounts(value.user));
72
+ return candidates.filter(Boolean);
73
+ }
74
+ return [];
75
+ }
76
+
77
+ function matchesAccount(value, matchAccount) {
78
+ const candidates = extractAccounts(value);
79
+ return candidates.includes(matchAccount);
80
+ }
81
+
57
82
  class ZentaoClient {
58
83
  constructor({ baseUrl, account, password }) {
59
84
  this.baseUrl = normalizeBaseUrl(baseUrl);
@@ -234,14 +259,23 @@ class ZentaoClient {
234
259
  async bugsMine({
235
260
  account,
236
261
  scope,
262
+ status,
237
263
  productIds,
238
264
  includeZero,
239
265
  perPage,
240
266
  maxItems,
241
267
  includeDetails,
242
268
  }) {
243
- const matchAccount = normalizeAccount(account || this.account);
269
+ const matchAccount = normalizeAccountValue(account || this.account);
244
270
  const targetScope = (scope || "assigned").toLowerCase();
271
+ const rawStatus = status ?? "active";
272
+ const statusList = Array.isArray(rawStatus)
273
+ ? rawStatus
274
+ : String(rawStatus).split(/[|,]/);
275
+ const statusSet = new Set(
276
+ statusList.map((item) => String(item).trim().toLowerCase()).filter(Boolean)
277
+ );
278
+ const allowAllStatus = statusSet.has("all") || statusSet.size === 0;
245
279
 
246
280
  const productsResponse = await this.listProducts({ page: 1, limit: 1000 });
247
281
  if (productsResponse.status !== 1) return productsResponse;
@@ -264,17 +298,17 @@ class ZentaoClient {
264
298
  });
265
299
 
266
300
  const matches = productBugs.filter((bug) => {
267
- const assigned = normalizeAccount(bug.assignedTo);
268
- const opened = normalizeAccount(bug.openedBy);
269
- const resolved = normalizeAccount(bug.resolvedBy);
270
- if (targetScope === "assigned") return assigned === matchAccount;
271
- if (targetScope === "opened") return opened === matchAccount;
272
- if (targetScope === "resolved") return resolved === matchAccount;
273
- return (
274
- assigned === matchAccount ||
275
- opened === matchAccount ||
276
- resolved === matchAccount
277
- );
301
+ if (!allowAllStatus) {
302
+ const bugStatus = String(bug.status || "").trim().toLowerCase();
303
+ if (!statusSet.has(bugStatus)) return false;
304
+ }
305
+ const assigned = matchesAccount(bug.assignedTo, matchAccount);
306
+ const opened = matchesAccount(bug.openedBy, matchAccount);
307
+ const resolved = matchesAccount(bug.resolvedBy, matchAccount);
308
+ if (targetScope === "assigned") return assigned;
309
+ if (targetScope === "opened") return opened;
310
+ if (targetScope === "resolved") return resolved;
311
+ return assigned || opened || resolved;
278
312
  });
279
313
 
280
314
  if (!includeZero && matches.length === 0) continue;
@@ -309,6 +343,7 @@ class ZentaoClient {
309
343
  return normalizeResult({
310
344
  account: matchAccount,
311
345
  scope: targetScope,
346
+ status: allowAllStatus ? "all" : Array.from(statusSet),
312
347
  total: totalMatches,
313
348
  products: rows,
314
349
  bugs: includeDetails ? bugs : [],
@@ -337,7 +372,7 @@ function getClient() {
337
372
  const server = new Server(
338
373
  {
339
374
  name: "zentao-mcp",
340
- version: "0.2.2",
375
+ version: "0.3.2",
341
376
  },
342
377
  {
343
378
  capabilities: {
@@ -396,6 +431,10 @@ const tools = [
396
431
  type: "string",
397
432
  description: "Filter scope: assigned|opened|resolved|all (default assigned).",
398
433
  },
434
+ status: {
435
+ type: ["string", "array"],
436
+ description: "Status filter: active|resolved|closed|all (default active).",
437
+ },
399
438
  productIds: {
400
439
  type: "array",
401
440
  items: { type: "integer" },