@testkase/mcp-server 2.0.4 → 2.0.9
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 +280 -659
- 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,31 +156,7 @@ const GET_TESTCASE_DETAIL_TOOL = {
|
|
|
101
156
|
properties: {
|
|
102
157
|
projectId: {
|
|
103
158
|
type: "string",
|
|
104
|
-
description: "
|
|
105
|
-
},
|
|
106
|
-
testcaseId: {
|
|
107
|
-
type: "string",
|
|
108
|
-
description: "The unique identifier of the testcase",
|
|
109
|
-
},
|
|
110
|
-
organizationId: {
|
|
111
|
-
type: "number",
|
|
112
|
-
description: "The organization ID that owns the project",
|
|
113
|
-
},
|
|
114
|
-
},
|
|
115
|
-
required: ["projectId", "testcaseId", "organizationId"],
|
|
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",
|
|
159
|
+
description: "Project ID (optional if active project is set)",
|
|
129
160
|
},
|
|
130
161
|
testcaseId: {
|
|
131
162
|
type: "string",
|
|
@@ -133,10 +164,10 @@ const GET_TESTCASE_HISTORY_TOOL = {
|
|
|
133
164
|
},
|
|
134
165
|
organizationId: {
|
|
135
166
|
type: "number",
|
|
136
|
-
description: "
|
|
167
|
+
description: "Organization ID (optional - auto-detected from PAT token)",
|
|
137
168
|
},
|
|
138
169
|
},
|
|
139
|
-
required: ["
|
|
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,28 +248,18 @@ 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)" },
|
|
250
256
|
status: { type: "string", description: "Status" },
|
|
251
257
|
folder_id: { type: "string", description: "Folder ID" },
|
|
252
|
-
labels: { type: "
|
|
258
|
+
labels: { type: "string", description: "Comma-separated label IDs" },
|
|
253
259
|
automated: { type: "boolean", description: "Is automated test" },
|
|
254
|
-
test_steps: {
|
|
255
|
-
type: "array",
|
|
256
|
-
items: {
|
|
257
|
-
type: "object",
|
|
258
|
-
properties: {
|
|
259
|
-
step: { type: "string", description: "Step description" },
|
|
260
|
-
expected_result: { type: "string", description: "Expected result" }
|
|
261
|
-
}
|
|
262
|
-
},
|
|
263
|
-
description: "Array of test steps"
|
|
264
|
-
},
|
|
260
|
+
test_steps: { type: "string", description: "JSON string of test steps array. Each step: {step: string, expected_result: string}" },
|
|
265
261
|
},
|
|
266
|
-
required: ["
|
|
262
|
+
required: ["title"],
|
|
267
263
|
},
|
|
268
264
|
};
|
|
269
265
|
const CREATE_BULK_TESTCASE_TOOL = {
|
|
@@ -272,11 +268,11 @@ const CREATE_BULK_TESTCASE_TOOL = {
|
|
|
272
268
|
inputSchema: {
|
|
273
269
|
type: "object",
|
|
274
270
|
properties: {
|
|
275
|
-
projectId: { type: "string", description: "Project ID" },
|
|
276
|
-
organizationId: { type: "number", description: "Organization ID" },
|
|
271
|
+
projectId: { type: "string", description: "Project ID (optional if active project is set)" },
|
|
272
|
+
organizationId: { type: "number", description: "Organization ID (optional - auto-detected from PAT token)" },
|
|
277
273
|
testcases: { type: "string", description: "JSON string of testcase array" },
|
|
278
274
|
},
|
|
279
|
-
required: ["
|
|
275
|
+
required: ["testcases"],
|
|
280
276
|
},
|
|
281
277
|
};
|
|
282
278
|
const UPDATE_TESTCASE_FIELD_TOOL = {
|
|
@@ -285,13 +281,13 @@ const UPDATE_TESTCASE_FIELD_TOOL = {
|
|
|
285
281
|
inputSchema: {
|
|
286
282
|
type: "object",
|
|
287
283
|
properties: {
|
|
288
|
-
projectId: { type: "string", description: "Project ID" },
|
|
289
|
-
organizationId: { type: "number", description: "Organization ID" },
|
|
284
|
+
projectId: { type: "string", description: "Project ID (optional if active project is set)" },
|
|
285
|
+
organizationId: { type: "number", description: "Organization ID (optional - auto-detected from PAT token)" },
|
|
290
286
|
ids: { type: "string", description: "Comma-separated testcase IDs" },
|
|
291
287
|
field: { type: "string", description: "Field name to update" },
|
|
292
288
|
value: { type: "string", description: "New field value" },
|
|
293
289
|
},
|
|
294
|
-
required: ["
|
|
290
|
+
required: ["ids", "field", "value"],
|
|
295
291
|
},
|
|
296
292
|
};
|
|
297
293
|
const DELETE_TESTCASE_TOOL = {
|
|
@@ -300,133 +296,31 @@ const DELETE_TESTCASE_TOOL = {
|
|
|
300
296
|
inputSchema: {
|
|
301
297
|
type: "object",
|
|
302
298
|
properties: {
|
|
303
|
-
projectId: { type: "string", description: "Project ID" },
|
|
304
|
-
organizationId: { type: "number", description: "Organization ID" },
|
|
299
|
+
projectId: { type: "string", description: "Project ID (optional if active project is set)" },
|
|
300
|
+
organizationId: { type: "number", description: "Organization ID (optional - auto-detected from PAT token)" },
|
|
305
301
|
ids: { type: "string", description: "Comma-separated testcase IDs to delete" },
|
|
306
302
|
},
|
|
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"],
|
|
303
|
+
required: ["ids"],
|
|
374
304
|
},
|
|
375
305
|
};
|
|
376
306
|
const CREATE_FOLDER_TOOL = {
|
|
377
307
|
name: "create_folder",
|
|
378
|
-
description: "Create a new folder in a project",
|
|
308
|
+
description: "Create a new folder in a project for testcases",
|
|
379
309
|
inputSchema: {
|
|
380
310
|
type: "object",
|
|
381
311
|
properties: {
|
|
382
|
-
organizationId: { type: "number", description: "Organization ID" },
|
|
383
|
-
projectId: { type: "string", description: "Project ID" },
|
|
312
|
+
organizationId: { type: "number", description: "Organization ID (optional - auto-detected from PAT token)" },
|
|
313
|
+
projectId: { type: "string", description: "Project ID (optional if active project is set)" },
|
|
384
314
|
section: {
|
|
385
315
|
type: "string",
|
|
386
316
|
enum: ["TESTCASE", "TEST_CYCLE", "TEST_PLAN", "REQUIREMENT"],
|
|
387
|
-
description: "Folder section type (TESTCASE
|
|
317
|
+
description: "Folder section type (default: TESTCASE)",
|
|
318
|
+
default: "TESTCASE",
|
|
388
319
|
},
|
|
389
320
|
name: { type: "string", description: "Name of the new folder (max 50 characters)" },
|
|
390
321
|
parentId: { type: "number", description: "Parent folder ID for creating subfolder (optional)" },
|
|
391
322
|
},
|
|
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"],
|
|
323
|
+
required: ["name"],
|
|
430
324
|
},
|
|
431
325
|
};
|
|
432
326
|
const CREATE_LABEL_TOOL = {
|
|
@@ -435,11 +329,11 @@ const CREATE_LABEL_TOOL = {
|
|
|
435
329
|
inputSchema: {
|
|
436
330
|
type: "object",
|
|
437
331
|
properties: {
|
|
438
|
-
projectId: { type: "string", description: "Project ID" },
|
|
439
|
-
organizationId: { type: "number", description: "Organization ID" },
|
|
440
|
-
values: { type: "
|
|
332
|
+
projectId: { type: "string", description: "Project ID (optional if active project is set)" },
|
|
333
|
+
organizationId: { type: "number", description: "Organization ID (optional - auto-detected from PAT token)" },
|
|
334
|
+
values: { type: "string", description: "Comma-separated label names to create" },
|
|
441
335
|
},
|
|
442
|
-
required: ["
|
|
336
|
+
required: ["values"],
|
|
443
337
|
},
|
|
444
338
|
};
|
|
445
339
|
const GET_USER_PROFILE_TOOL = {
|
|
@@ -451,87 +345,29 @@ const GET_USER_PROFILE_TOOL = {
|
|
|
451
345
|
required: [],
|
|
452
346
|
},
|
|
453
347
|
};
|
|
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",
|
|
466
|
-
inputSchema: {
|
|
467
|
-
type: "object",
|
|
468
|
-
properties: {
|
|
469
|
-
organization_id: { type: "number", description: "Organization ID" },
|
|
470
|
-
project_id: { type: "string", description: "Project ID" },
|
|
471
|
-
integration_type: { type: "string", description: "Integration type (requirement/defect)" },
|
|
472
|
-
testcase_id: { type: "string", description: "Test case ID" },
|
|
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",
|
|
348
|
+
const SET_ACTIVE_PROJECT_TOOL = {
|
|
349
|
+
name: "set_active_project",
|
|
350
|
+
description: "Set the active project AFTER the user has chosen one from get_my_projects results. " +
|
|
351
|
+
"NEVER call this without first asking the user which project they want. " +
|
|
352
|
+
"Once set, other tools will use this project automatically.",
|
|
495
353
|
inputSchema: {
|
|
496
354
|
type: "object",
|
|
497
355
|
properties: {
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
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" },
|
|
356
|
+
projectId: {
|
|
357
|
+
type: "string",
|
|
358
|
+
description: "The project ID to set as active (e.g., PRJ-1234)",
|
|
359
|
+
},
|
|
517
360
|
},
|
|
518
|
-
required: [
|
|
361
|
+
required: [],
|
|
519
362
|
},
|
|
520
363
|
};
|
|
521
|
-
const
|
|
522
|
-
name: "
|
|
523
|
-
description: "
|
|
364
|
+
const GET_ACTIVE_PROJECT_TOOL = {
|
|
365
|
+
name: "get_active_project",
|
|
366
|
+
description: "Get the currently active project ID that is being used for operations",
|
|
524
367
|
inputSchema: {
|
|
525
368
|
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"],
|
|
369
|
+
properties: {},
|
|
370
|
+
required: [],
|
|
535
371
|
},
|
|
536
372
|
};
|
|
537
373
|
// Create server instance
|
|
@@ -543,37 +379,24 @@ const server = new Server({
|
|
|
543
379
|
tools: {},
|
|
544
380
|
},
|
|
545
381
|
});
|
|
546
|
-
// List available tools
|
|
382
|
+
// List available tools (14 tools)
|
|
547
383
|
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
548
384
|
return {
|
|
549
385
|
tools: [
|
|
386
|
+
GET_MY_PROJECTS_TOOL,
|
|
387
|
+
SET_ACTIVE_PROJECT_TOOL,
|
|
388
|
+
GET_ACTIVE_PROJECT_TOOL,
|
|
550
389
|
GET_TESTCASES_TOOL,
|
|
551
390
|
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
391
|
CREATE_TESTCASE_TOOL,
|
|
558
392
|
CREATE_BULK_TESTCASE_TOOL,
|
|
559
393
|
UPDATE_TESTCASE_FIELD_TOOL,
|
|
560
394
|
DELETE_TESTCASE_TOOL,
|
|
561
|
-
|
|
562
|
-
CREATE_TEST_STEP_TOOL,
|
|
563
|
-
DELETE_TEST_STEP_TOOL,
|
|
564
|
-
DOWNLOAD_TESTCASES_TOOL,
|
|
565
|
-
IMPORT_TESTCASES_TOOL,
|
|
395
|
+
GET_FOLDERS_TOOL,
|
|
566
396
|
CREATE_FOLDER_TOOL,
|
|
567
|
-
|
|
568
|
-
UPDATE_FOLDER_TOOL,
|
|
397
|
+
GET_LABELS_TOOL,
|
|
569
398
|
CREATE_LABEL_TOOL,
|
|
570
399
|
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
400
|
],
|
|
578
401
|
};
|
|
579
402
|
});
|
|
@@ -581,26 +404,20 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
581
404
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
582
405
|
const { name, arguments: args } = request.params;
|
|
583
406
|
try {
|
|
584
|
-
if (name === "
|
|
585
|
-
return await
|
|
586
|
-
}
|
|
587
|
-
else if (name === "get_testcase_detail") {
|
|
588
|
-
return await handleGetTestcaseDetail(args);
|
|
407
|
+
if (name === "get_my_projects") {
|
|
408
|
+
return await handleGetMyProjects(args);
|
|
589
409
|
}
|
|
590
|
-
else if (name === "
|
|
591
|
-
return await
|
|
410
|
+
else if (name === "set_active_project") {
|
|
411
|
+
return await handleSetActiveProject(args);
|
|
592
412
|
}
|
|
593
|
-
else if (name === "
|
|
594
|
-
return await
|
|
413
|
+
else if (name === "get_active_project") {
|
|
414
|
+
return await handleGetActiveProject();
|
|
595
415
|
}
|
|
596
|
-
else if (name === "
|
|
597
|
-
return await
|
|
598
|
-
}
|
|
599
|
-
else if (name === "get_issues") {
|
|
600
|
-
return await handleGetIssues(args);
|
|
416
|
+
else if (name === "get_testcases") {
|
|
417
|
+
return await handleGetTestcases(args);
|
|
601
418
|
}
|
|
602
|
-
else if (name === "
|
|
603
|
-
return await
|
|
419
|
+
else if (name === "get_testcase_detail") {
|
|
420
|
+
return await handleGetTestcaseDetail(args);
|
|
604
421
|
}
|
|
605
422
|
else if (name === "create_testcase") {
|
|
606
423
|
return await handleCreateTestcase(args);
|
|
@@ -614,29 +431,14 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
614
431
|
else if (name === "delete_testcase") {
|
|
615
432
|
return await handleDeleteTestcase(args);
|
|
616
433
|
}
|
|
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);
|
|
434
|
+
else if (name === "get_folders") {
|
|
435
|
+
return await handleGetFolders(args);
|
|
631
436
|
}
|
|
632
437
|
else if (name === "create_folder") {
|
|
633
438
|
return await handleCreateFolder(args);
|
|
634
439
|
}
|
|
635
|
-
else if (name === "
|
|
636
|
-
return await
|
|
637
|
-
}
|
|
638
|
-
else if (name === "update_folder") {
|
|
639
|
-
return await handleUpdateFolder(args);
|
|
440
|
+
else if (name === "get_labels") {
|
|
441
|
+
return await handleGetLabels(args);
|
|
640
442
|
}
|
|
641
443
|
else if (name === "create_label") {
|
|
642
444
|
return await handleCreateLabel(args);
|
|
@@ -644,24 +446,6 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
644
446
|
else if (name === "get_user_profile") {
|
|
645
447
|
return await handleGetUserProfile(args);
|
|
646
448
|
}
|
|
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
449
|
throw new Error(`Unknown tool: ${name}`);
|
|
666
450
|
}
|
|
667
451
|
catch (error) {
|
|
@@ -681,26 +465,13 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
681
465
|
});
|
|
682
466
|
// Handler for get_testcases tool
|
|
683
467
|
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
|
-
}
|
|
468
|
+
validateAuth();
|
|
469
|
+
// Get project and organization IDs (from args or cached)
|
|
470
|
+
const projectId = getProjectId(args.projectId);
|
|
471
|
+
const organizationId = await getOrganizationId(args.organizationId);
|
|
701
472
|
// Build query parameters
|
|
702
473
|
const queryParams = new URLSearchParams();
|
|
703
|
-
queryParams.append("organizationId",
|
|
474
|
+
queryParams.append("organizationId", organizationId.toString());
|
|
704
475
|
if (args.page !== undefined)
|
|
705
476
|
queryParams.append("page", args.page.toString());
|
|
706
477
|
if (args.limit !== undefined)
|
|
@@ -728,7 +499,7 @@ async function handleGetTestcases(args) {
|
|
|
728
499
|
if (args.updated_end_date)
|
|
729
500
|
queryParams.append("updated_end_date", args.updated_end_date);
|
|
730
501
|
// Construct API URL
|
|
731
|
-
const url = `${config.apiBaseUrl}/api/v1/projects/testcases/get-testcase/${
|
|
502
|
+
const url = `${config.apiBaseUrl}/api/v1/projects/testcases/get-testcase/${projectId}?${queryParams.toString()}`;
|
|
732
503
|
// Prepare headers with PAT token
|
|
733
504
|
// The PAT token is sent as Bearer token and will be validated by TestKase's JwtAuthGuard
|
|
734
505
|
// which checks for 'xyz_' prefix and validates against the PAT database
|
|
@@ -806,9 +577,11 @@ async function handleGetTestcases(args) {
|
|
|
806
577
|
// Handler for get_testcase_detail tool
|
|
807
578
|
async function handleGetTestcaseDetail(args) {
|
|
808
579
|
validateAuth();
|
|
580
|
+
const projectId = getProjectId(args.projectId);
|
|
581
|
+
const organizationId = await getOrganizationId(args.organizationId);
|
|
809
582
|
const queryParams = new URLSearchParams();
|
|
810
|
-
queryParams.append("organizationId",
|
|
811
|
-
const url = `${config.apiBaseUrl}/api/v1/projects/testcases/get-testcase-detail/${
|
|
583
|
+
queryParams.append("organizationId", organizationId.toString());
|
|
584
|
+
const url = `${config.apiBaseUrl}/api/v1/projects/testcases/get-testcase-detail/${projectId}/${args.testcaseId}?${queryParams.toString()}`;
|
|
812
585
|
try {
|
|
813
586
|
const response = await axios.get(url, {
|
|
814
587
|
headers: {
|
|
@@ -823,33 +596,21 @@ async function handleGetTestcaseDetail(args) {
|
|
|
823
596
|
throw handleApiError(error);
|
|
824
597
|
}
|
|
825
598
|
}
|
|
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
599
|
// Handler for get_folders tool
|
|
847
600
|
async function handleGetFolders(args) {
|
|
848
601
|
validateAuth();
|
|
602
|
+
const projectId = getProjectId(args.projectId);
|
|
603
|
+
const organizationId = await getOrganizationId(args.organizationId);
|
|
604
|
+
// Validate and normalize section - default to TESTCASE
|
|
605
|
+
const validSections = ["TESTCASE", "TEST_CYCLE", "TEST_PLAN", "REQUIREMENT"];
|
|
606
|
+
let section = (args.section || "TESTCASE").toUpperCase();
|
|
607
|
+
if (!validSections.includes(section)) {
|
|
608
|
+
section = "TESTCASE"; // Default if invalid
|
|
609
|
+
}
|
|
849
610
|
const queryParams = new URLSearchParams();
|
|
850
|
-
queryParams.append("organizationId",
|
|
851
|
-
queryParams.append("projectId",
|
|
852
|
-
queryParams.append("section",
|
|
611
|
+
queryParams.append("organizationId", organizationId.toString());
|
|
612
|
+
queryParams.append("projectId", projectId);
|
|
613
|
+
queryParams.append("section", section);
|
|
853
614
|
if (args.search) {
|
|
854
615
|
queryParams.append("search", args.search);
|
|
855
616
|
}
|
|
@@ -871,9 +632,11 @@ async function handleGetFolders(args) {
|
|
|
871
632
|
// Handler for get_labels tool
|
|
872
633
|
async function handleGetLabels(args) {
|
|
873
634
|
validateAuth();
|
|
635
|
+
const projectId = getProjectId(args.projectId);
|
|
636
|
+
const organizationId = await getOrganizationId(args.organizationId);
|
|
874
637
|
const queryParams = new URLSearchParams();
|
|
875
|
-
queryParams.append("organizationId",
|
|
876
|
-
const url = `${config.apiBaseUrl}/api/v1/projects/labels/get-label/${
|
|
638
|
+
queryParams.append("organizationId", organizationId.toString());
|
|
639
|
+
const url = `${config.apiBaseUrl}/api/v1/projects/labels/get-label/${projectId}?${queryParams.toString()}`;
|
|
877
640
|
try {
|
|
878
641
|
const response = await axios.get(url, {
|
|
879
642
|
headers: {
|
|
@@ -888,32 +651,6 @@ async function handleGetLabels(args) {
|
|
|
888
651
|
throw handleApiError(error);
|
|
889
652
|
}
|
|
890
653
|
}
|
|
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
654
|
// Handler for get_my_projects tool
|
|
918
655
|
async function handleGetMyProjects(args) {
|
|
919
656
|
validateAuth();
|
|
@@ -937,34 +674,86 @@ async function handleGetMyProjects(args) {
|
|
|
937
674
|
throw handleApiError(error);
|
|
938
675
|
}
|
|
939
676
|
}
|
|
940
|
-
// Handler for create_testcase tool
|
|
677
|
+
// Handler for create_testcase tool (handles test_steps internally)
|
|
941
678
|
async function handleCreateTestcase(args) {
|
|
942
679
|
validateAuth();
|
|
680
|
+
const projectId = getProjectId(args.projectId);
|
|
681
|
+
const organizationId = await getOrganizationId(args.organizationId);
|
|
682
|
+
// Extract and parse test_steps (JSON string) before sending to API
|
|
683
|
+
const { test_steps: test_steps_raw, labels: labels_raw, ...testcaseData } = args;
|
|
684
|
+
// Parse test_steps from JSON string
|
|
685
|
+
const test_steps = test_steps_raw ? (typeof test_steps_raw === 'string' ? JSON.parse(test_steps_raw) : test_steps_raw) : null;
|
|
686
|
+
// Parse labels from comma-separated string
|
|
687
|
+
if (labels_raw) {
|
|
688
|
+
testcaseData.labels = typeof labels_raw === 'string' ? labels_raw.split(',').map((l) => l.trim()) : labels_raw;
|
|
689
|
+
}
|
|
943
690
|
const queryParams = new URLSearchParams();
|
|
944
|
-
queryParams.append("organizationId",
|
|
945
|
-
const url = `${config.apiBaseUrl}/api/v1/projects/testcases/create-testcase/${
|
|
691
|
+
queryParams.append("organizationId", organizationId.toString());
|
|
692
|
+
const url = `${config.apiBaseUrl}/api/v1/projects/testcases/create-testcase/${projectId}?${queryParams.toString()}`;
|
|
946
693
|
try {
|
|
947
|
-
|
|
694
|
+
// Step 1: Create testcase
|
|
695
|
+
const response = await axios.post(url, testcaseData, {
|
|
948
696
|
headers: { "Content-Type": "application/json", "Authorization": `Bearer ${config.patToken}` },
|
|
949
697
|
});
|
|
950
|
-
|
|
698
|
+
const createdTestcase = response.data?.data || response.data;
|
|
699
|
+
const testcaseId = createdTestcase?.id || createdTestcase?.testcaseId;
|
|
700
|
+
// Step 2: If test_steps provided, create them
|
|
701
|
+
if (test_steps && Array.isArray(test_steps) && test_steps.length > 0 && testcaseId) {
|
|
702
|
+
const stepsUrl = `${config.apiBaseUrl}/api/v1/projects/testcases/teststeps/${projectId}/${testcaseId}?organizationId=${organizationId}`;
|
|
703
|
+
await axios.post(stepsUrl, { steps: test_steps }, {
|
|
704
|
+
headers: { "Content-Type": "application/json", "Authorization": `Bearer ${config.patToken}` },
|
|
705
|
+
});
|
|
706
|
+
}
|
|
707
|
+
return formatResponse(response.data, "Testcase Created" + (test_steps?.length ? ` with ${test_steps.length} test steps` : ""));
|
|
951
708
|
}
|
|
952
709
|
catch (error) {
|
|
953
710
|
throw handleApiError(error);
|
|
954
711
|
}
|
|
955
712
|
}
|
|
956
|
-
// Handler for create_bulk_testcase tool
|
|
713
|
+
// Handler for create_bulk_testcase tool (handles test_steps internally)
|
|
957
714
|
async function handleCreateBulkTestcase(args) {
|
|
958
715
|
validateAuth();
|
|
959
|
-
const
|
|
960
|
-
|
|
961
|
-
const url = `${config.apiBaseUrl}/api/v1/projects/testcases/create-bulk-testcase/${args.projectId}?${queryParams.toString()}`;
|
|
716
|
+
const projectId = getProjectId(args.projectId);
|
|
717
|
+
const organizationId = await getOrganizationId(args.organizationId);
|
|
962
718
|
const testcases = typeof args.testcases === 'string' ? JSON.parse(args.testcases) : args.testcases;
|
|
719
|
+
// Separate test_steps from each testcase for later processing
|
|
720
|
+
const testcasesWithSteps = testcases.map((tc) => ({
|
|
721
|
+
testcase: { ...tc, test_steps: undefined },
|
|
722
|
+
test_steps: tc.test_steps
|
|
723
|
+
}));
|
|
724
|
+
const queryParams = new URLSearchParams();
|
|
725
|
+
queryParams.append("organizationId", organizationId.toString());
|
|
726
|
+
const url = `${config.apiBaseUrl}/api/v1/projects/testcases/create-bulk-testcase/${projectId}?${queryParams.toString()}`;
|
|
963
727
|
try {
|
|
964
|
-
|
|
728
|
+
// Step 1: Create all testcases (without steps)
|
|
729
|
+
const cleanTestcases = testcasesWithSteps.map((t) => t.testcase);
|
|
730
|
+
const response = await axios.post(url, { testcases: cleanTestcases }, {
|
|
965
731
|
headers: { "Content-Type": "application/json", "Authorization": `Bearer ${config.patToken}` },
|
|
966
732
|
});
|
|
967
|
-
|
|
733
|
+
const results = response.data?.results || [];
|
|
734
|
+
// Step 2: Create test steps for each successfully created testcase
|
|
735
|
+
let stepsCreated = 0;
|
|
736
|
+
for (let i = 0; i < results.length; i++) {
|
|
737
|
+
const result = results[i];
|
|
738
|
+
const steps = testcasesWithSteps[i]?.test_steps;
|
|
739
|
+
if (result.status === 'success' && steps && Array.isArray(steps) && steps.length > 0) {
|
|
740
|
+
const testcaseId = result.testcaseId || result.id;
|
|
741
|
+
if (testcaseId) {
|
|
742
|
+
try {
|
|
743
|
+
const stepsUrl = `${config.apiBaseUrl}/api/v1/projects/testcases/teststeps/${projectId}/${testcaseId}?organizationId=${organizationId}`;
|
|
744
|
+
await axios.post(stepsUrl, { steps }, {
|
|
745
|
+
headers: { "Content-Type": "application/json", "Authorization": `Bearer ${config.patToken}` },
|
|
746
|
+
});
|
|
747
|
+
stepsCreated++;
|
|
748
|
+
}
|
|
749
|
+
catch (e) {
|
|
750
|
+
// Log but don't fail the whole operation
|
|
751
|
+
console.error(`Failed to create steps for testcase ${testcaseId}`);
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
return formatResponse(response.data, `Bulk Testcases Created (${stepsCreated} with test steps)`);
|
|
968
757
|
}
|
|
969
758
|
catch (error) {
|
|
970
759
|
throw handleApiError(error);
|
|
@@ -973,9 +762,11 @@ async function handleCreateBulkTestcase(args) {
|
|
|
973
762
|
// Handler for update_testcase_field tool
|
|
974
763
|
async function handleUpdateTestcaseField(args) {
|
|
975
764
|
validateAuth();
|
|
765
|
+
const projectId = getProjectId(args.projectId);
|
|
766
|
+
const organizationId = await getOrganizationId(args.organizationId);
|
|
976
767
|
const queryParams = new URLSearchParams();
|
|
977
|
-
queryParams.append("organizationId",
|
|
978
|
-
const url = `${config.apiBaseUrl}/api/v1/projects/testcases/update-field/${
|
|
768
|
+
queryParams.append("organizationId", organizationId.toString());
|
|
769
|
+
const url = `${config.apiBaseUrl}/api/v1/projects/testcases/update-field/${projectId}?${queryParams.toString()}`;
|
|
979
770
|
try {
|
|
980
771
|
const response = await axios.patch(url, {
|
|
981
772
|
ids: args.ids.split(','),
|
|
@@ -993,9 +784,11 @@ async function handleUpdateTestcaseField(args) {
|
|
|
993
784
|
// Handler for delete_testcase tool
|
|
994
785
|
async function handleDeleteTestcase(args) {
|
|
995
786
|
validateAuth();
|
|
787
|
+
const projectId = getProjectId(args.projectId);
|
|
788
|
+
const organizationId = await getOrganizationId(args.organizationId);
|
|
996
789
|
const queryParams = new URLSearchParams();
|
|
997
|
-
queryParams.append("organizationId",
|
|
998
|
-
const url = `${config.apiBaseUrl}/api/v1/projects/testcases/delete-testcase/${
|
|
790
|
+
queryParams.append("organizationId", organizationId.toString());
|
|
791
|
+
const url = `${config.apiBaseUrl}/api/v1/projects/testcases/delete-testcase/${projectId}?${queryParams.toString()}`;
|
|
999
792
|
try {
|
|
1000
793
|
const response = await axios.delete(url, {
|
|
1001
794
|
headers: { "Content-Type": "application/json", "Authorization": `Bearer ${config.patToken}` },
|
|
@@ -1007,98 +800,22 @@ async function handleDeleteTestcase(args) {
|
|
|
1007
800
|
throw handleApiError(error);
|
|
1008
801
|
}
|
|
1009
802
|
}
|
|
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
803
|
// Handler for create_folder tool
|
|
1095
804
|
async function handleCreateFolder(args) {
|
|
1096
805
|
validateAuth();
|
|
806
|
+
const projectId = getProjectId(args.projectId);
|
|
807
|
+
const organizationId = await getOrganizationId(args.organizationId);
|
|
1097
808
|
const url = `${config.apiBaseUrl}/api/v1/folders/create-folders`;
|
|
809
|
+
// Validate and normalize section - default to TESTCASE
|
|
810
|
+
const validSections = ["TESTCASE", "TEST_CYCLE", "TEST_PLAN", "REQUIREMENT"];
|
|
811
|
+
let section = (args.section || "TESTCASE").toUpperCase();
|
|
812
|
+
if (!validSections.includes(section)) {
|
|
813
|
+
section = "TESTCASE";
|
|
814
|
+
}
|
|
1098
815
|
const body = {
|
|
1099
|
-
organizationId:
|
|
1100
|
-
projectId:
|
|
1101
|
-
section:
|
|
816
|
+
organizationId: organizationId,
|
|
817
|
+
projectId: projectId,
|
|
818
|
+
section: section,
|
|
1102
819
|
name: args.name,
|
|
1103
820
|
...(args.parentId && { parentId: args.parentId }),
|
|
1104
821
|
};
|
|
@@ -1112,50 +829,18 @@ async function handleCreateFolder(args) {
|
|
|
1112
829
|
throw handleApiError(error);
|
|
1113
830
|
}
|
|
1114
831
|
}
|
|
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
832
|
// Handler for create_label tool
|
|
1152
833
|
async function handleCreateLabel(args) {
|
|
1153
834
|
validateAuth();
|
|
835
|
+
const projectId = getProjectId(args.projectId);
|
|
836
|
+
const organizationId = await getOrganizationId(args.organizationId);
|
|
1154
837
|
const queryParams = new URLSearchParams();
|
|
1155
|
-
queryParams.append("organizationId",
|
|
1156
|
-
const url = `${config.apiBaseUrl}/api/v1/projects/labels/create/${
|
|
838
|
+
queryParams.append("organizationId", organizationId.toString());
|
|
839
|
+
const url = `${config.apiBaseUrl}/api/v1/projects/labels/create/${projectId}?${queryParams.toString()}`;
|
|
840
|
+
// Parse values from comma-separated string
|
|
841
|
+
const values = typeof args.values === 'string' ? args.values.split(',').map((v) => v.trim()) : args.values;
|
|
1157
842
|
try {
|
|
1158
|
-
const response = await axios.post(url, { values
|
|
843
|
+
const response = await axios.post(url, { values }, {
|
|
1159
844
|
headers: { "Content-Type": "application/json", "Authorization": `Bearer ${config.patToken}` },
|
|
1160
845
|
});
|
|
1161
846
|
return formatResponse(response.data, "Label Created");
|
|
@@ -1178,106 +863,42 @@ async function handleGetUserProfile(args) {
|
|
|
1178
863
|
throw handleApiError(error);
|
|
1179
864
|
}
|
|
1180
865
|
}
|
|
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);
|
|
866
|
+
// Handler for set_active_project tool
|
|
867
|
+
async function handleSetActiveProject(args) {
|
|
868
|
+
if (!args.projectId) {
|
|
869
|
+
throw new Error("projectId is required");
|
|
1264
870
|
}
|
|
871
|
+
setActiveProject(args.projectId);
|
|
872
|
+
return {
|
|
873
|
+
content: [
|
|
874
|
+
{
|
|
875
|
+
type: "text",
|
|
876
|
+
text: `Active project set to: ${args.projectId}\n\nYou can now use other tools without specifying projectId.`,
|
|
877
|
+
},
|
|
878
|
+
],
|
|
879
|
+
};
|
|
1265
880
|
}
|
|
1266
|
-
// Handler for
|
|
1267
|
-
async function
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
catch (error) {
|
|
1279
|
-
throw handleApiError(error);
|
|
881
|
+
// Handler for get_active_project tool
|
|
882
|
+
async function handleGetActiveProject() {
|
|
883
|
+
const projectId = cachedProjectId;
|
|
884
|
+
if (!projectId) {
|
|
885
|
+
return {
|
|
886
|
+
content: [
|
|
887
|
+
{
|
|
888
|
+
type: "text",
|
|
889
|
+
text: "No active project is set. Use set_active_project or specify projectId in your next command.",
|
|
890
|
+
},
|
|
891
|
+
],
|
|
892
|
+
};
|
|
1280
893
|
}
|
|
894
|
+
return {
|
|
895
|
+
content: [
|
|
896
|
+
{
|
|
897
|
+
type: "text",
|
|
898
|
+
text: `Current active project: ${projectId}`,
|
|
899
|
+
},
|
|
900
|
+
],
|
|
901
|
+
};
|
|
1281
902
|
}
|
|
1282
903
|
// Helper function to validate authentication
|
|
1283
904
|
function validateAuth() {
|