@smartbear/mcp 0.23.0 → 0.25.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.
Files changed (148) hide show
  1. package/README.md +10 -2
  2. package/dist/bearq/client.js +12 -13
  3. package/dist/bearq/tool/tasks/chat-with-qa-lead.js +1 -0
  4. package/dist/bearq/tool/tasks/expand-application-model.js +1 -0
  5. package/dist/bearq/tool/tasks/get-task-status.js +1 -0
  6. package/dist/bearq/tool/tasks/get-task.js +1 -0
  7. package/dist/bearq/tool/tasks/refine-all-draft-tests.js +1 -0
  8. package/dist/bearq/tool/tasks/refine-test-cases.js +1 -0
  9. package/dist/bearq/tool/tasks/refine-tests-in-functional-areas.js +1 -0
  10. package/dist/bearq/tool/tasks/run-regression-tests.js +1 -0
  11. package/dist/bearq/tool/tasks/run-test-cases.js +1 -0
  12. package/dist/bearq/tool/tasks/run-tests-in-functional-areas.js +1 -0
  13. package/dist/bearq/tool/tasks/stop-task.js +1 -0
  14. package/dist/bearq/tool/tasks/wait-for-task.js +1 -0
  15. package/dist/bugsnag/client.js +24 -44
  16. package/dist/bugsnag/tool/error/get-error.js +1 -0
  17. package/dist/bugsnag/tool/error/list-project-errors.js +1 -0
  18. package/dist/bugsnag/tool/error/update-error.js +1 -0
  19. package/dist/bugsnag/tool/event/get-event-details-from-dashboard-url.js +1 -0
  20. package/dist/bugsnag/tool/event/get-event.js +1 -0
  21. package/dist/bugsnag/tool/event/list-error-events.js +1 -0
  22. package/dist/bugsnag/tool/performance/get-network-endpoint-groupings.js +1 -0
  23. package/dist/bugsnag/tool/performance/get-span-group.js +1 -0
  24. package/dist/bugsnag/tool/performance/get-trace.js +1 -0
  25. package/dist/bugsnag/tool/performance/list-span-groups.js +1 -0
  26. package/dist/bugsnag/tool/performance/list-spans.js +1 -0
  27. package/dist/bugsnag/tool/performance/list-trace-fields.js +1 -0
  28. package/dist/bugsnag/tool/performance/set-network-endpoint-groupings.js +1 -0
  29. package/dist/bugsnag/tool/project/get-current-project.js +1 -0
  30. package/dist/bugsnag/tool/project/list-project-event-filters.js +1 -0
  31. package/dist/bugsnag/tool/project/list-projects.js +1 -0
  32. package/dist/bugsnag/tool/release/get-build.js +1 -0
  33. package/dist/bugsnag/tool/release/get-release.js +1 -0
  34. package/dist/bugsnag/tool/release/list-releases.js +1 -0
  35. package/dist/collaborator/client.js +24 -19
  36. package/dist/common/client-registry.js +63 -29
  37. package/dist/common/server.js +57 -1
  38. package/dist/common/transport-http.js +90 -69
  39. package/dist/common/transport-stdio.js +29 -24
  40. package/dist/package.json.js +1 -1
  41. package/dist/pactflow/client/base.js +3 -3
  42. package/dist/pactflow/client/tools.js +102 -0
  43. package/dist/pactflow/client.js +30 -43
  44. package/dist/qmetry/client/tools/automation-tools.js +2 -0
  45. package/dist/qmetry/client/tools/issue-tools.js +6 -0
  46. package/dist/qmetry/client/tools/project-tools.js +9 -0
  47. package/dist/qmetry/client/tools/requirement-tools.js +5 -0
  48. package/dist/qmetry/client/tools/testcase-tools.js +7 -0
  49. package/dist/qmetry/client/tools/testsuite-tools.js +11 -0
  50. package/dist/qmetry/client.js +20 -18
  51. package/dist/qtm4j/client.js +57 -23
  52. package/dist/qtm4j/config/constants.js +197 -5
  53. package/dist/qtm4j/config/field-resolution.types.js +5 -2
  54. package/dist/qtm4j/http/api-client.js +90 -3
  55. package/dist/qtm4j/resolver/cache/cache.js +1 -1
  56. package/dist/qtm4j/resolver/resolver-registry.js +7 -1
  57. package/dist/qtm4j/resolver/resolvers/common-attribute-resolver.js +1 -0
  58. package/dist/qtm4j/resolver/resolvers/component-resolver.js +2 -0
  59. package/dist/qtm4j/resolver/resolvers/label-resolver.js +2 -0
  60. package/dist/qtm4j/resolver/resolvers/requirement-id-resolver.js +28 -0
  61. package/dist/qtm4j/resolver/resolvers/test-cycle-uid-resolver.js +28 -0
  62. package/dist/qtm4j/schema/automation.schema.js +107 -0
  63. package/dist/qtm4j/schema/linked-items.schema.js +95 -0
  64. package/dist/qtm4j/schema/requirements.schema.js +109 -0
  65. package/dist/qtm4j/schema/search-test-cycle.schema.js +133 -0
  66. package/dist/qtm4j/schema/test-cycle.link.schema.js +260 -0
  67. package/dist/qtm4j/schema/test-cycle.schema.js +39 -0
  68. package/dist/qtm4j/schema/update-test-cycle.schema.js +54 -0
  69. package/dist/qtm4j/tool/project/get-projects.js +2 -1
  70. package/dist/qtm4j/tool/project/set-project-context.js +2 -1
  71. package/dist/qtm4j/tool/requirement/get-linked-testcases.js +93 -0
  72. package/dist/qtm4j/tool/requirement/link-testcases.js +107 -0
  73. package/dist/qtm4j/tool/requirement/unlink-testcases.js +97 -0
  74. package/dist/qtm4j/tool/test-automation/get-automation-history.js +70 -0
  75. package/dist/qtm4j/tool/test-automation/upload-automation-result.js +144 -0
  76. package/dist/qtm4j/tool/test-case/create-test-case.js +2 -1
  77. package/dist/qtm4j/tool/test-case/get-linked-requirements.js +67 -0
  78. package/dist/qtm4j/tool/test-case/get-test-cases.js +2 -1
  79. package/dist/qtm4j/tool/test-case/get-test-steps.js +2 -1
  80. package/dist/qtm4j/tool/test-case/link-requirements.js +124 -0
  81. package/dist/qtm4j/tool/test-case/unlink-requirements.js +116 -0
  82. package/dist/qtm4j/tool/test-case/update-test-case.js +2 -1
  83. package/dist/qtm4j/tool/test-cycle/create-test-cycle.js +81 -0
  84. package/dist/qtm4j/tool/test-cycle/get-linked-requirements.js +71 -0
  85. package/dist/qtm4j/tool/test-cycle/link-requirements.js +91 -0
  86. package/dist/qtm4j/tool/test-cycle/link-testcases.js +118 -0
  87. package/dist/qtm4j/tool/test-cycle/search-linked-testcases.js +114 -0
  88. package/dist/qtm4j/tool/test-cycle/search-test-cycle.js +137 -0
  89. package/dist/qtm4j/tool/test-cycle/unlink-requirements.js +87 -0
  90. package/dist/qtm4j/tool/test-cycle/unlink-testcases.js +103 -0
  91. package/dist/qtm4j/tool/test-cycle/update-test-cycle.js +162 -0
  92. package/dist/reflect/client.js +15 -24
  93. package/dist/reflect/config/constants.js +0 -2
  94. package/dist/reflect/tool/recording/add-prompt-step.js +1 -0
  95. package/dist/reflect/tool/recording/add-segment.js +1 -0
  96. package/dist/reflect/tool/recording/connect-to-session.js +1 -0
  97. package/dist/reflect/tool/recording/delete-previous-step.js +1 -0
  98. package/dist/reflect/tool/recording/get-screenshot.js +1 -0
  99. package/dist/reflect/tool/suites/cancel-suite-execution.js +1 -0
  100. package/dist/reflect/tool/suites/execute-suite.js +1 -0
  101. package/dist/reflect/tool/suites/get-suite-execution-status.js +1 -0
  102. package/dist/reflect/tool/suites/list-suite-executions.js +1 -0
  103. package/dist/reflect/tool/suites/list-suites.js +1 -0
  104. package/dist/reflect/tool/tests/get-test-status.js +1 -0
  105. package/dist/reflect/tool/tests/list-segments.js +1 -0
  106. package/dist/reflect/tool/tests/list-tests.js +1 -0
  107. package/dist/reflect/tool/tests/run-test.js +1 -0
  108. package/dist/swagger/client/portal-types.js +1 -1
  109. package/dist/swagger/client/tools.js +23 -0
  110. package/dist/swagger/client.js +25 -28
  111. package/dist/zephyr/client.js +14 -21
  112. package/dist/zephyr/tool/environment/get-environments.js +1 -0
  113. package/dist/zephyr/tool/folder/create-folder.js +1 -0
  114. package/dist/zephyr/tool/issue-link/get-test-cases.js +1 -0
  115. package/dist/zephyr/tool/issue-link/get-test-cycles.js +1 -0
  116. package/dist/zephyr/tool/issue-link/get-test-executions.js +1 -0
  117. package/dist/zephyr/tool/priority/get-priorities.js +1 -0
  118. package/dist/zephyr/tool/project/get-project.js +1 -0
  119. package/dist/zephyr/tool/project/get-projects.js +1 -0
  120. package/dist/zephyr/tool/status/get-statuses.js +1 -0
  121. package/dist/zephyr/tool/test-case/create-issue-link.js +1 -0
  122. package/dist/zephyr/tool/test-case/create-test-case.js +1 -0
  123. package/dist/zephyr/tool/test-case/create-test-script.js +1 -0
  124. package/dist/zephyr/tool/test-case/create-test-steps.js +1 -0
  125. package/dist/zephyr/tool/test-case/create-web-link.js +1 -0
  126. package/dist/zephyr/tool/test-case/get-links.js +1 -0
  127. package/dist/zephyr/tool/test-case/get-test-case.js +1 -0
  128. package/dist/zephyr/tool/test-case/get-test-cases.js +1 -0
  129. package/dist/zephyr/tool/test-case/get-test-script.js +1 -0
  130. package/dist/zephyr/tool/test-case/get-test-steps.js +1 -0
  131. package/dist/zephyr/tool/test-case/update-test-case.js +1 -0
  132. package/dist/zephyr/tool/test-cycle/create-issue-link.js +1 -0
  133. package/dist/zephyr/tool/test-cycle/create-test-cycle.js +1 -0
  134. package/dist/zephyr/tool/test-cycle/create-web-link.js +1 -0
  135. package/dist/zephyr/tool/test-cycle/get-links.js +1 -0
  136. package/dist/zephyr/tool/test-cycle/get-test-cycle.js +1 -0
  137. package/dist/zephyr/tool/test-cycle/get-test-cycles.js +1 -0
  138. package/dist/zephyr/tool/test-cycle/update-test-cycle.js +1 -0
  139. package/dist/zephyr/tool/test-execution/create-issue-link.js +1 -0
  140. package/dist/zephyr/tool/test-execution/create-test-execution.js +1 -0
  141. package/dist/zephyr/tool/test-execution/get-test-execution-links.js +1 -0
  142. package/dist/zephyr/tool/test-execution/get-test-execution.js +1 -0
  143. package/dist/zephyr/tool/test-execution/get-test-executions.js +1 -0
  144. package/dist/zephyr/tool/test-execution/get-test-steps.js +1 -0
  145. package/dist/zephyr/tool/test-execution/update-test-execution.js +1 -0
  146. package/dist/zephyr/tool/test-execution/update-test-steps.js +1 -0
  147. package/package.json +1 -1
  148. package/dist/common/request-context.js +0 -20
