zephyr-scale-mcp-server 0.0.5 → 0.1.1

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,75 @@ 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
+ test_plan_key: {
272
+ type: 'string',
273
+ description: 'Test plan key to link this test run to (optional)',
274
+ },
275
+ folder: {
276
+ type: 'string',
277
+ description: 'Folder path (optional)',
278
+ },
279
+ planned_start_date: {
280
+ type: 'string',
281
+ description: 'Planned start date in ISO format (optional)',
282
+ },
283
+ planned_end_date: {
284
+ type: 'string',
285
+ description: 'Planned end date in ISO format (optional)',
286
+ },
287
+ description: {
288
+ type: 'string',
289
+ description: 'Test run description (optional)',
290
+ },
291
+ owner: {
292
+ type: 'string',
293
+ description: 'Test run owner (optional)',
294
+ },
295
+ environment: {
296
+ type: 'string',
297
+ description: 'Test environment (optional)',
298
+ },
299
+ custom_fields: {
300
+ type: 'object',
301
+ description: 'Custom fields object (optional)',
302
+ },
303
+ },
304
+ required: ['project_key', 'name'],
305
+ },
306
+ },
307
+ {
308
+ name: 'get_test_run',
309
+ description: 'Get detailed information about a specific test run',
310
+ inputSchema: {
311
+ type: 'object',
312
+ properties: {
313
+ test_run_key: {
314
+ type: 'string',
315
+ description: 'Test run key (e.g., PROJ-R123)',
316
+ },
317
+ },
318
+ required: ['test_run_key'],
319
+ },
320
+ },
252
321
  ],
253
322
  }));
254
323
  this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
