@smartbear/mcp 0.20.0 → 0.22.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 +23 -6
- package/dist/common/register-clients.js +2 -0
- package/dist/common/server.js +1 -4
- package/dist/package.json.js +1 -1
- package/dist/qtm4j/client.js +109 -0
- package/dist/qtm4j/config/constants.js +169 -0
- package/dist/qtm4j/config/field-resolution.types.js +34 -0
- package/dist/qtm4j/http/api-client.js +123 -0
- package/dist/qtm4j/http/auth-service.js +23 -0
- package/dist/qtm4j/resolver/cache/cache.js +52 -0
- package/dist/qtm4j/resolver/resolver-registry.js +70 -0
- package/dist/qtm4j/resolver/resolvers/common-attribute-resolver.js +56 -0
- package/dist/qtm4j/resolver/resolvers/component-resolver.js +56 -0
- package/dist/qtm4j/resolver/resolvers/label-resolver.js +56 -0
- package/dist/qtm4j/resolver/resolvers/resolver.js +6 -0
- package/dist/qtm4j/resolver/resolvers/test-case-uid-resolver.js +28 -0
- package/dist/qtm4j/schema/get-test-case.schema.js +153 -0
- package/dist/qtm4j/schema/get-test-steps.schema.js +74 -0
- package/dist/qtm4j/schema/project.schema.js +43 -0
- package/dist/qtm4j/schema/test-case.schema.js +41 -0
- package/dist/qtm4j/schema/update-test-case.schema.js +45 -0
- package/dist/qtm4j/tool/project/get-projects.js +111 -0
- package/dist/qtm4j/tool/project/set-project-context.js +99 -0
- package/dist/qtm4j/tool/test-case/create-test-case.js +113 -0
- package/dist/qtm4j/tool/test-case/get-test-cases.js +295 -0
- package/dist/qtm4j/tool/test-case/get-test-steps.js +111 -0
- package/dist/qtm4j/tool/test-case/update-test-case.js +158 -0
- package/package.json +5 -4
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { Tool } from "../../../common/tools.js";
|
|
2
|
+
import { TOOL_NAMES, RESPONSE_FIELDS, PAGINATION, ENDPOINTS } from "../../config/constants.js";
|
|
3
|
+
import { GetProjectsResponse, GetProjectsBody } from "../../schema/project.schema.js";
|
|
4
|
+
class GetProjects extends Tool {
|
|
5
|
+
specification = {
|
|
6
|
+
title: TOOL_NAMES.GET_PROJECTS.TITLE,
|
|
7
|
+
summary: TOOL_NAMES.GET_PROJECTS.SUMMARY,
|
|
8
|
+
readOnly: true,
|
|
9
|
+
idempotent: true,
|
|
10
|
+
inputSchema: GetProjectsBody,
|
|
11
|
+
outputSchema: GetProjectsResponse,
|
|
12
|
+
purpose: "Retrieve project information including IDs, keys, and names from QTM4J. Useful for discovering available projects, validating project access, and getting project metadata.",
|
|
13
|
+
useCases: [
|
|
14
|
+
"Discover all projects available in QTM4J instance",
|
|
15
|
+
"Get project IDs and keys for reference in other operations",
|
|
16
|
+
"Find specific projects by ID",
|
|
17
|
+
"Search projects by text in project key or name",
|
|
18
|
+
"Filter projects by QMetry integration status",
|
|
19
|
+
"List projects with pagination for large QTM4J instances",
|
|
20
|
+
"Retrieve complete project details (ID, key, name, avatarUrl, projectTypeKey, qmetryEnabled, favorite)",
|
|
21
|
+
"Validate project access and permissions",
|
|
22
|
+
"Browse available projects before performing other operations"
|
|
23
|
+
],
|
|
24
|
+
examples: [
|
|
25
|
+
{
|
|
26
|
+
description: "Get all projects (default pagination - first 100)",
|
|
27
|
+
parameters: {},
|
|
28
|
+
expectedOutput: "List of all projects with IDs, keys, and names (first 100 projects)"
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
description: "Get the first 10 projects",
|
|
32
|
+
parameters: {
|
|
33
|
+
maxResults: 10
|
|
34
|
+
},
|
|
35
|
+
expectedOutput: "List of first 10 projects with their details"
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
description: "Get a specific project by ID",
|
|
39
|
+
parameters: {
|
|
40
|
+
projectId: 1e4
|
|
41
|
+
},
|
|
42
|
+
expectedOutput: "Single project with ID 10000 including key, name, and QMetry status"
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
description: "Search projects by text in project key or name",
|
|
46
|
+
parameters: {
|
|
47
|
+
search: "SCRUM"
|
|
48
|
+
},
|
|
49
|
+
expectedOutput: "Projects matching 'SCRUM' search text in their project keys or names"
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
description: "Get only QMetry-enabled projects",
|
|
53
|
+
parameters: {
|
|
54
|
+
qmetryEnabled: true
|
|
55
|
+
},
|
|
56
|
+
expectedOutput: "List of projects that have QMetry integration enabled"
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
description: "Get projects with custom pagination (page 2)",
|
|
60
|
+
parameters: {
|
|
61
|
+
startAt: 50,
|
|
62
|
+
maxResults: 50
|
|
63
|
+
},
|
|
64
|
+
expectedOutput: "Second page of projects (items 51-100) with their details"
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
description: "Search QMetry-enabled projects by text",
|
|
68
|
+
parameters: {
|
|
69
|
+
search: "TEST",
|
|
70
|
+
qmetryEnabled: true
|
|
71
|
+
},
|
|
72
|
+
expectedOutput: "QMetry-enabled projects containing 'TEST' in their project keys"
|
|
73
|
+
}
|
|
74
|
+
],
|
|
75
|
+
hints: [
|
|
76
|
+
"Project IDs are numeric (e.g., 10000), project keys are strings (e.g., 'SCRUM')",
|
|
77
|
+
"Use 'projectId' parameter to filter by a specific project ID",
|
|
78
|
+
"Use 'search' parameter to search by text in project key or project name",
|
|
79
|
+
"Use 'qmetryEnabled' parameter to filter projects by QMetry integration status",
|
|
80
|
+
"Response contains complete project details: id, key, name, favorite, avatarUrl, projectTypeKey, qmetryEnabled",
|
|
81
|
+
"Pagination: startAt is zero-indexed, maxResults max is 100, default is 100",
|
|
82
|
+
"Default (no parameters) returns first 100 projects",
|
|
83
|
+
"Use 'isLast' in response to check if more pages are available",
|
|
84
|
+
"To get next page: increment startAt by maxResults (0 → 100 → 200)",
|
|
85
|
+
"Use 'total' in response for total count of matching projects"
|
|
86
|
+
],
|
|
87
|
+
outputDescription: "JSON object containing paginated list of projects with IDs, keys, names, and QMetry status, along with pagination metadata"
|
|
88
|
+
};
|
|
89
|
+
handle = async (args) => {
|
|
90
|
+
const input = GetProjectsBody.parse(args);
|
|
91
|
+
const body = {
|
|
92
|
+
[RESPONSE_FIELDS.START_AT]: input.startAt ?? PAGINATION.DEFAULT_START_AT,
|
|
93
|
+
[RESPONSE_FIELDS.MAX_RESULTS]: input.maxResults ?? PAGINATION.DEFAULT_MAX_RESULTS_PROJECTS,
|
|
94
|
+
...input.projectId !== void 0 && { projectId: input.projectId },
|
|
95
|
+
...input.search && { search: input.search },
|
|
96
|
+
...input.qmetryEnabled !== void 0 && {
|
|
97
|
+
qmetryEnabled: input.qmetryEnabled
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
const apiClient = this.client.getApiClient();
|
|
101
|
+
const response = await apiClient.post(ENDPOINTS.PROJECTS, body);
|
|
102
|
+
const validatedResponse = GetProjectsResponse.parse(response);
|
|
103
|
+
return {
|
|
104
|
+
structuredContent: validatedResponse,
|
|
105
|
+
content: []
|
|
106
|
+
};
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
export {
|
|
110
|
+
GetProjects
|
|
111
|
+
};
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { Tool, ToolError } from "../../../common/tools.js";
|
|
2
|
+
import { TOOL_NAMES, ENDPOINTS, PAGINATION, RESPONSE_FIELDS } from "../../config/constants.js";
|
|
3
|
+
import { SetProjectContextResponse, SetProjectContextBody, GetProjectsResponse } from "../../schema/project.schema.js";
|
|
4
|
+
const PROJECT_SEARCH_MAX_RESULTS = 10;
|
|
5
|
+
const ERRORS = {
|
|
6
|
+
PROJECT_NOT_FOUND: (key) => `Project with key '${key}' not found. Use the get_projects tool to list available projects.`,
|
|
7
|
+
NO_EXACT_MATCH: (key, candidates) => `No exact match for project key '${key}'. Did you mean one of: ${candidates.join(", ")}? Please provide the exact project key.`
|
|
8
|
+
};
|
|
9
|
+
class SetProjectContext extends Tool {
|
|
10
|
+
specification = {
|
|
11
|
+
title: TOOL_NAMES.SET_PROJECT_CONTEXT.TITLE,
|
|
12
|
+
summary: TOOL_NAMES.SET_PROJECT_CONTEXT.SUMMARY,
|
|
13
|
+
readOnly: false,
|
|
14
|
+
idempotent: true,
|
|
15
|
+
inputSchema: SetProjectContextBody,
|
|
16
|
+
outputSchema: SetProjectContextResponse,
|
|
17
|
+
purpose: "Set the active QTM4J project for the current session. This tool MUST be called before performing ANY project-specific operation. NEVER auto-select a project — if the user has not specified a project key, call get_projects first, show the list to the user, and ask them to choose.",
|
|
18
|
+
useCases: [
|
|
19
|
+
"Set the active project at the start of a new conversation",
|
|
20
|
+
"Switch to a different project mid-conversation",
|
|
21
|
+
"Validate that a project key exists before performing operations",
|
|
22
|
+
"Establish project context required by all project-specific tools"
|
|
23
|
+
],
|
|
24
|
+
examples: [
|
|
25
|
+
{
|
|
26
|
+
description: "Set SCRUM project as active",
|
|
27
|
+
parameters: { projectKey: "SCRUM" },
|
|
28
|
+
expectedOutput: "Project context set to SCRUM (ID: 10000)"
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
description: "Switch to AD project",
|
|
32
|
+
parameters: { projectKey: "AD" },
|
|
33
|
+
expectedOutput: "Project context switched to AD"
|
|
34
|
+
}
|
|
35
|
+
],
|
|
36
|
+
hints: [
|
|
37
|
+
"CRITICAL: This tool MUST be called before ANY project-specific tool.",
|
|
38
|
+
"NEVER auto-select a project. If the user does not specify a project key, call get_projects first, present the list to the user and ask them to choose. Do NOT pick one on their behalf.",
|
|
39
|
+
"The project key must be an exact match (e.g., 'SCRUM', not 'scrum project').",
|
|
40
|
+
"After calling this tool, use the availableFields in the response to map user input via NLP (e.g. user says 'Major' → send 'High', user says 'Critical' → send 'Blocker').",
|
|
41
|
+
"Switching projects clears the cached field metadata of the previous project only.",
|
|
42
|
+
"If this tool is called again in the same session, it resets the context to the new project."
|
|
43
|
+
],
|
|
44
|
+
outputDescription: "JSON object with projectId, projectKey, projectName, confirmation message, and availableFields. availableFields contains priority and status options for NLP mapping in subsequent tool calls."
|
|
45
|
+
};
|
|
46
|
+
handle = async (rawArgs) => {
|
|
47
|
+
const { projectKey } = SetProjectContextBody.parse(rawArgs);
|
|
48
|
+
const apiClient = this.client.getApiClient();
|
|
49
|
+
const body = {
|
|
50
|
+
[RESPONSE_FIELDS.START_AT]: PAGINATION.DEFAULT_START_AT,
|
|
51
|
+
[RESPONSE_FIELDS.MAX_RESULTS]: PROJECT_SEARCH_MAX_RESULTS,
|
|
52
|
+
search: projectKey
|
|
53
|
+
};
|
|
54
|
+
const response = await apiClient.post(ENDPOINTS.PROJECTS, body);
|
|
55
|
+
const parsed = GetProjectsResponse.parse(response);
|
|
56
|
+
if (parsed.data.length === 0) {
|
|
57
|
+
throw new ToolError(ERRORS.PROJECT_NOT_FOUND(projectKey));
|
|
58
|
+
}
|
|
59
|
+
const exactMatch = parsed.data.find(
|
|
60
|
+
(p) => p.key.toLowerCase() === projectKey.toLowerCase()
|
|
61
|
+
);
|
|
62
|
+
if (!exactMatch) {
|
|
63
|
+
const candidates = parsed.data.map((p) => `${p.key} (${p.name})`);
|
|
64
|
+
throw new ToolError(ERRORS.NO_EXACT_MATCH(projectKey, candidates));
|
|
65
|
+
}
|
|
66
|
+
const fieldResolver = this.client.getResolverRegistry();
|
|
67
|
+
try {
|
|
68
|
+
const previousContext = fieldResolver.requireProjectContext();
|
|
69
|
+
if (previousContext.projectKey !== exactMatch.key) {
|
|
70
|
+
fieldResolver.clearProjectCache(previousContext.projectKey);
|
|
71
|
+
}
|
|
72
|
+
} catch {
|
|
73
|
+
}
|
|
74
|
+
fieldResolver.setProjectContext({
|
|
75
|
+
projectId: exactMatch.id,
|
|
76
|
+
projectKey: exactMatch.key,
|
|
77
|
+
projectName: exactMatch.name
|
|
78
|
+
});
|
|
79
|
+
let availableFields = {};
|
|
80
|
+
try {
|
|
81
|
+
availableFields = await fieldResolver.getCommonAttributeResolver().preload(exactMatch.key, exactMatch.id);
|
|
82
|
+
} catch {
|
|
83
|
+
}
|
|
84
|
+
const result = {
|
|
85
|
+
projectId: exactMatch.id,
|
|
86
|
+
projectKey: exactMatch.key,
|
|
87
|
+
projectName: exactMatch.name,
|
|
88
|
+
message: `Project context set to '${exactMatch.key}' (${exactMatch.name}, ID: ${exactMatch.id}). You can now perform project-specific operations.`,
|
|
89
|
+
availableFields
|
|
90
|
+
};
|
|
91
|
+
return {
|
|
92
|
+
structuredContent: SetProjectContextResponse.parse(result),
|
|
93
|
+
content: []
|
|
94
|
+
};
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
export {
|
|
98
|
+
SetProjectContext
|
|
99
|
+
};
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { Tool } from "../../../common/tools.js";
|
|
2
|
+
import { TOOL_NAMES, ENDPOINTS } from "../../config/constants.js";
|
|
3
|
+
import { ResolverKeys, InputField } from "../../config/field-resolution.types.js";
|
|
4
|
+
import { CreateTestCaseResponse, CreateTestCaseBody } from "../../schema/test-case.schema.js";
|
|
5
|
+
const FIELD_CONFIG = {
|
|
6
|
+
[InputField.PRIORITY]: ResolverKeys.CommonAttribute.PRIORITY,
|
|
7
|
+
[InputField.STATUS]: ResolverKeys.CommonAttribute.TESTCASE_STATUS,
|
|
8
|
+
[InputField.FOLDER]: ResolverKeys.CommonAttribute.TESTCASE_FOLDER,
|
|
9
|
+
[InputField.COMPONENTS]: ResolverKeys.SearchableField.COMPONENTS,
|
|
10
|
+
[InputField.LABELS]: ResolverKeys.SearchableField.LABEL
|
|
11
|
+
};
|
|
12
|
+
class CreateTestCase extends Tool {
|
|
13
|
+
// ─── Tool Specification ────────────────────────────────────────────────────
|
|
14
|
+
specification = {
|
|
15
|
+
title: TOOL_NAMES.CREATE_TEST_CASE.TITLE,
|
|
16
|
+
summary: TOOL_NAMES.CREATE_TEST_CASE.SUMMARY,
|
|
17
|
+
readOnly: false,
|
|
18
|
+
idempotent: false,
|
|
19
|
+
inputSchema: CreateTestCaseBody,
|
|
20
|
+
outputSchema: CreateTestCaseResponse,
|
|
21
|
+
purpose: "Create a new test case in QTM4J with steps, metadata, and field auto-resolution. For priority and status, use the names returned by set_project_context and map via NLP. For labels and components, provide exact names — resolved on demand. PREREQUISITE: set_project_context must be called before this tool.",
|
|
22
|
+
useCases: [
|
|
23
|
+
"Create a basic test case with just a summary",
|
|
24
|
+
"Create a test case with priority and status using names from set_project_context response",
|
|
25
|
+
"Create a test case with labels and components by exact name",
|
|
26
|
+
"Add detailed test steps with step descriptions, test data, and expected results",
|
|
27
|
+
"Create a test case in a specific folder using folderId",
|
|
28
|
+
"Set assignee and reporter using Jira account IDs",
|
|
29
|
+
"Create test cases for manual testing with step-by-step instructions",
|
|
30
|
+
"Create test cases with all metadata fields for comprehensive test management"
|
|
31
|
+
],
|
|
32
|
+
examples: [
|
|
33
|
+
{
|
|
34
|
+
description: "Create a simple test case (project must be set via set_project_context first)",
|
|
35
|
+
parameters: { summary: "Search Functionality" },
|
|
36
|
+
expectedOutput: "Test case created with key 'SCRUM-TC-xxx'"
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
description: "Create a test case with priority and status",
|
|
40
|
+
parameters: {
|
|
41
|
+
summary: "Search Functionality",
|
|
42
|
+
description: "Verify search functionality works correctly",
|
|
43
|
+
priority: "High",
|
|
44
|
+
status: "To Do"
|
|
45
|
+
},
|
|
46
|
+
expectedOutput: "Test case created with resolved priority and status IDs"
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
description: "Create a test case with labels, components, and steps",
|
|
50
|
+
parameters: {
|
|
51
|
+
summary: "Search Functionality",
|
|
52
|
+
description: "Search Functionality Test",
|
|
53
|
+
priority: "High",
|
|
54
|
+
status: "To Do",
|
|
55
|
+
labels: ["Release_1", "Sprint 1"],
|
|
56
|
+
components: ["UI", "Cloud"],
|
|
57
|
+
steps: [
|
|
58
|
+
{
|
|
59
|
+
stepDetails: "Enter a keyword in the search box",
|
|
60
|
+
testData: 'Keyword = "Test"',
|
|
61
|
+
expectedResult: "The keyword should be visible in the search box"
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
stepDetails: "Click on the Search button",
|
|
65
|
+
testData: "Click on Search Button",
|
|
66
|
+
expectedResult: "Search results matching the keyword should be displayed"
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
stepDetails: "Verify the search results",
|
|
70
|
+
testData: "Expected results list",
|
|
71
|
+
expectedResult: "Results should be relevant to the entered keyword"
|
|
72
|
+
}
|
|
73
|
+
]
|
|
74
|
+
},
|
|
75
|
+
expectedOutput: "Test case created with resolved labels/components/priority/status and 3 steps"
|
|
76
|
+
}
|
|
77
|
+
],
|
|
78
|
+
hints: [
|
|
79
|
+
"PREREQUISITE: set_project_context must be called before this tool. NEVER auto-select a project.",
|
|
80
|
+
"Priority and status values were returned by set_project_context. Use NLP to map user input (e.g., 'Major' → 'High', 'Critical' → 'Blocker').",
|
|
81
|
+
"If priority or status name is not found, the operation proceeds without that field and a warning is returned.",
|
|
82
|
+
"Labels and components are resolved on demand. If a name is not found, it is skipped with a warning.",
|
|
83
|
+
"Steps: ALWAYS include all three fields — stepDetails, testData, and expectedResult. Generate reasonable values if not provided.",
|
|
84
|
+
"folderId is optional. assignee and reporter accept Jira account IDs."
|
|
85
|
+
],
|
|
86
|
+
outputDescription: "JSON object with test case ID, key, version number, and summary. Warnings included if any fields were skipped."
|
|
87
|
+
};
|
|
88
|
+
// ─── Handle Implementation ──────────────────────────────────────────────────
|
|
89
|
+
handle = async (rawArgs) => {
|
|
90
|
+
const fieldResolver = this.client.getResolverRegistry();
|
|
91
|
+
const context = fieldResolver.requireProjectContext();
|
|
92
|
+
const body = {
|
|
93
|
+
...CreateTestCaseBody.parse(rawArgs),
|
|
94
|
+
projectId: String(context.projectId),
|
|
95
|
+
folderId: "MCP Generated"
|
|
96
|
+
};
|
|
97
|
+
const warnings = [];
|
|
98
|
+
await Promise.all(
|
|
99
|
+
Object.entries(FIELD_CONFIG).map(
|
|
100
|
+
([inputField, resolverKey]) => fieldResolver.getResolver(resolverKey).resolve(inputField, resolverKey, body, context, warnings)
|
|
101
|
+
)
|
|
102
|
+
);
|
|
103
|
+
const response = await this.client.getApiClient().post(ENDPOINTS.CREATE_TEST_CASE, body);
|
|
104
|
+
const validated = CreateTestCaseResponse.parse(response);
|
|
105
|
+
return {
|
|
106
|
+
structuredContent: validated,
|
|
107
|
+
content: warnings.length > 0 ? [{ type: "text", text: `Note: ${warnings.join(" | ")}` }] : []
|
|
108
|
+
};
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
export {
|
|
112
|
+
CreateTestCase
|
|
113
|
+
};
|
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
import { Tool } from "../../../common/tools.js";
|
|
2
|
+
import { TOOL_NAMES, RESPONSE_FIELDS, ENDPOINTS } from "../../config/constants.js";
|
|
3
|
+
import { SearchTestCaseResponse, SearchTestCaseBody } from "../../schema/get-test-case.schema.js";
|
|
4
|
+
class GetTestCases extends Tool {
|
|
5
|
+
specification = {
|
|
6
|
+
title: TOOL_NAMES.SEARCH_TEST_CASES.TITLE,
|
|
7
|
+
summary: TOOL_NAMES.SEARCH_TEST_CASES.SUMMARY,
|
|
8
|
+
readOnly: true,
|
|
9
|
+
idempotent: true,
|
|
10
|
+
inputSchema: SearchTestCaseBody,
|
|
11
|
+
outputSchema: SearchTestCaseResponse,
|
|
12
|
+
purpose: "Search and filter test cases in a QTM4J project. The filter object is sent in the request body; fields, sort, startAt, and maxResults are sent as URL query parameters. All filter values accept string names directly (e.g., status: ['Done'], priority: ['High'], labels: ['Release_1']). PREREQUISITE: set_project_context must be called before this tool.",
|
|
13
|
+
useCases: [
|
|
14
|
+
"Search all test cases in a project",
|
|
15
|
+
"Filter test cases by status (e.g., 'Done', 'To Do', 'In Progress')",
|
|
16
|
+
"Filter test cases by priority (e.g., 'High', 'Medium', 'Low')",
|
|
17
|
+
"Filter test cases by labels and components",
|
|
18
|
+
"Search test cases by text in summary and description",
|
|
19
|
+
"Filter test cases by assignee or reporter",
|
|
20
|
+
"Filter test cases by creation/update date ranges",
|
|
21
|
+
"Filter test cases by automation status",
|
|
22
|
+
"Request only specific fields to reduce response size",
|
|
23
|
+
"Sort results using the sort query param (e.g., 'created:desc', 'priority:asc')",
|
|
24
|
+
"Paginate through large result sets using startAt and maxResults",
|
|
25
|
+
"Combine multiple filters for complex queries",
|
|
26
|
+
"Find all failed test cases that need attention (by status and execution date)",
|
|
27
|
+
"Get test cases for sprint planning (filter by sprint and status)",
|
|
28
|
+
"Audit test coverage by searching for untested areas (filter by executed date)",
|
|
29
|
+
"Find all manual test cases assigned to a specific tester",
|
|
30
|
+
"Generate test reports by filtering and sorting test cases",
|
|
31
|
+
"Track test case changes over time (filter by update date range)",
|
|
32
|
+
"Identify high-priority test cases pending review",
|
|
33
|
+
"Search for test cases related to specific features (using searchText)",
|
|
34
|
+
"Find duplicate or similar test cases (using searchText)",
|
|
35
|
+
"Get automated vs manual test distribution (filter by isAutomated)",
|
|
36
|
+
"Monitor test execution trends (filter by executed date ranges)",
|
|
37
|
+
"Prepare test execution schedules (filter by assignee and priority)"
|
|
38
|
+
],
|
|
39
|
+
examples: [
|
|
40
|
+
{
|
|
41
|
+
description: "Search all test cases in the project",
|
|
42
|
+
parameters: {},
|
|
43
|
+
expectedOutput: "Paginated list of all test cases with all fields (first 50 results)"
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
description: "Filter test cases by status",
|
|
47
|
+
parameters: {
|
|
48
|
+
filter: {
|
|
49
|
+
status: ["Done"]
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
expectedOutput: "List of test cases with 'Done' status"
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
description: "Filter by multiple statuses and priorities with specific fields",
|
|
56
|
+
parameters: {
|
|
57
|
+
filter: {
|
|
58
|
+
status: ["Done", "To Do"],
|
|
59
|
+
priority: ["High", "Medium"]
|
|
60
|
+
},
|
|
61
|
+
fields: ["key", "summary", "status", "priority", "assignee"]
|
|
62
|
+
},
|
|
63
|
+
expectedOutput: "Test cases matching the filters with only the selected fields returned"
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
description: "Search test cases by text in summary/description",
|
|
67
|
+
parameters: {
|
|
68
|
+
filter: {
|
|
69
|
+
searchText: "login functionality"
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
expectedOutput: "Test cases containing 'login functionality' in summary or description"
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
description: "Filter by labels and components",
|
|
76
|
+
parameters: {
|
|
77
|
+
filter: {
|
|
78
|
+
labels: ["Release_1", "Sprint 1"],
|
|
79
|
+
components: ["UI", "Cloud"]
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
expectedOutput: "Test cases tagged with the specified labels and components"
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
description: "Filter by assignee and automation status",
|
|
86
|
+
parameters: {
|
|
87
|
+
filter: {
|
|
88
|
+
assignee: ["712020:ddc8e24b-2de7-404b-b9ed-3d7b241e2ced"],
|
|
89
|
+
isAutomated: false
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
expectedOutput: "Manual test cases assigned to the specified user"
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
description: "Filter by creation date range",
|
|
96
|
+
parameters: {
|
|
97
|
+
filter: {
|
|
98
|
+
createdOnFrom: "01/Jan/2026",
|
|
99
|
+
createdOnTo: "31/Dec/2026"
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
expectedOutput: "Test cases created during 2026"
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
description: "Paginate and sort by creation date (newest first)",
|
|
106
|
+
parameters: {
|
|
107
|
+
filter: {
|
|
108
|
+
status: ["Done"]
|
|
109
|
+
},
|
|
110
|
+
startAt: 0,
|
|
111
|
+
maxResults: 50,
|
|
112
|
+
sort: "created:desc"
|
|
113
|
+
},
|
|
114
|
+
expectedOutput: "First 50 'Done' test cases sorted by creation date, newest first"
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
description: "Get all available fields for test cases",
|
|
118
|
+
parameters: {
|
|
119
|
+
filter: {},
|
|
120
|
+
fields: [
|
|
121
|
+
"key",
|
|
122
|
+
"summary",
|
|
123
|
+
"description",
|
|
124
|
+
"priority",
|
|
125
|
+
"status",
|
|
126
|
+
"assignee",
|
|
127
|
+
"isAutomated",
|
|
128
|
+
"reporter",
|
|
129
|
+
"estimatedTime",
|
|
130
|
+
"labels",
|
|
131
|
+
"components",
|
|
132
|
+
"fixVersions",
|
|
133
|
+
"sprint",
|
|
134
|
+
"folders",
|
|
135
|
+
"updated",
|
|
136
|
+
"created",
|
|
137
|
+
"executed",
|
|
138
|
+
"flakyScore",
|
|
139
|
+
"passRateScore",
|
|
140
|
+
"aiGenerated",
|
|
141
|
+
"precondition",
|
|
142
|
+
"orderNo",
|
|
143
|
+
"seqNo",
|
|
144
|
+
"version"
|
|
145
|
+
]
|
|
146
|
+
},
|
|
147
|
+
expectedOutput: "Test cases with all available fields explicitly requested"
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
description: "Filter by folder and fix version",
|
|
151
|
+
parameters: {
|
|
152
|
+
filter: {
|
|
153
|
+
folders: [123, 456],
|
|
154
|
+
fixVersions: [789]
|
|
155
|
+
}
|
|
156
|
+
},
|
|
157
|
+
expectedOutput: "Test cases in the specified folders and fix versions"
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
description: "Complex filter: multiple criteria combined with multi-field sort",
|
|
161
|
+
parameters: {
|
|
162
|
+
filter: {
|
|
163
|
+
status: ["Done", "In Progress"],
|
|
164
|
+
priority: ["High"],
|
|
165
|
+
labels: ["Release_1"],
|
|
166
|
+
isAutomated: false,
|
|
167
|
+
createdOnFrom: "01/Apr/2026",
|
|
168
|
+
createdOnTo: "30/Apr/2026"
|
|
169
|
+
},
|
|
170
|
+
fields: ["key", "summary", "status", "priority", "created"],
|
|
171
|
+
sort: "priority:asc,created:desc"
|
|
172
|
+
},
|
|
173
|
+
expectedOutput: "High-priority manual test cases created in April 2026 with 'Done' or 'In Progress' status, sorted by priority ascending then creation date descending"
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
description: "Find all automated test cases for CI/CD pipeline",
|
|
177
|
+
parameters: {
|
|
178
|
+
filter: {
|
|
179
|
+
isAutomated: true,
|
|
180
|
+
status: ["Done", "In Progress"]
|
|
181
|
+
},
|
|
182
|
+
fields: ["key", "summary", "status", "labels", "components"]
|
|
183
|
+
},
|
|
184
|
+
expectedOutput: "Up to 50 automated test cases ready for execution in CI/CD"
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
description: "Sprint planning: get pending test cases for a team, sorted by priority",
|
|
188
|
+
parameters: {
|
|
189
|
+
filter: {
|
|
190
|
+
status: ["To Do", "In Progress"],
|
|
191
|
+
assignee: [
|
|
192
|
+
"712020:ddc8e24b-2de7-404b-b9ed-3d7b241e2ced",
|
|
193
|
+
"712020:b8479b55-6d23-478c-a2ad-4c8ce176e1fc"
|
|
194
|
+
],
|
|
195
|
+
priority: ["High", "Medium"]
|
|
196
|
+
},
|
|
197
|
+
fields: [
|
|
198
|
+
"key",
|
|
199
|
+
"summary",
|
|
200
|
+
"status",
|
|
201
|
+
"priority",
|
|
202
|
+
"assignee",
|
|
203
|
+
"estimatedTime"
|
|
204
|
+
],
|
|
205
|
+
sort: "priority:desc"
|
|
206
|
+
},
|
|
207
|
+
expectedOutput: "Pending high and medium priority test cases assigned to the team, sorted by priority descending"
|
|
208
|
+
},
|
|
209
|
+
{
|
|
210
|
+
description: "Test coverage report: find completed cases sorted oldest first",
|
|
211
|
+
parameters: {
|
|
212
|
+
filter: {
|
|
213
|
+
status: ["Done"]
|
|
214
|
+
},
|
|
215
|
+
fields: ["key", "summary", "priority", "created", "assignee"],
|
|
216
|
+
sort: "created:asc"
|
|
217
|
+
},
|
|
218
|
+
expectedOutput: "Completed test cases sorted by creation date, oldest first"
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
description: "Find test cases by keyword for regression testing",
|
|
222
|
+
parameters: {
|
|
223
|
+
filter: {
|
|
224
|
+
searchText: "authentication login",
|
|
225
|
+
status: ["Done"]
|
|
226
|
+
},
|
|
227
|
+
fields: ["key", "summary", "description", "labels", "components"]
|
|
228
|
+
},
|
|
229
|
+
expectedOutput: "All completed test cases related to authentication/login functionality"
|
|
230
|
+
},
|
|
231
|
+
{
|
|
232
|
+
description: "Weekly test execution summary",
|
|
233
|
+
parameters: {
|
|
234
|
+
filter: {
|
|
235
|
+
executedOnFrom: "27/Apr/2026",
|
|
236
|
+
executedOnTo: "03/May/2026"
|
|
237
|
+
},
|
|
238
|
+
fields: [
|
|
239
|
+
"key",
|
|
240
|
+
"summary",
|
|
241
|
+
"status",
|
|
242
|
+
"executed",
|
|
243
|
+
"passRateScore",
|
|
244
|
+
"flakyScore"
|
|
245
|
+
],
|
|
246
|
+
sort: "executed:desc"
|
|
247
|
+
},
|
|
248
|
+
expectedOutput: "Test cases executed in the past week with their pass rates and flaky scores, sorted most-recent first"
|
|
249
|
+
}
|
|
250
|
+
],
|
|
251
|
+
hints: [
|
|
252
|
+
"PREREQUISITE: set_project_context must be called before this tool. NEVER auto-select a project.",
|
|
253
|
+
"REQUEST STRUCTURE: filter goes in the request body; fields, sort, startAt, and maxResults are URL query parameters.",
|
|
254
|
+
"The 'projectId' inside filter is auto-populated from the active project context if not provided.",
|
|
255
|
+
"All filter values accept string names directly — no ID resolution needed (e.g., status: ['Done'], priority: ['High']).",
|
|
256
|
+
"FIELDS: Pass as an array (sent as comma-separated URL param). Example: { fields: ['key', 'summary', 'status'] }. Omit to return all fields.",
|
|
257
|
+
"Available fields: key, summary, description, priority, status, assignee, isAutomated, reporter, estimatedTime, labels, components, fixVersions, sprint, folders, updated, created, executed, flakyScore, passRateScore, aiGenerated, precondition, orderNo, seqNo, version",
|
|
258
|
+
"SORTING: Use 'sort' with format 'fieldName:order' (asc/desc). Multiple fields: 'priority:asc,created:desc'. Sortable fields: key, summary, created, updated, status, priority, executed.",
|
|
259
|
+
"PAGINATION: startAt (default: 0) and maxResults (default: 50, max: 50) are sent as URL query params. Increment startAt by 50 to get the next page. Stop when startAt >= total.",
|
|
260
|
+
"Date format for all filter date fields: 'dd/MMM/yyyy' (e.g., '17/Apr/2026', '01/Jan/2026'). Case-sensitive.",
|
|
261
|
+
"FILTER LOGIC: Multiple values within one filter field use OR (status: ['Done', 'To Do'] = Done OR To Do).",
|
|
262
|
+
"FILTER LOGIC: Different filter fields are combined with AND (status + priority = both must match).",
|
|
263
|
+
"The 'searchText' filter searches both summary and description fields (case-insensitive). To get details of a specific test case by its key (e.g., 'SCRUM-TC-145'), pass the key as filter.searchText — there is no separate key filter field.",
|
|
264
|
+
"For assignee/reporter filters, use Jira account IDs (format: '712020:uuid'). Multiple IDs = OR logic.",
|
|
265
|
+
"Omitting filter entirely returns all test cases in the active project (paginated)."
|
|
266
|
+
],
|
|
267
|
+
outputDescription: "JSON object with total (total matching test cases), startAt, maxResults, and data (array of test case objects for this page)."
|
|
268
|
+
};
|
|
269
|
+
handle = async (rawArgs) => {
|
|
270
|
+
const args = SearchTestCaseBody.parse(rawArgs);
|
|
271
|
+
const context = this.client.getResolverRegistry().requireProjectContext();
|
|
272
|
+
if (!args.filter) {
|
|
273
|
+
args.filter = {};
|
|
274
|
+
}
|
|
275
|
+
if (!args.filter.projectId) {
|
|
276
|
+
args.filter.projectId = String(context.projectId);
|
|
277
|
+
}
|
|
278
|
+
const params = new URLSearchParams();
|
|
279
|
+
if (args.fields?.length)
|
|
280
|
+
params.set(RESPONSE_FIELDS.FIELDS, args.fields.join(","));
|
|
281
|
+
if (args.sort) params.set(RESPONSE_FIELDS.SORT, args.sort);
|
|
282
|
+
params.set(RESPONSE_FIELDS.START_AT, String(args.startAt));
|
|
283
|
+
params.set(RESPONSE_FIELDS.MAX_RESULTS, String(args.maxResults));
|
|
284
|
+
const endpoint = `${ENDPOINTS.SEARCH_TEST_CASES}?${params.toString()}`;
|
|
285
|
+
const response = await this.client.getApiClient().post(endpoint, { filter: args.filter });
|
|
286
|
+
const validated = SearchTestCaseResponse.parse(response);
|
|
287
|
+
return {
|
|
288
|
+
structuredContent: validated,
|
|
289
|
+
content: []
|
|
290
|
+
};
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
export {
|
|
294
|
+
GetTestCases
|
|
295
|
+
};
|