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 +18 -0
- package/build/index.js +171 -0
- package/package.json +1 -1
- package/src/index.ts +180 -0
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
|
|
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);
|