@@ -0,0 +1,162 @@
1
+ import { Tool } from "../../../common/tools.js";
2
+ import { TOOL_NAMES, TOOLSETS, ENDPOINTS } from "../../config/constants.js";
3
+ import { ResolverKeys, InputField } from "../../config/field-resolution.types.js";
4
+ import { UpdateTestCycleResponse, UpdateTestCycleBody } from "../../schema/update-test-cycle.schema.js";
5
+ const SIMPLE_FIELD_CONFIG = {
6
+ [InputField.PRIORITY]: ResolverKeys.CommonAttribute.PRIORITY,
7
+ [InputField.STATUS]: ResolverKeys.CommonAttribute.TEST_CYCLE_STATUS
8
+ };
9
+ const ADD_DELETE_FIELD_CONFIG = {
10
+ [InputField.LABELS]: ResolverKeys.SearchableField.LABEL,
11
+ [InputField.COMPONENTS]: ResolverKeys.SearchableField.COMPONENTS
12
+ };
13
+ async function resolveAddDelete(resolver, inputField, resolverKey, field, context, warnings) {
14
+ const result = {};
15
+ for (const op of ["add", "delete"]) {
16
+ const names = field[op];
17
+ if (!names?.length) continue;
18
+ const tempBody = { [inputField]: names };
19
+ await resolver.resolve(
20
+ inputField,
21
+ resolverKey,
22
+ tempBody,
23
+ context,
24
+ warnings
25
+ );
26
+ const val = tempBody[inputField];
27
+ if (Array.isArray(val) && val.length > 0 && typeof val[0] === "number") {
28
+ result[op] = val;
29
+ }
30
+ }
31
+ return result;
32
+ }
33
+ class UpdateTestCycle extends Tool {
34
+ specification = {
35
+ title: TOOL_NAMES.UPDATE_TEST_CYCLE.TITLE,
36
+ toolset: TOOLSETS.TEST_CYCLES,
37
+ summary: TOOL_NAMES.UPDATE_TEST_CYCLE.SUMMARY,
38
+ readOnly: false,
39
+ idempotent: true,
40
+ inputSchema: UpdateTestCycleBody,
41
+ outputSchema: UpdateTestCycleResponse,
42
+ purpose: "Update an existing test cycle in QTM4J by its human-readable key (e.g. 'SCRUM-TR-101'). Only provided fields are changed — omitted fields stay unchanged. Status and priority are auto-resolved from human-readable names. Labels and components use add/delete objects.",
43
+ useCases: [
44
+ "Update summary, status, priority, planned dates, assignee, or reporter",
45
+ "Clear a nullable field by passing null (e.g. description: null removes text, assignee: null unassigns owner)",
46
+ "Add or remove labels and components atomically without affecting other entries",
47
+ "Apply multiple field updates in a single call"
48
+ ],
49
+ examples: [
50
+ {
51
+ description: "Rename a test cycle",
52
+ parameters: {
53
+ key: "SCRUM-TR-101",
54
+ summary: "Regression Cycle - Sprint 12 Updated"
55
+ },
56
+ expectedOutput: "Test cycle updated with new summary"
57
+ },
58
+ {
59
+ description: "Change status and update planned dates",
60
+ parameters: {
61
+ key: "SCRUM-TR-101",
62
+ status: "In Progress",
63
+ plannedStartDate: "01/May/2026 09:00",
64
+ plannedEndDate: "31/May/2026 18:00"
65
+ },
66
+ expectedOutput: "Test cycle status and planned dates updated"
67
+ },
68
+ {
69
+ description: "Add a label and remove an old one",
70
+ parameters: {
71
+ key: "SCRUM-TR-101",
72
+ labels: { add: ["Regression", "Smoke"], delete: ["Sprint1"] }
73
+ },
74
+ expectedOutput: "Test cycle updated — Regression and Smoke labels added, Sprint1 removed"
75
+ },
76
+ {
77
+ description: "Clear the description text",
78
+ parameters: {
79
+ key: "SCRUM-TR-101",
80
+ description: null
81
+ },
82
+ expectedOutput: "Test cycle description cleared"
83
+ },
84
+ {
85
+ description: "Unassign the owner and clear planned dates",
86
+ parameters: {
87
+ key: "SCRUM-TR-101",
88
+ assignee: null,
89
+ plannedStartDate: null,
90
+ plannedEndDate: null
91
+ },
92
+ expectedOutput: "Test cycle owner unassigned and planned dates cleared"
93
+ },
94
+ {
95
+ description: "Full update with all fields",
96
+ parameters: {
97
+ key: "SCRUM-TR-101",
98
+ summary: "Final Regression Cycle",
99
+ description: "Updated for sprint 12.",
100
+ status: "In Progress",
101
+ priority: "High",
102
+ plannedStartDate: "15/May/2026 09:00",
103
+ plannedEndDate: "30/May/2026 18:00",
104
+ assignee: "5b10a2844c20165700ede21f",
105
+ labels: { add: ["Regression"], delete: ["Sprint1"] },
106
+ components: { add: ["Backend"], delete: ["Frontend"] }
107
+ },
108
+ expectedOutput: "Test cycle updated with all specified fields"
109
+ }
110
+ ],
111
+ hints: [
112
+ "PREREQUISITE: set_project_context must be called before this tool. NEVER auto-select a project.",
113
+ "KEY FORMAT: '{PROJECT_KEY}-TR-{number}' — e.g. 'SCRUM-TR-101'.",
114
+ "Pass explicit null to CLEAR a nullable field — e.g. description: null removes the description text, assignee: null unassigns the owner, plannedStartDate: null removes the date. Omitting a field leaves it unchanged.",
115
+ "Status and priority are auto-resolved from human-readable names loaded by set_project_context. If a name cannot be resolved, the cycle is still updated and a warning is returned.",
116
+ "Labels and components use add/delete — names are auto-resolved to IDs. Both operations can be combined in a single call.",
117
+ "Date format: 'dd/MMM/yyyy HH:mm' e.g. '15/May/2026 09:00'. Month must be capitalised (May not may or MAY).",
118
+ "Archived test cycles cannot be updated — the server returns 400. Unarchive first if needed."
119
+ ],
120
+ outputDescription: "Confirmation object with the test cycle key and updated: true. Warnings are included if any field names could not be resolved."
121
+ };
122
+ handle = async (rawArgs) => {
123
+ const args = UpdateTestCycleBody.parse(rawArgs);
124
+ const fieldResolver = this.client.getResolverRegistry();
125
+ const context = fieldResolver.requireProjectContext();
126
+ const warnings = [];
127
+ const { key: _key, labels, components, ...scalarArgs } = args;
128
+ const body = { ...scalarArgs };
129
+ await Promise.all(
130
+ Object.entries(SIMPLE_FIELD_CONFIG).map(
131
+ ([inputField, resolverKey]) => fieldResolver.getResolver(resolverKey).resolve(inputField, resolverKey, body, context, warnings)
132
+ )
133
+ );
134
+ for (const [inputField, resolverKey] of Object.entries(
135
+ ADD_DELETE_FIELD_CONFIG
136
+ )) {
137
+ const field = args[inputField];
138
+ if (!field) continue;
139
+ const resolvedField = await resolveAddDelete(
140
+ fieldResolver.getResolver(resolverKey),
141
+ inputField,
142
+ resolverKey,
143
+ field,
144
+ context,
145
+ warnings
146
+ );
147
+ if (Object.keys(resolvedField).length > 0)
148
+ body[inputField] = resolvedField;
149
+ }
150
+ await this.client.getApiClient().put(ENDPOINTS.UPDATE_TEST_CYCLE(args.key), body);
151
+ return {
152
+ structuredContent: UpdateTestCycleResponse.parse({
153
+ key: args.key,
154
+ updated: true
155
+ }),
156
+ content: warnings.length > 0 ? [{ type: "text", text: `Note: ${warnings.join(" | ")}` }] : []
157
+ };
158
+ };
159
+ }
160
+ export {
161
+ UpdateTestCycle
162
+ };
@@ -1,8 +1,7 @@
1
1
  import { z } from "zod";
