@smartbear/mcp 0.24.0 → 0.25.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.
Files changed (129) hide show
  1. package/dist/bearq/tool/tasks/chat-with-qa-lead.js +1 -0
  2. package/dist/bearq/tool/tasks/expand-application-model.js +1 -0
  3. package/dist/bearq/tool/tasks/get-task-status.js +1 -0
  4. package/dist/bearq/tool/tasks/get-task.js +1 -0
  5. package/dist/bearq/tool/tasks/refine-all-draft-tests.js +1 -0
  6. package/dist/bearq/tool/tasks/refine-test-cases.js +1 -0
  7. package/dist/bearq/tool/tasks/refine-tests-in-functional-areas.js +1 -0
  8. package/dist/bearq/tool/tasks/run-regression-tests.js +1 -0
  9. package/dist/bearq/tool/tasks/run-test-cases.js +1 -0
  10. package/dist/bearq/tool/tasks/run-tests-in-functional-areas.js +1 -0
  11. package/dist/bearq/tool/tasks/stop-task.js +1 -0
  12. package/dist/bearq/tool/tasks/wait-for-task.js +1 -0
  13. package/dist/bugsnag/client.js +1 -0
  14. package/dist/bugsnag/tool/error/get-error.js +1 -0
  15. package/dist/bugsnag/tool/error/list-project-errors.js +1 -0
  16. package/dist/bugsnag/tool/error/update-error.js +1 -0
  17. package/dist/bugsnag/tool/event/get-event-details-from-dashboard-url.js +1 -0
  18. package/dist/bugsnag/tool/event/get-event.js +1 -0
  19. package/dist/bugsnag/tool/event/list-error-events.js +1 -0
  20. package/dist/bugsnag/tool/performance/get-network-endpoint-groupings.js +1 -0
  21. package/dist/bugsnag/tool/performance/get-span-group.js +1 -0
  22. package/dist/bugsnag/tool/performance/get-trace.js +1 -0
  23. package/dist/bugsnag/tool/performance/list-span-groups.js +1 -0
  24. package/dist/bugsnag/tool/performance/list-spans.js +1 -0
  25. package/dist/bugsnag/tool/performance/list-trace-fields.js +1 -0
  26. package/dist/bugsnag/tool/performance/set-network-endpoint-groupings.js +1 -0
  27. package/dist/bugsnag/tool/project/get-current-project.js +1 -0
  28. package/dist/bugsnag/tool/project/list-project-event-filters.js +1 -0
  29. package/dist/bugsnag/tool/project/list-projects.js +1 -0
  30. package/dist/bugsnag/tool/release/get-build.js +1 -0
  31. package/dist/bugsnag/tool/release/get-release.js +1 -0
  32. package/dist/bugsnag/tool/release/list-releases.js +1 -0
  33. package/dist/collaborator/client.js +10 -0
  34. package/dist/common/client-registry.js +17 -9
  35. package/dist/common/server.js +44 -1
  36. package/dist/common/transport-http.js +24 -20
  37. package/dist/common/transport-stdio.js +7 -5
  38. package/dist/package.json.js +1 -1
  39. package/dist/pactflow/client/tools.js +102 -0
  40. package/dist/qmetry/client/tools/automation-tools.js +2 -0
  41. package/dist/qmetry/client/tools/issue-tools.js +6 -0
  42. package/dist/qmetry/client/tools/project-tools.js +9 -0
  43. package/dist/qmetry/client/tools/requirement-tools.js +5 -0
  44. package/dist/qmetry/client/tools/testcase-tools.js +7 -0
  45. package/dist/qmetry/client/tools/testsuite-tools.js +11 -0
  46. package/dist/qtm4j/client.js +25 -1
  47. package/dist/qtm4j/config/constants.js +101 -1
  48. package/dist/qtm4j/config/field-resolution.types.js +3 -1
  49. package/dist/qtm4j/http/api-client.js +20 -2
  50. package/dist/qtm4j/resolver/resolver-registry.js +7 -1
  51. package/dist/qtm4j/resolver/resolvers/requirement-id-resolver.js +28 -0
  52. package/dist/qtm4j/resolver/resolvers/test-cycle-uid-resolver.js +28 -0
  53. package/dist/qtm4j/schema/linked-items.schema.js +95 -0
  54. package/dist/qtm4j/schema/requirements.schema.js +109 -0
  55. package/dist/qtm4j/schema/test-cycle.link.schema.js +260 -0
  56. package/dist/qtm4j/tool/project/get-projects.js +2 -1
  57. package/dist/qtm4j/tool/project/set-project-context.js +2 -1
  58. package/dist/qtm4j/tool/requirement/get-linked-testcases.js +93 -0
  59. package/dist/qtm4j/tool/requirement/link-testcases.js +107 -0
  60. package/dist/qtm4j/tool/requirement/unlink-testcases.js +97 -0
  61. package/dist/qtm4j/tool/test-automation/get-automation-history.js +2 -1
  62. package/dist/qtm4j/tool/test-automation/upload-automation-result.js +2 -1
  63. package/dist/qtm4j/tool/test-case/create-test-case.js +2 -1
  64. package/dist/qtm4j/tool/test-case/get-linked-requirements.js +67 -0
  65. package/dist/qtm4j/tool/test-case/get-test-cases.js +2 -1
  66. package/dist/qtm4j/tool/test-case/get-test-steps.js +2 -1
  67. package/dist/qtm4j/tool/test-case/link-requirements.js +124 -0
  68. package/dist/qtm4j/tool/test-case/unlink-requirements.js +116 -0
  69. package/dist/qtm4j/tool/test-case/update-test-case.js +2 -1
  70. package/dist/qtm4j/tool/test-cycle/create-test-cycle.js +2 -1
  71. package/dist/qtm4j/tool/test-cycle/get-linked-requirements.js +71 -0
  72. package/dist/qtm4j/tool/test-cycle/link-requirements.js +91 -0
  73. package/dist/qtm4j/tool/test-cycle/link-testcases.js +118 -0
  74. package/dist/qtm4j/tool/test-cycle/search-linked-testcases.js +114 -0
  75. package/dist/qtm4j/tool/test-cycle/search-test-cycle.js +2 -1
  76. package/dist/qtm4j/tool/test-cycle/unlink-requirements.js +87 -0
  77. package/dist/qtm4j/tool/test-cycle/unlink-testcases.js +103 -0
  78. package/dist/qtm4j/tool/test-cycle/update-test-cycle.js +2 -1
  79. package/dist/reflect/tool/recording/add-prompt-step.js +1 -0
  80. package/dist/reflect/tool/recording/add-segment.js +1 -0
  81. package/dist/reflect/tool/recording/connect-to-session.js +1 -0
  82. package/dist/reflect/tool/recording/delete-previous-step.js +1 -0
  83. package/dist/reflect/tool/recording/get-screenshot.js +1 -0
  84. package/dist/reflect/tool/suites/cancel-suite-execution.js +1 -0
  85. package/dist/reflect/tool/suites/execute-suite.js +1 -0
  86. package/dist/reflect/tool/suites/get-suite-execution-status.js +1 -0
  87. package/dist/reflect/tool/suites/list-suite-executions.js +1 -0
  88. package/dist/reflect/tool/suites/list-suites.js +1 -0
  89. package/dist/reflect/tool/tests/get-test-status.js +1 -0
  90. package/dist/reflect/tool/tests/list-segments.js +1 -0
  91. package/dist/reflect/tool/tests/list-tests.js +1 -0
  92. package/dist/reflect/tool/tests/run-test.js +1 -0
  93. package/dist/swagger/client/tools.js +23 -0
  94. package/dist/zephyr/tool/environment/get-environments.js +1 -0
  95. package/dist/zephyr/tool/folder/create-folder.js +1 -0
  96. package/dist/zephyr/tool/issue-link/get-test-cases.js +1 -0
  97. package/dist/zephyr/tool/issue-link/get-test-cycles.js +1 -0
  98. package/dist/zephyr/tool/issue-link/get-test-executions.js +1 -0
  99. package/dist/zephyr/tool/priority/get-priorities.js +1 -0
  100. package/dist/zephyr/tool/project/get-project.js +1 -0
  101. package/dist/zephyr/tool/project/get-projects.js +1 -0
  102. package/dist/zephyr/tool/status/get-statuses.js +1 -0
  103. package/dist/zephyr/tool/test-case/create-issue-link.js +1 -0
  104. package/dist/zephyr/tool/test-case/create-test-case.js +1 -0
  105. package/dist/zephyr/tool/test-case/create-test-script.js +1 -0
  106. package/dist/zephyr/tool/test-case/create-test-steps.js +1 -0
  107. package/dist/zephyr/tool/test-case/create-web-link.js +1 -0
  108. package/dist/zephyr/tool/test-case/get-links.js +1 -0
  109. package/dist/zephyr/tool/test-case/get-test-case.js +1 -0
  110. package/dist/zephyr/tool/test-case/get-test-cases.js +1 -0
  111. package/dist/zephyr/tool/test-case/get-test-script.js +1 -0
  112. package/dist/zephyr/tool/test-case/get-test-steps.js +1 -0
  113. package/dist/zephyr/tool/test-case/update-test-case.js +1 -0
  114. package/dist/zephyr/tool/test-cycle/create-issue-link.js +1 -0
  115. package/dist/zephyr/tool/test-cycle/create-test-cycle.js +1 -0
  116. package/dist/zephyr/tool/test-cycle/create-web-link.js +1 -0
  117. package/dist/zephyr/tool/test-cycle/get-links.js +1 -0
  118. package/dist/zephyr/tool/test-cycle/get-test-cycle.js +1 -0
  119. package/dist/zephyr/tool/test-cycle/get-test-cycles.js +1 -0
  120. package/dist/zephyr/tool/test-cycle/update-test-cycle.js +1 -0
  121. package/dist/zephyr/tool/test-execution/create-issue-link.js +1 -0
  122. package/dist/zephyr/tool/test-execution/create-test-execution.js +1 -0
  123. package/dist/zephyr/tool/test-execution/get-test-execution-links.js +1 -0
  124. package/dist/zephyr/tool/test-execution/get-test-execution.js +1 -0
  125. package/dist/zephyr/tool/test-execution/get-test-executions.js +1 -0
  126. package/dist/zephyr/tool/test-execution/get-test-steps.js +1 -0
  127. package/dist/zephyr/tool/test-execution/update-test-execution.js +1 -0
  128. package/dist/zephyr/tool/test-execution/update-test-steps.js +1 -0
  129. package/package.json +1 -1
