zephyr-scale-mcp-server 0.3.0 → 0.3.2

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/README.md CHANGED
@@ -68,7 +68,7 @@ The server automatically detects your Jira environment and uses the appropriate
68
68
  - **Jira Cloud**: Uses Zephyr Scale API v2.
69
69
  - **Jira Data Center**: Uses Zephyr Scale API v1.
70
70
 
71
- This may result in slightly different behavior for some tools, such as `add_test_cases_to_run`.
71
+ Some tools are platform-specific. For example, `add_test_cases_to_run` is only available on Cloud, as the Data Center API (v1) does not support modifying test runs after creation.
72
72
 
73
73
  ### Resource System
74
74
  The server provides access to various resources through URI schemes:
@@ -88,11 +88,12 @@ The server provides access to various resources through URI schemes:
88
88
  - `create_test_run`: Create a new test run.
89
89
  - `get_test_run`: Get detailed information about a specific test run.
90
90
  - `get_test_run_cases`: Get test case keys from a test run.
91
- - `add_test_cases_to_run`: Add test cases to an existing test run.
91
+ - `add_test_cases_to_run`: Add test cases to an existing test run. *(Cloud only)*
92
92
 
93
93
  ### Test Execution & Search
94
94
  - `get_test_execution`: Get detailed individual test execution results.
95
95
  - `search_test_cases_by_folder`: Search for test cases in a specific folder.
96
+ - `search_test_runs`: Search for test runs by project key and/or folder path.
96
97
 
97
98
  ### Organization
98
99
  - `create_folder`: Create a new folder in Zephyr Scale.