@@ -268,6 +337,10 @@ class ZephyrServer {
268
337
  return await this.getTestRunCases(request.params.arguments);
269
338
  case 'delete_test_case':
270
339
  return await this.deleteTestCase(request.params.arguments);
340
+ case 'create_test_run':
341
+ return await this.createTestRun(request.params.arguments);
342
+ case 'get_test_run':
343
+ return await this.getTestRun(request.params.arguments);
271
344
  default:
272
345
  throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${request.params.name}`);
273
346
  }
@@ -605,6 +678,110 @@ class ZephyrServer {
605
678
  throw new McpError(ErrorCode.InternalError, `Failed to delete test case: ${errorMessage}`);
606
679
  }
607
680
  }
681
+ async createTestRun(args) {
682
+ const { project_key, name, test_case_keys, test_plan_key, folder, planned_start_date, planned_end_date, description, owner, environment, custom_fields } = args;
683
+ // Build the basic payload
684
+ const payload = {
685
+ projectKey: project_key,
686
+ name: name,
687
+ };
688
+ // Add optional fields
689
+ if (test_case_keys && test_case_keys.length > 0) {
690
+ payload.items = test_case_keys.map((testCaseKey) => ({
691
+ testCaseKey: testCaseKey
692
+ }));
693
+ }
694
+ if (folder)
695
+ payload.folder = folder;
696
+ if (planned_start_date)
697
+ payload.plannedStartDate = planned_start_date;
698
+ if (planned_end_date)
699
+ payload.plannedEndDate = planned_end_date;
700
+ if (description)
701
+ payload.description = description;
702
+ if (owner)
703
+ payload.owner = owner;
704
+ if (environment)
705
+ payload.environment = environment;
706
+ if (custom_fields)
707
+ payload.customFields = custom_fields;
708
+ if (test_plan_key)
709
+ payload.testPlanKey = test_plan_key;
710
+ try {
711
+ const response = await this.axiosInstance.post('/rest/atm/1.0/testrun', payload);
712
+ if (response.status === 201) {
713
+ const testRunKey = response.data.key || 'Unknown';
714
+ return {
715
+ content: [
716
+ {
717
+ type: 'text',
718
+ text: `✅ Test run created successfully: ${testRunKey}\n${JSON.stringify({
719
+ key: testRunKey,
720
+ name: name,
721
+ testCaseCount: test_case_keys?.length || 0,
722
+ environment: environment || 'Not specified'
723
+ }, null, 2)}`,
724
+ },
725
+ ],
726
+ };
727
+ }
728
+ else {
729
+ throw new Error(`Unexpected status code: ${response.status}`);
730
+ }
731
+ }
732
+ catch (error) {
733
+ let errorMessage = 'Unknown error';
734
+ if (error instanceof Error && 'response' in error) {
735
+ const axiosError = error;
736
+ errorMessage = `Status: ${axiosError.response?.status}, Data: ${JSON.stringify(axiosError.response?.data)}`;
737
+ }
738
+ else if (error instanceof Error) {
739
+ errorMessage = error.message;
740
+ }
741
+ else {
742
+ errorMessage = String(error);
743
+ }
744
+ throw new McpError(ErrorCode.InternalError, `Failed to create test run: ${errorMessage}`);
745
+ }
746
+ }
747
+ async getTestRun(args) {
748
+ const { test_run_key } = args;
749
+ try {
750
+ const response = await this.axiosInstance.get(`/rest/atm/1.0/testrun/${test_run_key}`);
751
+ if (response.status === 200) {
752
+ return {
753
+ content: [
754
+ {
755
+ type: 'text',
756
+ text: JSON.stringify(response.data, null, 2),
757
+ },
758
+ ],
759
+ };
760
+ }
761
+ else {
762
+ throw new Error(`Failed to retrieve test run: ${response.status}`);
763
+ }
764
+ }
765
+ catch (error) {
766
+ let errorMessage = 'Unknown error';
767
+ if (error instanceof Error && 'response' in error) {
768
+ const axiosError = error;
769
+ if (axiosError.response?.status === 404) {
770
+ errorMessage = `Test run ${test_run_key} not found`;
771
+ }
772
+ else {
773
+ errorMessage = `Status: ${axiosError.response?.status}, Data: ${JSON.stringify(axiosError.response?.data)}`;
774
+ }
775
+ }
776
+ else if (error instanceof Error) {
777
+ errorMessage = error.message;
778
+ }
779
+ else {
780
+ errorMessage = String(error);
781
+ }
782
+ throw new McpError(ErrorCode.InternalError, `Failed to get test run: ${errorMessage}`);
783
+ }
784
+ }
608
785
  async run() {
609
786
  const transport = new StdioServerTransport();
610
787
  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.1",
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,75 @@ 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
+ test_plan_key: {
289
+ type: 'string',
290
+ description: 'Test plan key to link this test run to (optional)',
291
+ },
292
+ folder: {
293
+ type: 'string',
294
+ description: 'Folder path (optional)',
295
+ },
296
+ planned_start_date: {
297
+ type: 'string',
298
+ description: 'Planned start date in ISO format (optional)',
299
+ },
300
+ planned_end_date: {
301
+ type: 'string',
302
+ description: 'Planned end date in ISO format (optional)',
303
+ },
304
+ description: {
305
+ type: 'string',
306
+ description: 'Test run description (optional)',
307
+ },
308
+ owner: {
309
+ type: 'string',
310
+ description: 'Test run owner (optional)',
311
+ },
312
+ environment: {
313
+ type: 'string',
314
+ description: 'Test environment (optional)',
315
+ },
316
+ custom_fields: {
317
+ type: 'object',
318
+ description: 'Custom fields object (optional)',
319
+ },
320
+ },
321
+ required: ['project_key', 'name'],
322
+ },
323
+ },
324
+ {
325
+ name: 'get_test_run',
326
+ description: 'Get detailed information about a specific test run',
327
+ inputSchema: {
328
+ type: 'object',
329
+ properties: {
330
+ test_run_key: {
331
+ type: 'string',
332
+ description: 'Test run key (e.g., PROJ-R123)',
333
+ },
334
+ },
335
+ required: ['test_run_key'],
336
+ },
337
+ },
269
338
  ],
270
339
  }));
271
340
 
@@ -286,6 +355,10 @@ class ZephyrServer {
286
355
  return await this.getTestRunCases(request.params.arguments);
287
356
  case 'delete_test_case':
288
357
  return await this.deleteTestCase(request.params.arguments);
358
+ case 'create_test_run':
359
+ return await this.createTestRun(request.params.arguments);
360
+ case 'get_test_run':
361
+ return await this.getTestRun(request.params.arguments);
289
362
  default:
290
363
  throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${request.params.name}`);
291
364
  }
@@ -660,6 +733,119 @@ class ZephyrServer {
660
733
  }
