@testkase/mcp-server 2.0.4 → 2.0.7

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