@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.
- package/README.md +10 -2
- package/dist/bearq/client.js +12 -13
- package/dist/bearq/tool/tasks/chat-with-qa-lead.js +1 -0
- package/dist/bearq/tool/tasks/expand-application-model.js +1 -0
- package/dist/bearq/tool/tasks/get-task-status.js +1 -0
- package/dist/bearq/tool/tasks/get-task.js +1 -0
- package/dist/bearq/tool/tasks/refine-all-draft-tests.js +1 -0
- package/dist/bearq/tool/tasks/refine-test-cases.js +1 -0
- package/dist/bearq/tool/tasks/refine-tests-in-functional-areas.js +1 -0
- package/dist/bearq/tool/tasks/run-regression-tests.js +1 -0
- package/dist/bearq/tool/tasks/run-test-cases.js +1 -0
- package/dist/bearq/tool/tasks/run-tests-in-functional-areas.js +1 -0
- package/dist/bearq/tool/tasks/stop-task.js +1 -0
- package/dist/bearq/tool/tasks/wait-for-task.js +1 -0
- package/dist/bugsnag/client.js +24 -44
- package/dist/bugsnag/tool/error/get-error.js +1 -0
- package/dist/bugsnag/tool/error/list-project-errors.js +1 -0
- package/dist/bugsnag/tool/error/update-error.js +1 -0
- package/dist/bugsnag/tool/event/get-event-details-from-dashboard-url.js +1 -0
- package/dist/bugsnag/tool/event/get-event.js +1 -0
- package/dist/bugsnag/tool/event/list-error-events.js +1 -0
- package/dist/bugsnag/tool/performance/get-network-endpoint-groupings.js +1 -0
- package/dist/bugsnag/tool/performance/get-span-group.js +1 -0
- package/dist/bugsnag/tool/performance/get-trace.js +1 -0
- package/dist/bugsnag/tool/performance/list-span-groups.js +1 -0
- package/dist/bugsnag/tool/performance/list-spans.js +1 -0
- package/dist/bugsnag/tool/performance/list-trace-fields.js +1 -0
- package/dist/bugsnag/tool/performance/set-network-endpoint-groupings.js +1 -0
- package/dist/bugsnag/tool/project/get-current-project.js +1 -0
- package/dist/bugsnag/tool/project/list-project-event-filters.js +1 -0
- package/dist/bugsnag/tool/project/list-projects.js +1 -0
- package/dist/bugsnag/tool/release/get-build.js +1 -0
- package/dist/bugsnag/tool/release/get-release.js +1 -0
- package/dist/bugsnag/tool/release/list-releases.js +1 -0
- package/dist/collaborator/client.js +24 -19
- package/dist/common/client-registry.js +63 -29
- package/dist/common/server.js +57 -1
- package/dist/common/transport-http.js +90 -69
- package/dist/common/transport-stdio.js +29 -24
- package/dist/package.json.js +1 -1
- package/dist/pactflow/client/base.js +3 -3
- package/dist/pactflow/client/tools.js +102 -0
- package/dist/pactflow/client.js +30 -43
- package/dist/qmetry/client/tools/automation-tools.js +2 -0
- package/dist/qmetry/client/tools/issue-tools.js +6 -0
- package/dist/qmetry/client/tools/project-tools.js +9 -0
- package/dist/qmetry/client/tools/requirement-tools.js +5 -0
- package/dist/qmetry/client/tools/testcase-tools.js +7 -0
- package/dist/qmetry/client/tools/testsuite-tools.js +11 -0
- package/dist/qmetry/client.js +20 -18
- package/dist/qtm4j/client.js +57 -23
- package/dist/qtm4j/config/constants.js +197 -5
- package/dist/qtm4j/config/field-resolution.types.js +5 -2
- package/dist/qtm4j/http/api-client.js +90 -3
- package/dist/qtm4j/resolver/cache/cache.js +1 -1
- package/dist/qtm4j/resolver/resolver-registry.js +7 -1
- package/dist/qtm4j/resolver/resolvers/common-attribute-resolver.js +1 -0
- package/dist/qtm4j/resolver/resolvers/component-resolver.js +2 -0
- package/dist/qtm4j/resolver/resolvers/label-resolver.js +2 -0
- package/dist/qtm4j/resolver/resolvers/requirement-id-resolver.js +28 -0
- package/dist/qtm4j/resolver/resolvers/test-cycle-uid-resolver.js +28 -0
- package/dist/qtm4j/schema/automation.schema.js +107 -0
- package/dist/qtm4j/schema/linked-items.schema.js +95 -0
- package/dist/qtm4j/schema/requirements.schema.js +109 -0
- package/dist/qtm4j/schema/search-test-cycle.schema.js +133 -0
- package/dist/qtm4j/schema/test-cycle.link.schema.js +260 -0
- package/dist/qtm4j/schema/test-cycle.schema.js +39 -0
- package/dist/qtm4j/schema/update-test-cycle.schema.js +54 -0
- package/dist/qtm4j/tool/project/get-projects.js +2 -1
- package/dist/qtm4j/tool/project/set-project-context.js +2 -1
- package/dist/qtm4j/tool/requirement/get-linked-testcases.js +93 -0
- package/dist/qtm4j/tool/requirement/link-testcases.js +107 -0
- package/dist/qtm4j/tool/requirement/unlink-testcases.js +97 -0
- package/dist/qtm4j/tool/test-automation/get-automation-history.js +70 -0
- package/dist/qtm4j/tool/test-automation/upload-automation-result.js +144 -0
- package/dist/qtm4j/tool/test-case/create-test-case.js +2 -1
- package/dist/qtm4j/tool/test-case/get-linked-requirements.js +67 -0
- package/dist/qtm4j/tool/test-case/get-test-cases.js +2 -1
- package/dist/qtm4j/tool/test-case/get-test-steps.js +2 -1
- package/dist/qtm4j/tool/test-case/link-requirements.js +124 -0
- package/dist/qtm4j/tool/test-case/unlink-requirements.js +116 -0
- package/dist/qtm4j/tool/test-case/update-test-case.js +2 -1
- package/dist/qtm4j/tool/test-cycle/create-test-cycle.js +81 -0
- package/dist/qtm4j/tool/test-cycle/get-linked-requirements.js +71 -0
- package/dist/qtm4j/tool/test-cycle/link-requirements.js +91 -0
- package/dist/qtm4j/tool/test-cycle/link-testcases.js +118 -0
- package/dist/qtm4j/tool/test-cycle/search-linked-testcases.js +114 -0
- package/dist/qtm4j/tool/test-cycle/search-test-cycle.js +137 -0
- package/dist/qtm4j/tool/test-cycle/unlink-requirements.js +87 -0
- package/dist/qtm4j/tool/test-cycle/unlink-testcases.js +103 -0
- package/dist/qtm4j/tool/test-cycle/update-test-cycle.js +162 -0
- package/dist/reflect/client.js +15 -24
- package/dist/reflect/config/constants.js +0 -2
- package/dist/reflect/tool/recording/add-prompt-step.js +1 -0
- package/dist/reflect/tool/recording/add-segment.js +1 -0
- package/dist/reflect/tool/recording/connect-to-session.js +1 -0
- package/dist/reflect/tool/recording/delete-previous-step.js +1 -0
- package/dist/reflect/tool/recording/get-screenshot.js +1 -0
- package/dist/reflect/tool/suites/cancel-suite-execution.js +1 -0
- package/dist/reflect/tool/suites/execute-suite.js +1 -0
- package/dist/reflect/tool/suites/get-suite-execution-status.js +1 -0
- package/dist/reflect/tool/suites/list-suite-executions.js +1 -0
- package/dist/reflect/tool/suites/list-suites.js +1 -0
- package/dist/reflect/tool/tests/get-test-status.js +1 -0
- package/dist/reflect/tool/tests/list-segments.js +1 -0
- package/dist/reflect/tool/tests/list-tests.js +1 -0
- package/dist/reflect/tool/tests/run-test.js +1 -0
- package/dist/swagger/client/portal-types.js +1 -1
- package/dist/swagger/client/tools.js +23 -0
- package/dist/swagger/client.js +25 -28
- package/dist/zephyr/client.js +14 -21
- package/dist/zephyr/tool/environment/get-environments.js +1 -0
- package/dist/zephyr/tool/folder/create-folder.js +1 -0
- package/dist/zephyr/tool/issue-link/get-test-cases.js +1 -0
- package/dist/zephyr/tool/issue-link/get-test-cycles.js +1 -0
- package/dist/zephyr/tool/issue-link/get-test-executions.js +1 -0
- package/dist/zephyr/tool/priority/get-priorities.js +1 -0
- package/dist/zephyr/tool/project/get-project.js +1 -0
- package/dist/zephyr/tool/project/get-projects.js +1 -0
- package/dist/zephyr/tool/status/get-statuses.js +1 -0
- package/dist/zephyr/tool/test-case/create-issue-link.js +1 -0
- package/dist/zephyr/tool/test-case/create-test-case.js +1 -0
- package/dist/zephyr/tool/test-case/create-test-script.js +1 -0
- package/dist/zephyr/tool/test-case/create-test-steps.js +1 -0
- package/dist/zephyr/tool/test-case/create-web-link.js +1 -0
- package/dist/zephyr/tool/test-case/get-links.js +1 -0
- package/dist/zephyr/tool/test-case/get-test-case.js +1 -0
- package/dist/zephyr/tool/test-case/get-test-cases.js +1 -0
- package/dist/zephyr/tool/test-case/get-test-script.js +1 -0
- package/dist/zephyr/tool/test-case/get-test-steps.js +1 -0
- package/dist/zephyr/tool/test-case/update-test-case.js +1 -0
- package/dist/zephyr/tool/test-cycle/create-issue-link.js +1 -0
- package/dist/zephyr/tool/test-cycle/create-test-cycle.js +1 -0
- package/dist/zephyr/tool/test-cycle/create-web-link.js +1 -0
- package/dist/zephyr/tool/test-cycle/get-links.js +1 -0
- package/dist/zephyr/tool/test-cycle/get-test-cycle.js +1 -0
- package/dist/zephyr/tool/test-cycle/get-test-cycles.js +1 -0
- package/dist/zephyr/tool/test-cycle/update-test-cycle.js +1 -0
- package/dist/zephyr/tool/test-execution/create-issue-link.js +1 -0
- package/dist/zephyr/tool/test-execution/create-test-execution.js +1 -0
- package/dist/zephyr/tool/test-execution/get-test-execution-links.js +1 -0
- package/dist/zephyr/tool/test-execution/get-test-execution.js +1 -0
- package/dist/zephyr/tool/test-execution/get-test-executions.js +1 -0
- package/dist/zephyr/tool/test-execution/get-test-steps.js +1 -0
- package/dist/zephyr/tool/test-execution/update-test-execution.js +1 -0
- package/dist/zephyr/tool/test-execution/update-test-steps.js +1 -0
- package/package.json +1 -1
- 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
|
+
};
|
package/dist/reflect/client.js
CHANGED
|
@@ -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,
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
34
|
-
|
|
33
|
+
authenticationFields = AuthenticationSchema;
|
|
34
|
+
async configure(server, _config) {
|
|
35
|
+
this._server = server;
|
|
35
36
|
}
|
|
36
37
|
getAuthToken() {
|
|
37
|
-
|
|
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
|
|
41
|
+
return !!this._server;
|
|
42
|
+
}
|
|
43
|
+
hasAuth() {
|
|
44
|
+
return this.isConfigured() && !!this.getAuthToken();
|
|
49
45
|
}
|
|
50
46
|
isOAuthRequest() {
|
|
51
|
-
if (
|
|
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
|
-
|
|
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 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 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
|
|
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"
|
package/dist/swagger/client.js
CHANGED
|
@@ -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
|
-
|
|
19
|
-
|
|
19
|
+
apiConfig;
|
|
20
|
+
server;
|
|
20
21
|
name = "Swagger";
|
|
21
22
|
capabilityPrefix = "swagger";
|
|
22
23
|
configPrefix = "Swagger";
|
|
23
24
|
config = ConfigurationSchema;
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
this.
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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
|
-
|
|
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
|
-
|
|
48
|
-
return this.
|
|
41
|
+
hasAuth() {
|
|
42
|
+
return this.isConfigured() && !!this.getAuthToken();
|
|
49
43
|
}
|
|
50
44
|
getApi() {
|
|
51
|
-
if (!this.
|
|
52
|
-
return
|
|
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() {
|