@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/.env.example +3 -3
- package/README.md +29 -17
- package/package.json +11 -2
- package/scripts/smoke.mjs +2 -1
- package/src/index.js +36 -52
- package/src/tools.js +82 -64
- package/src/zentao.js +549 -86
- package/IMPROVEMENTS.md +0 -179
- package/scripts/release-npm.sh +0 -481
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:
|
|
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: {
|
|
57
|
-
|
|
58
|
-
|
|
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: {
|
|
85
|
-
|
|
86
|
-
|
|
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:
|
|
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: {
|
|
104
|
-
|
|
105
|
-
|
|
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: {
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
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: {
|
|
154
|
-
|
|
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
|
|
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
|
|
190
|
-
throw new Error("get_bug_detail.path
|
|
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
|
|
198
|
-
throw new Error("resolve_bug.path
|
|
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 >
|
|
212
|
-
throw new Error("batch_resolve_my_bugs.maxItems must be a number between 1 and
|
|
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.
|
|
221
|
-
throw new Error("batch_resolve_my_bugs.
|
|
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.
|
|
224
|
-
throw new Error("batch_resolve_my_bugs
|
|
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
|
|
235
|
-
throw new Error("close_bug.path
|
|
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
|
|
249
|
-
throw new Error("verify_bug
|
|
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
|
|
263
|
-
throw new Error("comment_bug.path
|
|
280
|
+
if (args.path !== undefined) {
|
|
281
|
+
throw new Error("comment_bug.path is no longer supported");
|
|
264
282
|
}
|
|
265
283
|
}
|
|
266
284
|
}
|