zephyr-scale-mcp-server 0.0.5 → 0.1.0

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
@@ -60,6 +60,8 @@ Then configure:
60
60
  - `delete_test_case` - Delete a specific test case
61
61
  - `create_folder` - Create a new folder in Zephyr Scale
62
62
  - `get_test_run_cases` - Get test case keys from a test run
63
+ - `create_test_run` - Create a new test run
64
+ - `get_test_run` - Get detailed information about a specific test run
63
65
 
64
66
  ## Examples
65
67
 
@@ -103,7 +105,23 @@ Then configure:
103
105
  }
104
106
  ```
105
107
 
108
+ ### Create Test Run
109
+ ```json
110
+ {
111
+ "project_key": "PROJ",
112
+ "name": "Sprint 1 Test Run",
113
+ "test_case_keys": ["PROJ-T123", "PROJ-T124", "PROJ-T125"],
114
+ "environment": "Production",
115
+ "description": "Testing core functionality for Sprint 1"
116
+ }
117
+ ```
106
118
 
119
+ ### Get Test Run
120
+ ```json
121
+ {
122
+ "test_run_key": "PROJ-R456"
123
+ }
124
+ ```
107
125
 
108
126
  ## Authentication
109
127
 
package/build/index.js CHANGED
@@ -249,6 +249,71 @@ class ZephyrServer {
249
249
  required: ['test_case_key'],
250
250
  },
251
251
  },
252
+ {
253
+ name: 'create_test_run',
254
+ description: 'Create a new test run',
255
+ inputSchema: {
256
+ type: 'object',
257
+ properties: {
258
+ project_key: {
259
+ type: 'string',
260
+ description: 'Project key (required)',
261
+ },
262
+ name: {
263
+ type: 'string',
264
+ description: 'Test run name (required)',
265
+ },
266
+ test_case_keys: {
267
+ type: 'array',
268
+ description: 'Array of test case keys to include in the test run',
269
+ items: { type: 'string' }
270
+ },
271
+ folder: {
272
+ type: 'string',
273
+ description: 'Folder path (optional)',
274
+ },
275
+ planned_start_date: {
276
+ type: 'string',
277
+ description: 'Planned start date in ISO format (optional)',
278
+ },
279
+ planned_end_date: {
280
+ type: 'string',
281
+ description: 'Planned end date in ISO format (optional)',
282
+ },
283
+ description: {
284
+ type: 'string',
285
+ description: 'Test run description (optional)',
286
+ },
287
+ owner: {
288
+ type: 'string',
289
+ description: 'Test run owner (optional)',
290
+ },
291
+ environment: {
292
+ type: 'string',
293
+ description: 'Test environment (optional)',
294
+ },
295
+ custom_fields: {
296
+ type: 'object',
297
+ description: 'Custom fields object (optional)',
298
+ },
299
+ },
300
+ required: ['project_key', 'name'],
301
+ },
302
+ },
303
+ {
304
+ name: 'get_test_run',
305
+ description: 'Get detailed information about a specific test run',
306
+ inputSchema: {
307
+ type: 'object',
308
+ properties: {
309
+ test_run_key: {
310
+ type: 'string',
311
+ description: 'Test run key (e.g., PROJ-R123)',
312
+ },
313
+ },
314
+ required: ['test_run_key'],
315
+ },
316
+ },
252
317
  ],
253
318
  }));
254
319
  this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
@@ -268,6 +333,10 @@ class ZephyrServer {
268
333
  return await this.getTestRunCases(request.params.arguments);
269
334
  case 'delete_test_case':
270
335
  return await this.deleteTestCase(request.params.arguments);
336
+ case 'create_test_run':
337
+ return await this.createTestRun(request.params.arguments);
338
+ case 'get_test_run':
339
+ return await this.getTestRun(request.params.arguments);
271
340
  default:
272
341
  throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${request.params.name}`);
273
342
  }
@@ -605,6 +674,108 @@ class ZephyrServer {
605
674
  throw new McpError(ErrorCode.InternalError, `Failed to delete test case: ${errorMessage}`);
606
675
  }
607
676
  }