package/build/index.js CHANGED
@@ -14,7 +14,7 @@ class ZephyrServer {
14
14
  const jiraConfig = createJiraConfig();
15
15
  this.server = new Server({
16
16
  name: 'zephyr-server',
17
- version: '0.3.0',
17
+ version: '0.3.1',
18
18
  }, {
19
19
  capabilities: {
20
20
  tools: {},
@@ -55,6 +55,8 @@ class ZephyrServer {
55
55
  return await this.toolHandlers.getTestRunCases(args);
56
56
  case 'delete_test_case':
57
57
  return await this.toolHandlers.deleteTestCase(args);
58
+ case 'delete_test_run':
59
+ return await this.toolHandlers.deleteTestRun(args);
58
60
  case 'create_test_run':
59
61
  return await this.toolHandlers.createTestRun(args);
60
62
  case 'get_test_run':
@@ -273,6 +273,26 @@ export class ZephyrToolHandlers {
273
273
  throw new McpError(ErrorCode.InternalError, `Failed to delete test case: ${error instanceof Error ? error.message : String(error)}`);
274
274
  }
275
275
  }
276
+ async deleteTestRun(args) {
277
+ const { test_run_key } = args;
278
+ try {
279
+ const response = await this.axiosInstance.delete(`${this.jiraConfig.apiEndpoints.testrun}/${test_run_key}`);
280
+ if (response.status === 204) {
281
+ return {
282
+ content: [{ type: 'text', text: `Test run ${test_run_key} deleted successfully.` }],
283
+ };
284
+ }
285
+ else {
286
+ return {
287
+ content: [{ type: 'text', text: `Failed to delete test run. Status: ${response.status}` }],
288
+ isError: true,
289
+ };
290
+ }
291
+ }
292
+ catch (error) {
293
+ throw new McpError(ErrorCode.InternalError, `Failed to delete test run: ${error instanceof Error ? error.message : String(error)}`);
294
+ }
295
+ }
276
296
  async createTestRun(args) {
277
297
  const { project_key, name, test_case_keys, test_plan_key, folder, planned_start_date, planned_end_date, description, owner, environment, issue_key, issue_links, custom_fields } = args;
278
298
  // Build the basic payload
@@ -557,48 +577,18 @@ export class ZephyrToolHandlers {
557
577
  }
558
578
  async addTestCasesToRun(args) {
559
579
  const { test_run_key, test_case_keys } = args;
580
+ if (this.jiraConfig.type === 'datacenter') {
581
+ throw new McpError(ErrorCode.InvalidRequest, 'add_test_cases_to_run is only supported on Zephyr Scale Cloud. The Data Center API (v1) does not provide an endpoint to modify test runs after creation.');
582
+ }
560
583
  try {
561
- // For Data Center, we need to get the current items and then update
562
- if (this.jiraConfig.type === 'datacenter') {
563
- const getResponse = await this.axiosInstance.get(`${this.jiraConfig.apiEndpoints.testrun}/${test_run_key}`);
564
- const existingItems = getResponse.data.items || [];
565
- const existingKeys = new Set(existingItems.map((item) => item.testCaseKey));
566
- const newItems = test_case_keys
567
- .filter(key => !existingKeys.has(key))
568
- .map(key => ({ testCaseKey: key, testResultStatus: 'Not Executed' }));
569
- if (newItems.length > 0) {
570
- let response;
571
- if (this.jiraConfig.type === 'datacenter') {
572
- const minimalPayload = {
573
- items: [...existingItems, ...newItems]
574
- };
575
- response = await this.axiosInstance.put(`${this.jiraConfig.apiEndpoints.testrun}/${test_run_key}`, minimalPayload);
576
- }
577
- else {
578
- const postPayload = { items: newItems.map(item => item.testCaseKey) };
579
- response = await this.axiosInstance.post(`${this.jiraConfig.apiEndpoints.testrun}/${test_run_key}/testcases`, postPayload);
580
- }
581
- if (response.status === 200 || response.status === 201 || response.status === 204) {
582
- return {
583
- content: [{ type: 'text', text: `Added ${newItems.length} new test cases to test run ${test_run_key}.` }],
584
- };
585
- }
586
- }
587
- else {
588
- return {
589
- content: [{ type: 'text', text: 'All specified test cases are already in the test run.' }],
590
- };
591
- }
592
- }
593
- else {
594
- // For Cloud, we can just post the new test case keys
595
- const fullPayload = { items: test_case_keys };
596
- const response = await this.axiosInstance.put(`${this.jiraConfig.apiEndpoints.testrun}/${test_run_key}`, fullPayload);
597
- if (response.status === 200 || response.status === 204) {
598
- return {
599
- content: [{ type: 'text', text: `Successfully updated test cases for test run ${test_run_key}.` }],
600
- };
601
- }
584
+ const payload = {
585
+ items: test_case_keys.map(key => ({ testCaseKey: key }))
586
+ };
587
+ const response = await this.axiosInstance.post(`/testcycles/${test_run_key}/testcases`, payload);
588
+ if (response.status === 200 || response.status === 201 || response.status === 204) {
589
+ return {
590
+ content: [{ type: 'text', text: `Added ${test_case_keys.length} test case(s) to test run ${test_run_key}.` }],
591
+ };
602
592
  }
603
593
  }
604
594
  catch (error) {
@@ -373,9 +373,23 @@ export const toolSchemas = [
373
373
  },
374
374
  },
375
375
  },
376
+ {
377
+ name: 'delete_test_run',
378
+ description: 'Delete a specific test run',
379
+ inputSchema: {
380
+ type: 'object',
381
+ properties: {
382
+ test_run_key: {
383
+ type: 'string',
384
+ description: 'Test run key to delete (e.g., PROJ-R123)',
385
+ },
386
+ },
387
+ required: ['test_run_key'],
388
+ },
389
+ },
376
390
  {
377
391
  name: 'add_test_cases_to_run',
378
- description: 'Add test cases to an existing test run',
392
+ description: 'Add test cases to an existing test run (Cloud only — not supported on Data Center)',
379
393
  inputSchema: {
380
394
  type: 'object',
381
395
  properties: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zephyr-scale-mcp-server",
3
- "version": "0.3.0",
3
+ "version": "0.3.2",
4
4
  "description": "Model Context Protocol (MCP) server for Zephyr Scale test case management with comprehensive STEP_BY_STEP, PLAIN_TEXT, and BDD support",
5
5
  "type": "module",
6
6
  "main": "./build/index.js",
@@ -22,6 +22,7 @@
22
22
  "test": "node test/run-tests.cjs",
23
23
  "test:unit": "node test/zephyr-server.test.cjs",
24
24
  "test:integration": "node test/integration.test.cjs",
25
+ "report:weekly": "node scripts/weekly-report.cjs",
25
26
  "prepublishOnly": "npm run build"
26
27
  },
27
28
  "keywords": [
package/src/index.ts CHANGED
@@ -25,7 +25,7 @@ class ZephyrServer {
25
25
  this.server = new Server(
26
26
  {
27
27
  name: 'zephyr-server',
28
- version: '0.3.0',
28
+ version: '0.3.1',
29
29
  },
30
30
  {
31
31
  capabilities: {
@@ -75,6 +75,8 @@ class ZephyrServer {
75
75
  return await this.toolHandlers.getTestRunCases(args);
76
76
  case 'delete_test_case':
77
77
  return await this.toolHandlers.deleteTestCase(args);
78
+ case 'delete_test_run':
79
+ return await this.toolHandlers.deleteTestRun(args);
78
80
  case 'create_test_run':
79
81
  return await this.toolHandlers.createTestRun(args as any);
80
82
  case 'get_test_run':
@@ -301,6 +301,25 @@ export class ZephyrToolHandlers {
301
301
  }
302
302
  }
303
303
 
304
+ async deleteTestRun(args: any) {
305
+ const { test_run_key } = args;
306
+ try {
307
+ const response = await this.axiosInstance.delete(`${this.jiraConfig.apiEndpoints.testrun}/${test_run_key}`);
308
+ if (response.status === 204) {
309
+ return {
310
+ content: [{ type: 'text', text: `Test run ${test_run_key} deleted successfully.` }],
311
+ };
312
+ } else {
313
+ return {
314
+ content: [{ type: 'text', text: `Failed to delete test run. Status: ${response.status}` }],
315
+ isError: true,
316
+ };
317
+ }
318
+ } catch (error) {
319
+ throw new McpError(ErrorCode.InternalError, `Failed to delete test run: ${error instanceof Error ? error.message : String(error)}`);
320
+ }
321
+ }
322
+
304
323
  async createTestRun(args: TestRunArgs) {
305
324
  const {
306
325
  project_key,
@@ -611,49 +630,23 @@ export class ZephyrToolHandlers {
611
630
  async addTestCasesToRun(args: AddTestCasesToRunArgs) {
612
631
  const { test_run_key, test_case_keys } = args;
613
632
 
633
+ if (this.jiraConfig.type === 'datacenter') {
634
+ throw new McpError(
635
+ ErrorCode.InvalidRequest,
636
+ 'add_test_cases_to_run is only supported on Zephyr Scale Cloud. The Data Center API (v1) does not provide an endpoint to modify test runs after creation.'
637
+ );
638
+ }
639
+
614
640
  try {
615
- // For Data Center, we need to get the current items and then update
616
- if (this.jiraConfig.type === 'datacenter') {
617
- const getResponse = await this.axiosInstance.get(`${this.jiraConfig.apiEndpoints.testrun}/${test_run_key}`);
618
- const existingItems = getResponse.data.items || [];
619
- const existingKeys = new Set(existingItems.map((item: any) => item.testCaseKey));
620
-
621
- const newItems = test_case_keys
622
- .filter(key => !existingKeys.has(key))
623
- .map(key => ({ testCaseKey: key, testResultStatus: 'Not Executed' }));
624
-
625
- if (newItems.length > 0) {
626
- let response;
627
- if (this.jiraConfig.type === 'datacenter') {
628
- const minimalPayload = {
629
- items: [...existingItems, ...newItems]
630
- };
631
- response = await this.axiosInstance.put(`${this.jiraConfig.apiEndpoints.testrun}/${test_run_key}`, minimalPayload);
632
- } else {
633
- const postPayload = { items: newItems.map(item => item.testCaseKey) };
634
- response = await this.axiosInstance.post(`${this.jiraConfig.apiEndpoints.testrun}/${test_run_key}/testcases`, postPayload);
635
- }
641
+ const payload = {
642
+ items: test_case_keys.map(key => ({ testCaseKey: key }))
643
+ };
644
+ const response = await this.axiosInstance.post(`/testcycles/${test_run_key}/testcases`, payload);
636
645
 
637
- if (response.status === 200 || response.status === 201 || response.status === 204) {
638
- return {
639
- content: [{ type: 'text', text: `Added ${newItems.length} new test cases to test run ${test_run_key}.` }],
640
- };
641
- }
642
- } else {
643
- return {
644
- content: [{ type: 'text', text: 'All specified test cases are already in the test run.' }],
645
- };
646
- }
647
- } else {
648
- // For Cloud, we can just post the new test case keys
649
- const fullPayload = { items: test_case_keys };
650
- const response = await this.axiosInstance.put(`${this.jiraConfig.apiEndpoints.testrun}/${test_run_key}`, fullPayload);
651
-
652
- if (response.status === 200 || response.status === 204) {
653
- return {
654
- content: [{ type: 'text', text: `Successfully updated test cases for test run ${test_run_key}.` }],
655
- };
656
- }
646
+ if (response.status === 200 || response.status === 201 || response.status === 204) {
647
+ return {
648
+ content: [{ type: 'text', text: `Added ${test_case_keys.length} test case(s) to test run ${test_run_key}.` }],
649
+ };
657
650
  }
658
651
  } catch (error) {
659
652
  throw new McpError(ErrorCode.InternalError, `Failed to add test cases: ${error instanceof Error ? error.message : String(error)}`);
@@ -373,9 +373,23 @@ export const toolSchemas = [
373
373
  },
374
374
  },
375
375
  },
376
+ {
377
+ name: 'delete_test_run',
378
+ description: 'Delete a specific test run',
379
+ inputSchema: {
380
+ type: 'object',
381
+ properties: {
382
+ test_run_key: {
383
+ type: 'string',
384
+ description: 'Test run key to delete (e.g., PROJ-R123)',
385
+ },
386
+ },
387
+ required: ['test_run_key'],
388
+ },
389
+ },
376
390
  {
377
391
  name: 'add_test_cases_to_run',
378
- description: 'Add test cases to an existing test run',
392
+ description: 'Add test cases to an existing test run (Cloud only — not supported on Data Center)',
379
393
  inputSchema: {
380
394
  type: 'object',
381
395
  properties: {