@@ -0,0 +1,71 @@
1
+ import { Tool, ToolError } from "../../../common/tools.js";
2
+ import { TOOLSETS, TOOL_NAMES, ENDPOINTS } from "../../config/constants.js";
3
+ import { ResolverKeys } from "../../config/field-resolution.types.js";
4
+ import { GetLinkedRequirementsResponse } from "../../schema/linked-items.schema.js";
5
+ import { GetLinkedRequirementsForCycleBody } from "../../schema/test-cycle.link.schema.js";
6
+ class GetLinkedRequirementsForCycle extends Tool {
7
+ specification = {
8
+ title: TOOL_NAMES.GET_LINKED_REQUIREMENTS_FOR_CYCLE.TITLE,
9
+ summary: TOOL_NAMES.GET_LINKED_REQUIREMENTS_FOR_CYCLE.SUMMARY,
10
+ readOnly: true,
11
+ idempotent: true,
12
+ toolset: TOOLSETS.TEST_CYCLES,
13
+ inputSchema: GetLinkedRequirementsForCycleBody,
14
+ outputSchema: GetLinkedRequirementsResponse,
15
+ purpose: "Retrieve all Jira requirements linked to a QTM4J test cycle. The cycle's human-readable key (e.g. 'SCRUM-TR-1') is resolved to the internal UID automatically. Returns a paginated list of linked requirements with their Jira metadata (key, summary, status, priority, issue type). PREREQUISITE: set_project_context must be called before this tool.",
16
+ useCases: [
17
+ "Check which Jira stories or bugs are covered by a test cycle",
18
+ "Audit requirement traceability for a test cycle",
19
+ "Retrieve requirement keys linked to a cycle before a release",
20
+ "Verify correct requirements are linked to a test cycle"
21
+ ],
22
+ examples: [
23
+ {
24
+ description: "Get all requirements linked to a test cycle",
25
+ parameters: { cycleKey: "SCRUM-TR-1" },
26
+ expectedOutput: "Paginated list of linked requirements with Jira metadata"
27
+ },
28
+ {
29
+ description: "Get requirements sorted by priority with custom page size",
30
+ parameters: {
31
+ cycleKey: "SCRUM-TR-5",
32
+ sort: "priority:asc",
33
+ maxResults: 20
34
+ },
35
+ expectedOutput: "Requirements linked to cycle sorted by priority ascending"
36
+ }
37
+ ],
38
+ hints: [
39
+ "PREREQUISITE: set_project_context must be called before this tool. NEVER auto-select a project.",
40
+ "CYCLE KEY FORMAT: '{PROJECT_KEY}-TR-{id}' — e.g. 'SCRUM-TR-1'. Resolved to internal UID automatically.",
41
+ "Allowed sort fields: key, status, priority. Default sort: 'key:desc'.",
42
+ "Paginate using startAt — increment by maxResults until startAt >= total."
43
+ ],
44
+ outputDescription: "Paginated result with total, startAt, maxResults, and data array of linked requirement objects (id, key, summary, status, priority, issueType)."
45
+ };
46
+ handle = async (rawArgs) => {
47
+ const args = GetLinkedRequirementsForCycleBody.parse(rawArgs);
48
+ const fieldResolver = this.client.getResolverRegistry();
49
+ const context = fieldResolver.requireProjectContext();
50
+ const cycleMap = await fieldResolver.getResolver(ResolverKeys.SearchableField.TEST_CYCLE_KEY_TO_UID).resolveAndReturn(context.projectId, [args.cycleKey]);
51
+ const cycleEntry = cycleMap[args.cycleKey];
52
+ if (!cycleEntry) {
53
+ throw new ToolError(
54
+ `Test cycle '${args.cycleKey}' not found in project '${context.projectKey}'. Verify the key is a valid QTM4J test cycle key.`
55
+ );
56
+ }
57
+ const params = {
58
+ maxResults: args.maxResults,
59
+ startAt: args.startAt,
60
+ sort: args.sort
61
+ };
62
+ const response = await this.client.getApiClient().get(ENDPOINTS.GET_LINKED_REQUIREMENTS_FOR_CYCLE(cycleEntry.uid), params);
63
+ return {
64
+ structuredContent: GetLinkedRequirementsResponse.parse(response),
65
+ content: []
66
+ };
67
+ };
68
+ }
69
+ export {
70
+ GetLinkedRequirementsForCycle
71
+ };
@@ -0,0 +1,91 @@
1
+ import { Tool, ToolError } from "../../../common/tools.js";
2
+ import { TOOLSETS, TOOL_NAMES, ENDPOINTS } from "../../config/constants.js";
3
+ import { ResolverKeys } from "../../config/field-resolution.types.js";
4
+ import { LinkRequirementsToCycleResponse, LinkRequirementsToCycleBody } from "../../schema/test-cycle.link.schema.js";
5
+ class LinkRequirementsToCycle extends Tool {
6
+ specification = {
7
+ title: TOOL_NAMES.LINK_REQUIREMENTS_TO_CYCLE.TITLE,
8
+ summary: TOOL_NAMES.LINK_REQUIREMENTS_TO_CYCLE.SUMMARY,
9
+ readOnly: false,
10
+ idempotent: false,
11
+ toolset: TOOLSETS.TEST_CYCLES,
12
+ inputSchema: LinkRequirementsToCycleBody,
13
+ outputSchema: LinkRequirementsToCycleResponse,
14
+ purpose: "Link Jira requirements to a QTM4J test cycle using the cycle's human-readable key. The cycle key (e.g. 'SCRUM-TR-1') is resolved to the internal UID automatically. Requirements can be specified by their human-readable keys (e.g. 'SCRUM-1') which are resolved to internal IDs automatically, or via a JQL filter. Requirements that cannot be linked are reported as warnings. PREREQUISITE: set_project_context must be called before this tool.",
15
+ useCases: [
16
+ "Link one or more Jira requirements (stories, bugs, epics) to a test cycle",
17
+ "Associate requirements with a test cycle using a JQL filter",
18
+ "Build traceability between requirements and a test cycle",
19
+ "Link all stories from a sprint to a test cycle using JQL"
20
+ ],
21
+ examples: [
22
+ {
23
+ description: "Link two requirements by key",
24
+ parameters: {
25
+ cycleKey: "SCRUM-TR-1",
26
+ requirementKeys: ["SCRUM-1", "SCRUM-2"]
27
+ },
28
+ expectedOutput: "Requirements SCRUM-1 and SCRUM-2 linked to test cycle"
29
+ },
30
+ {
31
+ description: "Link requirements by JQL filter",
32
+ parameters: {
33
+ cycleKey: "SCRUM-TR-1",
34
+ filter: { jql: "project = DEMO AND issuetype = Story" }
35
+ },
36
+ expectedOutput: "Requirements matched by JQL linked to test cycle"
37
+ }
38
+ ],
39
+ hints: [
40
+ "PREREQUISITE: set_project_context must be called before this tool. NEVER auto-select a project.",
41
+ "CYCLE KEY FORMAT: '{PROJECT_KEY}-TR-{id}' — e.g. 'SCRUM-TR-1'. Resolved to internal UID automatically.",
42
+ "Requirement keys follow Jira issue key format: '{PROJECT_KEY}-{number}' (e.g. 'SCRUM-1').",
43
+ "Provide either requirementKeys or filter.jql — not both.",
44
+ "If a requirement key cannot be resolved or linked, it is reported in warnings and other requirements are still linked."
45
+ ],
46
+ outputDescription: "Confirmation with the cycle key and linked: true. Warnings are included if any requirements could not be resolved or linked."
47
+ };
48
+ handle = async (rawArgs) => {
49
+ const args = LinkRequirementsToCycleBody.parse(rawArgs);
50
+ const fieldResolver = this.client.getResolverRegistry();
51
+ const context = fieldResolver.requireProjectContext();
52
+ const warnings = [];
53
+ const cycleMap = await fieldResolver.getResolver(ResolverKeys.SearchableField.TEST_CYCLE_KEY_TO_UID).resolveAndReturn(context.projectId, [args.cycleKey]);
54
+ const cycleEntry = cycleMap[args.cycleKey];
55
+ if (!cycleEntry) {
56
+ throw new ToolError(
57
+ `Test cycle '${args.cycleKey}' not found in project '${context.projectKey}'. Verify the key is a valid QTM4J test cycle key.`
58
+ );
59
+ }
60
+ const body = {};
61
+ if (args.requirementKeys?.length) {
62
+ const reqMap = await fieldResolver.getResolver(ResolverKeys.SearchableField.REQUIREMENT_KEY_TO_ID).resolveAndReturn(context.projectId, args.requirementKeys);
63
+ const requirementIds = [];
64
+ for (const reqKey of args.requirementKeys) {
65
+ const resolved = reqMap[reqKey];
66
+ if (resolved) {
67
+ requirementIds.push(Number(resolved.id));
68
+ } else {
69
+ warnings.push(
70
+ `Requirement '${reqKey}' could not be resolved and was skipped.`
71
+ );
72
+ }
73
+ }
74
+ if (requirementIds.length > 0) body.requirementIds = requirementIds;
75
+ }
76
+ if (args.filter) {
77
+ body.filter = args.filter;
78
+ }
79
+ await this.client.getApiClient().post(ENDPOINTS.LINK_REQUIREMENTS_TO_CYCLE(cycleEntry.uid), body);
80
+ return {
81
+ structuredContent: LinkRequirementsToCycleResponse.parse({
82
+ cycleKey: args.cycleKey,
83
+ linked: true
84
+ }),
85
+ content: warnings.length > 0 ? [{ type: "text", text: `Note: ${warnings.join(" | ")}` }] : []
86
+ };
87
+ };
88
+ }
89
+ export {
90
+ LinkRequirementsToCycle
91
+ };
@@ -0,0 +1,118 @@
1
+ import { Tool, ToolError } from "../../../common/tools.js";
2
+ import { TOOLSETS, TOOL_NAMES, ENDPOINTS } from "../../config/constants.js";
3
+ import { ResolverKeys } from "../../config/field-resolution.types.js";
4
+ import { TestCycleLinkResponse, LinkTestCasesToCycleBody } from "../../schema/test-cycle.link.schema.js";
5
+ class LinkTestCasesToCycle extends Tool {
6
+ specification = {
7
+ title: TOOL_NAMES.LINK_TESTCASES_TO_CYCLE.TITLE,
8
+ summary: TOOL_NAMES.LINK_TESTCASES_TO_CYCLE.SUMMARY,
9
+ readOnly: false,
10
+ idempotent: false,
11
+ toolset: TOOLSETS.TEST_CYCLES,
12
+ inputSchema: LinkTestCasesToCycleBody,
13
+ outputSchema: TestCycleLinkResponse,
14
+ purpose: "Link test cases to a QTM4J test cycle using the cycle's human-readable key. The cycle key (e.g. 'SCRUM-TR-1') is resolved to the internal UID automatically. Test cases can be specified by their human-readable keys (e.g. 'SCRUM-TC-1'), which are also resolved to internal IDs and latest versions automatically. Alternatively, use a filter object to select test cases by criteria such as status, priority, labels, or folder. The active project ID is injected automatically into the filter. Test cases that cannot be linked are reported in warnings. PREREQUISITE: set_project_context must be called before this tool.",
15
+ useCases: [
16
+ "Add specific test cases to a test cycle by key",
17
+ "Populate a test cycle with test cases matching a filter (e.g., all High priority test cases)",
18
+ "Link test cases from a specific folder to a test cycle",
19
+ "Add test cases to a cycle with a specific assignee or environment"
20
+ ],
21
+ examples: [
22
+ {
23
+ description: "Link two test cases by key",
24
+ parameters: {
25
+ cycleKey: "SCRUM-TR-1",
26
+ testCaseKeys: ["SCRUM-TC-10", "SCRUM-TC-11"]
27
+ },
28
+ expectedOutput: "Test cases linked to test cycle"
29
+ },
30
+ {
31
+ description: "Link test cases matching a filter with assignee",
32
+ parameters: {
33
+ cycleKey: "SCRUM-TR-1",
34
+ filter: { priority: ["High"], status: ["To Do"] },
35
+ assignee: "5b10a2844c20165700ede21f",
36
+ startNewExecution: true
37
+ },
38
+ expectedOutput: "Filtered test cases linked to cycle with assignee"
39
+ },
40
+ {
41
+ description: "Link test cases in a folder to a cycle with planned date",
42
+ parameters: {
43
+ cycleKey: "SCRUM-TR-5",
44
+ filter: { folderId: 42, withChild: true },
45
+ executionPlannedDate: "2024-03-31",
46
+ sort: "key:asc"
47
+ },
48
+ expectedOutput: "Folder test cases linked to cycle with planned date"
49
+ }
50
+ ],
51
+ hints: [
52
+ "PREREQUISITE: set_project_context must be called before this tool. NEVER auto-select a project.",
53
+ "CYCLE KEY FORMAT: '{PROJECT_KEY}-TR-{id}' — e.g. 'SCRUM-TR-1'. Resolved to internal UID automatically.",
54
+ "TEST CASE KEY FORMAT: '{PROJECT_KEY}-TC-{number}' — e.g. 'SCRUM-TC-145'.",
55
+ "Provide either testCaseKeys or filter — not both.",
56
+ "projectId in filter is auto-filled from the active project context — do not set it manually.",
57
+ "filter.excludeCycleId excludes test cases already in another cycle.",
58
+ "startNewExecution: true creates a fresh execution record for each linked test case.",
59
+ "executionPlannedDate must be in yyyy-MM-dd format (e.g., '2024-03-31').",
60
+ "actualTime must be in HH:mm format (e.g., '02:30').",
61
+ "If a test case key cannot be resolved, it is reported in warnings and others are still linked."
62
+ ],
63
+ outputDescription: "Confirmation with the cycle key and linked: true. Warnings included if any test cases could not be resolved or linked."
64
+ };
65
+ handle = async (rawArgs) => {
66
+ const args = LinkTestCasesToCycleBody.parse(rawArgs);
67
+ const fieldResolver = this.client.getResolverRegistry();
68
+ const context = fieldResolver.requireProjectContext();
69
+ const warnings = [];
70
+ const cycleMap = await fieldResolver.getResolver(ResolverKeys.SearchableField.TEST_CYCLE_KEY_TO_UID).resolveAndReturn(context.projectId, [args.cycleKey]);
71
+ const cycleEntry = cycleMap[args.cycleKey];
72
+ if (!cycleEntry) {
73
+ throw new ToolError(
74
+ `Test cycle '${args.cycleKey}' not found in project '${context.projectKey}'. Verify the key is a valid QTM4J test cycle key.`
75
+ );
76
+ }
77
+ const body = {};
78
+ if (args.testCaseKeys?.length) {
79
+ const uidMap = await fieldResolver.getResolver(ResolverKeys.SearchableField.TEST_CASE_KEY_TO_UID).resolveAndReturn(context.projectId, args.testCaseKeys);
80
+ const testCases = [];
81
+ for (const tcKey of args.testCaseKeys) {
82
+ const resolved = uidMap[tcKey];
83
+ if (resolved) {
84
+ testCases.push({
85
+ id: resolved.uid,
86
+ versionNo: resolved.latestVersion
87
+ });
88
+ } else {
89
+ warnings.push(
90
+ `Test case '${tcKey}' could not be resolved and was skipped.`
91
+ );
92
+ }
93
+ }
94
+ if (testCases.length > 0) body.testCases = testCases;
95
+ }
96
+ if (args.filter) {
97
+ body.filter = { ...args.filter, projectId: context.projectId };
98
+ }
99
+ const {
100
+ cycleKey: _cycleKey,
101
+ testCaseKeys: _tcKeys,
102
+ filter: _filter,
103
+ ...rest
104
+ } = args;
105
+ Object.assign(body, rest);
106
+ await this.client.getApiClient().post(ENDPOINTS.LINK_TESTCASES_TO_CYCLE(cycleEntry.uid), body);
107
+ return {
108
+ structuredContent: TestCycleLinkResponse.parse({
109
+ cycleKey: args.cycleKey,
110
+ linked: true
111
+ }),
112
+ content: warnings.length > 0 ? [{ type: "text", text: `Note: ${warnings.join(" | ")}` }] : []
113
+ };
114
+ };
115
+ }
116
+ export {
117
+ LinkTestCasesToCycle
118
+ };
@@ -0,0 +1,114 @@
1
+ import { Tool, ToolError } from "../../../common/tools.js";
2
+ import { TOOLSETS, TOOL_NAMES, RESPONSE_FIELDS, ENDPOINTS } from "../../config/constants.js";
3
+ import { ResolverKeys } from "../../config/field-resolution.types.js";
4
+ import { SearchLinkedTestCasesInCycleResponse, SearchLinkedTestCasesInCycleBody } from "../../schema/test-cycle.link.schema.js";
5
+ class SearchLinkedTestCasesInCycle extends Tool {
6
+ specification = {
7
+ title: TOOL_NAMES.SEARCH_LINKED_TESTCASES_IN_CYCLE.TITLE,
8
+ summary: TOOL_NAMES.SEARCH_LINKED_TESTCASES_IN_CYCLE.SUMMARY,
9
+ readOnly: true,
10
+ idempotent: true,
11
+ toolset: TOOLSETS.TEST_CYCLES,
12
+ inputSchema: SearchLinkedTestCasesInCycleBody,
13
+ outputSchema: SearchLinkedTestCasesInCycleResponse,
14
+ purpose: "Search and filter test case executions linked to a QTM4J test cycle. The cycle key (e.g. 'SCRUM-TR-1') is resolved to the internal UID automatically. Supports pagination, field selection, sorting, and rich filter criteria such as execution result, priority, status, environment, assignee, and date ranges. The active project ID is injected automatically into the filter. PREREQUISITE: set_project_context must be called before this tool.",
15
+ useCases: [
16
+ "List all test cases linked to a test cycle",
17
+ "Find failed or blocked test case executions in a cycle",
18
+ "Search for test cases in a cycle by execution result (Pass, Fail, Blocked)",
19
+ "Filter test cases in a cycle by priority or status",
20
+ "Retrieve test cases assigned to a specific executor in a cycle",
21
+ "Get test cases with defects in a cycle",
22
+ "Paginate through large test cycle execution lists",
23
+ "Request only specific fields to reduce response size"
24
+ ],
25
+ examples: [
26
+ {
27
+ description: "List all test cases in a cycle",
28
+ parameters: { cycleKey: "SCRUM-TR-1" },
29
+ expectedOutput: "Paginated list of test case executions in the cycle (first 50 results)"
30
+ },
31
+ {
32
+ description: "Find failed test cases in a cycle",
33
+ parameters: {
34
+ cycleKey: "SCRUM-TR-1",
35
+ filter: { executionResult: ["Fail"] },
36
+ fields: ["key", "summary", "executionResult", "priority"]
37
+ },
38
+ expectedOutput: "Test case executions with Fail result"
39
+ },
40
+ {
41
+ description: "Search with pagination and sort",
42
+ parameters: {
43
+ cycleKey: "SCRUM-TR-5",
44
+ maxResults: 25,
45
+ startAt: 0,
46
+ sort: "key:asc",
47
+ filter: { status: ["To Do"] }
48
+ },
49
+ expectedOutput: "First 25 To Do test cases in the cycle sorted by key ascending"
50
+ },
51
+ {
52
+ description: "Filter by execution assignee and environment",
53
+ parameters: {
54
+ cycleKey: "SCRUM-TR-2",
55
+ filter: {
56
+ executionAssignee: ["5b10a2844c20165700ede21f"],
57
+ environment: ["Staging"]
58
+ },
59
+ fields: [
60
+ "key",
61
+ "summary",
62
+ "executionResult",
63
+ "environment",
64
+ "actualTime"
65
+ ]
66
+ },
67
+ expectedOutput: "Test cases assigned to the specified user in the Staging environment"
68
+ }
69
+ ],
70
+ hints: [
71
+ "PREREQUISITE: set_project_context must be called before this tool. NEVER auto-select a project.",
72
+ "CYCLE KEY FORMAT: '{PROJECT_KEY}-TR-{id}' — e.g. 'SCRUM-TR-1'. Resolved to internal UID automatically.",
73
+ "projectId in filter is auto-filled from the active project context — do not set it manually.",
74
+ "fields is sent as a query parameter; filter is sent in the request body.",
75
+ "Allowed fields: id, key, summary, description, executionResult, status, priority, environment, tcWithDefects, estimatedTime, actualTime, createdOn, updatedOn, sprint, seqNo, latestTcExecutionId, customFields, flakyScore, passRateScore.",
76
+ "Allowed sort fields: id, key, summary, description, executionResult, status, priority, environment, tcWithDefects, estimatedTime, actualTime, createdOn, updatedOn, sprint, flakyScore, passRateScore.",
77
+ "Date range format for filter fields: 'dd/mmm/yyyy,dd/mmm/yyyy' (e.g., '01/Jan/2024,31/Mar/2024').",
78
+ "maxResults defaults to 50, maximum is 100. Use startAt to paginate.",
79
+ "executionResult filter accepts values like 'Pass', 'Fail', 'Blocked', 'Unexecuted'."
80
+ ],
81
+ outputDescription: "JSON object with total (total matching executions), startAt, maxResults, and data (array of test case execution objects for this page)."
82
+ };
83
+ handle = async (rawArgs) => {
84
+ const args = SearchLinkedTestCasesInCycleBody.parse(rawArgs);
85
+ const fieldResolver = this.client.getResolverRegistry();
86
+ const context = fieldResolver.requireProjectContext();
87
+ const cycleMap = await fieldResolver.getResolver(ResolverKeys.SearchableField.TEST_CYCLE_KEY_TO_UID).resolveAndReturn(context.projectId, [args.cycleKey]);
88
+ const cycleEntry = cycleMap[args.cycleKey];
89
+ if (!cycleEntry) {
90
+ throw new ToolError(
91
+ `Test cycle '${args.cycleKey}' not found in project '${context.projectKey}'. Verify the key is a valid QTM4J test cycle key.`
92
+ );
93
+ }
94
+ const params = new URLSearchParams();
95
+ if (args.fields?.length) {
96
+ params.set(RESPONSE_FIELDS.FIELDS, args.fields.join(","));
97
+ }
98
+ if (args.sort) {
99
+ params.set(RESPONSE_FIELDS.SORT, args.sort);
100
+ }
101
+ params.set(RESPONSE_FIELDS.MAX_RESULTS, String(args.maxResults));
102
+ params.set(RESPONSE_FIELDS.START_AT, String(args.startAt));
103
+ const endpoint = `${ENDPOINTS.SEARCH_LINKED_TESTCASES_IN_CYCLE(cycleEntry.uid)}?${params.toString()}`;
104
+ const filter = args.filter ? { ...args.filter, projectId: [context.projectId] } : { projectId: [context.projectId] };
105
+ const response = await this.client.getApiClient().post(endpoint, { filter });
106
+ return {
107
+ structuredContent: SearchLinkedTestCasesInCycleResponse.parse(response),
108
+ content: []
109
+ };
110
+ };
111
+ }
112
+ export {
113
+ SearchLinkedTestCasesInCycle
114
+ };
@@ -1,9 +1,10 @@
1
1
  import { Tool } from "../../../common/tools.js";