677
+ async createTestRun(args) {
678
+ const { project_key, name, test_case_keys, folder, planned_start_date, planned_end_date, description, owner, environment, custom_fields } = args;
679
+ // Build the basic payload
680
+ const payload = {
681
+ projectKey: project_key,
682
+ name: name,
683
+ };
684
+ // Add optional fields
685
+ if (test_case_keys && test_case_keys.length > 0) {
686
+ payload.items = test_case_keys.map((testCaseKey) => ({
687
+ testCaseKey: testCaseKey
688
+ }));
689
+ }
690
+ if (folder)
691
+ payload.folder = folder;
692
+ if (planned_start_date)
693
+ payload.plannedStartDate = planned_start_date;
694
+ if (planned_end_date)
695
+ payload.plannedEndDate = planned_end_date;
696
+ if (description)
697
+ payload.description = description;
698
+ if (owner)
699
+ payload.owner = owner;
700
+ if (environment)
701
+ payload.environment = environment;
702
+ if (custom_fields)
703
+ payload.customFields = custom_fields;
704
+ try {
705
+ const response = await this.axiosInstance.post('/rest/atm/1.0/testrun', payload);
706
+ if (response.status === 201) {
707
+ const testRunKey = response.data.key || 'Unknown';
708
+ return {
709
+ content: [
710
+ {
711
+ type: 'text',
712
+ text: `✅ Test run created successfully: ${testRunKey}\n${JSON.stringify({
713
+ key: testRunKey,
714
+ name: name,
715
+ testCaseCount: test_case_keys?.length || 0,
716
+ environment: environment || 'Not specified'
717
+ }, null, 2)}`,
718
+ },
719
+ ],
720
+ };
721
+ }
722
+ else {
723
+ throw new Error(`Unexpected status code: ${response.status}`);
724
+ }
725
+ }
726
+ catch (error) {
727
+ let errorMessage = 'Unknown error';
728
+ if (error instanceof Error && 'response' in error) {
729
+ const axiosError = error;
730
+ errorMessage = `Status: ${axiosError.response?.status}, Data: ${JSON.stringify(axiosError.response?.data)}`;
731
+ }
732
+ else if (error instanceof Error) {
733
+ errorMessage = error.message;
734
+ }
735
+ else {
736
+ errorMessage = String(error);
737
+ }
738
+ throw new McpError(ErrorCode.InternalError, `Failed to create test run: ${errorMessage}`);
739
+ }
740
+ }
741
+ async getTestRun(args) {
742
+ const { test_run_key } = args;
743
+ try {
744
+ const response = await this.axiosInstance.get(`/rest/atm/1.0/testrun/${test_run_key}`);
745
+ if (response.status === 200) {
746
+ return {
747
+ content: [
748
+ {
749
+ type: 'text',
750
+ text: JSON.stringify(response.data, null, 2),
751
+ },
752
+ ],
753
+ };
754
+ }
755
+ else {
756
+ throw new Error(`Failed to retrieve test run: ${response.status}`);
757
+ }
758
+ }
759
+ catch (error) {
760
+ let errorMessage = 'Unknown error';
761
+ if (error instanceof Error && 'response' in error) {
762
+ const axiosError = error;
763
+ if (axiosError.response?.status === 404) {
764
+ errorMessage = `Test run ${test_run_key} not found`;
765
+ }
766
+ else {
767
+ errorMessage = `Status: ${axiosError.response?.status}, Data: ${JSON.stringify(axiosError.response?.data)}`;
768
+ }
769
+ }
770
+ else if (error instanceof Error) {
771
+ errorMessage = error.message;
772
+ }
773
+ else {
774
+ errorMessage = String(error);
775
+ }
776
+ throw new McpError(ErrorCode.InternalError, `Failed to get test run: ${errorMessage}`);
777
+ }
778
+ }
608
779
  async run() {
609
780
  const transport = new StdioServerTransport();
610
781
  await this.server.connect(transport);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zephyr-scale-mcp-server",
3
- "version": "0.0.5",
3
+ "version": "0.1.0",
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",
package/src/index.ts CHANGED
@@ -266,6 +266,71 @@ class ZephyrServer {
266
266
  required: ['test_case_key'],
267
267
  },
268
268
  },
269
+ {
270
+ name: 'create_test_run',
271
+ description: 'Create a new test run',
272
+ inputSchema: {
273
+ type: 'object',
274
+ properties: {
275
+ project_key: {
276
+ type: 'string',
277
+ description: 'Project key (required)',
278
+ },
279
+ name: {
280
+ type: 'string',
281
+ description: 'Test run name (required)',
282
+ },
283
+ test_case_keys: {
284
+ type: 'array',
285
+ description: 'Array of test case keys to include in the test run',
286
+ items: { type: 'string' }
287
+ },
288
+ folder: {
289
+ type: 'string',
290
+ description: 'Folder path (optional)',
291
+ },
292
+ planned_start_date: {
293
+ type: 'string',
294
+ description: 'Planned start date in ISO format (optional)',
295
+ },
296
+ planned_end_date: {
297
+ type: 'string',
298
+ description: 'Planned end date in ISO format (optional)',
299
+ },
300
+ description: {
301
+ type: 'string',
302
+ description: 'Test run description (optional)',
303
+ },
304
+ owner: {
305
+ type: 'string',
306
+ description: 'Test run owner (optional)',
307
+ },
308
+ environment: {
309
+ type: 'string',
310
+ description: 'Test environment (optional)',
311
+ },
312
+ custom_fields: {
313
+ type: 'object',
314
+ description: 'Custom fields object (optional)',
315
+ },
316
+ },
317
+ required: ['project_key', 'name'],
318
+ },
319
+ },
320
+ {
321
+ name: 'get_test_run',
322
+ description: 'Get detailed information about a specific test run',
323
+ inputSchema: {
324
+ type: 'object',
325
+ properties: {
326
+ test_run_key: {
327
+ type: 'string',
328
+ description: 'Test run key (e.g., PROJ-R123)',
329
+ },
330
+ },
331
+ required: ['test_run_key'],
332
+ },
333
+ },
269
334
  ],
