zephyr-scale-mcp-server 0.0.4 → 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
@@ -4,35 +4,23 @@ Model Context Protocol server for Zephyr Scale test management. Create, read, an
4
4
 
5
5
  ## Quick Start
6
6
 
7
- ### Option 1: Install from npm
8
- ```bash
9
- npm install -g zephyr-scale-mcp-server
10
- ```
7
+ ### Option 1: Using npx (Recommended - No installation required)
8
+ Just configure your MCP client with the npx command below.
11
9
 
12
- ### Option 2: Build locally
10
+ ### Option 2: Install from npm
13
11
  ```bash
14
- # Clone and build
15
- git clone <repository-url>
16
- cd zephyr-scale-mcp-server
17
- npm install
18
- npm run build
19
-
20
- # Configure environment
21
- export ZEPHYR_API_KEY="your-api-token"
22
- export ZEPHYR_BASE_URL="https://your-company.atlassian.net"
23
-
24
- # Test the server
25
- node build/index.js
12
+ npm install -g zephyr-scale-mcp-server
26
13
  ```
27
14
 
28
15
  ## MCP Configuration
29
16
 
30
- ### For npm installation:
17
+ ### Option 1: Using npx (Recommended - No installation required)
31
18
  ```json
32
19
  {
33
20
  "mcpServers": {
34
21
  "zephyr-server": {
35
- "command": "zephyr-scale-mcp",
22
+ "command": "npx",
23
+ "args": ["zephyr-scale-mcp-server@latest"],
36
24
  "env": {
37
25
  "ZEPHYR_API_KEY": "your-api-token",
38
26
  "ZEPHYR_BASE_URL": "https://your-company.atlassian.net"
@@ -42,13 +30,18 @@ node build/index.js
42
30
  }
43
31
  ```
44
32
 
45
- ### For local build:
33
+ ### Option 2: Using global npm installation
34
+ First install the package globally:
35
+ ```bash
36
+ npm install -g zephyr-scale-mcp-server
37
+ ```
38
+
39
+ Then configure:
46
40
  ```json
47
41
  {
48
42
  "mcpServers": {
49
43
  "zephyr-server": {
50
- "command": "node",
51
- "args": ["/path/to/zephyr-scale-mcp-server/build/index.js"],
44
+ "command": "zephyr-scale-mcp",
52
45
  "env": {
53
46
  "ZEPHYR_API_KEY": "your-api-token",
54
47
  "ZEPHYR_BASE_URL": "https://your-company.atlassian.net"
@@ -60,13 +53,15 @@ node build/index.js
60
53
 
61
54
  ## Available Tools
62
55
 
63
- - `create_test_case` - Create STEP_BY_STEP or PLAIN_TEXT test cases
64
- - `create_test_case_with_bdd` - Create BDD/Gherkin test cases
65
- - `get_test_case` - Get test case details
66
- - `update_test_case_bdd` - Update BDD test cases
56
+ - `get_test_case` - Get detailed information about a specific test case
57
+ - `create_test_case` - Create a new test case with STEP_BY_STEP or PLAIN_TEXT content
58
+ - `create_test_case_with_bdd` - Create a new test case with BDD content
59
+ - `update_test_case_bdd` - Update an existing test case with BDD content
67
60
  - `delete_test_case` - Delete a specific test case
68
- - `create_folder` - Create test case folders
69
- - `get_test_run_cases` - Get test cases from test runs
61
+ - `create_folder` - Create a new folder in Zephyr Scale
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
70
65
 
71
66
  ## Examples
72
67
 
@@ -110,37 +105,24 @@ node build/index.js
110
105
  }
111
106
  ```
112
107
 
113
- ## Verification
114
-
115
- After installation, verify the package works:
116
-
117
- ```bash
118
- # Check installation
119
- which zephyr-scale-mcp
120
- # Should show: /opt/homebrew/bin/zephyr-scale-mcp (or similar path)
121
-
122
- # Test with environment variables
123
- export ZEPHYR_API_KEY="your-api-token"
124
- export ZEPHYR_BASE_URL="https://your-company.atlassian.net"
125
- zephyr-scale-mcp
126
- # Should start the MCP server (Ctrl+C to exit)
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
+ }
127
117
  ```
128
118
 
129
- ### MCP Integration Test
130
-
131
- Once configured in your MCP client, test with:
132
-
119
+ ### Get Test Run
133
120
  ```json
134
121
  {
135
- "tool": "get_test_case",
136
- "arguments": {
137
- "test_case_key": "PROJ-T123"
138
- }
122
+ "test_run_key": "PROJ-R456"
139
123
  }
140
124
  ```
141
125
 
142
- Expected response includes test case details, BDD scripts, and metadata.
143
-
144
126
  ## Authentication
145
127
 
146
128
  Get your API token from:
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.4",
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);