661
734
  }
662
735
 
736
+ private async createTestRun(args: any) {
737
+ const {
738
+ project_key,
739
+ name,
740
+ test_case_keys,
741
+ test_plan_key,
742
+ folder,
743
+ planned_start_date,
744
+ planned_end_date,
745
+ description,
746
+ owner,
747
+ environment,
748
+ custom_fields
749
+ } = args;
750
+
751
+ // Build the basic payload
752
+ const payload: any = {
753
+ projectKey: project_key,
754
+ name: name,
755
+ };
756
+
757
+ // Add optional fields
758
+ if (test_case_keys && test_case_keys.length > 0) {
759
+ payload.items = test_case_keys.map((testCaseKey: string) => ({
760
+ testCaseKey: testCaseKey
761
+ }));
762
+ }
763
+ if (folder) payload.folder = folder;
764
+ if (planned_start_date) payload.plannedStartDate = planned_start_date;
765
+ if (planned_end_date) payload.plannedEndDate = planned_end_date;
766
+ if (description) payload.description = description;
767
+ if (owner) payload.owner = owner;
768
+ if (environment) payload.environment = environment;
769
+ if (custom_fields) payload.customFields = custom_fields;
770
+ if (test_plan_key) payload.testPlanKey = test_plan_key;
771
+
772
+ try {
773
+ const response = await this.axiosInstance.post('/rest/atm/1.0/testrun', payload);
774
+
775
+ if (response.status === 201) {
776
+ const testRunKey = response.data.key || 'Unknown';
777
+ return {
778
+ content: [
779
+ {
780
+ type: 'text',
781
+ text: `✅ Test run created successfully: ${testRunKey}\n${JSON.stringify({
782
+ key: testRunKey,
783
+ name: name,
784
+ testCaseCount: test_case_keys?.length || 0,
785
+ environment: environment || 'Not specified'
786
+ }, null, 2)}`,
787
+ },
788
+ ],
789
+ };
790
+ } else {
791
+ throw new Error(`Unexpected status code: ${response.status}`);
792
+ }
793
+ } catch (error) {
794
+ let errorMessage = 'Unknown error';
795
+ if (error instanceof Error && 'response' in error) {
796
+ const axiosError = error as any;
797
+ errorMessage = `Status: ${axiosError.response?.status}, Data: ${JSON.stringify(axiosError.response?.data)}`;
798
+ } else if (error instanceof Error) {
799
+ errorMessage = error.message;
800
+ } else {
801
+ errorMessage = String(error);
802
+ }
803
+ throw new McpError(
804
+ ErrorCode.InternalError,
805
+ `Failed to create test run: ${errorMessage}`
806
+ );
807
+ }
808
+ }
809
+
810
+ private async getTestRun(args: any) {
811
+ const { test_run_key } = args;
812
+
813
+ try {
814
+ const response = await this.axiosInstance.get(`/rest/atm/1.0/testrun/${test_run_key}`);
815
+
816
+ if (response.status === 200) {
817
+ return {
818
+ content: [
819
+ {
820
+ type: 'text',
821
+ text: JSON.stringify(response.data, null, 2),
822
+ },
823
+ ],
824
+ };
825
+ } else {
826
+ throw new Error(`Failed to retrieve test run: ${response.status}`);
827
+ }
828
+ } catch (error) {
829
+ let errorMessage = 'Unknown error';
830
+ if (error instanceof Error && 'response' in error) {
831
+ const axiosError = error as any;
832
+ if (axiosError.response?.status === 404) {
833
+ errorMessage = `Test run ${test_run_key} not found`;
834
+ } else {
835
+ errorMessage = `Status: ${axiosError.response?.status}, Data: ${JSON.stringify(axiosError.response?.data)}`;
836
+ }
837
+ } else if (error instanceof Error) {
838
+ errorMessage = error.message;
839
+ } else {
840
+ errorMessage = String(error);
841
+ }
842
+ throw new McpError(
843
+ ErrorCode.InternalError,
844
+ `Failed to get test run: ${errorMessage}`
845
+ );
846
+ }
847
+ }
848
+
663
849
  async run() {
664
850
  const transport = new StdioServerTransport();
665
851
  await this.server.connect(transport);