270
335
  }));
271
336
 
@@ -286,6 +351,10 @@ class ZephyrServer {
286
351
  return await this.getTestRunCases(request.params.arguments);
287
352
  case 'delete_test_case':
288
353
  return await this.deleteTestCase(request.params.arguments);
354
+ case 'create_test_run':
355
+ return await this.createTestRun(request.params.arguments);
356
+ case 'get_test_run':
357
+ return await this.getTestRun(request.params.arguments);
289
358
  default:
290
359
  throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${request.params.name}`);
291
360
  }
@@ -660,6 +729,117 @@ class ZephyrServer {
660
729
  }
661
730
  }
662
731
 
732
+ private async createTestRun(args: any) {
733
+ const {
734
+ project_key,
735
+ name,
736
+ test_case_keys,
737
+ folder,
738
+ planned_start_date,
739
+ planned_end_date,
740
+ description,
741
+ owner,
742
+ environment,
743
+ custom_fields
744
+ } = args;
745
+
746
+ // Build the basic payload
747
+ const payload: any = {
748
+ projectKey: project_key,
749
+ name: name,
750
+ };
751
+
752
+ // Add optional fields
753
+ if (test_case_keys && test_case_keys.length > 0) {
754
+ payload.items = test_case_keys.map((testCaseKey: string) => ({
755
+ testCaseKey: testCaseKey
756
+ }));
757
+ }
758
+ if (folder) payload.folder = folder;
759
+ if (planned_start_date) payload.plannedStartDate = planned_start_date;
760
+ if (planned_end_date) payload.plannedEndDate = planned_end_date;
761
+ if (description) payload.description = description;
762
+ if (owner) payload.owner = owner;
763
+ if (environment) payload.environment = environment;
764
+ if (custom_fields) payload.customFields = custom_fields;
765
+
766
+ try {
767
+ const response = await this.axiosInstance.post('/rest/atm/1.0/testrun', payload);
768
+
769
+ if (response.status === 201) {
770
+ const testRunKey = response.data.key || 'Unknown';
771
+ return {
772
+ content: [
773
+ {
774
+ type: 'text',
775
+ text: `✅ Test run created successfully: ${testRunKey}\n${JSON.stringify({
776
+ key: testRunKey,
777
+ name: name,
778
+ testCaseCount: test_case_keys?.length || 0,
779
+ environment: environment || 'Not specified'
780
+ }, null, 2)}`,
781
+ },
782
+ ],
783
+ };
784
+ } else {
785
+ throw new Error(`Unexpected status code: ${response.status}`);
786
+ }
787
+ } catch (error) {
788
+ let errorMessage = 'Unknown error';
789
+ if (error instanceof Error && 'response' in error) {
790
+ const axiosError = error as any;
791
+ errorMessage = `Status: ${axiosError.response?.status}, Data: ${JSON.stringify(axiosError.response?.data)}`;
792
+ } else if (error instanceof Error) {
793
+ errorMessage = error.message;
794
+ } else {
795
+ errorMessage = String(error);
796
+ }
797
+ throw new McpError(
798
+ ErrorCode.InternalError,
799
+ `Failed to create test run: ${errorMessage}`
800
+ );
801
+ }
802
+ }
803
+
804
+ private async getTestRun(args: any) {
805
+ const { test_run_key } = args;
806
+
807
+ try {
808
+ const response = await this.axiosInstance.get(`/rest/atm/1.0/testrun/${test_run_key}`);
809
+
810
+ if (response.status === 200) {
811
+ return {
812
+ content: [
813
+ {
814
+ type: 'text',
815
+ text: JSON.stringify(response.data, null, 2),
816
+ },
817
+ ],
818
+ };
819
+ } else {
820
+ throw new Error(`Failed to retrieve test run: ${response.status}`);
821
+ }
822
+ } catch (error) {
823
+ let errorMessage = 'Unknown error';
824
+ if (error instanceof Error && 'response' in error) {
825
+ const axiosError = error as any;
826
+ if (axiosError.response?.status === 404) {
827
+ errorMessage = `Test run ${test_run_key} not found`;
828
+ } else {
829
+ errorMessage = `Status: ${axiosError.response?.status}, Data: ${JSON.stringify(axiosError.response?.data)}`;
830
+ }
831
+ } else if (error instanceof Error) {
832
+ errorMessage = error.message;
833
+ } else {
834
+ errorMessage = String(error);
835
+ }
836
+ throw new McpError(
837
+ ErrorCode.InternalError,
838
+ `Failed to get test run: ${errorMessage}`
839
+ );
840
+ }
841
+ }
842
+
663
843
  async run() {
664
844
  const transport = new StdioServerTransport();
665
845
  await this.server.connect(transport);