zephyr-scale-mcp-server 0.2.9 → 0.3.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/build/index.js +3 -1
- package/build/tool-handlers.js +65 -0
- package/build/tool-schemas.js +26 -0
- package/package.json +1 -1
- package/src/index.ts +3 -1
- package/src/tool-handlers.ts +66 -0
- package/src/tool-schemas.ts +26 -0
- package/src/types.ts +7 -0
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.
|
|
17
|
+
version: '0.3.0',
|
|
18
18
|
}, {
|
|
19
19
|
capabilities: {
|
|
20
20
|
tools: {},
|
|
@@ -63,6 +63,8 @@ class ZephyrServer {
|
|
|
63
63
|
return await this.toolHandlers.getTestExecution(args);
|
|
64
64
|
case 'search_test_cases_by_folder':
|
|
65
65
|
return await this.toolHandlers.searchTestCasesByFolder(args);
|
|
66
|
+
case 'search_test_runs':
|
|
67
|
+
return await this.toolHandlers.searchTestRuns(args);
|
|
66
68
|
case 'add_test_cases_to_run':
|
|
67
69
|
return await this.toolHandlers.addTestCasesToRun(args);
|
|
68
70
|
default:
|
package/build/tool-handlers.js
CHANGED
|
@@ -490,6 +490,71 @@ export class ZephyrToolHandlers {
|
|
|
490
490
|
throw new McpError(ErrorCode.InternalError, `Failed to search test cases by folder: ${errorMessage}`);
|
|
491
491
|
}
|
|
492
492
|
}
|
|
493
|
+
async searchTestRuns(args) {
|
|
494
|
+
const { project_key, folder, max_results = 200, fields } = args;
|
|
495
|
+
// Build query string per Zephyr Scale API docs
|
|
496
|
+
const queryParts = [];
|
|
497
|
+
if (project_key)
|
|
498
|
+
queryParts.push(`projectKey = "${project_key}"`);
|
|
499
|
+
if (folder)
|
|
500
|
+
queryParts.push(`folder = "${folder}"`);
|
|
501
|
+
if (queryParts.length === 0) {
|
|
502
|
+
throw new McpError(ErrorCode.InvalidParams, 'At least one of project_key or folder must be provided.');
|
|
503
|
+
}
|
|
504
|
+
const query = queryParts.join(' AND ');
|
|
505
|
+
const params = { query, maxResults: max_results };
|
|
506
|
+
if (fields)
|
|
507
|
+
params.fields = fields;
|
|
508
|
+
const searchEndpoint = this.jiraConfig.type === 'cloud'
|
|
509
|
+
? '/testruns/search'
|
|
510
|
+
: '/rest/atm/1.0/testrun/search';
|
|
511
|
+
try {
|
|
512
|
+
const response = await this.axiosInstance.get(searchEndpoint, { params });
|
|
513
|
+
let testRuns = [];
|
|
514
|
+
if (Array.isArray(response.data)) {
|
|
515
|
+
testRuns = response.data;
|
|
516
|
+
}
|
|
517
|
+
else if (response.data.values && Array.isArray(response.data.values)) {
|
|
518
|
+
testRuns = response.data.values;
|
|
519
|
+
}
|
|
520
|
+
else if (response.data.results && Array.isArray(response.data.results)) {
|
|
521
|
+
testRuns = response.data.results;
|
|
522
|
+
}
|
|
523
|
+
return {
|
|
524
|
+
content: [
|
|
525
|
+
{
|
|
526
|
+
type: 'text',
|
|
527
|
+
text: `✅ Found ${testRuns.length} test run(s) matching query "${query}":\n${JSON.stringify({
|
|
528
|
+
query,
|
|
529
|
+
totalCount: testRuns.length,
|
|
530
|
+
testRuns: testRuns.map((tr) => ({
|
|
531
|
+
key: tr.key,
|
|
532
|
+
name: tr.name,
|
|
533
|
+
status: tr.status,
|
|
534
|
+
folder: tr.folder,
|
|
535
|
+
testCaseCount: tr.testCaseCount,
|
|
536
|
+
issueKey: tr.issueKey,
|
|
537
|
+
}))
|
|
538
|
+
}, null, 2)}`,
|
|
539
|
+
},
|
|
540
|
+
],
|
|
541
|
+
};
|
|
542
|
+
}
|
|
543
|
+
catch (error) {
|
|
544
|
+
let errorMessage = 'Unknown error';
|
|
545
|
+
if (error instanceof Error && 'response' in error) {
|
|
546
|
+
const axiosError = error;
|
|
547
|
+
errorMessage = `Status: ${axiosError.response?.status}, Data: ${JSON.stringify(axiosError.response?.data)}`;
|
|
548
|
+
}
|
|
549
|
+
else if (error instanceof Error) {
|
|
550
|
+
errorMessage = error.message;
|
|
551
|
+
}
|
|
552
|
+
else {
|
|
553
|
+
errorMessage = String(error);
|
|
554
|
+
}
|
|
555
|
+
throw new McpError(ErrorCode.InternalError, `Failed to search test runs: ${errorMessage}`);
|
|
556
|
+
}
|
|
557
|
+
}
|
|
493
558
|
async addTestCasesToRun(args) {
|
|
494
559
|
const { test_run_key, test_case_keys } = args;
|
|
495
560
|
try {
|
package/build/tool-schemas.js
CHANGED
|
@@ -347,6 +347,32 @@ export const toolSchemas = [
|
|
|
347
347
|
required: ['project_key', 'folder_path'],
|
|
348
348
|
},
|
|
349
349
|
},
|
|
350
|
+
{
|
|
351
|
+
name: 'search_test_runs',
|
|
352
|
+
description: 'Search for test runs using a query. Supports filtering by projectKey and/or folder path.',
|
|
353
|
+
inputSchema: {
|
|
354
|
+
type: 'object',
|
|
355
|
+
properties: {
|
|
356
|
+
project_key: {
|
|
357
|
+
type: 'string',
|
|
358
|
+
description: 'Project key to filter by (e.g., "PROJ"). Can be a single key or omitted if using folder only.',
|
|
359
|
+
},
|
|
360
|
+
folder: {
|
|
361
|
+
type: 'string',
|
|
362
|
+
description: 'Folder path to filter test runs by (e.g., "/MyFolder/SubFolder")',
|
|
363
|
+
},
|
|
364
|
+
max_results: {
|
|
365
|
+
type: 'number',
|
|
366
|
+
description: 'Maximum number of results to return (optional, default 200)',
|
|
367
|
+
default: 200,
|
|
368
|
+
},
|
|
369
|
+
fields: {
|
|
370
|
+
type: 'string',
|
|
371
|
+
description: 'Comma-separated list of fields to include in the response (optional, e.g., "key,name,status,folder"). If not set, all fields are returned.',
|
|
372
|
+
},
|
|
373
|
+
},
|
|
374
|
+
},
|
|
375
|
+
},
|
|
350
376
|
{
|
|
351
377
|
name: 'add_test_cases_to_run',
|
|
352
378
|
description: 'Add test cases to an existing test run',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zephyr-scale-mcp-server",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.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
|
@@ -25,7 +25,7 @@ class ZephyrServer {
|
|
|
25
25
|
this.server = new Server(
|
|
26
26
|
{
|
|
27
27
|
name: 'zephyr-server',
|
|
28
|
-
version: '0.
|
|
28
|
+
version: '0.3.0',
|
|
29
29
|
},
|
|
30
30
|
{
|
|
31
31
|
capabilities: {
|
|
@@ -83,6 +83,8 @@ class ZephyrServer {
|
|
|
83
83
|
return await this.toolHandlers.getTestExecution(args);
|
|
84
84
|
case 'search_test_cases_by_folder':
|
|
85
85
|
return await this.toolHandlers.searchTestCasesByFolder(args as any);
|
|
86
|
+
case 'search_test_runs':
|
|
87
|
+
return await this.toolHandlers.searchTestRuns(args as any);
|
|
86
88
|
case 'add_test_cases_to_run':
|
|
87
89
|
return await this.toolHandlers.addTestCasesToRun(args as any);
|
|
88
90
|
default:
|
package/src/tool-handlers.ts
CHANGED
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
TestRunArgs,
|
|
8
8
|
SearchTestCasesArgs,
|
|
9
9
|
AddTestCasesToRunArgs,
|
|
10
|
+
SearchTestRunsArgs,
|
|
10
11
|
JiraConfig
|
|
11
12
|
} from './types.js';
|
|
12
13
|
import { convertToGherkin, customPriorityMapping, priorityMapping } from './utils.js';
|
|
@@ -542,6 +543,71 @@ export class ZephyrToolHandlers {
|
|
|
542
543
|
}
|
|
543
544
|
}
|
|
544
545
|
|
|
546
|
+
async searchTestRuns(args: SearchTestRunsArgs) {
|
|
547
|
+
const { project_key, folder, max_results = 200, fields } = args;
|
|
548
|
+
|
|
549
|
+
// Build query string per Zephyr Scale API docs
|
|
550
|
+
const queryParts: string[] = [];
|
|
551
|
+
if (project_key) queryParts.push(`projectKey = "${project_key}"`);
|
|
552
|
+
if (folder) queryParts.push(`folder = "${folder}"`);
|
|
553
|
+
|
|
554
|
+
if (queryParts.length === 0) {
|
|
555
|
+
throw new McpError(ErrorCode.InvalidParams, 'At least one of project_key or folder must be provided.');
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
const query = queryParts.join(' AND ');
|
|
559
|
+
const params: Record<string, any> = { query, maxResults: max_results };
|
|
560
|
+
if (fields) params.fields = fields;
|
|
561
|
+
|
|
562
|
+
const searchEndpoint = this.jiraConfig.type === 'cloud'
|
|
563
|
+
? '/testruns/search'
|
|
564
|
+
: '/rest/atm/1.0/testrun/search';
|
|
565
|
+
|
|
566
|
+
try {
|
|
567
|
+
const response = await this.axiosInstance.get(searchEndpoint, { params });
|
|
568
|
+
|
|
569
|
+
let testRuns: any[] = [];
|
|
570
|
+
if (Array.isArray(response.data)) {
|
|
571
|
+
testRuns = response.data;
|
|
572
|
+
} else if (response.data.values && Array.isArray(response.data.values)) {
|
|
573
|
+
testRuns = response.data.values;
|
|
574
|
+
} else if (response.data.results && Array.isArray(response.data.results)) {
|
|
575
|
+
testRuns = response.data.results;
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
return {
|
|
579
|
+
content: [
|
|
580
|
+
{
|
|
581
|
+
type: 'text',
|
|
582
|
+
text: `✅ Found ${testRuns.length} test run(s) matching query "${query}":\n${JSON.stringify({
|
|
583
|
+
query,
|
|
584
|
+
totalCount: testRuns.length,
|
|
585
|
+
testRuns: testRuns.map((tr: any) => ({
|
|
586
|
+
key: tr.key,
|
|
587
|
+
name: tr.name,
|
|
588
|
+
status: tr.status,
|
|
589
|
+
folder: tr.folder,
|
|
590
|
+
testCaseCount: tr.testCaseCount,
|
|
591
|
+
issueKey: tr.issueKey,
|
|
592
|
+
}))
|
|
593
|
+
}, null, 2)}`,
|
|
594
|
+
},
|
|
595
|
+
],
|
|
596
|
+
};
|
|
597
|
+
} catch (error) {
|
|
598
|
+
let errorMessage = 'Unknown error';
|
|
599
|
+
if (error instanceof Error && 'response' in error) {
|
|
600
|
+
const axiosError = error as any;
|
|
601
|
+
errorMessage = `Status: ${axiosError.response?.status}, Data: ${JSON.stringify(axiosError.response?.data)}`;
|
|
602
|
+
} else if (error instanceof Error) {
|
|
603
|
+
errorMessage = error.message;
|
|
604
|
+
} else {
|
|
605
|
+
errorMessage = String(error);
|
|
606
|
+
}
|
|
607
|
+
throw new McpError(ErrorCode.InternalError, `Failed to search test runs: ${errorMessage}`);
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
|
|
545
611
|
async addTestCasesToRun(args: AddTestCasesToRunArgs) {
|
|
546
612
|
const { test_run_key, test_case_keys } = args;
|
|
547
613
|
|
package/src/tool-schemas.ts
CHANGED
|
@@ -347,6 +347,32 @@ export const toolSchemas = [
|
|
|
347
347
|
required: ['project_key', 'folder_path'],
|
|
348
348
|
},
|
|
349
349
|
},
|
|
350
|
+
{
|
|
351
|
+
name: 'search_test_runs',
|
|
352
|
+
description: 'Search for test runs using a query. Supports filtering by projectKey and/or folder path.',
|
|
353
|
+
inputSchema: {
|
|
354
|
+
type: 'object',
|
|
355
|
+
properties: {
|
|
356
|
+
project_key: {
|
|
357
|
+
type: 'string',
|
|
358
|
+
description: 'Project key to filter by (e.g., "PROJ"). Can be a single key or omitted if using folder only.',
|
|
359
|
+
},
|
|
360
|
+
folder: {
|
|
361
|
+
type: 'string',
|
|
362
|
+
description: 'Folder path to filter test runs by (e.g., "/MyFolder/SubFolder")',
|
|
363
|
+
},
|
|
364
|
+
max_results: {
|
|
365
|
+
type: 'number',
|
|
366
|
+
description: 'Maximum number of results to return (optional, default 200)',
|
|
367
|
+
default: 200,
|
|
368
|
+
},
|
|
369
|
+
fields: {
|
|
370
|
+
type: 'string',
|
|
371
|
+
description: 'Comma-separated list of fields to include in the response (optional, e.g., "key,name,status,folder"). If not set, all fields are returned.',
|
|
372
|
+
},
|
|
373
|
+
},
|
|
374
|
+
},
|
|
375
|
+
},
|
|
350
376
|
{
|
|
351
377
|
name: 'add_test_cases_to_run',
|
|
352
378
|
description: 'Add test cases to an existing test run',
|
package/src/types.ts
CHANGED
|
@@ -79,6 +79,13 @@ export interface AddTestCasesToRunArgs {
|
|
|
79
79
|
test_case_keys: string[];
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
+
export interface SearchTestRunsArgs {
|
|
83
|
+
project_key?: string;
|
|
84
|
+
folder?: string;
|
|
85
|
+
max_results?: number;
|
|
86
|
+
fields?: string;
|
|
87
|
+
}
|
|
88
|
+
|
|
82
89
|
export type JiraType = 'cloud' | 'datacenter';
|
|
83
90
|
|
|
84
91
|
export interface ApiEndpoints {
|