2
- import { TOOL_NAMES, RESPONSE_FIELDS, ENDPOINTS } from "../../config/constants.js";
2
+ import { TOOL_NAMES, TOOLSETS, RESPONSE_FIELDS, ENDPOINTS } from "../../config/constants.js";
3
3
  import { SearchTestCycleResponse, SearchTestCycleBody } from "../../schema/search-test-cycle.schema.js";
4
4
  class SearchTestCycles extends Tool {
5
5
  specification = {
6
6
  title: TOOL_NAMES.SEARCH_TEST_CYCLES.TITLE,
7
+ toolset: TOOLSETS.TEST_CYCLES,
7
8
  summary: TOOL_NAMES.SEARCH_TEST_CYCLES.SUMMARY,
8
9
  readOnly: true,
9
10
  idempotent: true,
@@ -0,0 +1,87 @@
1
+ import { Tool, ToolError } from "../../../common/tools.js";
2
+ import { TOOLSETS, TOOL_NAMES, ENDPOINTS } from "../../config/constants.js";
3
+ import { ResolverKeys } from "../../config/field-resolution.types.js";
4
+ import { UnlinkRequirementsFromCycleResponse, UnlinkRequirementsFromCycleBody } from "../../schema/test-cycle.link.schema.js";
5
+ class UnlinkRequirementsFromCycle extends Tool {
6
+ specification = {
7
+ title: TOOL_NAMES.UNLINK_REQUIREMENTS_FROM_CYCLE.TITLE,
8
+ summary: TOOL_NAMES.UNLINK_REQUIREMENTS_FROM_CYCLE.SUMMARY,
9
+ readOnly: false,
10
+ idempotent: false,
11
+ toolset: TOOLSETS.TEST_CYCLES,
12
+ inputSchema: UnlinkRequirementsFromCycleBody,
13
+ outputSchema: UnlinkRequirementsFromCycleResponse,
14
+ purpose: "Unlink Jira requirements from a QTM4J test cycle using the cycle's human-readable key. The cycle key (e.g. 'SCRUM-TR-1') is resolved to the internal UID automatically. Requirements can be specified by their human-readable keys (e.g. 'SCRUM-1') which are resolved to internal IDs automatically, or set unLinkAll to true to remove all requirements from the cycle in one call. Requirements that cannot be unlinked are reported as warnings. PREREQUISITE: set_project_context must be called before this tool.",
15
+ useCases: [
16
+ "Remove one or more Jira requirements from a test cycle by key",
17
+ "Unlink all requirements from a test cycle at once",
18
+ "Clean up requirement links before repopulating a test cycle"
19
+ ],
20
+ examples: [
21
+ {
22
+ description: "Unlink two requirements by key",
23
+ parameters: {
24
+ cycleKey: "SCRUM-TR-1",
25
+ requirementKeys: ["SCRUM-1", "SCRUM-2"]
26
+ },
27
+ expectedOutput: "Requirements SCRUM-1 and SCRUM-2 unlinked from test cycle"
28
+ },
29
+ {
30
+ description: "Unlink all requirements from a cycle",
31
+ parameters: { cycleKey: "SCRUM-TR-1", unLinkAll: true },
32
+ expectedOutput: "All requirements unlinked from test cycle"
33
+ }
34
+ ],
35
+ hints: [
36
+ "PREREQUISITE: set_project_context must be called before this tool. NEVER auto-select a project.",
37
+ "CYCLE KEY FORMAT: '{PROJECT_KEY}-TR-{id}' — e.g. 'SCRUM-TR-1'. Resolved to internal UID automatically.",
38
+ "Requirement keys follow Jira issue key format: '{PROJECT_KEY}-{number}' (e.g. 'SCRUM-1').",
39
+ "Provide either requirementKeys or unLinkAll: true — not both.",
40
+ "unLinkAll: true removes every requirement from the cycle — no need to list them individually.",
41
+ "If a requirement key cannot be resolved or unlinked, it is reported in warnings and other requirements are still unlinked."
42
+ ],
43
+ outputDescription: "Confirmation with the cycle key and unlinked: true. Warnings are included if any requirements could not be resolved or unlinked."
44
+ };
45
+ handle = async (rawArgs) => {
46
+ const args = UnlinkRequirementsFromCycleBody.parse(rawArgs);
47
+ const fieldResolver = this.client.getResolverRegistry();
48
+ const context = fieldResolver.requireProjectContext();
49
+ const warnings = [];
50
+ const cycleMap = await fieldResolver.getResolver(ResolverKeys.SearchableField.TEST_CYCLE_KEY_TO_UID).resolveAndReturn(context.projectId, [args.cycleKey]);
51
+ const cycleEntry = cycleMap[args.cycleKey];
52
+ if (!cycleEntry) {
53
+ throw new ToolError(
54
+ `Test cycle '${args.cycleKey}' not found in project '${context.projectKey}'. Verify the key is a valid QTM4J test cycle key.`
55
+ );
56
+ }
57
+ const body = {};
58
+ if (args.unLinkAll) {
59
+ body.unLinkAll = true;
60
+ } else if (args.requirementKeys?.length) {
61
+ const reqMap = await fieldResolver.getResolver(ResolverKeys.SearchableField.REQUIREMENT_KEY_TO_ID).resolveAndReturn(context.projectId, args.requirementKeys);
62
+ const requirementIds = [];
63
+ for (const reqKey of args.requirementKeys) {
64
+ const resolved = reqMap[reqKey];
65
+ if (resolved) {
66
+ requirementIds.push(Number(resolved.id));
67
+ } else {
68
+ warnings.push(
69
+ `Requirement '${reqKey}' could not be resolved and was skipped.`
70
+ );
71
+ }
72
+ }
73
+ if (requirementIds.length > 0) body.requirementIds = requirementIds;
74
+ }
75
+ await this.client.getApiClient().post(ENDPOINTS.UNLINK_REQUIREMENTS_FROM_CYCLE(cycleEntry.uid), body);
76
+ return {
77
+ structuredContent: UnlinkRequirementsFromCycleResponse.parse({
78
+ cycleKey: args.cycleKey,
79
+ unlinked: true
80
+ }),
81
+ content: warnings.length > 0 ? [{ type: "text", text: `Note: ${warnings.join(" | ")}` }] : []
82
+ };
83
+ };
84
+ }
85
+ export {
86
+ UnlinkRequirementsFromCycle
87
+ };
@@ -0,0 +1,103 @@
1
+ import { Tool, ToolError } from "../../../common/tools.js";
2
+ import { TOOLSETS, TOOL_NAMES, ENDPOINTS } from "../../config/constants.js";
3
+ import { ResolverKeys } from "../../config/field-resolution.types.js";
4
+ import { TestCycleLinkResponse, UnlinkTestCasesFromCycleBody } from "../../schema/test-cycle.link.schema.js";
5
+ class UnlinkTestCasesFromCycle extends Tool {
6
+ specification = {
7
+ title: TOOL_NAMES.UNLINK_TESTCASES_FROM_CYCLE.TITLE,
8
+ summary: TOOL_NAMES.UNLINK_TESTCASES_FROM_CYCLE.SUMMARY,
9
+ readOnly: false,
10
+ idempotent: false,
11
+ toolset: TOOLSETS.TEST_CYCLES,
12
+ inputSchema: UnlinkTestCasesFromCycleBody,
13
+ outputSchema: TestCycleLinkResponse,
14
+ purpose: "Unlink test cases from a QTM4J test cycle using the cycle's human-readable key. The cycle key (e.g. 'SCRUM-TR-1') is resolved to the internal UID automatically. Test cases can be specified by their human-readable keys (e.g. 'SCRUM-TC-1'), which are also resolved to internal IDs and latest versions automatically. Alternatively, set unlinkAll to true to remove all test cases from the cycle in one call, or use a filter to select which test cases to unlink by criteria. The active project ID is injected automatically into the filter. PREREQUISITE: set_project_context must be called before this tool.",
15
+ useCases: [
16
+ "Remove specific test cases from a test cycle by key",
17
+ "Unlink all test cases from a test cycle at once",
18
+ "Remove test cases matching a filter from a cycle (e.g., all 'Done' test cases)",
19
+ "Clean up a test cycle before repopulating it"
20
+ ],
21
+ examples: [
22
+ {
23
+ description: "Unlink two specific test cases",
24
+ parameters: {
25
+ cycleKey: "SCRUM-TR-1",
26
+ testCaseKeys: ["SCRUM-TC-10", "SCRUM-TC-11"]
27
+ },
28
+ expectedOutput: "Test cases unlinked from test cycle"
29
+ },
30
+ {
31
+ description: "Unlink all test cases from a cycle",
32
+ parameters: { cycleKey: "SCRUM-TR-1", unlinkAll: true },
33
+ expectedOutput: "All test cases unlinked from cycle"
34
+ },
35
+ {
36
+ description: "Unlink test cases matching a status filter",
37
+ parameters: {
38
+ cycleKey: "SCRUM-TR-5",
39
+ filter: { status: ["Done"], labels: ["Deprecated"] }
40
+ },
41
+ expectedOutput: "Filtered test cases unlinked from cycle"
42
+ }
43
+ ],
44
+ hints: [
45
+ "PREREQUISITE: set_project_context must be called before this tool. NEVER auto-select a project.",
46
+ "CYCLE KEY FORMAT: '{PROJECT_KEY}-TR-{id}' — e.g. 'SCRUM-TR-1'. Resolved to internal UID automatically.",
47
+ "TEST CASE KEY FORMAT: '{PROJECT_KEY}-TC-{number}' — e.g. 'SCRUM-TC-145'.",
48
+ "Provide exactly one of: testCaseKeys, unlinkAll, or filter.",
49
+ "unlinkAll: true removes every test case from the cycle — no need to list them individually.",
50
+ "projectId in filter is auto-filled from the active project context — do not set it manually.",
51
+ "If a test case key cannot be resolved, it is reported in warnings and others are still unlinked."
52
+ ],
53
+ outputDescription: "Confirmation with the cycle key and unlinked: true. Warnings included if any test cases could not be resolved or unlinked."
54
+ };
55
+ handle = async (rawArgs) => {
56
+ const args = UnlinkTestCasesFromCycleBody.parse(rawArgs);
57
+ const fieldResolver = this.client.getResolverRegistry();
58
+ const context = fieldResolver.requireProjectContext();
59
+ const warnings = [];
60
+ const cycleMap = await fieldResolver.getResolver(ResolverKeys.SearchableField.TEST_CYCLE_KEY_TO_UID).resolveAndReturn(context.projectId, [args.cycleKey]);
61
+ const cycleEntry = cycleMap[args.cycleKey];
62
+ if (!cycleEntry) {
63
+ throw new ToolError(
64
+ `Test cycle '${args.cycleKey}' not found in project '${context.projectKey}'. Verify the key is a valid QTM4J test cycle key.`
65
+ );
66
+ }
67
+ const body = {};
68
+ if (args.unlinkAll) {
69
+ body.unlinkAll = true;
70
+ } else if (args.testCaseKeys?.length) {
71
+ const uidMap = await fieldResolver.getResolver(ResolverKeys.SearchableField.TEST_CASE_KEY_TO_UID).resolveAndReturn(context.projectId, args.testCaseKeys);
72
+ const testCases = [];
73
+ for (const tcKey of args.testCaseKeys) {
74
+ const resolved = uidMap[tcKey];
75
+ if (resolved) {
76
+ testCases.push({
77
+ id: resolved.uid,
78
+ versionNo: resolved.latestVersion
79
+ });
80
+ } else {
81
+ warnings.push(
82
+ `Test case '${tcKey}' could not be resolved and was skipped.`
83
+ );
84
+ }
85
+ }
86
+ if (testCases.length > 0) body.testCases = testCases;
87
+ }
88
+ if (args.filter) {
89
+ body.filter = { ...args.filter, projectId: context.projectId };
90
+ }
91
+ await this.client.getApiClient().delete(ENDPOINTS.UNLINK_TESTCASES_FROM_CYCLE(cycleEntry.uid), body);
92
+ return {
93
+ structuredContent: TestCycleLinkResponse.parse({
94
+ cycleKey: args.cycleKey,
95
+ unlinked: true
96
+ }),
97
+ content: warnings.length > 0 ? [{ type: "text", text: `Note: ${warnings.join(" | ")}` }] : []
98
+ };
99
+ };
100
+ }
101
+ export {
102
+ UnlinkTestCasesFromCycle
103
+ };
@@ -1,5 +1,5 @@
1
1
  import { Tool } from "../../../common/tools.js";
2
- import { TOOL_NAMES, ENDPOINTS } from "../../config/constants.js";
2
+ import { TOOL_NAMES, TOOLSETS, ENDPOINTS } from "../../config/constants.js";
3
3
  import { ResolverKeys, InputField } from "../../config/field-resolution.types.js";
4
4
  import { UpdateTestCycleResponse, UpdateTestCycleBody } from "../../schema/update-test-cycle.schema.js";
5
5
  const SIMPLE_FIELD_CONFIG = {
@@ -33,6 +33,7 @@ async function resolveAddDelete(resolver, inputField, resolverKey, field, contex
33
33
  class UpdateTestCycle extends Tool {
34
34
  specification = {
35
35
  title: TOOL_NAMES.UPDATE_TEST_CYCLE.TITLE,
36
+ toolset: TOOLSETS.TEST_CYCLES,
36
37
  summary: TOOL_NAMES.UPDATE_TEST_CYCLE.SUMMARY,
37
38
  readOnly: false,
38
39
  idempotent: true,
@@ -4,6 +4,7 @@ import { Tool, ToolError } from "../../../common/tools.js";
4
4
  class AddPromptStep extends Tool {
5
5
  specification = {
6
6
  title: "Add Prompt Step",
7
+ toolset: "Recording",
7
8
  summary: "Add a natural language prompt step to an active Reflect recording session",
8
9
  readOnly: false,
9
10
  idempotent: false,
@@ -4,6 +4,7 @@ import { Tool, ToolError } from "../../../common/tools.js";
4
4
  class AddSegment extends Tool {
5
5
  specification = {
6
6
  title: "Add Segment",
7
+ toolset: "Recording",
7
8
  summary: "Insert a reusable test segment into an active Reflect recording session",
8
9
  readOnly: false,
9
10
  idempotent: false,
@@ -5,6 +5,7 @@ import { WebSocketManager } from "../../websocket-manager.js";
5
5
  class ConnectToSession extends Tool {
6
6
  specification = {
7
7
  title: "Connect To Session",
8
+ toolset: "Recording",
8
9
  summary: `Connect to an active Reflect recording session via WebSocket to enable interactive control. When creating or editing a Reflect test using a connected recording session, follow these guidelines:
9
10
 
10
11
  1. After connecting to a session, get the list of segments for the session's platform type so you know what actions could be added via segments vs needing to create new steps. Do not list tests, only list segments.
@@ -4,6 +4,7 @@ import { Tool, ToolError } from "../../../common/tools.js";
4
4
  class DeletePreviousStep extends Tool {
5
5
  specification = {
6
6
  title: "Delete Previous Step",
7
+ toolset: "Recording",
7
8
  summary: "Delete the last step added to an active Reflect recording session",
8
9
  readOnly: false,
9
10
  idempotent: false,
@@ -4,6 +4,7 @@ import { Tool, ToolError } from "../../../common/tools.js";
4
4
  class GetScreenshot extends Tool {
5
5
  specification = {
6
6
  title: "Get Screenshot",
7
+ toolset: "Recording",
7
8
  summary: "Capture a screenshot from the current state of an active Reflect recording session",
8
9
  readOnly: true,
9
10
  idempotent: true,