@testkase/mcp-server 2.0.4 → 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 +258 -645
- 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,23 +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"],
|
|
161
|
-
description: "Folder section type (TESTCASE
|
|
192
|
+
description: "Folder section type (default: TESTCASE)",
|
|
193
|
+
default: "TESTCASE",
|
|
162
194
|
},
|
|
163
195
|
search: {
|
|
164
196
|
type: "string",
|
|
165
197
|
description: "Optional: Search term to filter folders (min 3 characters)",
|
|
166
198
|
},
|
|
167
199
|
},
|
|
168
|
-
required: [
|
|
200
|
+
required: [],
|
|
169
201
|
},
|
|
170
202
|
};
|
|
171
203
|
const GET_LABELS_TOOL = {
|
|
@@ -178,49 +210,23 @@ const GET_LABELS_TOOL = {
|
|
|
178
210
|
properties: {
|
|
179
211
|
projectId: {
|
|
180
212
|
type: "string",
|
|
181
|
-
description: "
|
|
213
|
+
description: "Project ID (optional if active project is set)",
|
|
182
214
|
},
|
|
183
215
|
organizationId: {
|
|
184
216
|
type: "number",
|
|
185
|
-
description: "
|
|
217
|
+
description: "Organization ID (optional - auto-detected from PAT token)",
|
|
186
218
|
},
|
|
187
219
|
},
|
|
188
|
-
required: [
|
|
189
|
-
},
|
|
190
|
-
};
|
|
191
|
-
const GET_ISSUES_TOOL = {
|
|
192
|
-
name: "get_issues",
|
|
193
|
-
description: "Get all issues/defects reported in a project. " +
|
|
194
|
-
"Returns a list of issues with their status, priority, and linked testcases. " +
|
|
195
|
-
"Useful for tracking bugs and defects found during testing.",
|
|
196
|
-
inputSchema: {
|
|
197
|
-
type: "object",
|
|
198
|
-
properties: {
|
|
199
|
-
projectId: {
|
|
200
|
-
type: "string",
|
|
201
|
-
description: "The unique identifier of the project",
|
|
202
|
-
},
|
|
203
|
-
organizationId: {
|
|
204
|
-
type: "number",
|
|
205
|
-
description: "The organization ID",
|
|
206
|
-
},
|
|
207
|
-
page: {
|
|
208
|
-
type: "number",
|
|
209
|
-
description: "Page number for pagination (default: 1)",
|
|
210
|
-
},
|
|
211
|
-
limit: {
|
|
212
|
-
type: "number",
|
|
213
|
-
description: "Number of items per page (default: 20)",
|
|
214
|
-
},
|
|
215
|
-
},
|
|
216
|
-
required: ["projectId", "organizationId"],
|
|
220
|
+
required: [],
|
|
217
221
|
},
|
|
218
222
|
};
|
|
219
223
|
const GET_MY_PROJECTS_TOOL = {
|
|
220
224
|
name: "get_my_projects",
|
|
221
|
-
description: "
|
|
222
|
-
"
|
|
223
|
-
"
|
|
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.",
|
|
224
230
|
inputSchema: {
|
|
225
231
|
type: "object",
|
|
226
232
|
properties: {
|
|
@@ -242,8 +248,8 @@ const CREATE_TESTCASE_TOOL = {
|
|
|
242
248
|
inputSchema: {
|
|
243
249
|
type: "object",
|
|
244
250
|
properties: {
|
|
245
|
-
projectId: { type: "string", description: "Project ID" },
|
|
246
|
-
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)" },
|
|
247
253
|
title: { type: "string", description: "Test case title" },
|
|
248
254
|
summary: { type: "string", description: "Test case summary/description" },
|
|
249
255
|
priority: { type: "string", description: "Priority (low, medium, high, critical)" },
|
|
@@ -263,7 +269,7 @@ const CREATE_TESTCASE_TOOL = {
|
|
|
263
269
|
description: "Array of test steps"
|
|
264
270
|
},
|
|
265
271
|
},
|
|
266
|
-
required: ["
|
|
272
|
+
required: ["title"],
|
|
267
273
|
},
|
|
268
274
|
};
|
|
269
275
|
const CREATE_BULK_TESTCASE_TOOL = {
|
|
@@ -272,11 +278,11 @@ const CREATE_BULK_TESTCASE_TOOL = {
|
|
|
272
278
|
inputSchema: {
|
|
273
279
|
type: "object",
|
|
274
280
|
properties: {
|
|
275
|
-
projectId: { type: "string", description: "Project ID" },
|
|
276
|
-
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)" },
|
|
277
283
|
testcases: { type: "string", description: "JSON string of testcase array" },
|
|
278
284
|
},
|
|
279
|
-
required: ["
|
|
285
|
+
required: ["testcases"],
|
|
280
286
|
},
|
|
281
287
|
};
|
|
282
288
|
const UPDATE_TESTCASE_FIELD_TOOL = {
|
|
@@ -285,13 +291,13 @@ const UPDATE_TESTCASE_FIELD_TOOL = {
|
|
|
285
291
|
inputSchema: {
|
|
286
292
|
type: "object",
|
|
287
293
|
properties: {
|
|
288
|
-
projectId: { type: "string", description: "Project ID" },
|
|
289
|
-
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)" },
|
|
290
296
|
ids: { type: "string", description: "Comma-separated testcase IDs" },
|
|
291
297
|
field: { type: "string", description: "Field name to update" },
|
|
292
298
|
value: { type: "string", description: "New field value" },
|
|
293
299
|
},
|
|
294
|
-
required: ["
|
|
300
|
+
required: ["ids", "field", "value"],
|
|
295
301
|
},
|
|
296
302
|
};
|
|
297
303
|
const DELETE_TESTCASE_TOOL = {
|
|
@@ -300,133 +306,31 @@ const DELETE_TESTCASE_TOOL = {
|
|
|
300
306
|
inputSchema: {
|
|
301
307
|
type: "object",
|
|
302
308
|
properties: {
|
|
303
|
-
projectId: { type: "string", description: "Project ID" },
|
|
304
|
-
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)" },
|
|
305
311
|
ids: { type: "string", description: "Comma-separated testcase IDs to delete" },
|
|
306
312
|
},
|
|
307
|
-
required: ["
|
|
308
|
-
},
|
|
309
|
-
};
|
|
310
|
-
const CLONE_TESTCASE_TOOL = {
|
|
311
|
-
name: "clone_testcase",
|
|
312
|
-
description: "Clone an existing test case with all its steps",
|
|
313
|
-
inputSchema: {
|
|
314
|
-
type: "object",
|
|
315
|
-
properties: {
|
|
316
|
-
projectId: { type: "string", description: "Project ID" },
|
|
317
|
-
id: { type: "string", description: "Test case ID to clone" },
|
|
318
|
-
organizationId: { type: "number", description: "Organization ID" },
|
|
319
|
-
},
|
|
320
|
-
required: ["projectId", "id", "organizationId"],
|
|
321
|
-
},
|
|
322
|
-
};
|
|
323
|
-
const CREATE_TEST_STEP_TOOL = {
|
|
324
|
-
name: "create_test_step",
|
|
325
|
-
description: "Create or update test steps for a test case",
|
|
326
|
-
inputSchema: {
|
|
327
|
-
type: "object",
|
|
328
|
-
properties: {
|
|
329
|
-
projectId: { type: "string", description: "Project ID" },
|
|
330
|
-
testcaseId: { type: "string", description: "Test case ID" },
|
|
331
|
-
organizationId: { type: "number", description: "Organization ID" },
|
|
332
|
-
steps: { type: "string", description: "JSON string of test steps array" },
|
|
333
|
-
},
|
|
334
|
-
required: ["projectId", "testcaseId", "organizationId", "steps"],
|
|
335
|
-
},
|
|
336
|
-
};
|
|
337
|
-
const DELETE_TEST_STEP_TOOL = {
|
|
338
|
-
name: "delete_test_step",
|
|
339
|
-
description: "Delete test steps from a test case",
|
|
340
|
-
inputSchema: {
|
|
341
|
-
type: "object",
|
|
342
|
-
properties: {
|
|
343
|
-
testcaseId: { type: "string", description: "Test case ID" },
|
|
344
|
-
organizationId: { type: "number", description: "Organization ID" },
|
|
345
|
-
stepIds: { type: "string", description: "Comma-separated step IDs to delete" },
|
|
346
|
-
},
|
|
347
|
-
required: ["testcaseId", "organizationId", "stepIds"],
|
|
348
|
-
},
|
|
349
|
-
};
|
|
350
|
-
const DOWNLOAD_TESTCASES_TOOL = {
|
|
351
|
-
name: "download_testcases",
|
|
352
|
-
description: "Download test cases as CSV file (returns download URL or data)",
|
|
353
|
-
inputSchema: {
|
|
354
|
-
type: "object",
|
|
355
|
-
properties: {
|
|
356
|
-
projectId: { type: "string", description: "Project ID" },
|
|
357
|
-
organizationId: { type: "number", description: "Organization ID" },
|
|
358
|
-
filters: { type: "string", description: "Optional filters as JSON string" },
|
|
359
|
-
},
|
|
360
|
-
required: ["projectId", "organizationId"],
|
|
361
|
-
},
|
|
362
|
-
};
|
|
363
|
-
const IMPORT_TESTCASES_TOOL = {
|
|
364
|
-
name: "import_testcases",
|
|
365
|
-
description: "Import test cases from a CSV file",
|
|
366
|
-
inputSchema: {
|
|
367
|
-
type: "object",
|
|
368
|
-
properties: {
|
|
369
|
-
projectId: { type: "string", description: "Project ID" },
|
|
370
|
-
organizationId: { type: "number", description: "Organization ID" },
|
|
371
|
-
file: { type: "string", description: "File data or path" },
|
|
372
|
-
},
|
|
373
|
-
required: ["projectId", "organizationId", "file"],
|
|
313
|
+
required: ["ids"],
|
|
374
314
|
},
|
|
375
315
|
};
|
|
376
316
|
const CREATE_FOLDER_TOOL = {
|
|
377
317
|
name: "create_folder",
|
|
378
|
-
description: "Create a new folder in a project",
|
|
318
|
+
description: "Create a new folder in a project for testcases",
|
|
379
319
|
inputSchema: {
|
|
380
320
|
type: "object",
|
|
381
321
|
properties: {
|
|
382
|
-
organizationId: { type: "number", description: "Organization ID" },
|
|
383
|
-
projectId: { type: "string", description: "Project ID" },
|
|
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)" },
|
|
384
324
|
section: {
|
|
385
325
|
type: "string",
|
|
386
326
|
enum: ["TESTCASE", "TEST_CYCLE", "TEST_PLAN", "REQUIREMENT"],
|
|
387
|
-
description: "Folder section type (TESTCASE
|
|
327
|
+
description: "Folder section type (default: TESTCASE)",
|
|
328
|
+
default: "TESTCASE",
|
|
388
329
|
},
|
|
389
330
|
name: { type: "string", description: "Name of the new folder (max 50 characters)" },
|
|
390
331
|
parentId: { type: "number", description: "Parent folder ID for creating subfolder (optional)" },
|
|
391
332
|
},
|
|
392
|
-
required: ["
|
|
393
|
-
},
|
|
394
|
-
};
|
|
395
|
-
const DELETE_FOLDER_TOOL = {
|
|
396
|
-
name: "delete_folder",
|
|
397
|
-
description: "Delete a folder and its subfolders",
|
|
398
|
-
inputSchema: {
|
|
399
|
-
type: "object",
|
|
400
|
-
properties: {
|
|
401
|
-
id: { type: "number", description: "Folder ID to delete" },
|
|
402
|
-
organizationId: { type: "number", description: "Organization ID" },
|
|
403
|
-
projectId: { type: "string", description: "Project ID" },
|
|
404
|
-
section: {
|
|
405
|
-
type: "string",
|
|
406
|
-
enum: ["TESTCASE", "TEST_CYCLE", "TEST_PLAN", "REQUIREMENT"],
|
|
407
|
-
description: "Folder section type (TESTCASE, TEST_CYCLE, TEST_PLAN, or REQUIREMENT)",
|
|
408
|
-
},
|
|
409
|
-
},
|
|
410
|
-
required: ["id", "organizationId", "projectId", "section"],
|
|
411
|
-
},
|
|
412
|
-
};
|
|
413
|
-
const UPDATE_FOLDER_TOOL = {
|
|
414
|
-
name: "update_folder",
|
|
415
|
-
description: "Rename or update folder details",
|
|
416
|
-
inputSchema: {
|
|
417
|
-
type: "object",
|
|
418
|
-
properties: {
|
|
419
|
-
id: { type: "number", description: "Folder ID" },
|
|
420
|
-
organizationId: { type: "number", description: "Organization ID" },
|
|
421
|
-
projectId: { type: "string", description: "Project ID" },
|
|
422
|
-
section: {
|
|
423
|
-
type: "string",
|
|
424
|
-
enum: ["TESTCASE", "TEST_CYCLE", "TEST_PLAN", "REQUIREMENT"],
|
|
425
|
-
description: "Folder section type (TESTCASE, TEST_CYCLE, TEST_PLAN, or REQUIREMENT)",
|
|
426
|
-
},
|
|
427
|
-
name: { type: "string", description: "New folder name" },
|
|
428
|
-
},
|
|
429
|
-
required: ["id", "organizationId", "projectId", "section", "name"],
|
|
333
|
+
required: ["name"],
|
|
430
334
|
},
|
|
431
335
|
};
|
|
432
336
|
const CREATE_LABEL_TOOL = {
|
|
@@ -435,11 +339,11 @@ const CREATE_LABEL_TOOL = {
|
|
|
435
339
|
inputSchema: {
|
|
436
340
|
type: "object",
|
|
437
341
|
properties: {
|
|
438
|
-
projectId: { type: "string", description: "Project ID" },
|
|
439
|
-
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)" },
|
|
440
344
|
values: { type: "array", items: { type: "string" }, description: "Array of label names" },
|
|
441
345
|
},
|
|
442
|
-
required: ["
|
|
346
|
+
required: ["values"],
|
|
443
347
|
},
|
|
444
348
|
};
|
|
445
349
|
const GET_USER_PROFILE_TOOL = {
|
|
@@ -451,87 +355,29 @@ const GET_USER_PROFILE_TOOL = {
|
|
|
451
355
|
required: [],
|
|
452
356
|
},
|
|
453
357
|
};
|
|
454
|
-
const
|
|
455
|
-
name: "
|
|
456
|
-
description: "
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
properties: {},
|
|
460
|
-
required: [],
|
|
461
|
-
},
|
|
462
|
-
};
|
|
463
|
-
const GET_MAPPED_ISSUES_TOOL = {
|
|
464
|
-
name: "get_mapped_issues",
|
|
465
|
-
description: "Get mapped issues for a specific test case from integration platforms",
|
|
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.",
|
|
466
363
|
inputSchema: {
|
|
467
364
|
type: "object",
|
|
468
365
|
properties: {
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
},
|
|
474
|
-
required: ["organization_id", "project_id", "integration_type", "testcase_id"],
|
|
475
|
-
},
|
|
476
|
-
};
|
|
477
|
-
const SEARCH_ISSUES_TOOL = {
|
|
478
|
-
name: "search_issues",
|
|
479
|
-
description: "Search issues from integrated platform (Jira, GitHub, GitLab)",
|
|
480
|
-
inputSchema: {
|
|
481
|
-
type: "object",
|
|
482
|
-
properties: {
|
|
483
|
-
project_id: { type: "string", description: "Project ID" },
|
|
484
|
-
integration_type: { type: "string", description: "Integration type (requirement/defect)" },
|
|
485
|
-
search: { type: "string", description: "Search query string" },
|
|
486
|
-
page: { type: "number", description: "Page number" },
|
|
487
|
-
limit: { type: "number", description: "Items per page" },
|
|
488
|
-
},
|
|
489
|
-
required: ["project_id", "integration_type"],
|
|
490
|
-
},
|
|
491
|
-
};
|
|
492
|
-
const MAP_ISSUES_TOOL = {
|
|
493
|
-
name: "map_issues",
|
|
494
|
-
description: "Map integration issues to a TestKase requirement/defect",
|
|
495
|
-
inputSchema: {
|
|
496
|
-
type: "object",
|
|
497
|
-
properties: {
|
|
498
|
-
organization_id: { type: "number", description: "Organization ID" },
|
|
499
|
-
project_id: { type: "string", description: "Project ID" },
|
|
500
|
-
testcase_id: { type: "string", description: "Test case ID" },
|
|
501
|
-
integration_type: { type: "string", description: "Integration type (requirement/defect)" },
|
|
502
|
-
issues: { type: "string", description: "JSON string of issue IDs to map" },
|
|
503
|
-
},
|
|
504
|
-
required: ["organization_id", "project_id", "testcase_id", "integration_type", "issues"],
|
|
505
|
-
},
|
|
506
|
-
};
|
|
507
|
-
const GENERATE_TESTCASES_TOOL = {
|
|
508
|
-
name: "generate_testcases",
|
|
509
|
-
description: "Generate test cases using AI from requirement text",
|
|
510
|
-
inputSchema: {
|
|
511
|
-
type: "object",
|
|
512
|
-
properties: {
|
|
513
|
-
project_id: { type: "string", description: "Project ID" },
|
|
514
|
-
requirement: { type: "string", description: "Requirement text" },
|
|
515
|
-
issue_id: { type: "string", description: "Issue ID (optional)" },
|
|
516
|
-
selected_fields: { type: "string", description: "Comma-separated field names" },
|
|
366
|
+
projectId: {
|
|
367
|
+
type: "string",
|
|
368
|
+
description: "The project ID to set as active (e.g., PRJ-1234)",
|
|
369
|
+
},
|
|
517
370
|
},
|
|
518
|
-
required: [
|
|
371
|
+
required: [],
|
|
519
372
|
},
|
|
520
373
|
};
|
|
521
|
-
const
|
|
522
|
-
name: "
|
|
523
|
-
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",
|
|
524
377
|
inputSchema: {
|
|
525
378
|
type: "object",
|
|
526
|
-
properties: {
|
|
527
|
-
|
|
528
|
-
organizationId: { type: "number", description: "Organization ID" },
|
|
529
|
-
type: { type: "string", description: "Issue type (REQUIREMENT or DEFECT)" },
|
|
530
|
-
title: { type: "string", description: "Issue title" },
|
|
531
|
-
description: { type: "string", description: "Issue description" },
|
|
532
|
-
id: { type: "string", description: "Issue ID (if updating)" },
|
|
533
|
-
},
|
|
534
|
-
required: ["projectId", "organizationId", "type", "title"],
|
|
379
|
+
properties: {},
|
|
380
|
+
required: [],
|
|
535
381
|
},
|
|
536
382
|
};
|
|
537
383
|
// Create server instance
|
|
@@ -543,37 +389,24 @@ const server = new Server({
|
|
|
543
389
|
tools: {},
|
|
544
390
|
},
|
|
545
391
|
});
|
|
546
|
-
// List available tools
|
|
392
|
+
// List available tools (14 tools)
|
|
547
393
|
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
548
394
|
return {
|
|
549
395
|
tools: [
|
|
396
|
+
GET_MY_PROJECTS_TOOL,
|
|
397
|
+
SET_ACTIVE_PROJECT_TOOL,
|
|
398
|
+
GET_ACTIVE_PROJECT_TOOL,
|
|
550
399
|
GET_TESTCASES_TOOL,
|
|
551
400
|
GET_TESTCASE_DETAIL_TOOL,
|
|
552
|
-
GET_TESTCASE_HISTORY_TOOL,
|
|
553
|
-
GET_FOLDERS_TOOL,
|
|
554
|
-
GET_LABELS_TOOL,
|
|
555
|
-
GET_ISSUES_TOOL,
|
|
556
|
-
GET_MY_PROJECTS_TOOL,
|
|
557
401
|
CREATE_TESTCASE_TOOL,
|
|
558
402
|
CREATE_BULK_TESTCASE_TOOL,
|
|
559
403
|
UPDATE_TESTCASE_FIELD_TOOL,
|
|
560
404
|
DELETE_TESTCASE_TOOL,
|
|
561
|
-
|
|
562
|
-
CREATE_TEST_STEP_TOOL,
|
|
563
|
-
DELETE_TEST_STEP_TOOL,
|
|
564
|
-
DOWNLOAD_TESTCASES_TOOL,
|
|
565
|
-
IMPORT_TESTCASES_TOOL,
|
|
405
|
+
GET_FOLDERS_TOOL,
|
|
566
406
|
CREATE_FOLDER_TOOL,
|
|
567
|
-
|
|
568
|
-
UPDATE_FOLDER_TOOL,
|
|
407
|
+
GET_LABELS_TOOL,
|
|
569
408
|
CREATE_LABEL_TOOL,
|
|
570
409
|
GET_USER_PROFILE_TOOL,
|
|
571
|
-
GET_ORGANIZATION_TOOL,
|
|
572
|
-
GET_MAPPED_ISSUES_TOOL,
|
|
573
|
-
SEARCH_ISSUES_TOOL,
|
|
574
|
-
MAP_ISSUES_TOOL,
|
|
575
|
-
GENERATE_TESTCASES_TOOL,
|
|
576
|
-
CREATE_ISSUE_TOOL,
|
|
577
410
|
],
|
|
578
411
|
};
|
|
579
412
|
});
|
|
@@ -581,26 +414,20 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
581
414
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
582
415
|
const { name, arguments: args } = request.params;
|
|
583
416
|
try {
|
|
584
|
-
if (name === "
|
|
585
|
-
return await
|
|
586
|
-
}
|
|
587
|
-
else if (name === "get_testcase_detail") {
|
|
588
|
-
return await handleGetTestcaseDetail(args);
|
|
589
|
-
}
|
|
590
|
-
else if (name === "get_testcase_history") {
|
|
591
|
-
return await handleGetTestcaseHistory(args);
|
|
417
|
+
if (name === "get_my_projects") {
|
|
418
|
+
return await handleGetMyProjects(args);
|
|
592
419
|
}
|
|
593
|
-
else if (name === "
|
|
594
|
-
return await
|
|
420
|
+
else if (name === "set_active_project") {
|
|
421
|
+
return await handleSetActiveProject(args);
|
|
595
422
|
}
|
|
596
|
-
else if (name === "
|
|
597
|
-
return await
|
|
423
|
+
else if (name === "get_active_project") {
|
|
424
|
+
return await handleGetActiveProject();
|
|
598
425
|
}
|
|
599
|
-
else if (name === "
|
|
600
|
-
return await
|
|
426
|
+
else if (name === "get_testcases") {
|
|
427
|
+
return await handleGetTestcases(args);
|
|
601
428
|
}
|
|
602
|
-
else if (name === "
|
|
603
|
-
return await
|
|
429
|
+
else if (name === "get_testcase_detail") {
|
|
430
|
+
return await handleGetTestcaseDetail(args);
|
|
604
431
|
}
|
|
605
432
|
else if (name === "create_testcase") {
|
|
606
433
|
return await handleCreateTestcase(args);
|
|
@@ -614,29 +441,14 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
614
441
|
else if (name === "delete_testcase") {
|
|
615
442
|
return await handleDeleteTestcase(args);
|
|
616
443
|
}
|
|
617
|
-
else if (name === "
|
|
618
|
-
return await
|
|
619
|
-
}
|
|
620
|
-
else if (name === "create_test_step") {
|
|
621
|
-
return await handleCreateTestStep(args);
|
|
622
|
-
}
|
|
623
|
-
else if (name === "delete_test_step") {
|
|
624
|
-
return await handleDeleteTestStep(args);
|
|
625
|
-
}
|
|
626
|
-
else if (name === "download_testcases") {
|
|
627
|
-
return await handleDownloadTestcases(args);
|
|
628
|
-
}
|
|
629
|
-
else if (name === "import_testcases") {
|
|
630
|
-
return await handleImportTestcases(args);
|
|
444
|
+
else if (name === "get_folders") {
|
|
445
|
+
return await handleGetFolders(args);
|
|
631
446
|
}
|
|
632
447
|
else if (name === "create_folder") {
|
|
633
448
|
return await handleCreateFolder(args);
|
|
634
449
|
}
|
|
635
|
-
else if (name === "
|
|
636
|
-
return await
|
|
637
|
-
}
|
|
638
|
-
else if (name === "update_folder") {
|
|
639
|
-
return await handleUpdateFolder(args);
|
|
450
|
+
else if (name === "get_labels") {
|
|
451
|
+
return await handleGetLabels(args);
|
|
640
452
|
}
|
|
641
453
|
else if (name === "create_label") {
|
|
642
454
|
return await handleCreateLabel(args);
|
|
@@ -644,24 +456,6 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
644
456
|
else if (name === "get_user_profile") {
|
|
645
457
|
return await handleGetUserProfile(args);
|
|
646
458
|
}
|
|
647
|
-
else if (name === "get_organization") {
|
|
648
|
-
return await handleGetOrganization(args);
|
|
649
|
-
}
|
|
650
|
-
else if (name === "get_mapped_issues") {
|
|
651
|
-
return await handleGetMappedIssues(args);
|
|
652
|
-
}
|
|
653
|
-
else if (name === "search_issues") {
|
|
654
|
-
return await handleSearchIssues(args);
|
|
655
|
-
}
|
|
656
|
-
else if (name === "map_issues") {
|
|
657
|
-
return await handleMapIssues(args);
|
|
658
|
-
}
|
|
659
|
-
else if (name === "generate_testcases") {
|
|
660
|
-
return await handleGenerateTestcases(args);
|
|
661
|
-
}
|
|
662
|
-
else if (name === "create_issue") {
|
|
663
|
-
return await handleCreateIssue(args);
|
|
664
|
-
}
|
|
665
459
|
throw new Error(`Unknown tool: ${name}`);
|
|
666
460
|
}
|
|
667
461
|
catch (error) {
|
|
@@ -681,26 +475,13 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
681
475
|
});
|
|
682
476
|
// Handler for get_testcases tool
|
|
683
477
|
async function handleGetTestcases(args) {
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
if (!args.organizationId) {
|
|
689
|
-
throw new Error("organizationId is required");
|
|
690
|
-
}
|
|
691
|
-
// Check for authentication
|
|
692
|
-
if (!config.patToken) {
|
|
693
|
-
throw new Error("Authentication required. Please set TESTKASE_PAT_TOKEN environment variable. " +
|
|
694
|
-
"Generate a Personal Access Token (PAT) from your TestKase account settings.");
|
|
695
|
-
}
|
|
696
|
-
// Validate PAT token format
|
|
697
|
-
if (!config.patToken.startsWith("xyz_")) {
|
|
698
|
-
throw new Error("Invalid PAT token format. TestKase PAT tokens must start with 'xyz_' prefix. " +
|
|
699
|
-
"Please generate a valid PAT from your TestKase account settings.");
|
|
700
|
-
}
|
|
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);
|
|
701
482
|
// Build query parameters
|
|
702
483
|
const queryParams = new URLSearchParams();
|
|
703
|
-
queryParams.append("organizationId",
|
|
484
|
+
queryParams.append("organizationId", organizationId.toString());
|
|
704
485
|
if (args.page !== undefined)
|
|
705
486
|
queryParams.append("page", args.page.toString());
|
|
706
487
|
if (args.limit !== undefined)
|
|
@@ -728,7 +509,7 @@ async function handleGetTestcases(args) {
|
|
|
728
509
|
if (args.updated_end_date)
|
|
729
510
|
queryParams.append("updated_end_date", args.updated_end_date);
|
|
730
511
|
// Construct API URL
|
|
731
|
-
const url = `${config.apiBaseUrl}/api/v1/projects/testcases/get-testcase/${
|
|
512
|
+
const url = `${config.apiBaseUrl}/api/v1/projects/testcases/get-testcase/${projectId}?${queryParams.toString()}`;
|
|
732
513
|
// Prepare headers with PAT token
|
|
733
514
|
// The PAT token is sent as Bearer token and will be validated by TestKase's JwtAuthGuard
|
|
734
515
|
// which checks for 'xyz_' prefix and validates against the PAT database
|
|
@@ -806,9 +587,11 @@ async function handleGetTestcases(args) {
|
|
|
806
587
|
// Handler for get_testcase_detail tool
|
|
807
588
|
async function handleGetTestcaseDetail(args) {
|
|
808
589
|
validateAuth();
|
|
590
|
+
const projectId = getProjectId(args.projectId);
|
|
591
|
+
const organizationId = await getOrganizationId(args.organizationId);
|
|
809
592
|
const queryParams = new URLSearchParams();
|
|
810
|
-
queryParams.append("organizationId",
|
|
811
|
-
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()}`;
|
|
812
595
|
try {
|
|
813
596
|
const response = await axios.get(url, {
|
|
814
597
|
headers: {
|
|
@@ -823,33 +606,16 @@ async function handleGetTestcaseDetail(args) {
|
|
|
823
606
|
throw handleApiError(error);
|
|
824
607
|
}
|
|
825
608
|
}
|
|
826
|
-
// Handler for get_testcase_history tool
|
|
827
|
-
async function handleGetTestcaseHistory(args) {
|
|
828
|
-
validateAuth();
|
|
829
|
-
const queryParams = new URLSearchParams();
|
|
830
|
-
queryParams.append("organizationId", args.organizationId.toString());
|
|
831
|
-
const url = `${config.apiBaseUrl}/api/v1/projects/testcases/get-testcase-history/${args.projectId}/${args.testcaseId}?${queryParams.toString()}`;
|
|
832
|
-
try {
|
|
833
|
-
const response = await axios.get(url, {
|
|
834
|
-
headers: {
|
|
835
|
-
"Content-Type": "application/json",
|
|
836
|
-
"Authorization": `Bearer ${config.patToken}`,
|
|
837
|
-
},
|
|
838
|
-
});
|
|
839
|
-
const data = response.data;
|
|
840
|
-
return formatResponse(data, `Testcase History: ${args.testcaseId}`);
|
|
841
|
-
}
|
|
842
|
-
catch (error) {
|
|
843
|
-
throw handleApiError(error);
|
|
844
|
-
}
|
|
845
|
-
}
|
|
846
609
|
// Handler for get_folders tool
|
|
847
610
|
async function handleGetFolders(args) {
|
|
848
611
|
validateAuth();
|
|
612
|
+
const projectId = getProjectId(args.projectId);
|
|
613
|
+
const organizationId = await getOrganizationId(args.organizationId);
|
|
614
|
+
const section = args.section || "TESTCASE";
|
|
849
615
|
const queryParams = new URLSearchParams();
|
|
850
|
-
queryParams.append("organizationId",
|
|
851
|
-
queryParams.append("projectId",
|
|
852
|
-
queryParams.append("section",
|
|
616
|
+
queryParams.append("organizationId", organizationId.toString());
|
|
617
|
+
queryParams.append("projectId", projectId);
|
|
618
|
+
queryParams.append("section", section);
|
|
853
619
|
if (args.search) {
|
|
854
620
|
queryParams.append("search", args.search);
|
|
855
621
|
}
|
|
@@ -871,9 +637,11 @@ async function handleGetFolders(args) {
|
|
|
871
637
|
// Handler for get_labels tool
|
|
872
638
|
async function handleGetLabels(args) {
|
|
873
639
|
validateAuth();
|
|
640
|
+
const projectId = getProjectId(args.projectId);
|
|
641
|
+
const organizationId = await getOrganizationId(args.organizationId);
|
|
874
642
|
const queryParams = new URLSearchParams();
|
|
875
|
-
queryParams.append("organizationId",
|
|
876
|
-
const url = `${config.apiBaseUrl}/api/v1/projects/labels/get-label/${
|
|
643
|
+
queryParams.append("organizationId", organizationId.toString());
|
|
644
|
+
const url = `${config.apiBaseUrl}/api/v1/projects/labels/get-label/${projectId}?${queryParams.toString()}`;
|
|
877
645
|
try {
|
|
878
646
|
const response = await axios.get(url, {
|
|
879
647
|
headers: {
|
|
@@ -888,32 +656,6 @@ async function handleGetLabels(args) {
|
|
|
888
656
|
throw handleApiError(error);
|
|
889
657
|
}
|
|
890
658
|
}
|
|
891
|
-
// Handler for get_issues tool
|
|
892
|
-
async function handleGetIssues(args) {
|
|
893
|
-
validateAuth();
|
|
894
|
-
const queryParams = new URLSearchParams();
|
|
895
|
-
queryParams.append("organizationId", args.organizationId.toString());
|
|
896
|
-
if (args.type)
|
|
897
|
-
queryParams.append("type", args.type);
|
|
898
|
-
if (args.page)
|
|
899
|
-
queryParams.append("page", args.page.toString());
|
|
900
|
-
if (args.limit)
|
|
901
|
-
queryParams.append("limit", args.limit.toString());
|
|
902
|
-
const url = `${config.apiBaseUrl}/api/v1/projects/issues/get-issues/${args.projectId}?${queryParams.toString()}`;
|
|
903
|
-
try {
|
|
904
|
-
const response = await axios.get(url, {
|
|
905
|
-
headers: {
|
|
906
|
-
"Content-Type": "application/json",
|
|
907
|
-
"Authorization": `Bearer ${config.patToken}`,
|
|
908
|
-
},
|
|
909
|
-
});
|
|
910
|
-
const data = response.data;
|
|
911
|
-
return formatResponse(data, `Issues for Project: ${args.projectId}`);
|
|
912
|
-
}
|
|
913
|
-
catch (error) {
|
|
914
|
-
throw handleApiError(error);
|
|
915
|
-
}
|
|
916
|
-
}
|
|
917
659
|
// Handler for get_my_projects tool
|
|
918
660
|
async function handleGetMyProjects(args) {
|
|
919
661
|
validateAuth();
|
|
@@ -937,34 +679,80 @@ async function handleGetMyProjects(args) {
|
|
|
937
679
|
throw handleApiError(error);
|
|
938
680
|
}
|
|
939
681
|
}
|
|
940
|
-
// Handler for create_testcase tool
|
|
682
|
+
// Handler for create_testcase tool (handles test_steps internally)
|
|
941
683
|
async function handleCreateTestcase(args) {
|
|
942
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;
|
|
943
689
|
const queryParams = new URLSearchParams();
|
|
944
|
-
queryParams.append("organizationId",
|
|
945
|
-
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()}`;
|
|
946
692
|
try {
|
|
947
|
-
|
|
693
|
+
// Step 1: Create testcase
|
|
694
|
+
const response = await axios.post(url, testcaseData, {
|
|
948
695
|
headers: { "Content-Type": "application/json", "Authorization": `Bearer ${config.patToken}` },
|
|
949
696
|
});
|
|
950
|
-
|
|
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` : ""));
|
|
951
707
|
}
|
|
952
708
|
catch (error) {
|
|
953
709
|
throw handleApiError(error);
|
|
954
710
|
}
|
|
955
711
|
}
|
|
956
|
-
// Handler for create_bulk_testcase tool
|
|
712
|
+
// Handler for create_bulk_testcase tool (handles test_steps internally)
|
|
957
713
|
async function handleCreateBulkTestcase(args) {
|
|
958
714
|
validateAuth();
|
|
959
|
-
const
|
|
960
|
-
|
|
961
|
-
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);
|
|
962
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()}`;
|
|
963
726
|
try {
|
|
964
|
-
|
|
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 }, {
|
|
965
730
|
headers: { "Content-Type": "application/json", "Authorization": `Bearer ${config.patToken}` },
|
|
966
731
|
});
|
|
967
|
-
|
|
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)`);
|
|
968
756
|
}
|
|
969
757
|
catch (error) {
|
|
970
758
|
throw handleApiError(error);
|
|
@@ -973,9 +761,11 @@ async function handleCreateBulkTestcase(args) {
|
|
|
973
761
|
// Handler for update_testcase_field tool
|
|
974
762
|
async function handleUpdateTestcaseField(args) {
|
|
975
763
|
validateAuth();
|
|
764
|
+
const projectId = getProjectId(args.projectId);
|
|
765
|
+
const organizationId = await getOrganizationId(args.organizationId);
|
|
976
766
|
const queryParams = new URLSearchParams();
|
|
977
|
-
queryParams.append("organizationId",
|
|
978
|
-
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()}`;
|
|
979
769
|
try {
|
|
980
770
|
const response = await axios.patch(url, {
|
|
981
771
|
ids: args.ids.split(','),
|
|
@@ -993,9 +783,11 @@ async function handleUpdateTestcaseField(args) {
|
|
|
993
783
|
// Handler for delete_testcase tool
|
|
994
784
|
async function handleDeleteTestcase(args) {
|
|
995
785
|
validateAuth();
|
|
786
|
+
const projectId = getProjectId(args.projectId);
|
|
787
|
+
const organizationId = await getOrganizationId(args.organizationId);
|
|
996
788
|
const queryParams = new URLSearchParams();
|
|
997
|
-
queryParams.append("organizationId",
|
|
998
|
-
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()}`;
|
|
999
791
|
try {
|
|
1000
792
|
const response = await axios.delete(url, {
|
|
1001
793
|
headers: { "Content-Type": "application/json", "Authorization": `Bearer ${config.patToken}` },
|
|
@@ -1007,98 +799,17 @@ async function handleDeleteTestcase(args) {
|
|
|
1007
799
|
throw handleApiError(error);
|
|
1008
800
|
}
|
|
1009
801
|
}
|
|
1010
|
-
// Handler for clone_testcase tool
|
|
1011
|
-
async function handleCloneTestcase(args) {
|
|
1012
|
-
validateAuth();
|
|
1013
|
-
const queryParams = new URLSearchParams();
|
|
1014
|
-
queryParams.append("organizationId", args.organizationId.toString());
|
|
1015
|
-
const url = `${config.apiBaseUrl}/api/v1/projects/testcases/clone/${args.projectId}/${args.id}?${queryParams.toString()}`;
|
|
1016
|
-
try {
|
|
1017
|
-
const response = await axios.post(url, {}, {
|
|
1018
|
-
headers: { "Content-Type": "application/json", "Authorization": `Bearer ${config.patToken}` },
|
|
1019
|
-
});
|
|
1020
|
-
return formatResponse(response.data, "Testcase Cloned");
|
|
1021
|
-
}
|
|
1022
|
-
catch (error) {
|
|
1023
|
-
throw handleApiError(error);
|
|
1024
|
-
}
|
|
1025
|
-
}
|
|
1026
|
-
// Handler for create_test_step tool
|
|
1027
|
-
async function handleCreateTestStep(args) {
|
|
1028
|
-
validateAuth();
|
|
1029
|
-
const queryParams = new URLSearchParams();
|
|
1030
|
-
queryParams.append("organizationId", args.organizationId.toString());
|
|
1031
|
-
const url = `${config.apiBaseUrl}/api/v1/projects/testcases/teststeps/${args.projectId}/${args.testcaseId}?${queryParams.toString()}`;
|
|
1032
|
-
const steps = typeof args.steps === 'string' ? JSON.parse(args.steps) : args.steps;
|
|
1033
|
-
try {
|
|
1034
|
-
const response = await axios.post(url, { steps }, {
|
|
1035
|
-
headers: { "Content-Type": "application/json", "Authorization": `Bearer ${config.patToken}` },
|
|
1036
|
-
});
|
|
1037
|
-
return formatResponse(response.data, "Test Steps Created");
|
|
1038
|
-
}
|
|
1039
|
-
catch (error) {
|
|
1040
|
-
throw handleApiError(error);
|
|
1041
|
-
}
|
|
1042
|
-
}
|
|
1043
|
-
// Handler for delete_test_step tool
|
|
1044
|
-
async function handleDeleteTestStep(args) {
|
|
1045
|
-
validateAuth();
|
|
1046
|
-
const queryParams = new URLSearchParams();
|
|
1047
|
-
queryParams.append("organizationId", args.organizationId.toString());
|
|
1048
|
-
const url = `${config.apiBaseUrl}/api/v1/projects/testcases/teststeps/${args.testcaseId}?${queryParams.toString()}`;
|
|
1049
|
-
try {
|
|
1050
|
-
const response = await axios.delete(url, {
|
|
1051
|
-
headers: { "Content-Type": "application/json", "Authorization": `Bearer ${config.patToken}` },
|
|
1052
|
-
data: { stepIds: args.stepIds.split(',') },
|
|
1053
|
-
});
|
|
1054
|
-
return formatResponse(response.data, "Test Steps Deleted");
|
|
1055
|
-
}
|
|
1056
|
-
catch (error) {
|
|
1057
|
-
throw handleApiError(error);
|
|
1058
|
-
}
|
|
1059
|
-
}
|
|
1060
|
-
// Handler for download_testcases tool
|
|
1061
|
-
async function handleDownloadTestcases(args) {
|
|
1062
|
-
validateAuth();
|
|
1063
|
-
const queryParams = new URLSearchParams();
|
|
1064
|
-
queryParams.append("organizationId", args.organizationId.toString());
|
|
1065
|
-
if (args.filters)
|
|
1066
|
-
queryParams.append("filters", args.filters);
|
|
1067
|
-
const url = `${config.apiBaseUrl}/api/v1/projects/testcases/download/${args.projectId}?${queryParams.toString()}`;
|
|
1068
|
-
try {
|
|
1069
|
-
const response = await axios.get(url, {
|
|
1070
|
-
headers: { "Content-Type": "application/json", "Authorization": `Bearer ${config.patToken}` },
|
|
1071
|
-
});
|
|
1072
|
-
return formatResponse(response.data, "Testcases Download");
|
|
1073
|
-
}
|
|
1074
|
-
catch (error) {
|
|
1075
|
-
throw handleApiError(error);
|
|
1076
|
-
}
|
|
1077
|
-
}
|
|
1078
|
-
// Handler for import_testcases tool
|
|
1079
|
-
async function handleImportTestcases(args) {
|
|
1080
|
-
validateAuth();
|
|
1081
|
-
const queryParams = new URLSearchParams();
|
|
1082
|
-
queryParams.append("organizationId", args.organizationId.toString());
|
|
1083
|
-
const url = `${config.apiBaseUrl}/api/v1/projects/testcases/import/${args.projectId}?${queryParams.toString()}`;
|
|
1084
|
-
try {
|
|
1085
|
-
const response = await axios.post(url, { file: args.file }, {
|
|
1086
|
-
headers: { "Content-Type": "application/json", "Authorization": `Bearer ${config.patToken}` },
|
|
1087
|
-
});
|
|
1088
|
-
return formatResponse(response.data, "Testcases Imported");
|
|
1089
|
-
}
|
|
1090
|
-
catch (error) {
|
|
1091
|
-
throw handleApiError(error);
|
|
1092
|
-
}
|
|
1093
|
-
}
|
|
1094
802
|
// Handler for create_folder tool
|
|
1095
803
|
async function handleCreateFolder(args) {
|
|
1096
804
|
validateAuth();
|
|
805
|
+
const projectId = getProjectId(args.projectId);
|
|
806
|
+
const organizationId = await getOrganizationId(args.organizationId);
|
|
1097
807
|
const url = `${config.apiBaseUrl}/api/v1/folders/create-folders`;
|
|
808
|
+
const section = args.section || "TESTCASE";
|
|
1098
809
|
const body = {
|
|
1099
|
-
organizationId:
|
|
1100
|
-
projectId:
|
|
1101
|
-
section:
|
|
810
|
+
organizationId: organizationId,
|
|
811
|
+
projectId: projectId,
|
|
812
|
+
section: section,
|
|
1102
813
|
name: args.name,
|
|
1103
814
|
...(args.parentId && { parentId: args.parentId }),
|
|
1104
815
|
};
|
|
@@ -1112,48 +823,14 @@ async function handleCreateFolder(args) {
|
|
|
1112
823
|
throw handleApiError(error);
|
|
1113
824
|
}
|
|
1114
825
|
}
|
|
1115
|
-
// Handler for delete_folder tool
|
|
1116
|
-
async function handleDeleteFolder(args) {
|
|
1117
|
-
validateAuth();
|
|
1118
|
-
const queryParams = new URLSearchParams();
|
|
1119
|
-
queryParams.append("organizationId", args.organizationId.toString());
|
|
1120
|
-
queryParams.append("projectId", args.projectId);
|
|
1121
|
-
queryParams.append("section", args.section);
|
|
1122
|
-
const url = `${config.apiBaseUrl}/api/v1/folders/delete/${args.id}?${queryParams.toString()}`;
|
|
1123
|
-
try {
|
|
1124
|
-
const response = await axios.delete(url, {
|
|
1125
|
-
headers: { "Content-Type": "application/json", "Authorization": `Bearer ${config.patToken}` },
|
|
1126
|
-
});
|
|
1127
|
-
return formatResponse(response.data, "Folder Deleted");
|
|
1128
|
-
}
|
|
1129
|
-
catch (error) {
|
|
1130
|
-
throw handleApiError(error);
|
|
1131
|
-
}
|
|
1132
|
-
}
|
|
1133
|
-
// Handler for update_folder tool
|
|
1134
|
-
async function handleUpdateFolder(args) {
|
|
1135
|
-
validateAuth();
|
|
1136
|
-
const queryParams = new URLSearchParams();
|
|
1137
|
-
queryParams.append("organizationId", args.organizationId.toString());
|
|
1138
|
-
queryParams.append("projectId", args.projectId);
|
|
1139
|
-
queryParams.append("section", args.section);
|
|
1140
|
-
const url = `${config.apiBaseUrl}/api/v1/folders/${args.id}?${queryParams.toString()}`;
|
|
1141
|
-
try {
|
|
1142
|
-
const response = await axios.patch(url, { name: args.name }, {
|
|
1143
|
-
headers: { "Content-Type": "application/json", "Authorization": `Bearer ${config.patToken}` },
|
|
1144
|
-
});
|
|
1145
|
-
return formatResponse(response.data, "Folder Updated");
|
|
1146
|
-
}
|
|
1147
|
-
catch (error) {
|
|
1148
|
-
throw handleApiError(error);
|
|
1149
|
-
}
|
|
1150
|
-
}
|
|
1151
826
|
// Handler for create_label tool
|
|
1152
827
|
async function handleCreateLabel(args) {
|
|
1153
828
|
validateAuth();
|
|
829
|
+
const projectId = getProjectId(args.projectId);
|
|
830
|
+
const organizationId = await getOrganizationId(args.organizationId);
|
|
1154
831
|
const queryParams = new URLSearchParams();
|
|
1155
|
-
queryParams.append("organizationId",
|
|
1156
|
-
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()}`;
|
|
1157
834
|
try {
|
|
1158
835
|
const response = await axios.post(url, { values: args.values }, {
|
|
1159
836
|
headers: { "Content-Type": "application/json", "Authorization": `Bearer ${config.patToken}` },
|
|
@@ -1178,106 +855,42 @@ async function handleGetUserProfile(args) {
|
|
|
1178
855
|
throw handleApiError(error);
|
|
1179
856
|
}
|
|
1180
857
|
}
|
|
1181
|
-
// Handler for
|
|
1182
|
-
async function
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
try {
|
|
1186
|
-
const response = await axios.get(url, {
|
|
1187
|
-
headers: { "Content-Type": "application/json", "Authorization": `Bearer ${config.patToken}` },
|
|
1188
|
-
});
|
|
1189
|
-
return formatResponse(response.data, "Organization Details");
|
|
1190
|
-
}
|
|
1191
|
-
catch (error) {
|
|
1192
|
-
throw handleApiError(error);
|
|
1193
|
-
}
|
|
1194
|
-
}
|
|
1195
|
-
// Handler for get_mapped_issues tool
|
|
1196
|
-
async function handleGetMappedIssues(args) {
|
|
1197
|
-
validateAuth();
|
|
1198
|
-
const url = `${config.apiBaseUrl}/api/v1/${args.organization_id}/${args.project_id}/${args.integration_type}/testcases/${args.testcase_id}/issues`;
|
|
1199
|
-
try {
|
|
1200
|
-
const response = await axios.get(url, {
|
|
1201
|
-
headers: { "Content-Type": "application/json", "Authorization": `Bearer ${config.patToken}` },
|
|
1202
|
-
});
|
|
1203
|
-
return formatResponse(response.data, "Mapped Issues");
|
|
1204
|
-
}
|
|
1205
|
-
catch (error) {
|
|
1206
|
-
throw handleApiError(error);
|
|
1207
|
-
}
|
|
1208
|
-
}
|
|
1209
|
-
// Handler for search_issues tool
|
|
1210
|
-
async function handleSearchIssues(args) {
|
|
1211
|
-
validateAuth();
|
|
1212
|
-
const queryParams = new URLSearchParams();
|
|
1213
|
-
if (args.search)
|
|
1214
|
-
queryParams.append("search", args.search);
|
|
1215
|
-
if (args.page)
|
|
1216
|
-
queryParams.append("page", args.page.toString());
|
|
1217
|
-
if (args.limit)
|
|
1218
|
-
queryParams.append("limit", args.limit.toString());
|
|
1219
|
-
const url = `${config.apiBaseUrl}/api/v1/${args.project_id}/${args.integration_type}/issues/search?${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, "Search Issues");
|
|
1225
|
-
}
|
|
1226
|
-
catch (error) {
|
|
1227
|
-
throw handleApiError(error);
|
|
1228
|
-
}
|
|
1229
|
-
}
|
|
1230
|
-
// Handler for map_issues tool
|
|
1231
|
-
async function handleMapIssues(args) {
|
|
1232
|
-
validateAuth();
|
|
1233
|
-
const url = `${config.apiBaseUrl}/api/v1/${args.organization_id}/${args.project_id}/${args.testcase_id}/${args.integration_type}/issues/map`;
|
|
1234
|
-
const issues = typeof args.issues === 'string' ? JSON.parse(args.issues) : args.issues;
|
|
1235
|
-
try {
|
|
1236
|
-
const response = await axios.post(url, { issues }, {
|
|
1237
|
-
headers: { "Content-Type": "application/json", "Authorization": `Bearer ${config.patToken}` },
|
|
1238
|
-
});
|
|
1239
|
-
return formatResponse(response.data, "Issues Mapped");
|
|
1240
|
-
}
|
|
1241
|
-
catch (error) {
|
|
1242
|
-
throw handleApiError(error);
|
|
1243
|
-
}
|
|
1244
|
-
}
|
|
1245
|
-
// Handler for generate_testcases tool
|
|
1246
|
-
async function handleGenerateTestcases(args) {
|
|
1247
|
-
validateAuth();
|
|
1248
|
-
const queryParams = new URLSearchParams();
|
|
1249
|
-
if (args.requirement)
|
|
1250
|
-
queryParams.append("requirement", args.requirement);
|
|
1251
|
-
if (args.issue_id)
|
|
1252
|
-
queryParams.append("issue_id", args.issue_id);
|
|
1253
|
-
if (args.selected_fields)
|
|
1254
|
-
queryParams.append("selected_fields", args.selected_fields);
|
|
1255
|
-
const url = `${config.apiBaseUrl}/api/v1/${args.project_id}/testcases/generate?${queryParams.toString()}`;
|
|
1256
|
-
try {
|
|
1257
|
-
const response = await axios.get(url, {
|
|
1258
|
-
headers: { "Content-Type": "application/json", "Authorization": `Bearer ${config.patToken}` },
|
|
1259
|
-
});
|
|
1260
|
-
return formatResponse(response.data, "Generated Testcases");
|
|
1261
|
-
}
|
|
1262
|
-
catch (error) {
|
|
1263
|
-
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");
|
|
1264
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
|
+
};
|
|
1265
872
|
}
|
|
1266
|
-
// Handler for
|
|
1267
|
-
async function
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
catch (error) {
|
|
1279
|
-
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
|
+
};
|
|
1280
885
|
}
|
|
886
|
+
return {
|
|
887
|
+
content: [
|
|
888
|
+
{
|
|
889
|
+
type: "text",
|
|
890
|
+
text: `Current active project: ${projectId}`,
|
|
891
|
+
},
|
|
892
|
+
],
|
|
893
|
+
};
|
|
1281
894
|
}
|
|
1282
895
|
// Helper function to validate authentication
|
|
1283
896
|
function validateAuth() {
|