@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,107 @@
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 { RequirementTestCaseLinkResponse, LinkTestCasesToRequirementBody } from "../../schema/requirements.schema.js";
5
+ class LinkTestCasesToRequirement extends Tool {
6
+ specification = {
7
+ title: TOOL_NAMES.LINK_TESTCASES_TO_REQUIREMENT.TITLE,
8
+ summary: TOOL_NAMES.LINK_TESTCASES_TO_REQUIREMENT.SUMMARY,
9
+ readOnly: false,
10
+ idempotent: false,
11
+ toolset: TOOLSETS.REQUIREMENTS,
12
+ inputSchema: LinkTestCasesToRequirementBody,
13
+ outputSchema: RequirementTestCaseLinkResponse,
14
+ purpose: "Link test cases to a Jira requirement in QTM4J using the requirement's human-readable key. Test cases can be specified by their keys (e.g. 'SCRUM-TC-1'), which are 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
+ "Link specific test cases to a Jira story or bug by requirement key",
17
+ "Build traceability between Jira requirements and QTM4J test cases",
18
+ "Link all test cases matching a filter to a requirement",
19
+ "Associate test cases with a requirement as part of sprint planning"
20
+ ],
21
+ examples: [
22
+ {
23
+ description: "Link two test cases by key",
24
+ parameters: {
25
+ requirementKey: "SCRUM-1",
26
+ testCaseKeys: ["SCRUM-TC-10", "SCRUM-TC-11"]
27
+ },
28
+ expectedOutput: "Test cases linked to requirement SCRUM-1"
29
+ },
30
+ {
31
+ description: "Link test cases matching a filter",
32
+ parameters: {
33
+ requirementKey: "SCRUM-1",
34
+ filter: { priority: ["High"], status: ["To Do"] }
35
+ },
36
+ expectedOutput: "Filtered test cases linked to requirement"
37
+ },
38
+ {
39
+ description: "Link test cases in a specific folder",
40
+ parameters: {
41
+ requirementKey: "SCRUM-5",
42
+ filter: { folderId: 42, withChild: true },
43
+ sort: "key:asc"
44
+ },
45
+ expectedOutput: "Folder test cases linked to requirement"
46
+ }
47
+ ],
48
+ hints: [
49
+ "PREREQUISITE: set_project_context must be called before this tool. NEVER auto-select a project.",
50
+ "REQUIREMENT KEY FORMAT: '{PROJECT_KEY}-{number}' — e.g. 'SCRUM-1'.",
51
+ "TEST CASE KEY FORMAT: '{PROJECT_KEY}-TC-{number}' — e.g. 'SCRUM-TC-145'.",
52
+ "Provide either testCaseKeys or filter — not both.",
53
+ "projectId in filter is auto-filled from the active project context — do not set it manually.",
54
+ "filter.excludeRequirementId excludes test cases already linked to that requirement ID.",
55
+ "If a test case key cannot be resolved, it is reported in warnings and other test cases are still linked."
56
+ ],
57
+ outputDescription: "Confirmation with the requirement key and linked: true. Warnings included if any test cases could not be resolved or linked."
58
+ };
59
+ handle = async (rawArgs) => {
60
+ const args = LinkTestCasesToRequirementBody.parse(rawArgs);
61
+ const fieldResolver = this.client.getResolverRegistry();
62
+ const context = fieldResolver.requireProjectContext();
63
+ const warnings = [];
64
+ const reqMap = await fieldResolver.getResolver(ResolverKeys.SearchableField.REQUIREMENT_KEY_TO_ID).resolveAndReturn(context.projectId, [args.requirementKey]);
65
+ const reqEntry = reqMap[args.requirementKey];
66
+ if (!reqEntry) {
67
+ throw new ToolError(
68
+ `Requirement '${args.requirementKey}' not found in project '${context.projectKey}'. Verify the key is a valid Jira issue key.`
69
+ );
70
+ }
71
+ const requirementId = Number(reqEntry.id);
72
+ const body = {};
73
+ if (args.testCaseKeys?.length) {
74
+ const uidMap = await fieldResolver.getResolver(ResolverKeys.SearchableField.TEST_CASE_KEY_TO_UID).resolveAndReturn(context.projectId, args.testCaseKeys);
75
+ const testcases = [];
76
+ for (const tcKey of args.testCaseKeys) {
77
+ const resolved = uidMap[tcKey];
78
+ if (resolved) {
79
+ testcases.push({
80
+ id: resolved.uid,
81
+ versionNo: resolved.latestVersion
82
+ });
83
+ } else {
84
+ warnings.push(
85
+ `Test case '${tcKey}' could not be resolved and was skipped.`
86
+ );
87
+ }
88
+ }
89
+ if (testcases.length > 0) body.testcases = testcases;
90
+ }
91
+ if (args.filter) {
92
+ body.filter = { ...args.filter, projectId: context.projectId };
93
+ }
94
+ if (args.sort) body.sort = args.sort;
95
+ await this.client.getApiClient().post(ENDPOINTS.LINK_TESTCASES_TO_REQUIREMENT(requirementId), body);
96
+ return {
97
+ structuredContent: RequirementTestCaseLinkResponse.parse({
98
+ requirementKey: args.requirementKey,
99
+ linked: true
100
+ }),
101
+ content: warnings.length > 0 ? [{ type: "text", text: `Note: ${warnings.join(" | ")}` }] : []
102
+ };
103
+ };
104
+ }
105
+ export {
106
+ LinkTestCasesToRequirement
107
+ };
@@ -0,0 +1,97 @@
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 { RequirementTestCaseLinkResponse, UnlinkTestCasesFromRequirementBody } from "../../schema/requirements.schema.js";
5
+ class UnlinkTestCasesFromRequirement extends Tool {
6
+ specification = {
7
+ title: TOOL_NAMES.UNLINK_TESTCASES_FROM_REQUIREMENT.TITLE,
8
+ summary: TOOL_NAMES.UNLINK_TESTCASES_FROM_REQUIREMENT.SUMMARY,
9
+ readOnly: false,
10
+ idempotent: false,
11
+ toolset: TOOLSETS.REQUIREMENTS,
12
+ inputSchema: UnlinkTestCasesFromRequirementBody,
13
+ outputSchema: RequirementTestCaseLinkResponse,
14
+ purpose: "Unlink test cases from a Jira requirement in QTM4J using the requirement's human-readable key. Test cases can be specified by their keys (e.g. 'SCRUM-TC-1'), which are resolved to internal IDs and latest versions automatically. Alternatively, use a filter object to select test cases to unlink by criteria. The active project ID is injected automatically into the filter. Test cases that cannot be unlinked are reported in warnings. PREREQUISITE: set_project_context must be called before this tool.",
15
+ useCases: [
16
+ "Remove specific test cases from a Jira requirement",
17
+ "Unlink test cases matching a filter from a requirement",
18
+ "Clean up stale or incorrect test case links on a requirement",
19
+ "Remove traceability links as part of sprint cleanup"
20
+ ],
21
+ examples: [
22
+ {
23
+ description: "Unlink specific test cases by key",
24
+ parameters: {
25
+ requirementKey: "SCRUM-1",
26
+ testCaseKeys: ["SCRUM-TC-10", "SCRUM-TC-11"]
27
+ },
28
+ expectedOutput: "Test cases unlinked from requirement SCRUM-1"
29
+ },
30
+ {
31
+ description: "Unlink test cases matching a filter",
32
+ parameters: {
33
+ requirementKey: "SCRUM-1",
34
+ filter: { status: ["Done"], labels: ["Deprecated"] }
35
+ },
36
+ expectedOutput: "Filtered test cases unlinked from requirement"
37
+ }
38
+ ],
39
+ hints: [
40
+ "PREREQUISITE: set_project_context must be called before this tool. NEVER auto-select a project.",
41
+ "REQUIREMENT KEY FORMAT: '{PROJECT_KEY}-{number}' — e.g. 'SCRUM-1'.",
42
+ "TEST CASE KEY FORMAT: '{PROJECT_KEY}-TC-{number}' — e.g. 'SCRUM-TC-145'.",
43
+ "Provide either testCaseKeys or filter — not both.",
44
+ "projectId in filter is auto-filled from the active project context — do not set it manually.",
45
+ "filter.excludeTestCases can be used to exclude specific test cases when using filter-based unlinking.",
46
+ "If a test case key cannot be resolved, it is reported in warnings and other test cases are still unlinked."
47
+ ],
48
+ outputDescription: "Confirmation with the requirement key and unlinked: true. Warnings included if any test cases could not be resolved or unlinked."
49
+ };
50
+ handle = async (rawArgs) => {
51
+ const args = UnlinkTestCasesFromRequirementBody.parse(rawArgs);
52
+ const fieldResolver = this.client.getResolverRegistry();
53
+ const context = fieldResolver.requireProjectContext();
54
+ const warnings = [];
55
+ const reqMap = await fieldResolver.getResolver(ResolverKeys.SearchableField.REQUIREMENT_KEY_TO_ID).resolveAndReturn(context.projectId, [args.requirementKey]);
56
+ const reqEntry = reqMap[args.requirementKey];
57
+ if (!reqEntry) {
58
+ throw new ToolError(
59
+ `Requirement '${args.requirementKey}' not found in project '${context.projectKey}'. Verify the key is a valid Jira issue key.`
60
+ );
61
+ }
62
+ const requirementId = Number(reqEntry.id);
63
+ const body = {};
64
+ if (args.testCaseKeys?.length) {
65
+ const uidMap = await fieldResolver.getResolver(ResolverKeys.SearchableField.TEST_CASE_KEY_TO_UID).resolveAndReturn(context.projectId, args.testCaseKeys);
66
+ const testcases = [];
67
+ for (const tcKey of args.testCaseKeys) {
68
+ const resolved = uidMap[tcKey];
69
+ if (resolved) {
70
+ testcases.push({
71
+ id: resolved.uid,
72
+ versionNo: resolved.latestVersion
73
+ });
74
+ } else {
75
+ warnings.push(
76
+ `Test case '${tcKey}' could not be resolved and was skipped.`
77
+ );
78
+ }
79
+ }
80
+ if (testcases.length > 0) body.testcases = testcases;
81
+ }
82
+ if (args.filter) {
83
+ body.filter = { ...args.filter, projectId: context.projectId };
84
+ }
85
+ await this.client.getApiClient().delete(ENDPOINTS.UNLINK_TESTCASES_FROM_REQUIREMENT(requirementId), body);
86
+ return {
87
+ structuredContent: RequirementTestCaseLinkResponse.parse({
88
+ requirementKey: args.requirementKey,
89
+ unlinked: true
90
+ }),
91
+ content: warnings.length > 0 ? [{ type: "text", text: `Note: ${warnings.join(" | ")}` }] : []
92
+ };
93
+ };
94
+ }
95
+ export {
96
+ UnlinkTestCasesFromRequirement
97
+ };
@@ -1,9 +1,10 @@
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 { GetAutomationHistoryResponse, GetAutomationHistoryBody } from "../../schema/automation.schema.js";
4
4
  class GetAutomationHistory extends Tool {
5
5
  specification = {
6
6
  title: TOOL_NAMES.GET_AUTOMATION_HISTORY.TITLE,
7
+ toolset: TOOLSETS.TEST_AUTOMATION,
7
8
  summary: TOOL_NAMES.GET_AUTOMATION_HISTORY.SUMMARY,
8
9
  readOnly: true,
9
10
  idempotent: true,
@@ -1,12 +1,13 @@
1
1
  import { readFile } from "node:fs/promises";
2
2
  import { extname } from "node:path";
3
3
  import { Tool, ToolError } from "../../../common/tools.js";
4
- import { TOOL_NAMES, AUTOMATION_RESULT_DIRS, AUTOMATION_LIMITS, ENDPOINTS } from "../../config/constants.js";
4
+ import { TOOL_NAMES, TOOLSETS, AUTOMATION_RESULT_DIRS, AUTOMATION_LIMITS, ENDPOINTS } from "../../config/constants.js";
5
5
  import { UploadAutomationResultResponse, UploadAutomationResultBody } from "../../schema/automation.schema.js";
6
6
  const SUPPORTED_EXTENSIONS = /* @__PURE__ */ new Set([".xml", ".json", ".zip"]);
7
7
  class UploadAutomationResult extends Tool {
8
8
  specification = {
9
9
  title: TOOL_NAMES.UPLOAD_AUTOMATION_RESULT.TITLE,
10
+ toolset: TOOLSETS.TEST_AUTOMATION,
10
11
  summary: TOOL_NAMES.UPLOAD_AUTOMATION_RESULT.SUMMARY,
11
12
  readOnly: false,
12
13
  idempotent: false,
@@ -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 { CreateTestCaseResponse, CreateTestCaseBody } from "../../schema/test-case.schema.js";
5
5
  const FIELD_CONFIG = {
@@ -13,6 +13,7 @@ class CreateTestCase extends Tool {
13
13
  // ─── Tool Specification ────────────────────────────────────────────────────
14
14
  specification = {
15
15
  title: TOOL_NAMES.CREATE_TEST_CASE.TITLE,
16
+ toolset: TOOLSETS.TEST_CASES,
16
17
  summary: TOOL_NAMES.CREATE_TEST_CASE.SUMMARY,
17
18
  readOnly: false,
18
19
  idempotent: false,
@@ -0,0 +1,67 @@
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, GetLinkedRequirementsBody } from "../../schema/linked-items.schema.js";
5
+ class GetLinkedRequirements extends Tool {
6
+ specification = {
7
+ title: TOOL_NAMES.GET_LINKED_REQUIREMENTS.TITLE,
8
+ summary: TOOL_NAMES.GET_LINKED_REQUIREMENTS.SUMMARY,
9
+ readOnly: true,
10
+ idempotent: true,
11
+ toolset: TOOLSETS.TEST_CASES,
12
+ inputSchema: GetLinkedRequirementsBody,
13
+ outputSchema: GetLinkedRequirementsResponse,
14
+ purpose: "Retrieve all Jira requirements linked to a specific test case in QTM4J. The test case's human-readable key (e.g. 'SCRUM-TC-145') 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.",
15
+ useCases: [
16
+ "Check which Jira stories or bugs a test case covers",
17
+ "Audit requirement traceability for a test case",
18
+ "Retrieve requirement keys to use in other operations",
19
+ "Verify that the correct requirements are linked to a test case before a release"
20
+ ],
21
+ examples: [
22
+ {
23
+ description: "Get all requirements linked to a test case",
24
+ parameters: { key: "SCRUM-TC-145" },
25
+ expectedOutput: "Paginated list of linked requirements with Jira metadata"
26
+ },
27
+ {
28
+ description: "Get requirements for a specific version",
29
+ parameters: { key: "SCRUM-TC-85", versionNo: 2, maxResults: 20 },
30
+ expectedOutput: "Requirements linked to version 2 of the test case"
31
+ }
32
+ ],
33
+ hints: [
34
+ "PREREQUISITE: set_project_context must be called before this tool. NEVER auto-select a project.",
35
+ "KEY FORMAT: '{PROJECT_KEY}-TC-{number}' — e.g. 'SCRUM-TC-145'.",
36
+ "versionNo defaults to the latest version. Use search_test_cases to find available versions.",
37
+ "Paginate using startAt — increment by maxResults until startAt >= total."
38
+ ],
39
+ outputDescription: "Paginated list with total, startAt, maxResults, and data array of linked requirement objects (id, key, summary, status, priority, issueType)."
40
+ };
41
+ handle = async (rawArgs) => {
42
+ const args = GetLinkedRequirementsBody.parse(rawArgs);
43
+ const fieldResolver = this.client.getResolverRegistry();
44
+ const context = fieldResolver.requireProjectContext();
45
+ const uidMap = await fieldResolver.getResolver(ResolverKeys.SearchableField.TEST_CASE_KEY_TO_UID).resolveAndReturn(context.projectId, [args.key]);
46
+ const entry = uidMap[args.key];
47
+ if (!entry) {
48
+ throw new ToolError(
49
+ `Test case '${args.key}' not found in project '${context.projectKey}'. Verify the key using the search_test_cases tool.`
50
+ );
51
+ }
52
+ const params = {
53
+ maxResults: args.maxResults,
54
+ startAt: args.startAt,
55
+ sort: args.sort,
56
+ tcVersionNo: args.versionNo
57
+ };
58
+ const response = await this.client.getApiClient().get(ENDPOINTS.GET_LINKED_REQUIREMENTS(entry.uid), params);
59
+ return {
60
+ structuredContent: GetLinkedRequirementsResponse.parse(response),
61
+ content: []
62
+ };
63
+ };
64
+ }
65
+ export {
66
+ GetLinkedRequirements
67
+ };
@@ -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 { SearchTestCaseResponse, SearchTestCaseBody } from "../../schema/get-test-case.schema.js";
4
4
  class GetTestCases extends Tool {
5
5
  specification = {
6
6
  title: TOOL_NAMES.SEARCH_TEST_CASES.TITLE,
7
+ toolset: TOOLSETS.TEST_CASES,
7
8
  summary: TOOL_NAMES.SEARCH_TEST_CASES.SUMMARY,
8
9
  readOnly: true,
9
10
  idempotent: true,
@@ -1,10 +1,11 @@
1
1
  import { Tool, ToolError } 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 { ResolverKeys } from "../../config/field-resolution.types.js";
4
4
  import { GetTestStepsResponse, GetTestStepsBody } from "../../schema/get-test-steps.schema.js";
5
5
  class GetTestSteps extends Tool {
6
6
  specification = {
7
7
  title: TOOL_NAMES.GET_TEST_STEPS.TITLE,
8
+ toolset: TOOLSETS.TEST_CASES,
8
9
  summary: TOOL_NAMES.GET_TEST_STEPS.SUMMARY,
9
10
  readOnly: true,
10
11
  idempotent: true,
@@ -0,0 +1,124 @@
1
+ import zod__default from "zod";
2
+ import { Tool, ToolError } from "../../../common/tools.js";
3
+ import { TOOLSETS, TOOL_NAMES, ENDPOINTS } from "../../config/constants.js";
4
+ import { ResolverKeys } from "../../config/field-resolution.types.js";
5
+ const LinkRequirementsBody = zod__default.object({
6
+ key: zod__default.string().describe(
7
+ "Test case key in '{PROJECT_KEY}-TC-{number}' format (e.g., 'SCRUM-TC-145'). Required."
8
+ ),
9
+ versionNo: zod__default.number().int().optional().describe("Test case version number. Defaults to the latest version."),
10
+ requirementKeys: zod__default.array(zod__default.string()).optional().describe(
11
+ "List of requirement keys to link (e.g., ['SCRUM-1', 'SCRUM-2']). Resolved to internal IDs automatically. Provide this OR filter.jql — not both."
12
+ ),
13
+ filter: zod__default.object({
14
+ jql: zod__default.string().describe(
15
+ 'JQL query to filter requirements to link (e.g., "project = DEMO AND issuetype = Story").'
16
+ )
17
+ }).optional().describe(
18
+ "JQL filter to select requirements to link. Use instead of requirementKeys when filtering by JQL."
19
+ )
20
+ });
21
+ const LinkRequirementsResponse = zod__default.object({
22
+ key: zod__default.string(),
23
+ versionNo: zod__default.number().int(),
24
+ linked: zod__default.literal(true)
25
+ });
26
+ class LinkRequirements extends Tool {
27
+ specification = {
28
+ title: TOOL_NAMES.LINK_REQUIREMENTS.TITLE,
29
+ summary: TOOL_NAMES.LINK_REQUIREMENTS.SUMMARY,
30
+ readOnly: false,
31
+ idempotent: false,
32
+ toolset: TOOLSETS.TEST_CASES,
33
+ inputSchema: LinkRequirementsBody,
34
+ outputSchema: LinkRequirementsResponse,
35
+ purpose: "Link Jira requirements to a test case in QTM4J using the test case's human-readable key. 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.",
36
+ useCases: [
37
+ "Link one or more Jira requirements (stories, bugs, epics) to a test case",
38
+ "Associate requirements with a test case using a JQL filter",
39
+ "Build traceability between requirements and test cases",
40
+ "Link requirements to a specific test case version"
41
+ ],
42
+ examples: [
43
+ {
44
+ description: "Link two requirements by key",
45
+ parameters: {
46
+ key: "SCRUM-TC-145",
47
+ requirementKeys: ["SCRUM-1", "SCRUM-2"]
48
+ },
49
+ expectedOutput: "Requirements SCRUM-1 and SCRUM-2 linked to test case"
50
+ },
51
+ {
52
+ description: "Link requirements by JQL filter",
53
+ parameters: {
54
+ key: "SCRUM-TC-145",
55
+ filter: { jql: "project = DEMO AND issuetype = Story" }
56
+ },
57
+ expectedOutput: "Requirements matched by JQL linked to test case"
58
+ },
59
+ {
60
+ description: "Link a requirement to a specific version",
61
+ parameters: {
62
+ key: "SCRUM-TC-85",
63
+ versionNo: 2,
64
+ requirementKeys: ["SCRUM-10"]
65
+ },
66
+ expectedOutput: "Requirement linked to version 2 of test case"
67
+ }
68
+ ],
69
+ hints: [
70
+ "PREREQUISITE: set_project_context must be called before this tool. NEVER auto-select a project.",
71
+ "KEY FORMAT: '{PROJECT_KEY}-TC-{number}' — e.g. 'SCRUM-TC-145'.",
72
+ "Requirement keys follow the Jira issue key format: '{PROJECT_KEY}-{number}' (e.g. 'SCRUM-1').",
73
+ "Provide either requirementKeys or filter.jql — not both.",
74
+ "If a requirement key cannot be resolved or linked, it is reported in warnings and other requirements are still linked.",
75
+ "versionNo defaults to the latest version. Use search_test_cases to find available versions if needed."
76
+ ],
77
+ outputDescription: "Confirmation with the test case key, version number, and linked: true. Warnings are included if any requirements could not be resolved or linked."
78
+ };
79
+ handle = async (rawArgs) => {
80
+ const args = LinkRequirementsBody.parse(rawArgs);
81
+ const fieldResolver = this.client.getResolverRegistry();
82
+ const context = fieldResolver.requireProjectContext();
83
+ const warnings = [];
84
+ const uidMap = await fieldResolver.getResolver(ResolverKeys.SearchableField.TEST_CASE_KEY_TO_UID).resolveAndReturn(context.projectId, [args.key]);
85
+ const entry = uidMap[args.key];
86
+ if (!entry) {
87
+ throw new ToolError(
88
+ `Test case '${args.key}' not found in project '${context.projectKey}'. Verify the key using the search_test_cases tool.`
89
+ );
90
+ }
91
+ const versionNo = args.versionNo ?? entry.latestVersion;
92
+ const body = {};
93
+ if (args.requirementKeys?.length) {
94
+ const reqMap = await fieldResolver.getResolver(ResolverKeys.SearchableField.REQUIREMENT_KEY_TO_ID).resolveAndReturn(context.projectId, args.requirementKeys);
95
+ const requirementIds = [];
96
+ for (const reqKey of args.requirementKeys) {
97
+ const resolved = reqMap[reqKey];
98
+ if (resolved) {
99
+ requirementIds.push(Number(resolved.id));
100
+ } else {
101
+ warnings.push(
102
+ `Requirement '${reqKey}' could not be resolved and was skipped.`
103
+ );
104
+ }
105
+ }
106
+ if (requirementIds.length > 0) body.requirementIds = requirementIds;
107
+ }
108
+ if (args.filter) {
109
+ body.filter = args.filter;
110
+ }
111
+ await this.client.getApiClient().post(ENDPOINTS.LINK_REQUIREMENTS(entry.uid, versionNo), body);
112
+ return {
113
+ structuredContent: LinkRequirementsResponse.parse({
114
+ key: args.key,
115
+ versionNo,
116
+ linked: true
117
+ }),
118
+ content: warnings.length > 0 ? [{ type: "text", text: `Note: ${warnings.join(" | ")}` }] : []
119
+ };
120
+ };
121
+ }
122
+ export {
123
+ LinkRequirements
124
+ };
@@ -0,0 +1,116 @@
1
+ import zod__default from "zod";
2
+ import { Tool, ToolError } from "../../../common/tools.js";
3
+ import { TOOLSETS, TOOL_NAMES, ENDPOINTS } from "../../config/constants.js";
4
+ import { ResolverKeys } from "../../config/field-resolution.types.js";
5
+ const UnlinkRequirementsBody = zod__default.object({
6
+ key: zod__default.string().describe(
7
+ "Test case key in '{PROJECT_KEY}-TC-{number}' format (e.g., 'SCRUM-TC-145'). Required."
8
+ ),
9
+ versionNo: zod__default.number().int().optional().describe("Test case version number. Defaults to the latest version."),
10
+ requirementKeys: zod__default.array(zod__default.string()).optional().describe(
11
+ "List of requirement keys to unlink (e.g., ['SCRUM-1', 'SCRUM-2']). Ignored when unLinkAll is true."
12
+ ),
13
+ unLinkAll: zod__default.boolean().optional().describe(
14
+ "If true, all linked requirements are unlinked from this test case. Ignores requirementKeys when set."
15
+ )
16
+ });
17
+ const UnlinkRequirementsResponse = zod__default.object({
18
+ key: zod__default.string(),
19
+ versionNo: zod__default.number().int(),
20
+ unlinked: zod__default.literal(true)
21
+ });
22
+ class UnlinkRequirements extends Tool {
23
+ specification = {
24
+ title: TOOL_NAMES.UNLINK_REQUIREMENTS.TITLE,
25
+ summary: TOOL_NAMES.UNLINK_REQUIREMENTS.SUMMARY,
26
+ readOnly: false,
27
+ idempotent: false,
28
+ toolset: TOOLSETS.TEST_CASES,
29
+ inputSchema: UnlinkRequirementsBody,
30
+ outputSchema: UnlinkRequirementsResponse,
31
+ purpose: "Unlink Jira requirements from a test case in QTM4J using the test case's human-readable key. Specify individual requirement keys to unlink (resolved to internal IDs automatically), or set unLinkAll to true to remove all linked requirements in one call. Requirements that cannot be unlinked are reported as warnings. PREREQUISITE: set_project_context must be called before this tool.",
32
+ useCases: [
33
+ "Remove specific requirements from a test case",
34
+ "Unlink all requirements from a test case at once",
35
+ "Clean up stale or incorrect requirement links from a test case",
36
+ "Remove requirement associations from a specific test case version"
37
+ ],
38
+ examples: [
39
+ {
40
+ description: "Unlink specific requirements by key",
41
+ parameters: {
42
+ key: "SCRUM-TC-145",
43
+ requirementKeys: ["SCRUM-1", "SCRUM-2"]
44
+ },
45
+ expectedOutput: "Requirements SCRUM-1 and SCRUM-2 unlinked from test case"
46
+ },
47
+ {
48
+ description: "Unlink all requirements from a test case",
49
+ parameters: { key: "SCRUM-TC-145", unLinkAll: true },
50
+ expectedOutput: "All requirements unlinked from test case"
51
+ },
52
+ {
53
+ description: "Unlink a requirement from a specific version",
54
+ parameters: {
55
+ key: "SCRUM-TC-85",
56
+ versionNo: 2,
57
+ requirementKeys: ["SCRUM-10"]
58
+ },
59
+ expectedOutput: "Requirement unlinked from version 2 of test case"
60
+ }
61
+ ],
62
+ hints: [
63
+ "PREREQUISITE: set_project_context must be called before this tool. NEVER auto-select a project.",
64
+ "KEY FORMAT: '{PROJECT_KEY}-TC-{number}' — e.g. 'SCRUM-TC-145'.",
65
+ "Requirement keys follow the Jira issue key format: '{PROJECT_KEY}-{number}' (e.g. 'SCRUM-1').",
66
+ "Set unLinkAll to true to remove all requirements in one call — no need to list them individually.",
67
+ "If a requirement key cannot be resolved or unlinked, it is reported in warnings and others are still unlinked.",
68
+ "versionNo defaults to the latest version. Use search_test_cases to find available versions if needed."
69
+ ],
70
+ outputDescription: "Confirmation with the test case key, version number, and unlinked: true. Warnings are included if any requirements could not be resolved or unlinked."
71
+ };
72
+ handle = async (rawArgs) => {
73
+ const args = UnlinkRequirementsBody.parse(rawArgs);
74
+ const fieldResolver = this.client.getResolverRegistry();
75
+ const context = fieldResolver.requireProjectContext();
76
+ const warnings = [];
77
+ const uidMap = await fieldResolver.getResolver(ResolverKeys.SearchableField.TEST_CASE_KEY_TO_UID).resolveAndReturn(context.projectId, [args.key]);
78
+ const entry = uidMap[args.key];
79
+ if (!entry) {
80
+ throw new ToolError(
81
+ `Test case '${args.key}' not found in project '${context.projectKey}'. Verify the key using the search_test_cases tool.`
82
+ );
83
+ }
84
+ const versionNo = args.versionNo ?? entry.latestVersion;
85
+ const body = {};
86
+ if (args.unLinkAll) {
87
+ body.unLinkAll = true;
88
+ } else if (args.requirementKeys?.length) {
89
+ const reqMap = await fieldResolver.getResolver(ResolverKeys.SearchableField.REQUIREMENT_KEY_TO_ID).resolveAndReturn(context.projectId, args.requirementKeys);
90
+ const requirementIds = [];
91
+ for (const reqKey of args.requirementKeys) {
92
+ const resolved = reqMap[reqKey];
93
+ if (resolved) {
94
+ requirementIds.push(Number(resolved.id));
95
+ } else {
96
+ warnings.push(
97
+ `Requirement '${reqKey}' could not be resolved and was skipped.`
98
+ );
99
+ }
100
+ }
101
+ if (requirementIds.length > 0) body.requirementIds = requirementIds;
102
+ }
103
+ await this.client.getApiClient().post(ENDPOINTS.UNLINK_REQUIREMENTS(entry.uid, versionNo), body);
104
+ return {
105
+ structuredContent: UnlinkRequirementsResponse.parse({
106
+ key: args.key,
107
+ versionNo,
108
+ unlinked: true
109
+ }),
110
+ content: warnings.length > 0 ? [{ type: "text", text: `Note: ${warnings.join(" | ")}` }] : []
111
+ };
112
+ };
113
+ }
114
+ export {
115
+ UnlinkRequirements
116
+ };
@@ -1,5 +1,5 @@
1
1
  import { Tool, ToolError } 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 { UpdateTestCaseResponse, UpdateTestCaseBody } from "../../schema/update-test-case.schema.js";
5
5
  const SIMPLE_FIELD_CONFIG = {
@@ -33,6 +33,7 @@ async function resolveAddDelete(resolver, inputField, resolverKey, field, contex
33
33
  class UpdateTestCase extends Tool {
34
34
  specification = {
35
35
  title: TOOL_NAMES.UPDATE_TEST_CASE.TITLE,
36
+ toolset: TOOLSETS.TEST_CASES,
36
37
  summary: TOOL_NAMES.UPDATE_TEST_CASE.SUMMARY,
37
38
  readOnly: false,
38
39
  idempotent: true,
@@ -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 { CreateTestCycleResponse, CreateTestCycleBody } from "../../schema/test-cycle.schema.js";
5
5
  const FIELD_CONFIG = {
@@ -13,6 +13,7 @@ class CreateTestCycle extends Tool {
13
13
  // ─── Tool Specification ────────────────────────────────────────────────────
14
14
  specification = {
15
15
  title: TOOL_NAMES.CREATE_TEST_CYCLE.TITLE,
16
+ toolset: TOOLSETS.TEST_CYCLES,
16
17
  summary: TOOL_NAMES.CREATE_TEST_CYCLE.SUMMARY,
17
18
  readOnly: false,
18
19
  idempotent: false,