@testkase/mcp-server 2.0.3 → 2.0.7
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/build/index.js +277 -628
- package/build/index.js.map +1 -1
- package/package.json +1 -1
package/build/index.js
CHANGED
|
@@ -8,6 +8,61 @@ const config = {
|
|
|
8
8
|
apiBaseUrl: process.env.TESTKASE_API_BASE_URL || "https://apiqa.testkase.com",
|
|
9
9
|
patToken: process.env.TESTKASE_PAT_TOKEN,
|
|
10
10
|
};
|
|
11
|
+
// Cached organization data (fetched once from PAT token)
|
|
12
|
+
let cachedOrganizationId = null;
|
|
13
|
+
// Cached project ID (set when user works on a project)
|
|
14
|
+
let cachedProjectId = null;
|
|
15
|
+
// Helper to get organization ID (fetches and caches on first call)
|
|
16
|
+
async function getOrganizationId(providedOrgId) {
|
|
17
|
+
// If explicitly provided, use it
|
|
18
|
+
if (providedOrgId) {
|
|
19
|
+
return providedOrgId;
|
|
20
|
+
}
|
|
21
|
+
// If cached, return cached value
|
|
22
|
+
if (cachedOrganizationId !== null) {
|
|
23
|
+
return cachedOrganizationId;
|
|
24
|
+
}
|
|
25
|
+
// Fetch from API using PAT token
|
|
26
|
+
validateAuth();
|
|
27
|
+
try {
|
|
28
|
+
const response = await axios.get(`${config.apiBaseUrl}/api/v1/organizations/my-organization`, {
|
|
29
|
+
headers: {
|
|
30
|
+
"Content-Type": "application/json",
|
|
31
|
+
"Authorization": `Bearer ${config.patToken}`,
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
const orgData = response.data?.data || response.data;
|
|
35
|
+
if (orgData?.organization_id) {
|
|
36
|
+
const orgId = orgData.organization_id;
|
|
37
|
+
cachedOrganizationId = orgId;
|
|
38
|
+
return orgId;
|
|
39
|
+
}
|
|
40
|
+
throw new Error("Could not determine organization from PAT token");
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
throw new Error("Failed to fetch organization. Please provide organizationId or check your PAT token.");
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
// Helper to get project ID (uses cached value if available)
|
|
47
|
+
function getProjectId(providedProjectId) {
|
|
48
|
+
// If explicitly provided, cache it and use it
|
|
49
|
+
if (providedProjectId) {
|
|
50
|
+
cachedProjectId = providedProjectId;
|
|
51
|
+
return providedProjectId;
|
|
52
|
+
}
|
|
53
|
+
// If cached, return cached value
|
|
54
|
+
if (cachedProjectId !== null) {
|
|
55
|
+
return cachedProjectId;
|
|
56
|
+
}
|
|
57
|
+
throw new Error("No active project set. You MUST first: 1) Call get_my_projects to list projects, " +
|
|
58
|
+
"2) ASK the user which project they want to work with, " +
|
|
59
|
+
"3) Call set_active_project with the user's chosen project ID. " +
|
|
60
|
+
"Do NOT select a project automatically.");
|
|
61
|
+
}
|
|
62
|
+
// Helper to set active project (for explicit project switching)
|
|
63
|
+
function setActiveProject(projectId) {
|
|
64
|
+
cachedProjectId = projectId;
|
|
65
|
+
}
|
|
11
66
|
// Tool definitions
|
|
12
67
|
const GET_TESTCASES_TOOL = {
|
|
13
68
|
name: "get_testcases",
|
|
@@ -20,11 +75,11 @@ const GET_TESTCASES_TOOL = {
|
|
|
20
75
|
properties: {
|
|
21
76
|
projectId: {
|
|
22
77
|
type: "string",
|
|
23
|
-
description: "
|
|
78
|
+
description: "Project ID (optional if active project is set)",
|
|
24
79
|
},
|
|
25
80
|
organizationId: {
|
|
26
81
|
type: "number",
|
|
27
|
-
description: "
|
|
82
|
+
description: "Organization ID (optional - auto-detected from PAT token)",
|
|
28
83
|
},
|
|
29
84
|
page: {
|
|
30
85
|
type: "number",
|
|
@@ -89,7 +144,7 @@ const GET_TESTCASES_TOOL = {
|
|
|
89
144
|
description: "Filter by update end date (ISO 8601 format: YYYY-MM-DD)",
|
|
90
145
|
},
|
|
91
146
|
},
|
|
92
|
-
required: [
|
|
147
|
+
required: [],
|
|
93
148
|
},
|
|
94
149
|
};
|
|
95
150
|
const GET_TESTCASE_DETAIL_TOOL = {
|
|
@@ -101,7 +156,7 @@ const GET_TESTCASE_DETAIL_TOOL = {
|
|
|
101
156
|
properties: {
|
|
102
157
|
projectId: {
|
|
103
158
|
type: "string",
|
|
104
|
-
description: "
|
|
159
|
+
description: "Project ID (optional if active project is set)",
|
|
105
160
|
},
|
|
106
161
|
testcaseId: {
|
|
107
162
|
type: "string",
|
|
@@ -109,34 +164,10 @@ const GET_TESTCASE_DETAIL_TOOL = {
|
|
|
109
164
|
},
|
|
110
165
|
organizationId: {
|
|
111
166
|
type: "number",
|
|
112
|
-
description: "
|
|
167
|
+
description: "Organization ID (optional - auto-detected from PAT token)",
|
|
113
168
|
},
|
|
114
169
|
},
|
|
115
|
-
required: ["
|
|
116
|
-
},
|
|
117
|
-
};
|
|
118
|
-
const GET_TESTCASE_HISTORY_TOOL = {
|
|
119
|
-
name: "get_testcase_history",
|
|
120
|
-
description: "Get the complete history of changes made to a testcase. " +
|
|
121
|
-
"Returns a list of all modifications with timestamps, user information, and changed fields. " +
|
|
122
|
-
"Useful for tracking testcase evolution and audit trails.",
|
|
123
|
-
inputSchema: {
|
|
124
|
-
type: "object",
|
|
125
|
-
properties: {
|
|
126
|
-
projectId: {
|
|
127
|
-
type: "string",
|
|
128
|
-
description: "The unique identifier of the project",
|
|
129
|
-
},
|
|
130
|
-
testcaseId: {
|
|
131
|
-
type: "string",
|
|
132
|
-
description: "The unique identifier of the testcase",
|
|
133
|
-
},
|
|
134
|
-
organizationId: {
|
|
135
|
-
type: "number",
|
|
136
|
-
description: "The organization ID that owns the project",
|
|
137
|
-
},
|
|
138
|
-
},
|
|
139
|
-
required: ["projectId", "testcaseId", "organizationId"],
|
|
170
|
+
required: ["testcaseId"],
|
|
140
171
|
},
|
|
141
172
|
};
|
|
142
173
|
const GET_FOLDERS_TOOL = {
|
|
@@ -149,18 +180,24 @@ const GET_FOLDERS_TOOL = {
|
|
|
149
180
|
properties: {
|
|
150
181
|
organizationId: {
|
|
151
182
|
type: "number",
|
|
152
|
-
description: "
|
|
183
|
+
description: "Organization ID (optional - auto-detected from PAT token)",
|
|
153
184
|
},
|
|
154
185
|
projectId: {
|
|
155
186
|
type: "string",
|
|
156
|
-
description: "
|
|
187
|
+
description: "Project ID (optional if active project is set)",
|
|
157
188
|
},
|
|
158
|
-
|
|
189
|
+
section: {
|
|
159
190
|
type: "string",
|
|
160
|
-
|
|
191
|
+
enum: ["TESTCASE", "TEST_CYCLE", "TEST_PLAN", "REQUIREMENT"],
|
|
192
|
+
description: "Folder section type (default: TESTCASE)",
|
|
193
|
+
default: "TESTCASE",
|
|
194
|
+
},
|
|
195
|
+
search: {
|
|
196
|
+
type: "string",
|
|
197
|
+
description: "Optional: Search term to filter folders (min 3 characters)",
|
|
161
198
|
},
|
|
162
199
|
},
|
|
163
|
-
required: [
|
|
200
|
+
required: [],
|
|
164
201
|
},
|
|
165
202
|
};
|
|
166
203
|
const GET_LABELS_TOOL = {
|
|
@@ -173,49 +210,23 @@ const GET_LABELS_TOOL = {
|
|
|
173
210
|
properties: {
|
|
174
211
|
projectId: {
|
|
175
212
|
type: "string",
|
|
176
|
-
description: "
|
|
213
|
+
description: "Project ID (optional if active project is set)",
|
|
177
214
|
},
|
|
178
215
|
organizationId: {
|
|
179
216
|
type: "number",
|
|
180
|
-
description: "
|
|
217
|
+
description: "Organization ID (optional - auto-detected from PAT token)",
|
|
181
218
|
},
|
|
182
219
|
},
|
|
183
|
-
required: [
|
|
184
|
-
},
|
|
185
|
-
};
|
|
186
|
-
const GET_ISSUES_TOOL = {
|
|
187
|
-
name: "get_issues",
|
|
188
|
-
description: "Get all issues/defects reported in a project. " +
|
|
189
|
-
"Returns a list of issues with their status, priority, and linked testcases. " +
|
|
190
|
-
"Useful for tracking bugs and defects found during testing.",
|
|
191
|
-
inputSchema: {
|
|
192
|
-
type: "object",
|
|
193
|
-
properties: {
|
|
194
|
-
projectId: {
|
|
195
|
-
type: "string",
|
|
196
|
-
description: "The unique identifier of the project",
|
|
197
|
-
},
|
|
198
|
-
organizationId: {
|
|
199
|
-
type: "number",
|
|
200
|
-
description: "The organization ID",
|
|
201
|
-
},
|
|
202
|
-
page: {
|
|
203
|
-
type: "number",
|
|
204
|
-
description: "Page number for pagination (default: 1)",
|
|
205
|
-
},
|
|
206
|
-
limit: {
|
|
207
|
-
type: "number",
|
|
208
|
-
description: "Number of items per page (default: 20)",
|
|
209
|
-
},
|
|
210
|
-
},
|
|
211
|
-
required: ["projectId", "organizationId"],
|
|
220
|
+
required: [],
|
|
212
221
|
},
|
|
213
222
|
};
|
|
214
223
|
const GET_MY_PROJECTS_TOOL = {
|
|
215
224
|
name: "get_my_projects",
|
|
216
|
-
description: "
|
|
217
|
-
"
|
|
218
|
-
"
|
|
225
|
+
description: "IMPORTANT: Call this tool FIRST before any other TestKase operation. " +
|
|
226
|
+
"Lists all projects the user has access to. " +
|
|
227
|
+
"After getting results, ALWAYS ASK the user which project they want to work with. " +
|
|
228
|
+
"Do NOT automatically select a project - let the user choose. " +
|
|
229
|
+
"Then use set_active_project with the user's chosen project ID.",
|
|
219
230
|
inputSchema: {
|
|
220
231
|
type: "object",
|
|
221
232
|
properties: {
|
|
@@ -237,8 +248,8 @@ const CREATE_TESTCASE_TOOL = {
|
|
|
237
248
|
inputSchema: {
|
|
238
249
|
type: "object",
|
|
239
250
|
properties: {
|
|
240
|
-
projectId: { type: "string", description: "Project ID" },
|
|
241
|
-
organizationId: { type: "number", description: "Organization ID" },
|
|
251
|
+
projectId: { type: "string", description: "Project ID (optional if active project is set)" },
|
|
252
|
+
organizationId: { type: "number", description: "Organization ID (optional - auto-detected from PAT token)" },
|
|
242
253
|
title: { type: "string", description: "Test case title" },
|
|
243
254
|
summary: { type: "string", description: "Test case summary/description" },
|
|
244
255
|
priority: { type: "string", description: "Priority (low, medium, high, critical)" },
|
|
@@ -258,7 +269,7 @@ const CREATE_TESTCASE_TOOL = {
|
|
|
258
269
|
description: "Array of test steps"
|
|
259
270
|
},
|
|
260
271
|
},
|
|
261
|
-
required: ["
|
|
272
|
+
required: ["title"],
|
|
262
273
|
},
|
|
263
274
|
};
|
|
264
275
|
const CREATE_BULK_TESTCASE_TOOL = {
|
|
@@ -267,11 +278,11 @@ const CREATE_BULK_TESTCASE_TOOL = {
|
|
|
267
278
|
inputSchema: {
|
|
268
279
|
type: "object",
|
|
269
280
|
properties: {
|
|
270
|
-
projectId: { type: "string", description: "Project ID" },
|
|
271
|
-
organizationId: { type: "number", description: "Organization ID" },
|
|
281
|
+
projectId: { type: "string", description: "Project ID (optional if active project is set)" },
|
|
282
|
+
organizationId: { type: "number", description: "Organization ID (optional - auto-detected from PAT token)" },
|
|
272
283
|
testcases: { type: "string", description: "JSON string of testcase array" },
|
|
273
284
|
},
|
|
274
|
-
required: ["
|
|
285
|
+
required: ["testcases"],
|
|
275
286
|
},
|
|
276
287
|
};
|
|
277
288
|
const UPDATE_TESTCASE_FIELD_TOOL = {
|
|
@@ -280,13 +291,13 @@ const UPDATE_TESTCASE_FIELD_TOOL = {
|
|
|
280
291
|
inputSchema: {
|
|
281
292
|
type: "object",
|
|
282
293
|
properties: {
|
|
283
|
-
projectId: { type: "string", description: "Project ID" },
|
|
284
|
-
organizationId: { type: "number", description: "Organization ID" },
|
|
294
|
+
projectId: { type: "string", description: "Project ID (optional if active project is set)" },
|
|
295
|
+
organizationId: { type: "number", description: "Organization ID (optional - auto-detected from PAT token)" },
|
|
285
296
|
ids: { type: "string", description: "Comma-separated testcase IDs" },
|
|
286
297
|
field: { type: "string", description: "Field name to update" },
|
|
287
298
|
value: { type: "string", description: "New field value" },
|
|
288
299
|
},
|
|
289
|
-
required: ["
|
|
300
|
+
required: ["ids", "field", "value"],
|
|
290
301
|
},
|
|
291
302
|
};
|
|
292
303
|
const DELETE_TESTCASE_TOOL = {
|
|
@@ -295,116 +306,31 @@ const DELETE_TESTCASE_TOOL = {
|
|
|
295
306
|
inputSchema: {
|
|
296
307
|
type: "object",
|
|
297
308
|
properties: {
|
|
298
|
-
projectId: { type: "string", description: "Project ID" },
|
|
299
|
-
organizationId: { type: "number", description: "Organization ID" },
|
|
309
|
+
projectId: { type: "string", description: "Project ID (optional if active project is set)" },
|
|
310
|
+
organizationId: { type: "number", description: "Organization ID (optional - auto-detected from PAT token)" },
|
|
300
311
|
ids: { type: "string", description: "Comma-separated testcase IDs to delete" },
|
|
301
312
|
},
|
|
302
|
-
required: ["
|
|
303
|
-
},
|
|
304
|
-
};
|
|
305
|
-
const CLONE_TESTCASE_TOOL = {
|
|
306
|
-
name: "clone_testcase",
|
|
307
|
-
description: "Clone an existing test case with all its steps",
|
|
308
|
-
inputSchema: {
|
|
309
|
-
type: "object",
|
|
310
|
-
properties: {
|
|
311
|
-
projectId: { type: "string", description: "Project ID" },
|
|
312
|
-
id: { type: "string", description: "Test case ID to clone" },
|
|
313
|
-
organizationId: { type: "number", description: "Organization ID" },
|
|
314
|
-
},
|
|
315
|
-
required: ["projectId", "id", "organizationId"],
|
|
316
|
-
},
|
|
317
|
-
};
|
|
318
|
-
const CREATE_TEST_STEP_TOOL = {
|
|
319
|
-
name: "create_test_step",
|
|
320
|
-
description: "Create or update test steps for a test case",
|
|
321
|
-
inputSchema: {
|
|
322
|
-
type: "object",
|
|
323
|
-
properties: {
|
|
324
|
-
projectId: { type: "string", description: "Project ID" },
|
|
325
|
-
testcaseId: { type: "string", description: "Test case ID" },
|
|
326
|
-
organizationId: { type: "number", description: "Organization ID" },
|
|
327
|
-
steps: { type: "string", description: "JSON string of test steps array" },
|
|
328
|
-
},
|
|
329
|
-
required: ["projectId", "testcaseId", "organizationId", "steps"],
|
|
330
|
-
},
|
|
331
|
-
};
|
|
332
|
-
const DELETE_TEST_STEP_TOOL = {
|
|
333
|
-
name: "delete_test_step",
|
|
334
|
-
description: "Delete test steps from a test case",
|
|
335
|
-
inputSchema: {
|
|
336
|
-
type: "object",
|
|
337
|
-
properties: {
|
|
338
|
-
testcaseId: { type: "string", description: "Test case ID" },
|
|
339
|
-
organizationId: { type: "number", description: "Organization ID" },
|
|
340
|
-
stepIds: { type: "string", description: "Comma-separated step IDs to delete" },
|
|
341
|
-
},
|
|
342
|
-
required: ["testcaseId", "organizationId", "stepIds"],
|
|
343
|
-
},
|
|
344
|
-
};
|
|
345
|
-
const DOWNLOAD_TESTCASES_TOOL = {
|
|
346
|
-
name: "download_testcases",
|
|
347
|
-
description: "Download test cases as CSV file (returns download URL or data)",
|
|
348
|
-
inputSchema: {
|
|
349
|
-
type: "object",
|
|
350
|
-
properties: {
|
|
351
|
-
projectId: { type: "string", description: "Project ID" },
|
|
352
|
-
organizationId: { type: "number", description: "Organization ID" },
|
|
353
|
-
filters: { type: "string", description: "Optional filters as JSON string" },
|
|
354
|
-
},
|
|
355
|
-
required: ["projectId", "organizationId"],
|
|
356
|
-
},
|
|
357
|
-
};
|
|
358
|
-
const IMPORT_TESTCASES_TOOL = {
|
|
359
|
-
name: "import_testcases",
|
|
360
|
-
description: "Import test cases from a CSV file",
|
|
361
|
-
inputSchema: {
|
|
362
|
-
type: "object",
|
|
363
|
-
properties: {
|
|
364
|
-
projectId: { type: "string", description: "Project ID" },
|
|
365
|
-
organizationId: { type: "number", description: "Organization ID" },
|
|
366
|
-
file: { type: "string", description: "File data or path" },
|
|
367
|
-
},
|
|
368
|
-
required: ["projectId", "organizationId", "file"],
|
|
313
|
+
required: ["ids"],
|
|
369
314
|
},
|
|
370
315
|
};
|
|
371
316
|
const CREATE_FOLDER_TOOL = {
|
|
372
317
|
name: "create_folder",
|
|
373
|
-
description: "Create a new folder in a project",
|
|
374
|
-
inputSchema: {
|
|
375
|
-
type: "object",
|
|
376
|
-
properties: {
|
|
377
|
-
organizationId: { type: "number", description: "Organization ID" },
|
|
378
|
-
projectId: { type: "string", description: "Project ID" },
|
|
379
|
-
folder_name: { type: "string", description: "Name of the new folder" },
|
|
380
|
-
parent_folder_id: { type: "string", description: "Parent folder ID (optional)" },
|
|
381
|
-
},
|
|
382
|
-
required: ["organizationId", "projectId", "folder_name"],
|
|
383
|
-
},
|
|
384
|
-
};
|
|
385
|
-
const DELETE_FOLDER_TOOL = {
|
|
386
|
-
name: "delete_folder",
|
|
387
|
-
description: "Delete a folder and its subfolders",
|
|
318
|
+
description: "Create a new folder in a project for testcases",
|
|
388
319
|
inputSchema: {
|
|
389
320
|
type: "object",
|
|
390
321
|
properties: {
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
type: "object",
|
|
402
|
-
properties: {
|
|
403
|
-
id: { type: "string", description: "Folder ID" },
|
|
404
|
-
organizationId: { type: "number", description: "Organization ID" },
|
|
405
|
-
folder_name: { type: "string", description: "New folder name" },
|
|
322
|
+
organizationId: { type: "number", description: "Organization ID (optional - auto-detected from PAT token)" },
|
|
323
|
+
projectId: { type: "string", description: "Project ID (optional if active project is set)" },
|
|
324
|
+
section: {
|
|
325
|
+
type: "string",
|
|
326
|
+
enum: ["TESTCASE", "TEST_CYCLE", "TEST_PLAN", "REQUIREMENT"],
|
|
327
|
+
description: "Folder section type (default: TESTCASE)",
|
|
328
|
+
default: "TESTCASE",
|
|
329
|
+
},
|
|
330
|
+
name: { type: "string", description: "Name of the new folder (max 50 characters)" },
|
|
331
|
+
parentId: { type: "number", description: "Parent folder ID for creating subfolder (optional)" },
|
|
406
332
|
},
|
|
407
|
-
required: ["
|
|
333
|
+
required: ["name"],
|
|
408
334
|
},
|
|
409
335
|
};
|
|
410
336
|
const CREATE_LABEL_TOOL = {
|
|
@@ -413,11 +339,11 @@ const CREATE_LABEL_TOOL = {
|
|
|
413
339
|
inputSchema: {
|
|
414
340
|
type: "object",
|
|
415
341
|
properties: {
|
|
416
|
-
projectId: { type: "string", description: "Project ID" },
|
|
417
|
-
organizationId: { type: "number", description: "Organization ID" },
|
|
342
|
+
projectId: { type: "string", description: "Project ID (optional if active project is set)" },
|
|
343
|
+
organizationId: { type: "number", description: "Organization ID (optional - auto-detected from PAT token)" },
|
|
418
344
|
values: { type: "array", items: { type: "string" }, description: "Array of label names" },
|
|
419
345
|
},
|
|
420
|
-
required: ["
|
|
346
|
+
required: ["values"],
|
|
421
347
|
},
|
|
422
348
|
};
|
|
423
349
|
const GET_USER_PROFILE_TOOL = {
|
|
@@ -429,87 +355,29 @@ const GET_USER_PROFILE_TOOL = {
|
|
|
429
355
|
required: [],
|
|
430
356
|
},
|
|
431
357
|
};
|
|
432
|
-
const
|
|
433
|
-
name: "
|
|
434
|
-
description: "
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
properties: {},
|
|
438
|
-
required: [],
|
|
439
|
-
},
|
|
440
|
-
};
|
|
441
|
-
const GET_MAPPED_ISSUES_TOOL = {
|
|
442
|
-
name: "get_mapped_issues",
|
|
443
|
-
description: "Get mapped issues for a specific test case from integration platforms",
|
|
444
|
-
inputSchema: {
|
|
445
|
-
type: "object",
|
|
446
|
-
properties: {
|
|
447
|
-
organization_id: { type: "number", description: "Organization ID" },
|
|
448
|
-
project_id: { type: "string", description: "Project ID" },
|
|
449
|
-
integration_type: { type: "string", description: "Integration type (requirement/defect)" },
|
|
450
|
-
testcase_id: { type: "string", description: "Test case ID" },
|
|
451
|
-
},
|
|
452
|
-
required: ["organization_id", "project_id", "integration_type", "testcase_id"],
|
|
453
|
-
},
|
|
454
|
-
};
|
|
455
|
-
const SEARCH_ISSUES_TOOL = {
|
|
456
|
-
name: "search_issues",
|
|
457
|
-
description: "Search issues from integrated platform (Jira, GitHub, GitLab)",
|
|
458
|
-
inputSchema: {
|
|
459
|
-
type: "object",
|
|
460
|
-
properties: {
|
|
461
|
-
project_id: { type: "string", description: "Project ID" },
|
|
462
|
-
integration_type: { type: "string", description: "Integration type (requirement/defect)" },
|
|
463
|
-
search: { type: "string", description: "Search query string" },
|
|
464
|
-
page: { type: "number", description: "Page number" },
|
|
465
|
-
limit: { type: "number", description: "Items per page" },
|
|
466
|
-
},
|
|
467
|
-
required: ["project_id", "integration_type"],
|
|
468
|
-
},
|
|
469
|
-
};
|
|
470
|
-
const MAP_ISSUES_TOOL = {
|
|
471
|
-
name: "map_issues",
|
|
472
|
-
description: "Map integration issues to a TestKase requirement/defect",
|
|
473
|
-
inputSchema: {
|
|
474
|
-
type: "object",
|
|
475
|
-
properties: {
|
|
476
|
-
organization_id: { type: "number", description: "Organization ID" },
|
|
477
|
-
project_id: { type: "string", description: "Project ID" },
|
|
478
|
-
testcase_id: { type: "string", description: "Test case ID" },
|
|
479
|
-
integration_type: { type: "string", description: "Integration type (requirement/defect)" },
|
|
480
|
-
issues: { type: "string", description: "JSON string of issue IDs to map" },
|
|
481
|
-
},
|
|
482
|
-
required: ["organization_id", "project_id", "testcase_id", "integration_type", "issues"],
|
|
483
|
-
},
|
|
484
|
-
};
|
|
485
|
-
const GENERATE_TESTCASES_TOOL = {
|
|
486
|
-
name: "generate_testcases",
|
|
487
|
-
description: "Generate test cases using AI from requirement text",
|
|
358
|
+
const SET_ACTIVE_PROJECT_TOOL = {
|
|
359
|
+
name: "set_active_project",
|
|
360
|
+
description: "Set the active project AFTER the user has chosen one from get_my_projects results. " +
|
|
361
|
+
"NEVER call this without first asking the user which project they want. " +
|
|
362
|
+
"Once set, other tools will use this project automatically.",
|
|
488
363
|
inputSchema: {
|
|
489
364
|
type: "object",
|
|
490
365
|
properties: {
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
366
|
+
projectId: {
|
|
367
|
+
type: "string",
|
|
368
|
+
description: "The project ID to set as active (e.g., PRJ-1234)",
|
|
369
|
+
},
|
|
495
370
|
},
|
|
496
|
-
required: [
|
|
371
|
+
required: [],
|
|
497
372
|
},
|
|
498
373
|
};
|
|
499
|
-
const
|
|
500
|
-
name: "
|
|
501
|
-
description: "
|
|
374
|
+
const GET_ACTIVE_PROJECT_TOOL = {
|
|
375
|
+
name: "get_active_project",
|
|
376
|
+
description: "Get the currently active project ID that is being used for operations",
|
|
502
377
|
inputSchema: {
|
|
503
378
|
type: "object",
|
|
504
|
-
properties: {
|
|
505
|
-
|
|
506
|
-
organizationId: { type: "number", description: "Organization ID" },
|
|
507
|
-
type: { type: "string", description: "Issue type (REQUIREMENT or DEFECT)" },
|
|
508
|
-
title: { type: "string", description: "Issue title" },
|
|
509
|
-
description: { type: "string", description: "Issue description" },
|
|
510
|
-
id: { type: "string", description: "Issue ID (if updating)" },
|
|
511
|
-
},
|
|
512
|
-
required: ["projectId", "organizationId", "type", "title"],
|
|
379
|
+
properties: {},
|
|
380
|
+
required: [],
|
|
513
381
|
},
|
|
514
382
|
};
|
|
515
383
|
// Create server instance
|
|
@@ -521,37 +389,24 @@ const server = new Server({
|
|
|
521
389
|
tools: {},
|
|
522
390
|
},
|
|
523
391
|
});
|
|
524
|
-
// List available tools
|
|
392
|
+
// List available tools (14 tools)
|
|
525
393
|
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
526
394
|
return {
|
|
527
395
|
tools: [
|
|
396
|
+
GET_MY_PROJECTS_TOOL,
|
|
397
|
+
SET_ACTIVE_PROJECT_TOOL,
|
|
398
|
+
GET_ACTIVE_PROJECT_TOOL,
|
|
528
399
|
GET_TESTCASES_TOOL,
|
|
529
400
|
GET_TESTCASE_DETAIL_TOOL,
|
|
530
|
-
GET_TESTCASE_HISTORY_TOOL,
|
|
531
|
-
GET_FOLDERS_TOOL,
|
|
532
|
-
GET_LABELS_TOOL,
|
|
533
|
-
GET_ISSUES_TOOL,
|
|
534
|
-
GET_MY_PROJECTS_TOOL,
|
|
535
401
|
CREATE_TESTCASE_TOOL,
|
|
536
402
|
CREATE_BULK_TESTCASE_TOOL,
|
|
537
403
|
UPDATE_TESTCASE_FIELD_TOOL,
|
|
538
404
|
DELETE_TESTCASE_TOOL,
|
|
539
|
-
|
|
540
|
-
CREATE_TEST_STEP_TOOL,
|
|
541
|
-
DELETE_TEST_STEP_TOOL,
|
|
542
|
-
DOWNLOAD_TESTCASES_TOOL,
|
|
543
|
-
IMPORT_TESTCASES_TOOL,
|
|
405
|
+
GET_FOLDERS_TOOL,
|
|
544
406
|
CREATE_FOLDER_TOOL,
|
|
545
|
-
|
|
546
|
-
UPDATE_FOLDER_TOOL,
|
|
407
|
+
GET_LABELS_TOOL,
|
|
547
408
|
CREATE_LABEL_TOOL,
|
|
548
409
|
GET_USER_PROFILE_TOOL,
|
|
549
|
-
GET_ORGANIZATION_TOOL,
|
|
550
|
-
GET_MAPPED_ISSUES_TOOL,
|
|
551
|
-
SEARCH_ISSUES_TOOL,
|
|
552
|
-
MAP_ISSUES_TOOL,
|
|
553
|
-
GENERATE_TESTCASES_TOOL,
|
|
554
|
-
CREATE_ISSUE_TOOL,
|
|
555
410
|
],
|
|
556
411
|
};
|
|
557
412
|
});
|
|
@@ -559,26 +414,20 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
559
414
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
560
415
|
const { name, arguments: args } = request.params;
|
|
561
416
|
try {
|
|
562
|
-
if (name === "
|
|
563
|
-
return await
|
|
564
|
-
}
|
|
565
|
-
else if (name === "get_testcase_detail") {
|
|
566
|
-
return await handleGetTestcaseDetail(args);
|
|
567
|
-
}
|
|
568
|
-
else if (name === "get_testcase_history") {
|
|
569
|
-
return await handleGetTestcaseHistory(args);
|
|
417
|
+
if (name === "get_my_projects") {
|
|
418
|
+
return await handleGetMyProjects(args);
|
|
570
419
|
}
|
|
571
|
-
else if (name === "
|
|
572
|
-
return await
|
|
420
|
+
else if (name === "set_active_project") {
|
|
421
|
+
return await handleSetActiveProject(args);
|
|
573
422
|
}
|
|
574
|
-
else if (name === "
|
|
575
|
-
return await
|
|
423
|
+
else if (name === "get_active_project") {
|
|
424
|
+
return await handleGetActiveProject();
|
|
576
425
|
}
|
|
577
|
-
else if (name === "
|
|
578
|
-
return await
|
|
426
|
+
else if (name === "get_testcases") {
|
|
427
|
+
return await handleGetTestcases(args);
|
|
579
428
|
}
|
|
580
|
-
else if (name === "
|
|
581
|
-
return await
|
|
429
|
+
else if (name === "get_testcase_detail") {
|
|
430
|
+
return await handleGetTestcaseDetail(args);
|
|
582
431
|
}
|
|
583
432
|
else if (name === "create_testcase") {
|
|
584
433
|
return await handleCreateTestcase(args);
|
|
@@ -592,29 +441,14 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
592
441
|
else if (name === "delete_testcase") {
|
|
593
442
|
return await handleDeleteTestcase(args);
|
|
594
443
|
}
|
|
595
|
-
else if (name === "
|
|
596
|
-
return await
|
|
597
|
-
}
|
|
598
|
-
else if (name === "create_test_step") {
|
|
599
|
-
return await handleCreateTestStep(args);
|
|
600
|
-
}
|
|
601
|
-
else if (name === "delete_test_step") {
|
|
602
|
-
return await handleDeleteTestStep(args);
|
|
603
|
-
}
|
|
604
|
-
else if (name === "download_testcases") {
|
|
605
|
-
return await handleDownloadTestcases(args);
|
|
606
|
-
}
|
|
607
|
-
else if (name === "import_testcases") {
|
|
608
|
-
return await handleImportTestcases(args);
|
|
444
|
+
else if (name === "get_folders") {
|
|
445
|
+
return await handleGetFolders(args);
|
|
609
446
|
}
|
|
610
447
|
else if (name === "create_folder") {
|
|
611
448
|
return await handleCreateFolder(args);
|
|
612
449
|
}
|
|
613
|
-
else if (name === "
|
|
614
|
-
return await
|
|
615
|
-
}
|
|
616
|
-
else if (name === "update_folder") {
|
|
617
|
-
return await handleUpdateFolder(args);
|
|
450
|
+
else if (name === "get_labels") {
|
|
451
|
+
return await handleGetLabels(args);
|
|
618
452
|
}
|
|
619
453
|
else if (name === "create_label") {
|
|
620
454
|
return await handleCreateLabel(args);
|
|
@@ -622,24 +456,6 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
622
456
|
else if (name === "get_user_profile") {
|
|
623
457
|
return await handleGetUserProfile(args);
|
|
624
458
|
}
|
|
625
|
-
else if (name === "get_organization") {
|
|
626
|
-
return await handleGetOrganization(args);
|
|
627
|
-
}
|
|
628
|
-
else if (name === "get_mapped_issues") {
|
|
629
|
-
return await handleGetMappedIssues(args);
|
|
630
|
-
}
|
|
631
|
-
else if (name === "search_issues") {
|
|
632
|
-
return await handleSearchIssues(args);
|
|
633
|
-
}
|
|
634
|
-
else if (name === "map_issues") {
|
|
635
|
-
return await handleMapIssues(args);
|
|
636
|
-
}
|
|
637
|
-
else if (name === "generate_testcases") {
|
|
638
|
-
return await handleGenerateTestcases(args);
|
|
639
|
-
}
|
|
640
|
-
else if (name === "create_issue") {
|
|
641
|
-
return await handleCreateIssue(args);
|
|
642
|
-
}
|
|
643
459
|
throw new Error(`Unknown tool: ${name}`);
|
|
644
460
|
}
|
|
645
461
|
catch (error) {
|
|
@@ -659,26 +475,13 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
659
475
|
});
|
|
660
476
|
// Handler for get_testcases tool
|
|
661
477
|
async function handleGetTestcases(args) {
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
if (!args.organizationId) {
|
|
667
|
-
throw new Error("organizationId is required");
|
|
668
|
-
}
|
|
669
|
-
// Check for authentication
|
|
670
|
-
if (!config.patToken) {
|
|
671
|
-
throw new Error("Authentication required. Please set TESTKASE_PAT_TOKEN environment variable. " +
|
|
672
|
-
"Generate a Personal Access Token (PAT) from your TestKase account settings.");
|
|
673
|
-
}
|
|
674
|
-
// Validate PAT token format
|
|
675
|
-
if (!config.patToken.startsWith("xyz_")) {
|
|
676
|
-
throw new Error("Invalid PAT token format. TestKase PAT tokens must start with 'xyz_' prefix. " +
|
|
677
|
-
"Please generate a valid PAT from your TestKase account settings.");
|
|
678
|
-
}
|
|
478
|
+
validateAuth();
|
|
479
|
+
// Get project and organization IDs (from args or cached)
|
|
480
|
+
const projectId = getProjectId(args.projectId);
|
|
481
|
+
const organizationId = await getOrganizationId(args.organizationId);
|
|
679
482
|
// Build query parameters
|
|
680
483
|
const queryParams = new URLSearchParams();
|
|
681
|
-
queryParams.append("organizationId",
|
|
484
|
+
queryParams.append("organizationId", organizationId.toString());
|
|
682
485
|
if (args.page !== undefined)
|
|
683
486
|
queryParams.append("page", args.page.toString());
|
|
684
487
|
if (args.limit !== undefined)
|
|
@@ -706,7 +509,7 @@ async function handleGetTestcases(args) {
|
|
|
706
509
|
if (args.updated_end_date)
|
|
707
510
|
queryParams.append("updated_end_date", args.updated_end_date);
|
|
708
511
|
// Construct API URL
|
|
709
|
-
const url = `${config.apiBaseUrl}/api/v1/projects/testcases/get-testcase/${
|
|
512
|
+
const url = `${config.apiBaseUrl}/api/v1/projects/testcases/get-testcase/${projectId}?${queryParams.toString()}`;
|
|
710
513
|
// Prepare headers with PAT token
|
|
711
514
|
// The PAT token is sent as Bearer token and will be validated by TestKase's JwtAuthGuard
|
|
712
515
|
// which checks for 'xyz_' prefix and validates against the PAT database
|
|
@@ -784,9 +587,11 @@ async function handleGetTestcases(args) {
|
|
|
784
587
|
// Handler for get_testcase_detail tool
|
|
785
588
|
async function handleGetTestcaseDetail(args) {
|
|
786
589
|
validateAuth();
|
|
590
|
+
const projectId = getProjectId(args.projectId);
|
|
591
|
+
const organizationId = await getOrganizationId(args.organizationId);
|
|
787
592
|
const queryParams = new URLSearchParams();
|
|
788
|
-
queryParams.append("organizationId",
|
|
789
|
-
const url = `${config.apiBaseUrl}/api/v1/projects/testcases/get-testcase-detail/${
|
|
593
|
+
queryParams.append("organizationId", organizationId.toString());
|
|
594
|
+
const url = `${config.apiBaseUrl}/api/v1/projects/testcases/get-testcase-detail/${projectId}/${args.testcaseId}?${queryParams.toString()}`;
|
|
790
595
|
try {
|
|
791
596
|
const response = await axios.get(url, {
|
|
792
597
|
headers: {
|
|
@@ -801,34 +606,18 @@ async function handleGetTestcaseDetail(args) {
|
|
|
801
606
|
throw handleApiError(error);
|
|
802
607
|
}
|
|
803
608
|
}
|
|
804
|
-
// Handler for get_testcase_history tool
|
|
805
|
-
async function handleGetTestcaseHistory(args) {
|
|
806
|
-
validateAuth();
|
|
807
|
-
const queryParams = new URLSearchParams();
|
|
808
|
-
queryParams.append("organizationId", args.organizationId.toString());
|
|
809
|
-
const url = `${config.apiBaseUrl}/api/v1/projects/testcases/get-testcase-history/${args.projectId}/${args.testcaseId}?${queryParams.toString()}`;
|
|
810
|
-
try {
|
|
811
|
-
const response = await axios.get(url, {
|
|
812
|
-
headers: {
|
|
813
|
-
"Content-Type": "application/json",
|
|
814
|
-
"Authorization": `Bearer ${config.patToken}`,
|
|
815
|
-
},
|
|
816
|
-
});
|
|
817
|
-
const data = response.data;
|
|
818
|
-
return formatResponse(data, `Testcase History: ${args.testcaseId}`);
|
|
819
|
-
}
|
|
820
|
-
catch (error) {
|
|
821
|
-
throw handleApiError(error);
|
|
822
|
-
}
|
|
823
|
-
}
|
|
824
609
|
// Handler for get_folders tool
|
|
825
610
|
async function handleGetFolders(args) {
|
|
826
611
|
validateAuth();
|
|
612
|
+
const projectId = getProjectId(args.projectId);
|
|
613
|
+
const organizationId = await getOrganizationId(args.organizationId);
|
|
614
|
+
const section = args.section || "TESTCASE";
|
|
827
615
|
const queryParams = new URLSearchParams();
|
|
828
|
-
queryParams.append("organizationId",
|
|
829
|
-
queryParams.append("projectId",
|
|
830
|
-
|
|
831
|
-
|
|
616
|
+
queryParams.append("organizationId", organizationId.toString());
|
|
617
|
+
queryParams.append("projectId", projectId);
|
|
618
|
+
queryParams.append("section", section);
|
|
619
|
+
if (args.search) {
|
|
620
|
+
queryParams.append("search", args.search);
|
|
832
621
|
}
|
|
833
622
|
const url = `${config.apiBaseUrl}/api/v1/folders/get-folders?${queryParams.toString()}`;
|
|
834
623
|
try {
|
|
@@ -848,9 +637,11 @@ async function handleGetFolders(args) {
|
|
|
848
637
|
// Handler for get_labels tool
|
|
849
638
|
async function handleGetLabels(args) {
|
|
850
639
|
validateAuth();
|
|
640
|
+
const projectId = getProjectId(args.projectId);
|
|
641
|
+
const organizationId = await getOrganizationId(args.organizationId);
|
|
851
642
|
const queryParams = new URLSearchParams();
|
|
852
|
-
queryParams.append("organizationId",
|
|
853
|
-
const url = `${config.apiBaseUrl}/api/v1/projects/
|
|
643
|
+
queryParams.append("organizationId", organizationId.toString());
|
|
644
|
+
const url = `${config.apiBaseUrl}/api/v1/projects/labels/get-label/${projectId}?${queryParams.toString()}`;
|
|
854
645
|
try {
|
|
855
646
|
const response = await axios.get(url, {
|
|
856
647
|
headers: {
|
|
@@ -865,30 +656,6 @@ async function handleGetLabels(args) {
|
|
|
865
656
|
throw handleApiError(error);
|
|
866
657
|
}
|
|
867
658
|
}
|
|
868
|
-
// Handler for get_issues tool
|
|
869
|
-
async function handleGetIssues(args) {
|
|
870
|
-
validateAuth();
|
|
871
|
-
const queryParams = new URLSearchParams();
|
|
872
|
-
queryParams.append("organizationId", args.organizationId.toString());
|
|
873
|
-
if (args.page)
|
|
874
|
-
queryParams.append("page", args.page.toString());
|
|
875
|
-
if (args.limit)
|
|
876
|
-
queryParams.append("limit", args.limit.toString());
|
|
877
|
-
const url = `${config.apiBaseUrl}/api/v1/issues/get-issues/${args.projectId}?${queryParams.toString()}`;
|
|
878
|
-
try {
|
|
879
|
-
const response = await axios.get(url, {
|
|
880
|
-
headers: {
|
|
881
|
-
"Content-Type": "application/json",
|
|
882
|
-
"Authorization": `Bearer ${config.patToken}`,
|
|
883
|
-
},
|
|
884
|
-
});
|
|
885
|
-
const data = response.data;
|
|
886
|
-
return formatResponse(data, `Issues for Project: ${args.projectId}`);
|
|
887
|
-
}
|
|
888
|
-
catch (error) {
|
|
889
|
-
throw handleApiError(error);
|
|
890
|
-
}
|
|
891
|
-
}
|
|
892
659
|
// Handler for get_my_projects tool
|
|
893
660
|
async function handleGetMyProjects(args) {
|
|
894
661
|
validateAuth();
|
|
@@ -912,34 +679,80 @@ async function handleGetMyProjects(args) {
|
|
|
912
679
|
throw handleApiError(error);
|
|
913
680
|
}
|
|
914
681
|
}
|
|
915
|
-
// Handler for create_testcase tool
|
|
682
|
+
// Handler for create_testcase tool (handles test_steps internally)
|
|
916
683
|
async function handleCreateTestcase(args) {
|
|
917
684
|
validateAuth();
|
|
685
|
+
const projectId = getProjectId(args.projectId);
|
|
686
|
+
const organizationId = await getOrganizationId(args.organizationId);
|
|
687
|
+
// Extract test_steps before sending to API
|
|
688
|
+
const { test_steps, ...testcaseData } = args;
|
|
918
689
|
const queryParams = new URLSearchParams();
|
|
919
|
-
queryParams.append("organizationId",
|
|
920
|
-
const url = `${config.apiBaseUrl}/api/v1/projects/testcases/create-testcase/${
|
|
690
|
+
queryParams.append("organizationId", organizationId.toString());
|
|
691
|
+
const url = `${config.apiBaseUrl}/api/v1/projects/testcases/create-testcase/${projectId}?${queryParams.toString()}`;
|
|
921
692
|
try {
|
|
922
|
-
|
|
693
|
+
// Step 1: Create testcase
|
|
694
|
+
const response = await axios.post(url, testcaseData, {
|
|
923
695
|
headers: { "Content-Type": "application/json", "Authorization": `Bearer ${config.patToken}` },
|
|
924
696
|
});
|
|
925
|
-
|
|
697
|
+
const createdTestcase = response.data?.data || response.data;
|
|
698
|
+
const testcaseId = createdTestcase?.id || createdTestcase?.testcaseId;
|
|
699
|
+
// Step 2: If test_steps provided, create them
|
|
700
|
+
if (test_steps && Array.isArray(test_steps) && test_steps.length > 0 && testcaseId) {
|
|
701
|
+
const stepsUrl = `${config.apiBaseUrl}/api/v1/projects/testcases/teststeps/${projectId}/${testcaseId}?organizationId=${organizationId}`;
|
|
702
|
+
await axios.post(stepsUrl, { steps: test_steps }, {
|
|
703
|
+
headers: { "Content-Type": "application/json", "Authorization": `Bearer ${config.patToken}` },
|
|
704
|
+
});
|
|
705
|
+
}
|
|
706
|
+
return formatResponse(response.data, "Testcase Created" + (test_steps?.length ? ` with ${test_steps.length} test steps` : ""));
|
|
926
707
|
}
|
|
927
708
|
catch (error) {
|
|
928
709
|
throw handleApiError(error);
|
|
929
710
|
}
|
|
930
711
|
}
|
|
931
|
-
// Handler for create_bulk_testcase tool
|
|
712
|
+
// Handler for create_bulk_testcase tool (handles test_steps internally)
|
|
932
713
|
async function handleCreateBulkTestcase(args) {
|
|
933
714
|
validateAuth();
|
|
934
|
-
const
|
|
935
|
-
|
|
936
|
-
const url = `${config.apiBaseUrl}/api/v1/projects/testcases/create-bulk-testcase/${args.projectId}?${queryParams.toString()}`;
|
|
715
|
+
const projectId = getProjectId(args.projectId);
|
|
716
|
+
const organizationId = await getOrganizationId(args.organizationId);
|
|
937
717
|
const testcases = typeof args.testcases === 'string' ? JSON.parse(args.testcases) : args.testcases;
|
|
718
|
+
// Separate test_steps from each testcase for later processing
|
|
719
|
+
const testcasesWithSteps = testcases.map((tc) => ({
|
|
720
|
+
testcase: { ...tc, test_steps: undefined },
|
|
721
|
+
test_steps: tc.test_steps
|
|
722
|
+
}));
|
|
723
|
+
const queryParams = new URLSearchParams();
|
|
724
|
+
queryParams.append("organizationId", organizationId.toString());
|
|
725
|
+
const url = `${config.apiBaseUrl}/api/v1/projects/testcases/create-bulk-testcase/${projectId}?${queryParams.toString()}`;
|
|
938
726
|
try {
|
|
939
|
-
|
|
727
|
+
// Step 1: Create all testcases (without steps)
|
|
728
|
+
const cleanTestcases = testcasesWithSteps.map((t) => t.testcase);
|
|
729
|
+
const response = await axios.post(url, { testcases: cleanTestcases }, {
|
|
940
730
|
headers: { "Content-Type": "application/json", "Authorization": `Bearer ${config.patToken}` },
|
|
941
731
|
});
|
|
942
|
-
|
|
732
|
+
const results = response.data?.results || [];
|
|
733
|
+
// Step 2: Create test steps for each successfully created testcase
|
|
734
|
+
let stepsCreated = 0;
|
|
735
|
+
for (let i = 0; i < results.length; i++) {
|
|
736
|
+
const result = results[i];
|
|
737
|
+
const steps = testcasesWithSteps[i]?.test_steps;
|
|
738
|
+
if (result.status === 'success' && steps && Array.isArray(steps) && steps.length > 0) {
|
|
739
|
+
const testcaseId = result.testcaseId || result.id;
|
|
740
|
+
if (testcaseId) {
|
|
741
|
+
try {
|
|
742
|
+
const stepsUrl = `${config.apiBaseUrl}/api/v1/projects/testcases/teststeps/${projectId}/${testcaseId}?organizationId=${organizationId}`;
|
|
743
|
+
await axios.post(stepsUrl, { steps }, {
|
|
744
|
+
headers: { "Content-Type": "application/json", "Authorization": `Bearer ${config.patToken}` },
|
|
745
|
+
});
|
|
746
|
+
stepsCreated++;
|
|
747
|
+
}
|
|
748
|
+
catch (e) {
|
|
749
|
+
// Log but don't fail the whole operation
|
|
750
|
+
console.error(`Failed to create steps for testcase ${testcaseId}`);
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
return formatResponse(response.data, `Bulk Testcases Created (${stepsCreated} with test steps)`);
|
|
943
756
|
}
|
|
944
757
|
catch (error) {
|
|
945
758
|
throw handleApiError(error);
|
|
@@ -948,9 +761,11 @@ async function handleCreateBulkTestcase(args) {
|
|
|
948
761
|
// Handler for update_testcase_field tool
|
|
949
762
|
async function handleUpdateTestcaseField(args) {
|
|
950
763
|
validateAuth();
|
|
764
|
+
const projectId = getProjectId(args.projectId);
|
|
765
|
+
const organizationId = await getOrganizationId(args.organizationId);
|
|
951
766
|
const queryParams = new URLSearchParams();
|
|
952
|
-
queryParams.append("organizationId",
|
|
953
|
-
const url = `${config.apiBaseUrl}/api/v1/projects/testcases/update-field/${
|
|
767
|
+
queryParams.append("organizationId", organizationId.toString());
|
|
768
|
+
const url = `${config.apiBaseUrl}/api/v1/projects/testcases/update-field/${projectId}?${queryParams.toString()}`;
|
|
954
769
|
try {
|
|
955
770
|
const response = await axios.patch(url, {
|
|
956
771
|
ids: args.ids.split(','),
|
|
@@ -968,9 +783,11 @@ async function handleUpdateTestcaseField(args) {
|
|
|
968
783
|
// Handler for delete_testcase tool
|
|
969
784
|
async function handleDeleteTestcase(args) {
|
|
970
785
|
validateAuth();
|
|
786
|
+
const projectId = getProjectId(args.projectId);
|
|
787
|
+
const organizationId = await getOrganizationId(args.organizationId);
|
|
971
788
|
const queryParams = new URLSearchParams();
|
|
972
|
-
queryParams.append("organizationId",
|
|
973
|
-
const url = `${config.apiBaseUrl}/api/v1/projects/testcases/delete-testcase/${
|
|
789
|
+
queryParams.append("organizationId", organizationId.toString());
|
|
790
|
+
const url = `${config.apiBaseUrl}/api/v1/projects/testcases/delete-testcase/${projectId}?${queryParams.toString()}`;
|
|
974
791
|
try {
|
|
975
792
|
const response = await axios.delete(url, {
|
|
976
793
|
headers: { "Content-Type": "application/json", "Authorization": `Bearer ${config.patToken}` },
|
|
@@ -982,96 +799,22 @@ async function handleDeleteTestcase(args) {
|
|
|
982
799
|
throw handleApiError(error);
|
|
983
800
|
}
|
|
984
801
|
}
|
|
985
|
-
// Handler for clone_testcase tool
|
|
986
|
-
async function handleCloneTestcase(args) {
|
|
987
|
-
validateAuth();
|
|
988
|
-
const queryParams = new URLSearchParams();
|
|
989
|
-
queryParams.append("organizationId", args.organizationId.toString());
|
|
990
|
-
const url = `${config.apiBaseUrl}/api/v1/projects/testcases/clone/${args.projectId}/${args.id}?${queryParams.toString()}`;
|
|
991
|
-
try {
|
|
992
|
-
const response = await axios.post(url, {}, {
|
|
993
|
-
headers: { "Content-Type": "application/json", "Authorization": `Bearer ${config.patToken}` },
|
|
994
|
-
});
|
|
995
|
-
return formatResponse(response.data, "Testcase Cloned");
|
|
996
|
-
}
|
|
997
|
-
catch (error) {
|
|
998
|
-
throw handleApiError(error);
|
|
999
|
-
}
|
|
1000
|
-
}
|
|
1001
|
-
// Handler for create_test_step tool
|
|
1002
|
-
async function handleCreateTestStep(args) {
|
|
1003
|
-
validateAuth();
|
|
1004
|
-
const queryParams = new URLSearchParams();
|
|
1005
|
-
queryParams.append("organizationId", args.organizationId.toString());
|
|
1006
|
-
const url = `${config.apiBaseUrl}/api/v1/projects/testcases/teststeps/${args.projectId}/${args.testcaseId}?${queryParams.toString()}`;
|
|
1007
|
-
const steps = typeof args.steps === 'string' ? JSON.parse(args.steps) : args.steps;
|
|
1008
|
-
try {
|
|
1009
|
-
const response = await axios.post(url, { steps }, {
|
|
1010
|
-
headers: { "Content-Type": "application/json", "Authorization": `Bearer ${config.patToken}` },
|
|
1011
|
-
});
|
|
1012
|
-
return formatResponse(response.data, "Test Steps Created");
|
|
1013
|
-
}
|
|
1014
|
-
catch (error) {
|
|
1015
|
-
throw handleApiError(error);
|
|
1016
|
-
}
|
|
1017
|
-
}
|
|
1018
|
-
// Handler for delete_test_step tool
|
|
1019
|
-
async function handleDeleteTestStep(args) {
|
|
1020
|
-
validateAuth();
|
|
1021
|
-
const queryParams = new URLSearchParams();
|
|
1022
|
-
queryParams.append("organizationId", args.organizationId.toString());
|
|
1023
|
-
const url = `${config.apiBaseUrl}/api/v1/projects/testcases/teststeps/${args.testcaseId}?${queryParams.toString()}`;
|
|
1024
|
-
try {
|
|
1025
|
-
const response = await axios.delete(url, {
|
|
1026
|
-
headers: { "Content-Type": "application/json", "Authorization": `Bearer ${config.patToken}` },
|
|
1027
|
-
data: { stepIds: args.stepIds.split(',') },
|
|
1028
|
-
});
|
|
1029
|
-
return formatResponse(response.data, "Test Steps Deleted");
|
|
1030
|
-
}
|
|
1031
|
-
catch (error) {
|
|
1032
|
-
throw handleApiError(error);
|
|
1033
|
-
}
|
|
1034
|
-
}
|
|
1035
|
-
// Handler for download_testcases tool
|
|
1036
|
-
async function handleDownloadTestcases(args) {
|
|
1037
|
-
validateAuth();
|
|
1038
|
-
const queryParams = new URLSearchParams();
|
|
1039
|
-
queryParams.append("organizationId", args.organizationId.toString());
|
|
1040
|
-
if (args.filters)
|
|
1041
|
-
queryParams.append("filters", args.filters);
|
|
1042
|
-
const url = `${config.apiBaseUrl}/api/v1/projects/testcases/download/${args.projectId}?${queryParams.toString()}`;
|
|
1043
|
-
try {
|
|
1044
|
-
const response = await axios.get(url, {
|
|
1045
|
-
headers: { "Content-Type": "application/json", "Authorization": `Bearer ${config.patToken}` },
|
|
1046
|
-
});
|
|
1047
|
-
return formatResponse(response.data, "Testcases Download");
|
|
1048
|
-
}
|
|
1049
|
-
catch (error) {
|
|
1050
|
-
throw handleApiError(error);
|
|
1051
|
-
}
|
|
1052
|
-
}
|
|
1053
|
-
// Handler for import_testcases tool
|
|
1054
|
-
async function handleImportTestcases(args) {
|
|
1055
|
-
validateAuth();
|
|
1056
|
-
const queryParams = new URLSearchParams();
|
|
1057
|
-
queryParams.append("organizationId", args.organizationId.toString());
|
|
1058
|
-
const url = `${config.apiBaseUrl}/api/v1/projects/testcases/import/${args.projectId}?${queryParams.toString()}`;
|
|
1059
|
-
try {
|
|
1060
|
-
const response = await axios.post(url, { file: args.file }, {
|
|
1061
|
-
headers: { "Content-Type": "application/json", "Authorization": `Bearer ${config.patToken}` },
|
|
1062
|
-
});
|
|
1063
|
-
return formatResponse(response.data, "Testcases Imported");
|
|
1064
|
-
}
|
|
1065
|
-
catch (error) {
|
|
1066
|
-
throw handleApiError(error);
|
|
1067
|
-
}
|
|
1068
|
-
}
|
|
1069
802
|
// Handler for create_folder tool
|
|
1070
803
|
async function handleCreateFolder(args) {
|
|
1071
804
|
validateAuth();
|
|
805
|
+
const projectId = getProjectId(args.projectId);
|
|
806
|
+
const organizationId = await getOrganizationId(args.organizationId);
|
|
1072
807
|
const url = `${config.apiBaseUrl}/api/v1/folders/create-folders`;
|
|
808
|
+
const section = args.section || "TESTCASE";
|
|
809
|
+
const body = {
|
|
810
|
+
organizationId: organizationId,
|
|
811
|
+
projectId: projectId,
|
|
812
|
+
section: section,
|
|
813
|
+
name: args.name,
|
|
814
|
+
...(args.parentId && { parentId: args.parentId }),
|
|
815
|
+
};
|
|
1073
816
|
try {
|
|
1074
|
-
const response = await axios.post(url,
|
|
817
|
+
const response = await axios.post(url, body, {
|
|
1075
818
|
headers: { "Content-Type": "application/json", "Authorization": `Bearer ${config.patToken}` },
|
|
1076
819
|
});
|
|
1077
820
|
return formatResponse(response.data, "Folder Created");
|
|
@@ -1080,44 +823,14 @@ async function handleCreateFolder(args) {
|
|
|
1080
823
|
throw handleApiError(error);
|
|
1081
824
|
}
|
|
1082
825
|
}
|
|
1083
|
-
// Handler for delete_folder tool
|
|
1084
|
-
async function handleDeleteFolder(args) {
|
|
1085
|
-
validateAuth();
|
|
1086
|
-
const queryParams = new URLSearchParams();
|
|
1087
|
-
queryParams.append("organizationId", args.organizationId.toString());
|
|
1088
|
-
const url = `${config.apiBaseUrl}/api/v1/folders/delete/${args.id}?${queryParams.toString()}`;
|
|
1089
|
-
try {
|
|
1090
|
-
const response = await axios.delete(url, {
|
|
1091
|
-
headers: { "Content-Type": "application/json", "Authorization": `Bearer ${config.patToken}` },
|
|
1092
|
-
});
|
|
1093
|
-
return formatResponse(response.data, "Folder Deleted");
|
|
1094
|
-
}
|
|
1095
|
-
catch (error) {
|
|
1096
|
-
throw handleApiError(error);
|
|
1097
|
-
}
|
|
1098
|
-
}
|
|
1099
|
-
// Handler for update_folder tool
|
|
1100
|
-
async function handleUpdateFolder(args) {
|
|
1101
|
-
validateAuth();
|
|
1102
|
-
const queryParams = new URLSearchParams();
|
|
1103
|
-
queryParams.append("organizationId", args.organizationId.toString());
|
|
1104
|
-
const url = `${config.apiBaseUrl}/api/v1/folders/${args.id}?${queryParams.toString()}`;
|
|
1105
|
-
try {
|
|
1106
|
-
const response = await axios.patch(url, { folder_name: args.folder_name }, {
|
|
1107
|
-
headers: { "Content-Type": "application/json", "Authorization": `Bearer ${config.patToken}` },
|
|
1108
|
-
});
|
|
1109
|
-
return formatResponse(response.data, "Folder Updated");
|
|
1110
|
-
}
|
|
1111
|
-
catch (error) {
|
|
1112
|
-
throw handleApiError(error);
|
|
1113
|
-
}
|
|
1114
|
-
}
|
|
1115
826
|
// Handler for create_label tool
|
|
1116
827
|
async function handleCreateLabel(args) {
|
|
1117
828
|
validateAuth();
|
|
829
|
+
const projectId = getProjectId(args.projectId);
|
|
830
|
+
const organizationId = await getOrganizationId(args.organizationId);
|
|
1118
831
|
const queryParams = new URLSearchParams();
|
|
1119
|
-
queryParams.append("organizationId",
|
|
1120
|
-
const url = `${config.apiBaseUrl}/api/v1/projects/labels/create/${
|
|
832
|
+
queryParams.append("organizationId", organizationId.toString());
|
|
833
|
+
const url = `${config.apiBaseUrl}/api/v1/projects/labels/create/${projectId}?${queryParams.toString()}`;
|
|
1121
834
|
try {
|
|
1122
835
|
const response = await axios.post(url, { values: args.values }, {
|
|
1123
836
|
headers: { "Content-Type": "application/json", "Authorization": `Bearer ${config.patToken}` },
|
|
@@ -1142,106 +855,42 @@ async function handleGetUserProfile(args) {
|
|
|
1142
855
|
throw handleApiError(error);
|
|
1143
856
|
}
|
|
1144
857
|
}
|
|
1145
|
-
// Handler for
|
|
1146
|
-
async function
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
try {
|
|
1150
|
-
const response = await axios.get(url, {
|
|
1151
|
-
headers: { "Content-Type": "application/json", "Authorization": `Bearer ${config.patToken}` },
|
|
1152
|
-
});
|
|
1153
|
-
return formatResponse(response.data, "Organization Details");
|
|
1154
|
-
}
|
|
1155
|
-
catch (error) {
|
|
1156
|
-
throw handleApiError(error);
|
|
1157
|
-
}
|
|
1158
|
-
}
|
|
1159
|
-
// Handler for get_mapped_issues tool
|
|
1160
|
-
async function handleGetMappedIssues(args) {
|
|
1161
|
-
validateAuth();
|
|
1162
|
-
const url = `${config.apiBaseUrl}/api/v1/${args.organization_id}/${args.project_id}/${args.integration_type}/testcases/${args.testcase_id}/issues`;
|
|
1163
|
-
try {
|
|
1164
|
-
const response = await axios.get(url, {
|
|
1165
|
-
headers: { "Content-Type": "application/json", "Authorization": `Bearer ${config.patToken}` },
|
|
1166
|
-
});
|
|
1167
|
-
return formatResponse(response.data, "Mapped Issues");
|
|
1168
|
-
}
|
|
1169
|
-
catch (error) {
|
|
1170
|
-
throw handleApiError(error);
|
|
1171
|
-
}
|
|
1172
|
-
}
|
|
1173
|
-
// Handler for search_issues tool
|
|
1174
|
-
async function handleSearchIssues(args) {
|
|
1175
|
-
validateAuth();
|
|
1176
|
-
const queryParams = new URLSearchParams();
|
|
1177
|
-
if (args.search)
|
|
1178
|
-
queryParams.append("search", args.search);
|
|
1179
|
-
if (args.page)
|
|
1180
|
-
queryParams.append("page", args.page.toString());
|
|
1181
|
-
if (args.limit)
|
|
1182
|
-
queryParams.append("limit", args.limit.toString());
|
|
1183
|
-
const url = `${config.apiBaseUrl}/api/v1/${args.project_id}/${args.integration_type}/issues/search?${queryParams.toString()}`;
|
|
1184
|
-
try {
|
|
1185
|
-
const response = await axios.get(url, {
|
|
1186
|
-
headers: { "Content-Type": "application/json", "Authorization": `Bearer ${config.patToken}` },
|
|
1187
|
-
});
|
|
1188
|
-
return formatResponse(response.data, "Search Issues");
|
|
1189
|
-
}
|
|
1190
|
-
catch (error) {
|
|
1191
|
-
throw handleApiError(error);
|
|
1192
|
-
}
|
|
1193
|
-
}
|
|
1194
|
-
// Handler for map_issues tool
|
|
1195
|
-
async function handleMapIssues(args) {
|
|
1196
|
-
validateAuth();
|
|
1197
|
-
const url = `${config.apiBaseUrl}/api/v1/${args.organization_id}/${args.project_id}/${args.testcase_id}/${args.integration_type}/issues/map`;
|
|
1198
|
-
const issues = typeof args.issues === 'string' ? JSON.parse(args.issues) : args.issues;
|
|
1199
|
-
try {
|
|
1200
|
-
const response = await axios.post(url, { issues }, {
|
|
1201
|
-
headers: { "Content-Type": "application/json", "Authorization": `Bearer ${config.patToken}` },
|
|
1202
|
-
});
|
|
1203
|
-
return formatResponse(response.data, "Issues Mapped");
|
|
1204
|
-
}
|
|
1205
|
-
catch (error) {
|
|
1206
|
-
throw handleApiError(error);
|
|
1207
|
-
}
|
|
1208
|
-
}
|
|
1209
|
-
// Handler for generate_testcases tool
|
|
1210
|
-
async function handleGenerateTestcases(args) {
|
|
1211
|
-
validateAuth();
|
|
1212
|
-
const queryParams = new URLSearchParams();
|
|
1213
|
-
if (args.requirement)
|
|
1214
|
-
queryParams.append("requirement", args.requirement);
|
|
1215
|
-
if (args.issue_id)
|
|
1216
|
-
queryParams.append("issue_id", args.issue_id);
|
|
1217
|
-
if (args.selected_fields)
|
|
1218
|
-
queryParams.append("selected_fields", args.selected_fields);
|
|
1219
|
-
const url = `${config.apiBaseUrl}/api/v1/${args.project_id}/testcases/generate?${queryParams.toString()}`;
|
|
1220
|
-
try {
|
|
1221
|
-
const response = await axios.get(url, {
|
|
1222
|
-
headers: { "Content-Type": "application/json", "Authorization": `Bearer ${config.patToken}` },
|
|
1223
|
-
});
|
|
1224
|
-
return formatResponse(response.data, "Generated Testcases");
|
|
1225
|
-
}
|
|
1226
|
-
catch (error) {
|
|
1227
|
-
throw handleApiError(error);
|
|
858
|
+
// Handler for set_active_project tool
|
|
859
|
+
async function handleSetActiveProject(args) {
|
|
860
|
+
if (!args.projectId) {
|
|
861
|
+
throw new Error("projectId is required");
|
|
1228
862
|
}
|
|
863
|
+
setActiveProject(args.projectId);
|
|
864
|
+
return {
|
|
865
|
+
content: [
|
|
866
|
+
{
|
|
867
|
+
type: "text",
|
|
868
|
+
text: `Active project set to: ${args.projectId}\n\nYou can now use other tools without specifying projectId.`,
|
|
869
|
+
},
|
|
870
|
+
],
|
|
871
|
+
};
|
|
1229
872
|
}
|
|
1230
|
-
// Handler for
|
|
1231
|
-
async function
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
catch (error) {
|
|
1243
|
-
throw handleApiError(error);
|
|
873
|
+
// Handler for get_active_project tool
|
|
874
|
+
async function handleGetActiveProject() {
|
|
875
|
+
const projectId = cachedProjectId;
|
|
876
|
+
if (!projectId) {
|
|
877
|
+
return {
|
|
878
|
+
content: [
|
|
879
|
+
{
|
|
880
|
+
type: "text",
|
|
881
|
+
text: "No active project is set. Use set_active_project or specify projectId in your next command.",
|
|
882
|
+
},
|
|
883
|
+
],
|
|
884
|
+
};
|
|
1244
885
|
}
|
|
886
|
+
return {
|
|
887
|
+
content: [
|
|
888
|
+
{
|
|
889
|
+
type: "text",
|
|
890
|
+
text: `Current active project: ${projectId}`,
|
|
891
|
+
},
|
|
892
|
+
],
|
|
893
|
+
};
|
|
1245
894
|
}
|
|
1246
895
|
// Helper function to validate authentication
|
|
1247
896
|
function validateAuth() {
|