@aipper/zentao-mcp-server 0.1.10 → 0.1.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/src/tools.js CHANGED
@@ -8,6 +8,8 @@ export function toMcpTextResult(text, options = {}) {
8
8
  };
9
9
  }
10
10
 
11
+ const SAFE_BUG_LIST_PATHS = new Set(["/bugs", "/my/bug", "/my/bugs"]);
12
+
11
13
  export const TOOLS = [
12
14
  {
13
15
  name: "get_token",
@@ -18,24 +20,9 @@ export const TOOLS = [
18
20
  additionalProperties: false,
19
21
  },
20
22
  },
21
- {
22
- name: "call",
23
- description: "Call ZenTao REST API v1 path with Token header.",
24
- inputSchema: {
25
- type: "object",
26
- properties: {
27
- path: { type: "string", minLength: 1, description: "API path, e.g. /projects or bugs/123" },
28
- method: { type: "string", description: "GET/POST/PUT/DELETE..." },
29
- query: { type: "object", additionalProperties: true },
30
- body: {},
31
- },
32
- required: ["path"],
33
- additionalProperties: false,
34
- },
35
- },
36
23
  {
37
24
  name: "list_my_projects",
38
- description: "List projects I participate in (heuristic filtering).",
25
+ description: "List projects I participate in (heuristic filtering). Not reliable for project-set-only bugs when the project set has no concrete projects.",
39
26
  inputSchema: {
40
27
  type: "object",
41
28
  properties: { keyword: { type: "string" } },
@@ -44,7 +31,8 @@ export const TOOLS = [
44
31
  },
45
32
  {
46
33
  name: "get_my_bugs",
47
- description: "List bugs assigned to me (supports status/keyword/limit/page filter).",
34
+ description:
35
+ "List bugs assigned to me (supports status/keyword/limit/page filter). For project-set scope, prefer projectSetId or /my/bug instead of relying on list_my_projects.",
48
36
  inputSchema: {
49
37
  type: "object",
50
38
  properties: {
@@ -53,9 +41,15 @@ export const TOOLS = [
53
41
  limit: { type: "number", minimum: 1, maximum: 200, description: "Default 20, max 200" },
54
42
  page: { type: "number", minimum: 1, description: "Default 1" },
55
43
  productId: { type: "number", minimum: 1, description: "Optional product id (for instances requiring product scope)" },
56
- projectSetId: { type: "number", minimum: 1, description: "Optional project-set id (for project-set scope)" },
57
- path: { type: "string", description: "Optional bugs endpoint override, default /bugs" },
58
- assignedTo: { type: "string", description: "Optional assignee override, default current account" },
44
+ projectSetId: {
45
+ type: "number",
46
+ minimum: 1,
47
+ description: "Optional project-set id. Prefer this when bugs are under project-set scope, especially if no concrete project exists.",
48
+ },
49
+ path: {
50
+ type: "string",
51
+ description: "Optional safe list path override. Allowed values: /bugs, /my/bug, /my/bugs.",
52
+ },
59
53
  },
60
54
  additionalProperties: false,
61
55
  },
@@ -67,7 +61,6 @@ export const TOOLS = [
67
61
  type: "object",
68
62
  properties: {
69
63
  id: { type: "number", minimum: 1, description: "Bug ID" },
70
- path: { type: "string", description: "Optional detail endpoint template, default /bugs/{id}" },
71
64
  },
72
65
  required: ["id"],
73
66
  additionalProperties: false,
@@ -75,15 +68,21 @@ export const TOOLS = [
75
68
  },
76
69
  {
77
70
  name: "resolve_bug",
78
- description: "Resolve one bug by ID (default resolution=fixed).",
71
+ description: "Resolve one bug by ID (default resolution=fixed). Prefer a solution that explains root cause, fix approach, and logic changes.",
79
72
  inputSchema: {
80
73
  type: "object",
81
74
  properties: {
82
75
  id: { type: "number", minimum: 1, description: "Bug ID" },
83
76
  resolution: { type: "string", description: "Default fixed" },
84
- solution: { type: "string", description: "Resolution description (preferred)" },
85
- comment: { type: "string", description: "Optional resolve comment" },
86
- path: { type: "string", description: "Optional resolve endpoint template, default /bugs/{id}/resolve" },
77
+ solution: {
78
+ type: "string",
79
+ description:
80
+ "Preferred. Describe the fix idea, changed logic, and affected behavior. Avoid Evidence/Verify labels, file paths, and compile/test commands.",
81
+ },
82
+ comment: {
83
+ type: "string",
84
+ description: "Optional resolve comment. If used for bug updates, prefer change summary over build/test proof.",
85
+ },
87
86
  },
88
87
  required: ["id"],
89
88
  additionalProperties: false,
@@ -91,7 +90,8 @@ export const TOOLS = [
91
90
  },
92
91
  {
93
92
  name: "batch_resolve_my_bugs",
94
- description: "Batch resolve my bugs (default status=active, resolution=fixed).",
93
+ description:
94
+ "Batch resolve my bugs (default status=active, resolution=fixed). Prefer projectSetId or /my/bug for project-set scope, and use a shared solution that explains the common root cause and logic changes.",
95
95
  inputSchema: {
96
96
  type: "object",
97
97
  properties: {
@@ -100,15 +100,27 @@ export const TOOLS = [
100
100
  limit: { type: "number", minimum: 1, maximum: 200, description: "List page size, default 50" },
101
101
  page: { type: "number", minimum: 1, description: "Default 1" },
102
102
  productId: { type: "number", minimum: 1, description: "Optional product id (for instances requiring product scope)" },
103
- projectSetId: { type: "number", minimum: 1, description: "Optional project-set id (for project-set scope)" },
104
- maxItems: { type: "number", minimum: 1, maximum: 500, description: "Max resolve count, default 50" },
105
- assignedTo: { type: "string", description: "Optional assignee override" },
103
+ projectSetId: {
104
+ type: "number",
105
+ minimum: 1,
106
+ description: "Optional project-set id. Prefer this for project-set bugs, especially when the project set has no concrete project entries.",
107
+ },
108
+ maxItems: { type: "number", minimum: 1, maximum: 100, description: "Max resolve count, default 20" },
106
109
  resolution: { type: "string", description: "Default fixed" },
107
- solution: { type: "string", description: "Resolution description (preferred)" },
108
- comment: { type: "string", description: "Optional resolve comment" },
109
- listPath: { type: "string", description: "Optional list endpoint, default /bugs" },
110
- resolvePath: { type: "string", description: "Optional resolve path template, default /bugs/{id}/resolve" },
111
- stopOnError: { type: "boolean", description: "Default false; stop on first resolve failure" },
110
+ solution: {
111
+ type: "string",
112
+ description:
113
+ "Preferred. Summarize the shared fix approach and changed logic. Avoid Evidence/Verify labels, file paths, and compile/test commands.",
114
+ },
115
+ comment: {
116
+ type: "string",
117
+ description: "Optional resolve comment. Prefer business-facing change summary over proof-style output.",
118
+ },
119
+ path: {
120
+ type: "string",
121
+ description: "Optional safe list path override. Allowed values: /bugs, /my/bug, /my/bugs.",
122
+ },
123
+ stopOnError: { type: "boolean", description: "Default true; stop on first resolve failure" },
112
124
  },
113
125
  additionalProperties: false,
114
126
  },
@@ -121,7 +133,6 @@ export const TOOLS = [
121
133
  properties: {
122
134
  id: { type: "number", minimum: 1, description: "Bug ID" },
123
135
  comment: { type: "string", description: "Optional close comment" },
124
- path: { type: "string", description: "Optional close endpoint template, default /bugs/{id}/close" },
125
136
  },
126
137
  required: ["id"],
127
138
  additionalProperties: false,
@@ -136,8 +147,6 @@ export const TOOLS = [
136
147
  id: { type: "number", minimum: 1, description: "Bug ID" },
137
148
  result: { type: "string", description: "pass or fail, default pass" },
138
149
  comment: { type: "string", description: "Optional verification comment" },
139
- closePath: { type: "string", description: "Optional close endpoint template, default /bugs/{id}/close" },
140
- activatePath: { type: "string", description: "Optional activate endpoint template, default /bugs/{id}/activate" },
141
150
  },
142
151
  required: ["id"],
143
152
  additionalProperties: false,
@@ -145,13 +154,16 @@ export const TOOLS = [
145
154
  },
146
155
  {
147
156
  name: "comment_bug",
148
- description: "Add comment to one bug by ID.",
157
+ description: "Add comment to one bug by ID. For solution updates, explain analysis and logic changes rather than build/test evidence.",
149
158
  inputSchema: {
150
159
  type: "object",
151
160
  properties: {
152
161
  id: { type: "number", minimum: 1, description: "Bug ID" },
153
- comment: { type: "string", description: "Comment content" },
154
- path: { type: "string", description: "Optional comment endpoint template, default /bugs/{id}/comment" },
162
+ comment: {
163
+ type: "string",
164
+ description:
165
+ "Comment content. For bug handling, prefer root cause, fix idea, and changed logic; avoid Evidence/Verify labels, file paths, and compile/test commands.",
166
+ },
155
167
  },
156
168
  required: ["id", "comment"],
157
169
  additionalProperties: false,
@@ -162,9 +174,6 @@ export const TOOLS = [
162
174
  export function assertToolArgs(name, args) {
163
175
  if (args == null) return;
164
176
  if (typeof args !== "object") throw new Error(`Invalid arguments for ${name}: expected object`);
165
- if (name === "call" && typeof args.path !== "string") {
166
- throw new Error("call.path must be a string");
167
- }
168
177
  if (name === "get_my_bugs") {
169
178
  if (args.limit !== undefined && (!Number.isFinite(args.limit) || args.limit < 1 || args.limit > 200)) {
170
179
  throw new Error("get_my_bugs.limit must be a number between 1 and 200");
@@ -172,8 +181,14 @@ export function assertToolArgs(name, args) {
172
181
  if (args.page !== undefined && (!Number.isFinite(args.page) || args.page < 1)) {
173
182
  throw new Error("get_my_bugs.page must be a number >= 1");
174
183
  }
175
- if (args.path !== undefined && typeof args.path !== "string") {
176
- throw new Error("get_my_bugs.path must be a string");
184
+ if (args.path !== undefined) {
185
+ if (typeof args.path !== "string") throw new Error("get_my_bugs.path must be a string");
186
+ if (!SAFE_BUG_LIST_PATHS.has(args.path)) {
187
+ throw new Error("get_my_bugs.path must be one of /bugs, /my/bug, /my/bugs");
188
+ }
189
+ }
190
+ if (args.assignedTo !== undefined) {
191
+ throw new Error("get_my_bugs.assignedTo is no longer supported");
177
192
  }
178
193
  if (args.productId !== undefined && (!Number.isFinite(args.productId) || args.productId < 1)) {
179
194
  throw new Error("get_my_bugs.productId must be a number >= 1");
@@ -186,16 +201,16 @@ export function assertToolArgs(name, args) {
186
201
  if (!Number.isFinite(args.id) || Number(args.id) < 1) {
187
202
  throw new Error("get_bug_detail.id must be a number >= 1");
188
203
  }
189
- if (args.path !== undefined && typeof args.path !== "string") {
190
- throw new Error("get_bug_detail.path must be a string");
204
+ if (args.path !== undefined) {
205
+ throw new Error("get_bug_detail.path is no longer supported");
191
206
  }
192
207
  }
193
208
  if (name === "resolve_bug") {
194
209
  if (!Number.isFinite(args.id) || Number(args.id) < 1) {
195
210
  throw new Error("resolve_bug.id must be a number >= 1");
196
211
  }
197
- if (args.path !== undefined && typeof args.path !== "string") {
198
- throw new Error("resolve_bug.path must be a string");
212
+ if (args.path !== undefined) {
213
+ throw new Error("resolve_bug.path is no longer supported");
199
214
  }
200
215
  if (args.solution !== undefined && typeof args.solution !== "string") {
201
216
  throw new Error("resolve_bug.solution must be a string");
@@ -208,8 +223,8 @@ export function assertToolArgs(name, args) {
208
223
  if (args.page !== undefined && (!Number.isFinite(args.page) || args.page < 1)) {
209
224
  throw new Error("batch_resolve_my_bugs.page must be a number >= 1");
210
225
  }
211
- if (args.maxItems !== undefined && (!Number.isFinite(args.maxItems) || args.maxItems < 1 || args.maxItems > 500)) {
212
- throw new Error("batch_resolve_my_bugs.maxItems must be a number between 1 and 500");
226
+ if (args.maxItems !== undefined && (!Number.isFinite(args.maxItems) || args.maxItems < 1 || args.maxItems > 100)) {
227
+ throw new Error("batch_resolve_my_bugs.maxItems must be a number between 1 and 100");
213
228
  }
214
229
  if (args.productId !== undefined && (!Number.isFinite(args.productId) || args.productId < 1)) {
215
230
  throw new Error("batch_resolve_my_bugs.productId must be a number >= 1");
@@ -217,11 +232,17 @@ export function assertToolArgs(name, args) {
217
232
  if (args.projectSetId !== undefined && (!Number.isFinite(args.projectSetId) || args.projectSetId < 1)) {
218
233
  throw new Error("batch_resolve_my_bugs.projectSetId must be a number >= 1");
219
234
  }
220
- if (args.listPath !== undefined && typeof args.listPath !== "string") {
221
- throw new Error("batch_resolve_my_bugs.listPath must be a string");
235
+ if (args.path !== undefined) {
236
+ if (typeof args.path !== "string") throw new Error("batch_resolve_my_bugs.path must be a string");
237
+ if (!SAFE_BUG_LIST_PATHS.has(args.path)) {
238
+ throw new Error("batch_resolve_my_bugs.path must be one of /bugs, /my/bug, /my/bugs");
239
+ }
240
+ }
241
+ if (args.assignedTo !== undefined) {
242
+ throw new Error("batch_resolve_my_bugs.assignedTo is no longer supported");
222
243
  }
223
- if (args.resolvePath !== undefined && typeof args.resolvePath !== "string") {
224
- throw new Error("batch_resolve_my_bugs.resolvePath must be a string");
244
+ if (args.listPath !== undefined || args.resolvePath !== undefined) {
245
+ throw new Error("batch_resolve_my_bugs endpoint overrides are no longer supported");
225
246
  }
226
247
  if (args.solution !== undefined && typeof args.solution !== "string") {
227
248
  throw new Error("batch_resolve_my_bugs.solution must be a string");
@@ -231,8 +252,8 @@ export function assertToolArgs(name, args) {
231
252
  if (!Number.isFinite(args.id) || Number(args.id) < 1) {
232
253
  throw new Error("close_bug.id must be a number >= 1");
233
254
  }
234
- if (args.path !== undefined && typeof args.path !== "string") {
235
- throw new Error("close_bug.path must be a string");
255
+ if (args.path !== undefined) {
256
+ throw new Error("close_bug.path is no longer supported");
236
257
  }
237
258
  }
238
259
  if (name === "verify_bug") {
@@ -245,11 +266,8 @@ export function assertToolArgs(name, args) {
245
266
  throw new Error("verify_bug.result must be pass or fail");
246
267
  }
247
268
  }
248
- if (args.closePath !== undefined && typeof args.closePath !== "string") {
249
- throw new Error("verify_bug.closePath must be a string");
250
- }
251
- if (args.activatePath !== undefined && typeof args.activatePath !== "string") {
252
- throw new Error("verify_bug.activatePath must be a string");
269
+ if (args.closePath !== undefined || args.activatePath !== undefined) {
270
+ throw new Error("verify_bug endpoint overrides are no longer supported");
253
271
  }
254
272
  }
255
273
  if (name === "comment_bug") {
@@ -259,8 +277,8 @@ export function assertToolArgs(name, args) {
259
277
  if (typeof args.comment !== "string" || !args.comment.trim()) {
260
278
  throw new Error("comment_bug.comment must be a non-empty string");
261
279
  }
262
- if (args.path !== undefined && typeof args.path !== "string") {
263
- throw new Error("comment_bug.path must be a string");
280
+ if (args.path !== undefined) {
281
+ throw new Error("comment_bug.path is no longer supported");
264
282
  }
265
283
  }
266
284
  }