2
2
  import { MCP_SERVER_NAME, MCP_SERVER_VERSION } from "../common/info.js";
3
- import { getRequestHeader } from "../common/request-context.js";
4
3
  import { ToolError } from "../common/tools.js";
5
- import { API_KEY_HEADER, REFLECT_API_TOKEN_HEADER, AUTHORIZATION_HEADER } from "./config/constants.js";
4
+ import { API_KEY_HEADER, AUTHORIZATION_HEADER } from "./config/constants.js";
6
5
  import { SapTest } from "./prompt/sap-test.js";
7
6
  import { AddPromptStep } from "./tool/recording/add-prompt-step.js";
8
7
  import { AddSegment } from "./tool/recording/add-segment.js";
@@ -18,11 +17,12 @@ import { GetTestStatus } from "./tool/tests/get-test-status.js";
18
17
  import { ListSegments } from "./tool/tests/list-segments.js";
19
18
  import { ListTests } from "./tool/tests/list-tests.js";
20
19
  import { RunTest } from "./tool/tests/run-test.js";
21
- const ConfigurationSchema = z.object({
22
- api_token: z.string().describe("Reflect API authentication token")
20
+ const ConfigurationSchema = z.object({});
21
+ const AuthenticationSchema = z.object({
22
+ api_token: z.string().describe("Reflect API authentication token").optional()
23
23
  });
24
24
  class ReflectClient {
25
- _apiToken;
25
+ _server;
26
26
  activeConnections = /* @__PURE__ */ new Map();
27
27
  sessionStates = /* @__PURE__ */ new Map();
28
28
  mcpSessionConnections = /* @__PURE__ */ new Map();
@@ -30,33 +30,24 @@ class ReflectClient {
30
30
  capabilityPrefix = "reflect";
31
31
  configPrefix = "Reflect";
32
32
  config = ConfigurationSchema;
33
- async configure(_server, config, _cache) {
34
- this._apiToken = config.api_token;
33
+ authenticationFields = AuthenticationSchema;
34
+ async configure(server, _config) {
35
+ this._server = server;
35
36
  }
36
37
  getAuthToken() {
37
- const contextHeader = getRequestHeader(REFLECT_API_TOKEN_HEADER) || getRequestHeader(API_KEY_HEADER) || getRequestHeader(AUTHORIZATION_HEADER);
38
- if (contextHeader) {
39
- let token = Array.isArray(contextHeader) ? contextHeader[0] : contextHeader;
40
- if (token.startsWith("Bearer ")) {
41
- token = token.substring(7);
42
- }
43
- return token;
44
- }
45
- return this._apiToken || null;
38
+ return this._server?.getEnv("api_token", this) || this._server?.getEnv(API_KEY_HEADER) || this._server?.getEnv(AUTHORIZATION_HEADER) || null;
46
39
  }
47
40
  isConfigured() {
48
- return true;
41
+ return !!this._server;
42
+ }
43
+ hasAuth() {
44
+ return this.isConfigured() && !!this.getAuthToken();
49
45
  }
50
46
  isOAuthRequest() {
51
- if (getRequestHeader(REFLECT_API_TOKEN_HEADER) || getRequestHeader(API_KEY_HEADER)) {
52
- return false;
53
- }
54
- const authHeader = getRequestHeader(AUTHORIZATION_HEADER);
55
- if (!authHeader) {
47
+ if (this._server?.getEnv("api_token", this) || this._server?.getEnv(API_KEY_HEADER)) {
56
48
  return false;
57
49
  }
58
- const headerValue = Array.isArray(authHeader) ? authHeader[0] : authHeader;
59
- return headerValue.toLowerCase().startsWith("bearer ");
50
+ return !!this._server?.getEnv(AUTHORIZATION_HEADER);
60
51
  }
61
52
  getAuthHeader() {
62
53
  const token = this.getAuthToken();
@@ -1,5 +1,4 @@
1
1
  const API_KEY_HEADER = "X-API-KEY";
2
- const REFLECT_API_TOKEN_HEADER = "Reflect-Api-Token";
3
2
  const AUTHORIZATION_HEADER = "Authorization";
4
3
  const API_HOSTNAME = "api.reflect.run";
5
4
  const WEBSOCKET_HOSTNAME = "recording.us-east-1.reflect.run";
@@ -8,7 +7,6 @@ export {
8
7
  API_HOSTNAME,
9
8
  API_KEY_HEADER,
10
9
  AUTHORIZATION_HEADER,
11
- REFLECT_API_TOKEN_HEADER,
12
10
  WEBSOCKET_HOSTNAME,
13
11
  WEB_APP_HOSTNAME
14
12
  };
@@ -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,
@@ -4,6 +4,7 @@ import { API_HOSTNAME } from "../../config/constants.js";
4
4
  class CancelSuiteExecution extends Tool {
5
5
  specification = {
6
6
  title: "Cancel Suite Execution",
7
+ toolset: "Suites",
7
8
  summary: "Cancel a reflect suite execution",
8
9
  inputSchema: z.object({
9
10
  suiteId: z.string().describe("ID of the reflect suite to cancel execution for"),
@@ -4,6 +4,7 @@ import { API_HOSTNAME } from "../../config/constants.js";
4
4
  class ExecuteSuite extends Tool {
5
5
  specification = {
6
6
  title: "Execute Suite",
7
+ toolset: "Suites",
7
8
  summary: "Execute a reflect suite",
8
9
  inputSchema: z.object({
9
10
  suiteId: z.string().describe("ID of the reflect suite to execute")
@@ -4,6 +4,7 @@ import { API_HOSTNAME } from "../../config/constants.js";
4
4
  class GetSuiteExecutionStatus extends Tool {
5
5
  specification = {
6
6
  title: "Get Suite Execution Status",
7
+ toolset: "Suites",
7
8
  summary: "Get the status of a reflect suite execution",
8
9
  inputSchema: z.object({
9
10
  suiteId: z.string().describe("ID of the reflect suite to get execution status for"),
@@ -4,6 +4,7 @@ import { API_HOSTNAME } from "../../config/constants.js";
4
4
  class ListSuiteExecutions extends Tool {
5
5
  specification = {
6
6
  title: "List Suite Executions",
7
+ toolset: "Suites",
7
8
  summary: "List all executions for a given suite",
8
9
  inputSchema: z.object({
9
10
  suiteId: z.string().describe("ID of the reflect suite to list executions for")
@@ -4,6 +4,7 @@ import { API_HOSTNAME } from "../../config/constants.js";
4
4
  class ListSuites extends Tool {
5
5
  specification = {
6
6
  title: "List Suites",
7
+ toolset: "Suites",
7
8
  summary: "Retrieve a list of all reflect suites available",
8
9
  inputSchema: z.object({})
9
10
  };
@@ -4,6 +4,7 @@ import { API_HOSTNAME } from "../../config/constants.js";
4
4
  class GetTestStatus extends Tool {
5
5
  specification = {
6
6
  title: "Get Test Status",
7
+ toolset: "Tests",
7
8
  summary: "Get the status of a reflect test execution",
8
9
  inputSchema: z.object({
9
10
  executionId: z.string().describe("ID of the reflect test execution to get status for")
@@ -4,6 +4,7 @@ import { WEB_APP_HOSTNAME, API_HOSTNAME } from "../../config/constants.js";
4
4
  class ListSegments extends Tool {
5
5
  specification = {
6
6
  title: "List Segments",
7
+ toolset: "Tests",
7
8
  summary: "Retrieve available reusable test segments for the given platform type. Segments are reusable test steps with an optional set of parameters that can used across multiple tests.",
8
9
  readOnly: true,
9
10
  idempotent: true,
@@ -4,6 +4,7 @@ import { API_HOSTNAME } from "../../config/constants.js";
4
4
  class ListTests extends Tool {
5
5
  specification = {
6
6
  title: "List Tests",
7
+ toolset: "Tests",
7
8
  summary: "List all reflect tests",
8
9
  inputSchema: z.object({})
9
10
  };
@@ -4,6 +4,7 @@ import { API_HOSTNAME } from "../../config/constants.js";
4
4
  class RunTest extends Tool {
5
5
  specification = {
6
6
  title: "Run Test",
7
+ toolset: "Tests",
7
8
  summary: "Run a reflect test",
8
9
  inputSchema: z.object({
9
10
  testId: z.string().describe("ID of the reflect test to run")
@@ -98,7 +98,7 @@ const CreatePortalArgsSchema = z.object({
98
98
  "Whether authentication credentials are enabled for accessing the portal. When true, users can authenticate to access private content. Defaults to true"
99
99
  ),
100
100
  swaggerHubOrganizationId: z.string().describe(
101
- "The corresponding SwaggerHub organization UUID - required for portal creation. This links the portal to your SwaggerHub organization"
101
+ "The corresponding Swagger organization UUID - required for portal creation. This links the portal to your Swagger organization. Only one Portal per Swagger organization is allowed."
102
102
  ),
103
103
  openapiRenderer: z.string().optional().describe(
104
104
  "OpenAPI renderer type: 'SWAGGER_UI' (Swagger UI), 'ELEMENTS' (Stoplight Elements), or 'TOGGLE' (allows switching between both with Elements as default). Defaults to 'TOGGLE'"
@@ -4,47 +4,55 @@ import { OrganizationsQuerySchema } from "./user-management-types.js";
4
4
  const TOOLS = [
5
5
  {
6
6
  title: "List Portals",
7
+ toolset: "Portals",
7
8
  summary: "Search for available portals within Swagger. Only portals where you have at least a designer role, either at the product level or organization level, are returned.",
8
9
  handler: "getPortals"
9
10
  },
10
11
  {
11
12
  title: "Create Portal",
13
+ toolset: "Portals",
12
14
  summary: "Create a new portal within Swagger.",
13
15
  inputSchema: CreatePortalArgsSchema,
14
16
  handler: "createPortal"
15
17
  },
16
18
  {
17
19
  title: "Get Portal",
20
+ toolset: "Portals",
18
21
  summary: "Retrieve information about a specific portal.",
19
22
  inputSchema: PortalArgsSchema,
20
23
  handler: "getPortal"
21
24
  },
22
25
  {
23
26
  title: "Update Portal",
27
+ toolset: "Portals",
24
28
  summary: "Update a specific portal's configuration.",
25
29
  inputSchema: UpdatePortalArgsSchema,
26
30
  handler: "updatePortal"
27
31
  },
28
32
  {
29
33
  title: "List Portal Products",
34
+ toolset: "Products",
30
35
  summary: "Get products for a specific portal that match your criteria.",
31
36
  inputSchema: PortalArgsSchema,
32
37
  handler: "getPortalProducts"
33
38
  },
34
39
  {
35
40
  title: "Create Portal Product",
41
+ toolset: "Products",
36
42
  summary: "Create a new product for a specific portal.",
37
43
  inputSchema: CreateProductArgsSchema,
38
44
  handler: "createPortalProduct"
39
45
  },
40
46
  {
41
47
  title: "Get Portal Product",
48
+ toolset: "Products",
42
49
  summary: "Retrieve information about a specific product resource.",
43
50
  inputSchema: ProductArgsSchema,
44
51
  handler: "getPortalProduct"
45
52
  },
46
53
  {
47
54
  title: "Delete Portal Product",
55
+ toolset: "Products",
48
56
  summary: "Delete a product from a specific portal",
49
57
  inputSchema: ProductArgsSchema,
50
58
  handler: "deletePortalProduct",
@@ -52,36 +60,42 @@ const TOOLS = [
52
60
  },
53
61
  {
54
62
  title: "Update Portal Product",
63
+ toolset: "Products",
55
64
  summary: "Update a product's settings within a specific portal.",
56
65
  inputSchema: UpdateProductArgsSchema,
57
66
  handler: "updatePortalProduct"
58
67
  },
59
68
  {
60
69
  title: "Publish Portal Product",
70
+ toolset: "Products",
61
71
  summary: "Publish a product's content to make it live or as preview. This endpoint publishes the current content of a product, making it visible to portal visitors. Use preview mode to test before going live.",
62
72
  inputSchema: PublishProductArgsSchema,
63
73
  handler: "publishPortalProduct"
64
74
  },
65
75
  {
66
76
  title: "List Portal Product Sections",
77
+ toolset: "Sections",
67
78
  summary: "Get sections for a specific product within a portal.",
68
79
  inputSchema: GetProductSectionsArgsSchema,
69
80
  handler: "getPortalProductSections"
70
81
  },
71
82
  {
72
83
  title: "Create Table Of Contents",
84
+ toolset: "Table Of Contents",
73
85
  summary: "Create a new table of contents item in a portal product section. Supports API references, HTML content, and Markdown content types.",
74
86
  inputSchema: CreateTableOfContentsArgsSchema,
75
87
  handler: "createTableOfContents"
76
88
  },
77
89
  {
78
90
  title: "List Table Of Contents",
91
+ toolset: "Table Of Contents",
79
92
  summary: "Get table of contents for a section of a product within a portal.",
80
93
  inputSchema: GetTableOfContentsArgsSchema,
81
94
  handler: "getTableOfContents"
82
95
  },
83
96
  {
84
97
  title: "Delete Table Of Contents",
98
+ toolset: "Table Of Contents",
85
99
  summary: "Delete table of contents entry. Performs a soft-delete of an entry from the table of contents. Supports recursive deletion of nested items.",
86
100
  inputSchema: DeleteTableOfContentsArgsSchema,
87
101
  handler: "deleteTableOfContents"
@@ -89,12 +103,14 @@ const TOOLS = [
89
103
  // Document management tools
90
104
  {
91
105
  title: "Get Document",
106
+ toolset: "Documents",
92
107
  summary: "Get document content and metadata by document ID. Useful for retrieving HTML or Markdown content from table of contents items.",
93
108
  inputSchema: GetDocumentArgsSchema,
94
109
  handler: "getDocument"
95
110
  },
96
111
  {
97
112
  title: "Update Document",
113
+ toolset: "Documents",
98
114
  summary: "Update the content or source of an existing document. Supports both HTML and Markdown content types.",
99
115
  inputSchema: UpdateDocumentArgsSchema,
100
116
  handler: "updateDocument"
@@ -102,18 +118,21 @@ const TOOLS = [
102
118
  // Registry API tools for SwaggerHub Design functionality
103
119
  {
104
120
  title: "Search APIs and Domains",
121
+ toolset: "Registry API",
105
122
  summary: "Search for APIs and Domains in SwaggerHub Registry using the comprehensive /specs endpoint and retrieve metadata including owner, name, description, summary, version, and specification.",
106
123
  inputSchema: ApiSearchParamsSchema,
107
124
  handler: "searchApis"
108
125
  },
109
126
  {
110
127
  title: "Get API Definition",
128
+ toolset: "Registry API",
111
129
  summary: "Fetch resolved API definition from SwaggerHub Registry based on owner, API name, and version.",
112
130
  inputSchema: ApiDefinitionParamsSchema,
113
131
  handler: "getApiDefinition"
114
132
  },
115
133
  {
116
134
  title: "Create or Update API",
135
+ toolset: "Registry API",
117
136
  summary: "Create a new API or update an existing API in SwaggerHub Registry for Swagger Studio. The API specification type (OpenAPI, AsyncAPI) is automatically detected from the definition content. APIs are always created with fixed values: version 1.0.0, private visibility, and automock disabled (these values cannot be changed). Returns HTTP 201 for creation, HTTP 200 for update. Response includes 'operation' field indicating whether it was a 'create' or 'update' operation along with API details and SwaggerHub URL.",
118
137
  inputSchema: CreateApiParamsSchema,
119
138
  handler: "createOrUpdateApi"
@@ -121,24 +140,28 @@ const TOOLS = [
121
140
  // User Management API tools for organization management functionality
122
141
  {
123
142
  title: "List Organizations",
143
+ toolset: "Registry API",
124
144
  summary: "Get organizations for a user. Returns a list of organizations that the authenticating user is a member of. On-Premise admin gets a list of all organizations in the system.",
125
145
  inputSchema: OrganizationsQuerySchema,
126
146
  handler: "getOrganizations"
127
147
  },
128
148
  {
129
149
  title: "Scan API Standardization",
150
+ toolset: "Registry API",
130
151
  summary: "Run a standardization scan against an API definition using the organization's governance and standardization rules. Accepts a YAML or JSON OpenAPI/AsyncAPI definition and returns a list of standardization errors and validation issues. Use this tool when users ask to validate, scan, or check API governance or standardization.",
131
152
  inputSchema: ScanStandardizationParamsSchema,
132
153
  handler: "scanStandardization"
133
154
  },
134
155
  {
135
156
  title: "Create API from Prompt",
157
+ toolset: "Registry API",
136
158
  summary: "Generate and save an API definition based on a prompt using SmartBear AI. This tool automatically applies organization governance and standardization rules during API generation. The specType parameter determines the format of the generated definition. Use: 'openapi20' for OpenAPI 2.0, 'openapi30x' for OpenAPI 3.0.x, 'openapi31x' for OpenAPI 3.1.x, 'asyncapi2xx' for AsyncAPI 2.x, 'asyncapi30x' for AsyncAPI 3.0.x. Use this tool when creating APIs that comply with governance policies or when generating APIs from natural language descriptions. Use this tool when users ask to create, generate, or design APIs with governance or standardization requirements. Returns HTTP 201 for creation, HTTP 200 for update. Response includes 'operation' field indicating whether it was a 'create' or 'update' operation along with API details and SwaggerHub URL.",
137
159
  inputSchema: CreateApiFromPromptParamsSchema,
138
160
  handler: "createApiFromPrompt"
139
161
  },
140
162
  {
141
163
  title: "Standardize API",
164
+ toolset: "Registry API",
142
165
  summary: "Standardize and fix an API definition using AI to ensure compliance with governance policies. Scans the API definition for standardization errors and automatically fixes them using SmartBear AI. Optionally provide 'newVersion' (e.g. patch bump '1.0.0' → '1.0.1') to save the fixed definition as a new version — omitting it will overwrite the current version. Returns the number of errors found and the fixed definition if successful. Use this tool when users ask to standardize, fix, govern, or ensure governance compliance of APIs.",
143
166
  inputSchema: StandardizeApiParamsSchema,
144
167
  handler: "standardizeApi"
@@ -1,6 +1,5 @@
1
1
  import { z } from "zod";
2
2
  import { MCP_SERVER_NAME, MCP_SERVER_VERSION } from "../common/info.js";
3
- import { getRequestHeader } from "../common/request-context.js";
4
3
  import "./config-utils.js";
5
4
  import { SwaggerAPI } from "./client/api.js";
6
5
  import { SwaggerConfiguration } from "./client/configuration.js";
@@ -9,47 +8,45 @@ import "./client/registry-types.js";
9
8
  import { TOOLS } from "./client/tools.js";
10
9
  import "./client/user-management-types.js";
11
10
  const ConfigurationSchema = z.object({
12
- api_key: z.string().describe("Swagger API key for authentication"),
13
11
  portal_base_path: z.string().optional().describe("Base path for Portal API requests (optional)"),
14
12
  registry_base_path: z.string().optional().describe("Base path for Registry API requests (optional)"),
15
13
  ui_base_path: z.string().optional().describe("Base URL for the SwaggerHub UI (optional)")
16
14
  });
15
+ const AuthenticationSchema = z.object({
16
+ api_key: z.string().describe("Swagger API key for authentication").optional()
17
+ });
17
18
  class SwaggerClient {
18
- api;
19
- _apiKey;
19
+ apiConfig;
20
+ server;
20
21
  name = "Swagger";
21
22
  capabilityPrefix = "swagger";
22
23
  configPrefix = "Swagger";
23
24
  config = ConfigurationSchema;
24
- async configure(_server, config, _cache) {
25
- this._apiKey = config.api_key;
26
- this.api = new SwaggerAPI(
27
- new SwaggerConfiguration({
28
- token: () => this.getAuthToken(),
29
- portalBasePath: config.portal_base_path,
30
- registryBasePath: config.registry_base_path,
31
- uiBasePath: config.ui_base_path
32
- }),
33
- `${MCP_SERVER_NAME}/${MCP_SERVER_VERSION}`
34
- );
25
+ authenticationFields = AuthenticationSchema;
26
+ async configure(server, config) {
27
+ this.server = server;
28
+ this.apiConfig = new SwaggerConfiguration({
29
+ token: () => this.getAuthToken(),
30
+ portalBasePath: config.portal_base_path,
31
+ registryBasePath: config.registry_base_path,
32
+ uiBasePath: config.ui_base_path
33
+ });
34
+ }
35
+ isConfigured() {
36
+ return this.apiConfig !== void 0;
35
37
  }
36
38
  getAuthToken() {
37
- const contextHeader = getRequestHeader("Swagger-Api-Key") || getRequestHeader("Authorization");
38
- if (contextHeader) {
39
- let token = Array.isArray(contextHeader) ? contextHeader[0] : contextHeader;
40
- if (token.startsWith("Bearer ")) {
41
- token = token.substring(7);
42
- }
43
- return token;
44
- }
45
- return this._apiKey || null;
39
+ return this.server?.getEnv("api_key", this) || this.server?.getEnv("Authorization") || null;
46
40
  }
47
- isConfigured() {
48
- return this.api !== void 0;
41
+ hasAuth() {
42
+ return this.isConfigured() && !!this.getAuthToken();
49
43
  }
50
44
  getApi() {
51
- if (!this.api) throw new Error("Client not configured");
52
- return this.api;
45
+ if (!this.apiConfig) throw new Error("Client not configured");
46
+ return new SwaggerAPI(
47
+ this.apiConfig,
48
+ `${MCP_SERVER_NAME}/${MCP_SERVER_VERSION}`
49
+ );
53
50
  }
54
51
  // Delegate API methods to the SwaggerAPI instance
55
52
  async getPortals() {