@testkase/mcp-server 2.0.3 → 2.0.7

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