@smartbear/mcp 0.5.0 → 0.7.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 +22 -111
- package/dist/api-hub/client/api.js +253 -0
- package/dist/api-hub/client/configuration.js +27 -0
- package/dist/api-hub/client/index.js +5 -0
- package/dist/api-hub/client/portal-types.js +131 -0
- package/dist/api-hub/client/registry-types.js +55 -0
- package/dist/api-hub/client/tools.js +86 -0
- package/dist/api-hub/client.js +64 -404
- package/dist/bugsnag/client/api/CurrentUser.js +18 -13
- package/dist/bugsnag/client/api/Error.js +35 -35
- package/dist/bugsnag/client/api/Project.js +137 -9
- package/dist/bugsnag/client/api/base.js +27 -13
- package/dist/bugsnag/client/api/filters.js +9 -9
- package/dist/bugsnag/client.js +584 -145
- package/dist/common/info.js +1 -1
- package/dist/common/server.js +44 -27
- package/dist/index.js +13 -6
- package/dist/pactflow/client/ai.js +33 -2
- package/dist/pactflow/client/base.js +51 -3
- package/dist/pactflow/client/prompt-utils.js +89 -0
- package/dist/pactflow/client/prompts.js +131 -0
- package/dist/pactflow/client/tools.js +39 -7
- package/dist/pactflow/client/utils.js +1 -1
- package/dist/pactflow/client.js +147 -9
- package/dist/qmetry/client/api/client-api.js +39 -0
- package/dist/qmetry/client/handlers.js +11 -0
- package/dist/qmetry/client/project.js +27 -0
- package/dist/qmetry/client/testcase.js +104 -0
- package/dist/qmetry/client/tools.js +222 -0
- package/dist/qmetry/client.js +95 -0
- package/dist/qmetry/config/constants.js +12 -0
- package/dist/qmetry/config/rest-endpoints.js +11 -0
- package/dist/qmetry/types/common.js +174 -0
- package/dist/qmetry/types/testcase.js +19 -0
- package/dist/reflect/client.js +14 -14
- package/package.json +9 -6
package/dist/pactflow/client.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { MCP_SERVER_NAME, MCP_SERVER_VERSION } from "../common/info.js";
|
|
2
|
+
import { getOADMatcherRecommendations, getUserMatcherSelection, } from "./client/prompt-utils.js";
|
|
3
|
+
import { PROMPTS } from "./client/prompts.js";
|
|
2
4
|
import { TOOLS } from "./client/tools.js";
|
|
3
5
|
// Tool definitions for PactFlow AI API client
|
|
4
6
|
export class PactflowClient {
|
|
@@ -8,7 +10,16 @@ export class PactflowClient {
|
|
|
8
10
|
aiBaseUrl;
|
|
9
11
|
baseUrl;
|
|
10
12
|
clientType;
|
|
11
|
-
|
|
13
|
+
server;
|
|
14
|
+
/**
|
|
15
|
+
* Creates an instance of the PactflowClient.
|
|
16
|
+
*
|
|
17
|
+
* @param auth The authentication token or credentials.
|
|
18
|
+
* @param baseUrl The base URL for the API.
|
|
19
|
+
* @param clientType The type of client (e.g., PactFlow, Pact Broker).
|
|
20
|
+
* @param server The SmartBear MCP server instance.
|
|
21
|
+
*/
|
|
22
|
+
constructor(auth, baseUrl, clientType, server) {
|
|
12
23
|
// Set headers based on the type of auth provided
|
|
13
24
|
if (typeof auth === "string") {
|
|
14
25
|
this.headers = {
|
|
@@ -28,16 +39,25 @@ export class PactflowClient {
|
|
|
28
39
|
this.baseUrl = baseUrl;
|
|
29
40
|
this.aiBaseUrl = `${this.baseUrl}/api/ai`;
|
|
30
41
|
this.clientType = clientType;
|
|
42
|
+
this.server = server;
|
|
31
43
|
}
|
|
32
44
|
// PactFlow AI client methods
|
|
33
45
|
/**
|
|
34
46
|
* Generate new Pact tests based on the provided input.
|
|
35
47
|
*
|
|
36
48
|
* @param toolInput The input data for the generation process.
|
|
49
|
+
* @param getInput Function to get additional input from the user if needed.
|
|
37
50
|
* @returns The result of the generation process.
|
|
38
51
|
* @throws Error if the HTTP request fails or the operation times out.
|
|
39
52
|
*/
|
|
40
|
-
async generate(toolInput) {
|
|
53
|
+
async generate(toolInput, getInput) {
|
|
54
|
+
if (toolInput.openapi?.document &&
|
|
55
|
+
(!toolInput.openapi?.matcher ||
|
|
56
|
+
Object.keys(toolInput.openapi.matcher).length === 0)) {
|
|
57
|
+
const matcherResponse = await getOADMatcherRecommendations(toolInput.openapi.document, this.server);
|
|
58
|
+
const userSelection = await getUserMatcherSelection(matcherResponse, getInput);
|
|
59
|
+
toolInput.openapi.matcher = userSelection;
|
|
60
|
+
}
|
|
41
61
|
// Submit the generation request
|
|
42
62
|
const response = await fetch(`${this.aiBaseUrl}/generate`, {
|
|
43
63
|
method: "POST",
|
|
@@ -54,10 +74,18 @@ export class PactflowClient {
|
|
|
54
74
|
* Review the provided Pact tests and suggest improvements.
|
|
55
75
|
*
|
|
56
76
|
* @param toolInput The input data for the review process.
|
|
77
|
+
* @param getInput Function to get additional input from the user if needed.
|
|
57
78
|
* @returns The result of the review process.
|
|
58
79
|
* @throws Error if the HTTP request fails or the operation times out.
|
|
59
80
|
*/
|
|
60
|
-
async review(toolInput) {
|
|
81
|
+
async review(toolInput, getInput) {
|
|
82
|
+
if (toolInput.openapi?.document &&
|
|
83
|
+
(!toolInput.openapi?.matcher ||
|
|
84
|
+
Object.keys(toolInput.openapi.matcher).length === 0)) {
|
|
85
|
+
const matcherResponse = await getOADMatcherRecommendations(toolInput.openapi.document, this.server);
|
|
86
|
+
const userSelection = await getUserMatcherSelection(matcherResponse, getInput);
|
|
87
|
+
toolInput.openapi.matcher = userSelection;
|
|
88
|
+
}
|
|
61
89
|
// Submit review request
|
|
62
90
|
const response = await fetch(`${this.aiBaseUrl}/review`, {
|
|
63
91
|
method: "POST",
|
|
@@ -70,6 +98,32 @@ export class PactflowClient {
|
|
|
70
98
|
const status_response = await response.json();
|
|
71
99
|
return await this.pollForCompletion(status_response, "Review Pacts");
|
|
72
100
|
}
|
|
101
|
+
/**
|
|
102
|
+
* Retrieves AI status information for the current user
|
|
103
|
+
* and organization.
|
|
104
|
+
*
|
|
105
|
+
* @returns Entitlement containing AI status information, organization
|
|
106
|
+
* entitlements, and user entitlements.
|
|
107
|
+
* @throws Error if the request fails or returns a non-OK response.
|
|
108
|
+
*/
|
|
109
|
+
async getAIStatus() {
|
|
110
|
+
const url = `${this.aiBaseUrl}/entitlement`;
|
|
111
|
+
try {
|
|
112
|
+
const response = await fetch(url, {
|
|
113
|
+
method: "GET",
|
|
114
|
+
headers: this.headers,
|
|
115
|
+
});
|
|
116
|
+
if (!response.ok) {
|
|
117
|
+
const errorText = await response.text().catch(() => "");
|
|
118
|
+
throw new Error(`PactFlow AI Status Request Failed - status: ${response.status} ${response.statusText}${errorText ? ` - ${errorText}` : ""}`);
|
|
119
|
+
}
|
|
120
|
+
return (await response.json());
|
|
121
|
+
}
|
|
122
|
+
catch (error) {
|
|
123
|
+
process.stderr.write(`[GetAICredits] Unexpected error: ${error}\n`);
|
|
124
|
+
throw error;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
73
127
|
async getStatus(statusUrl) {
|
|
74
128
|
const response = await fetch(statusUrl, {
|
|
75
129
|
method: "HEAD",
|
|
@@ -80,6 +134,9 @@ export class PactflowClient {
|
|
|
80
134
|
isComplete: response.status === 200,
|
|
81
135
|
};
|
|
82
136
|
}
|
|
137
|
+
get requestHeaders() {
|
|
138
|
+
return this.headers;
|
|
139
|
+
}
|
|
83
140
|
async getResult(resultUrl) {
|
|
84
141
|
const response = await fetch(resultUrl, {
|
|
85
142
|
method: "GET",
|
|
@@ -111,7 +168,7 @@ export class PactflowClient {
|
|
|
111
168
|
throw new Error(`${operationName} timed out after ${timeout / 1000} seconds`);
|
|
112
169
|
}
|
|
113
170
|
// PactFlow / Pact_Broker client methods
|
|
114
|
-
async getProviderStates({ provider }) {
|
|
171
|
+
async getProviderStates({ provider, }) {
|
|
115
172
|
const uri_encoded_provider_name = encodeURIComponent(provider);
|
|
116
173
|
const response = await fetch(`${this.baseUrl}/pacts/provider/${uri_encoded_provider_name}/provider-states`, {
|
|
117
174
|
method: "GET",
|
|
@@ -153,19 +210,90 @@ export class PactflowClient {
|
|
|
153
210
|
return (await response.json());
|
|
154
211
|
}
|
|
155
212
|
catch (error) {
|
|
156
|
-
console.error(
|
|
213
|
+
console.error(`[CanIDeploy] Unexpected error: ${error}\n`);
|
|
214
|
+
throw error;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Retrieves the matrix of pact verification results for the specified pacticipants.
|
|
219
|
+
* This allows you to see which consumer/provider combinations have been verified
|
|
220
|
+
* and make deployment decisions based on contract test results.
|
|
221
|
+
*
|
|
222
|
+
* @param body - Matrix query parameters including pacticipants, versions, environments, etc.
|
|
223
|
+
* @returns MatrixResponse containing the verification matrix, notices, and summary
|
|
224
|
+
* @throws Error if the request fails or returns a non-OK response
|
|
225
|
+
*/
|
|
226
|
+
async getMatrix(body) {
|
|
227
|
+
const { q, latestby, limit } = body;
|
|
228
|
+
// Build query parameters manually to avoid URL encoding of square brackets
|
|
229
|
+
const queryParts = [];
|
|
230
|
+
// Add optional parameters
|
|
231
|
+
if (latestby) {
|
|
232
|
+
queryParts.push(`latestby=${encodeURIComponent(latestby)}`);
|
|
233
|
+
}
|
|
234
|
+
if (limit !== undefined) {
|
|
235
|
+
queryParts.push(`limit=${limit}`);
|
|
236
|
+
}
|
|
237
|
+
// Add the q parameters (pacticipant selectors)
|
|
238
|
+
q.forEach((selector) => {
|
|
239
|
+
queryParts.push(`q[]pacticipant=${encodeURIComponent(selector.pacticipant)}`);
|
|
240
|
+
if (selector.version) {
|
|
241
|
+
queryParts.push(`q[]version=${encodeURIComponent(selector.version)}`);
|
|
242
|
+
}
|
|
243
|
+
if (selector.branch) {
|
|
244
|
+
queryParts.push(`q[]branch=${encodeURIComponent(selector.branch)}`);
|
|
245
|
+
}
|
|
246
|
+
if (selector.environment) {
|
|
247
|
+
queryParts.push(`q[]environment=${encodeURIComponent(selector.environment)}`);
|
|
248
|
+
}
|
|
249
|
+
if (selector.latest !== undefined) {
|
|
250
|
+
queryParts.push(`q[]latest=${selector.latest}`);
|
|
251
|
+
}
|
|
252
|
+
if (selector.tag) {
|
|
253
|
+
queryParts.push(`q[]tag=${encodeURIComponent(selector.tag)}`);
|
|
254
|
+
}
|
|
255
|
+
if (selector.mainBranch !== undefined) {
|
|
256
|
+
queryParts.push(`q[]mainBranch=${selector.mainBranch}`);
|
|
257
|
+
}
|
|
258
|
+
});
|
|
259
|
+
const url = `${this.baseUrl}/matrix?${queryParts.join("&")}`;
|
|
260
|
+
try {
|
|
261
|
+
const response = await fetch(url, {
|
|
262
|
+
method: "GET",
|
|
263
|
+
headers: this.headers,
|
|
264
|
+
});
|
|
265
|
+
if (!response.ok) {
|
|
266
|
+
const errorText = await response.text().catch(() => "");
|
|
267
|
+
throw new Error(`Matrix Request Failed - status: ${response.status} ${response.statusText}${errorText ? ` - ${errorText}` : ""}`);
|
|
268
|
+
}
|
|
269
|
+
return (await response.json());
|
|
270
|
+
}
|
|
271
|
+
catch (error) {
|
|
272
|
+
console.error("[GetMatrix] Unexpected error:", error);
|
|
157
273
|
throw error;
|
|
158
274
|
}
|
|
159
275
|
}
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
276
|
+
/**
|
|
277
|
+
* Registers tools with the provided register function.
|
|
278
|
+
*
|
|
279
|
+
* @param register - The function used to register tools.
|
|
280
|
+
* @param getInput - The function used to get input for tools.
|
|
281
|
+
*/
|
|
282
|
+
registerTools(register, getInput) {
|
|
283
|
+
for (const tool of TOOLS.filter((t) => t.clients.includes(this.clientType))) {
|
|
284
|
+
const { handler, clients: _, formatResponse, ...toolparams } = tool;
|
|
163
285
|
register(toolparams, async (args, _extra) => {
|
|
164
286
|
const handler_fn = this[handler];
|
|
165
287
|
if (typeof handler_fn !== "function") {
|
|
166
288
|
throw new Error(`Handler '${handler}' not found on PactClient`);
|
|
167
289
|
}
|
|
168
|
-
|
|
290
|
+
let result;
|
|
291
|
+
if (tool.enableElicitation) {
|
|
292
|
+
result = await handler_fn.call(this, args, getInput);
|
|
293
|
+
}
|
|
294
|
+
else {
|
|
295
|
+
result = await handler_fn.call(this, args);
|
|
296
|
+
}
|
|
169
297
|
// Use custom response formatter if provided
|
|
170
298
|
if (formatResponse) {
|
|
171
299
|
return formatResponse(result);
|
|
@@ -177,4 +305,14 @@ export class PactflowClient {
|
|
|
177
305
|
});
|
|
178
306
|
}
|
|
179
307
|
}
|
|
308
|
+
/**
|
|
309
|
+
* Registers prompts with the provided register function.
|
|
310
|
+
*
|
|
311
|
+
* @param register - The function used to register prompts.
|
|
312
|
+
*/
|
|
313
|
+
registerPrompts(register) {
|
|
314
|
+
PROMPTS.forEach((prompt) => {
|
|
315
|
+
register(prompt.name, prompt.params, prompt.callback);
|
|
316
|
+
});
|
|
317
|
+
}
|
|
180
318
|
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { MCP_SERVER_NAME, MCP_SERVER_VERSION } from "../../../common/info.js";
|
|
2
|
+
import { QMETRY_DEFAULTS } from "../../config/constants.js";
|
|
3
|
+
export async function qmetryRequest({ method = "GET", path, token, project, baseUrl, body, }) {
|
|
4
|
+
const url = `${baseUrl}${path}`;
|
|
5
|
+
const headers = {
|
|
6
|
+
apikey: token,
|
|
7
|
+
project: project || QMETRY_DEFAULTS.PROJECT_KEY,
|
|
8
|
+
"User-Agent": `${MCP_SERVER_NAME}/${MCP_SERVER_VERSION}`,
|
|
9
|
+
};
|
|
10
|
+
if (body) {
|
|
11
|
+
headers["Content-Type"] = "application/json";
|
|
12
|
+
}
|
|
13
|
+
const init = {
|
|
14
|
+
method,
|
|
15
|
+
headers,
|
|
16
|
+
};
|
|
17
|
+
if (body && ["POST", "PUT", "PATCH"].includes(method)) {
|
|
18
|
+
init.body = JSON.stringify(body);
|
|
19
|
+
}
|
|
20
|
+
const res = await fetch(url, init);
|
|
21
|
+
if (!res.ok) {
|
|
22
|
+
let errorText;
|
|
23
|
+
try {
|
|
24
|
+
const contentType = res.headers.get("content-type");
|
|
25
|
+
if (contentType?.includes("application/json")) {
|
|
26
|
+
const json = await res.json();
|
|
27
|
+
errorText = JSON.stringify(json);
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
errorText = await res.text();
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
errorText = res.statusText;
|
|
35
|
+
}
|
|
36
|
+
throw new Error(`QMetry API request failed (${res.status}): ${errorText}`);
|
|
37
|
+
}
|
|
38
|
+
return (await res.json());
|
|
39
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { QMetryToolsHandlers } from "../config/constants.js";
|
|
2
|
+
import { getProjectInfo } from "./project.js";
|
|
3
|
+
import { fetchTestCaseDetails, fetchTestCaseSteps, fetchTestCases, fetchTestCaseVersionDetails, } from "./testcase.js";
|
|
4
|
+
export const QMETRY_HANDLER_MAP = {
|
|
5
|
+
[QMetryToolsHandlers.SET_PROJECT_INFO]: getProjectInfo,
|
|
6
|
+
[QMetryToolsHandlers.FETCH_PROJECT_INFO]: getProjectInfo,
|
|
7
|
+
[QMetryToolsHandlers.FETCH_TEST_CASES]: fetchTestCases,
|
|
8
|
+
[QMetryToolsHandlers.FETCH_TEST_CASE_DETAILS]: fetchTestCaseDetails,
|
|
9
|
+
[QMetryToolsHandlers.FETCH_TEST_CASE_VERSION_DETAILS]: fetchTestCaseVersionDetails,
|
|
10
|
+
[QMetryToolsHandlers.FETCH_TEST_CASE_STEPS]: fetchTestCaseSteps,
|
|
11
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { QMETRY_DEFAULTS } from "../config/constants.js";
|
|
2
|
+
import { QMETRY_PATHS } from "../config/rest-endpoints.js";
|
|
3
|
+
import { qmetryRequest } from "./api/client-api.js";
|
|
4
|
+
/**
|
|
5
|
+
* Retrieves project information from QMetry
|
|
6
|
+
*
|
|
7
|
+
* This function serves dual purpose:
|
|
8
|
+
* 1. SET_PROJECT_INFO - Sets/switches the current project context
|
|
9
|
+
* 2. FETCH_PROJECT_INFO - Retrieves project details and configuration
|
|
10
|
+
*
|
|
11
|
+
* Both operations use the same API endpoint as QMetry handles project context
|
|
12
|
+
* switching and information retrieval through the same GET request.
|
|
13
|
+
*
|
|
14
|
+
* @param token - QMetry API authentication token
|
|
15
|
+
* @param baseUrl - QMetry instance base URL (defaults to configured URL)
|
|
16
|
+
* @param project - Project key to retrieve info for (defaults to configured project)
|
|
17
|
+
* @returns Promise resolving to project information including viewIds, folders, and configuration
|
|
18
|
+
*/
|
|
19
|
+
export async function getProjectInfo(token, baseUrl, project) {
|
|
20
|
+
return qmetryRequest({
|
|
21
|
+
method: "GET",
|
|
22
|
+
path: QMETRY_PATHS.PROJECT.GET_INFO,
|
|
23
|
+
token,
|
|
24
|
+
baseUrl: baseUrl || QMETRY_DEFAULTS.BASE_URL,
|
|
25
|
+
project: project || QMETRY_DEFAULTS.PROJECT_KEY,
|
|
26
|
+
});
|
|
27
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { QMETRY_DEFAULTS } from "../config/constants.js";
|
|
2
|
+
import { QMETRY_PATHS } from "../config/rest-endpoints.js";
|
|
3
|
+
import { DEFAULT_FETCH_TESTCASE_DETAILS_PAYLOAD, DEFAULT_FETCH_TESTCASE_STEPS_PAYLOAD, DEFAULT_FETCH_TESTCASE_VERSION_DETAILS_PAYLOAD, DEFAULT_FETCH_TESTCASES_PAYLOAD, } from "../types/testcase.js";
|
|
4
|
+
import { qmetryRequest } from "./api/client-api.js";
|
|
5
|
+
function resolveDefaults(baseUrl, project) {
|
|
6
|
+
return {
|
|
7
|
+
resolvedBaseUrl: baseUrl || QMETRY_DEFAULTS.BASE_URL,
|
|
8
|
+
resolvedProject: project || QMETRY_DEFAULTS.PROJECT_KEY,
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Fetches a list of test cases.
|
|
13
|
+
* @throws If `viewId` or `folderPath` are missing/invalid.
|
|
14
|
+
*/
|
|
15
|
+
export async function fetchTestCases(token, baseUrl, project, payload) {
|
|
16
|
+
const { resolvedBaseUrl, resolvedProject } = resolveDefaults(baseUrl, project);
|
|
17
|
+
const body = {
|
|
18
|
+
...DEFAULT_FETCH_TESTCASES_PAYLOAD,
|
|
19
|
+
...payload,
|
|
20
|
+
};
|
|
21
|
+
if (typeof body.viewId !== "number") {
|
|
22
|
+
throw new Error("[fetchTestCases] Missing or invalid required parameter: 'viewId'.");
|
|
23
|
+
}
|
|
24
|
+
if (typeof body.folderPath !== "string") {
|
|
25
|
+
throw new Error("[fetchTestCases] Missing or invalid required parameter: 'folderPath'.");
|
|
26
|
+
}
|
|
27
|
+
return qmetryRequest({
|
|
28
|
+
method: "POST",
|
|
29
|
+
path: QMETRY_PATHS.TESTCASE.GET_TC_LIST,
|
|
30
|
+
token,
|
|
31
|
+
project: resolvedProject,
|
|
32
|
+
baseUrl: resolvedBaseUrl,
|
|
33
|
+
body,
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Fetches a test case details.
|
|
38
|
+
* @throws If `tcID` is missing/invalid.
|
|
39
|
+
*/
|
|
40
|
+
export async function fetchTestCaseDetails(token, baseUrl, project, payload) {
|
|
41
|
+
const { resolvedBaseUrl, resolvedProject } = resolveDefaults(baseUrl, project);
|
|
42
|
+
const body = {
|
|
43
|
+
...DEFAULT_FETCH_TESTCASE_DETAILS_PAYLOAD,
|
|
44
|
+
...payload,
|
|
45
|
+
};
|
|
46
|
+
if (typeof body.tcID !== "number") {
|
|
47
|
+
throw new Error("[fetchTestCaseDetails] Missing or invalid required parameter: 'tcID'.");
|
|
48
|
+
}
|
|
49
|
+
return qmetryRequest({
|
|
50
|
+
method: "POST",
|
|
51
|
+
path: QMETRY_PATHS.TESTCASE.GET_TC_DETAILS,
|
|
52
|
+
token,
|
|
53
|
+
project: resolvedProject,
|
|
54
|
+
baseUrl: resolvedBaseUrl,
|
|
55
|
+
body,
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Fetches a test case details by version.
|
|
60
|
+
* @throws If `id` is missing/invalid.
|
|
61
|
+
*/
|
|
62
|
+
export async function fetchTestCaseVersionDetails(token, baseUrl, project, payload) {
|
|
63
|
+
const { resolvedBaseUrl, resolvedProject } = resolveDefaults(baseUrl, project);
|
|
64
|
+
const body = {
|
|
65
|
+
...DEFAULT_FETCH_TESTCASE_VERSION_DETAILS_PAYLOAD,
|
|
66
|
+
...payload,
|
|
67
|
+
};
|
|
68
|
+
if (!body.id) {
|
|
69
|
+
throw new Error("[fetchTestCaseVersionDetails] Missing or invalid required parameter: 'id'.");
|
|
70
|
+
}
|
|
71
|
+
if (typeof body.version !== "number") {
|
|
72
|
+
throw new Error("[fetchTestCaseVersionDetails] Missing or invalid required parameter: 'version'.");
|
|
73
|
+
}
|
|
74
|
+
return qmetryRequest({
|
|
75
|
+
method: "POST",
|
|
76
|
+
path: QMETRY_PATHS.TESTCASE.GET_TC_DETAILS_BY_VERSION,
|
|
77
|
+
token,
|
|
78
|
+
project: resolvedProject,
|
|
79
|
+
baseUrl: resolvedBaseUrl,
|
|
80
|
+
body,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Fetches a test case steps.
|
|
85
|
+
* @throws If `id` is missing/invalid.
|
|
86
|
+
*/
|
|
87
|
+
export async function fetchTestCaseSteps(token, baseUrl, project, payload) {
|
|
88
|
+
const { resolvedBaseUrl, resolvedProject } = resolveDefaults(baseUrl, project);
|
|
89
|
+
const body = {
|
|
90
|
+
...DEFAULT_FETCH_TESTCASE_STEPS_PAYLOAD,
|
|
91
|
+
...payload,
|
|
92
|
+
};
|
|
93
|
+
if (typeof body.id !== "number") {
|
|
94
|
+
throw new Error("[fetchTestCaseSteps] Missing or invalid required parameter: 'id'.");
|
|
95
|
+
}
|
|
96
|
+
return qmetryRequest({
|
|
97
|
+
method: "POST",
|
|
98
|
+
path: QMETRY_PATHS.TESTCASE.GET_TC_STEPS,
|
|
99
|
+
token,
|
|
100
|
+
project: resolvedProject,
|
|
101
|
+
baseUrl: resolvedBaseUrl,
|
|
102
|
+
body,
|
|
103
|
+
});
|
|
104
|
+
}
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
import { QMetryToolsHandlers } from "../config/constants.js";
|
|
2
|
+
import { ProjectArgsSchema, TestCaseDetailsArgsSchema, TestCaseListArgsSchema, TestCaseStepsArgsSchema, TestCaseVersionDetailsArgsSchema, } from "../types/common.js";
|
|
3
|
+
export const TOOLS = [
|
|
4
|
+
{
|
|
5
|
+
title: "Set QMetry Project Info",
|
|
6
|
+
summary: "Set current QMetry project for your account",
|
|
7
|
+
handler: QMetryToolsHandlers.SET_PROJECT_INFO,
|
|
8
|
+
zodSchema: ProjectArgsSchema,
|
|
9
|
+
purpose: "Switch the active QMetry project context for the current session. " +
|
|
10
|
+
"This tool sets the default project that will be used for all subsequent QMetry operations. " +
|
|
11
|
+
"Essential for multi-project QMetry instances where you need to work with specific projects.",
|
|
12
|
+
useCases: [
|
|
13
|
+
"Switch to a specific project before performing test case operations",
|
|
14
|
+
"Set project context for batch operations on test cases",
|
|
15
|
+
"Configure the default project for the current session",
|
|
16
|
+
"Validate access to a specific project before proceeding with operations",
|
|
17
|
+
],
|
|
18
|
+
examples: [
|
|
19
|
+
{
|
|
20
|
+
description: "Set default project as active",
|
|
21
|
+
parameters: { projectKey: "default" },
|
|
22
|
+
expectedOutput: "Project context set to 'default' with confirmation of project details",
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
description: "Switch to UT project",
|
|
26
|
+
parameters: { projectKey: "UT" },
|
|
27
|
+
expectedOutput: "Project context switched to 'UT' project with available configurations",
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
description: "Set MAC project as active for test case operations",
|
|
31
|
+
parameters: { projectKey: "MAC" },
|
|
32
|
+
expectedOutput: "Project context set to 'MAC' with viewIds and folder structure",
|
|
33
|
+
},
|
|
34
|
+
],
|
|
35
|
+
hints: [
|
|
36
|
+
"Always set the project context before performing test case operations in multi-project environments",
|
|
37
|
+
"Use the same project key that you'll use in subsequent test case operations",
|
|
38
|
+
"Common project keys include 'default', 'UT', 'MAC', 'VT' - check with your QMetry admin for available projects",
|
|
39
|
+
"This operation must be performed before fetching test cases if working with non-default projects",
|
|
40
|
+
"The project context persists for the current session until changed again",
|
|
41
|
+
],
|
|
42
|
+
outputFormat: "JSON object containing project configuration details, confirmation of project switch, and available project metadata",
|
|
43
|
+
readOnly: false,
|
|
44
|
+
idempotent: true,
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
title: "Fetch QMetry Project Info",
|
|
48
|
+
summary: "Fetch QMetry project information including viewId and folderPath needed for other operations",
|
|
49
|
+
handler: QMetryToolsHandlers.FETCH_PROJECT_INFO,
|
|
50
|
+
zodSchema: ProjectArgsSchema,
|
|
51
|
+
purpose: "Prerequisite tool that provides project configuration data required by other QMetry operations. " +
|
|
52
|
+
"The project key to fetch info for. Use 'default' if not specified. " +
|
|
53
|
+
"Common project keys include 'UT', 'VT', 'MAC', etc. " +
|
|
54
|
+
"If user doesn't specify a project key, this tool will use 'default' automatically.",
|
|
55
|
+
useCases: [
|
|
56
|
+
"Get project configuration before fetching test cases",
|
|
57
|
+
"Retrieve available viewIds for test case listing",
|
|
58
|
+
"Get folderPath information for project navigation",
|
|
59
|
+
"Validate project access and permissions",
|
|
60
|
+
],
|
|
61
|
+
examples: [
|
|
62
|
+
{
|
|
63
|
+
description: "Get default project info",
|
|
64
|
+
parameters: {},
|
|
65
|
+
expectedOutput: "Project configuration with viewIds, folderPaths, and project details",
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
description: "Get specific project info",
|
|
69
|
+
parameters: { projectKey: "MAC" },
|
|
70
|
+
expectedOutput: "MAC project configuration with available views and folders",
|
|
71
|
+
},
|
|
72
|
+
],
|
|
73
|
+
hints: [
|
|
74
|
+
"Always call this first when user doesn't provide viewId or folderPath",
|
|
75
|
+
"Use 'default' project key when user doesn't specify one",
|
|
76
|
+
"Extract viewId from latestViews.TC.viewId for test case operations",
|
|
77
|
+
"Use empty string '' as folderPath for root directory",
|
|
78
|
+
],
|
|
79
|
+
outputFormat: "JSON object containing project details, viewIds, folderPaths, and project configuration",
|
|
80
|
+
readOnly: true,
|
|
81
|
+
idempotent: true,
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
title: "Fetch Test Cases",
|
|
85
|
+
summary: "Fetch QMetry test cases - automatically handles viewId resolution based on project",
|
|
86
|
+
handler: QMetryToolsHandlers.FETCH_TEST_CASES,
|
|
87
|
+
zodSchema: TestCaseListArgsSchema,
|
|
88
|
+
purpose: "Get test cases from QMetry. System automatically gets correct viewId from project info if not provided.",
|
|
89
|
+
useCases: [
|
|
90
|
+
"List all test cases in a project",
|
|
91
|
+
"Search for specific test cases using filters",
|
|
92
|
+
"Browse test cases in specific folders",
|
|
93
|
+
"Get paginated test case results",
|
|
94
|
+
],
|
|
95
|
+
examples: [
|
|
96
|
+
{
|
|
97
|
+
description: "Get all test cases from default project - system will auto-fetch viewId",
|
|
98
|
+
parameters: {},
|
|
99
|
+
expectedOutput: "List of test cases from default project with auto-resolved viewId",
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
description: "Get all test cases from UT project - system will auto-fetch UT project's viewId",
|
|
103
|
+
parameters: { projectKey: "UT" },
|
|
104
|
+
expectedOutput: "List of test cases from UT project using UT's specific TC viewId",
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
description: "Get test cases with manual viewId (skip auto-resolution)",
|
|
108
|
+
parameters: { projectKey: "MAC", viewId: 167136, folderPath: "" },
|
|
109
|
+
expectedOutput: "Test cases using manually specified viewId 167136",
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
description: "List test cases from specific project (ex: project key can be anything (VT, UT, PROJ1, TEST9)",
|
|
113
|
+
parameters: {
|
|
114
|
+
projectKey: "use specific given project key",
|
|
115
|
+
viewId: "fetch specific project given projectKey Test Case ViewId",
|
|
116
|
+
folderPath: "",
|
|
117
|
+
},
|
|
118
|
+
expectedOutput: "Test cases using manually specified viewId 167136 or projectKey",
|
|
119
|
+
},
|
|
120
|
+
],
|
|
121
|
+
hints: [
|
|
122
|
+
"CRITICAL WORKFLOW: Always use the SAME projectKey for both project info and test case fetching",
|
|
123
|
+
"Step 1: If user specifies projectKey (like 'UT', 'MAC'), use that EXACT projectKey for project info",
|
|
124
|
+
"Step 2: Get project info using that projectKey, extract latestViews.TC.viewId",
|
|
125
|
+
"Step 3: Use the SAME projectKey and the extracted TC viewId for fetching test cases",
|
|
126
|
+
"Step 4: If user doesn't specify projectKey, use 'default' for both project info and test case fetching",
|
|
127
|
+
"NEVER mix project keys - if user says 'MAC project', use projectKey='MAC' for everything",
|
|
128
|
+
'For search by test case key (like MAC-TC-1684), use filter: \'[{"type":"string","value":"MAC-TC-1684","field":"entityKeyId"}]\'',
|
|
129
|
+
],
|
|
130
|
+
outputFormat: "JSON object with 'data' array containing test cases and pagination info",
|
|
131
|
+
readOnly: true,
|
|
132
|
+
idempotent: true,
|
|
133
|
+
openWorld: false,
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
title: "Fetch Test Case Details",
|
|
137
|
+
summary: "Get detailed information for a specific QMetry test case by numeric ID",
|
|
138
|
+
handler: QMetryToolsHandlers.FETCH_TEST_CASE_DETAILS,
|
|
139
|
+
zodSchema: TestCaseDetailsArgsSchema,
|
|
140
|
+
purpose: "Retrieve comprehensive test case information including metadata, status, and basic properties",
|
|
141
|
+
useCases: [
|
|
142
|
+
"Get test case details by numeric ID",
|
|
143
|
+
"Retrieve test case metadata for reporting",
|
|
144
|
+
"Get test case summary and properties",
|
|
145
|
+
"Fetch test case details before accessing steps or version details",
|
|
146
|
+
],
|
|
147
|
+
examples: [
|
|
148
|
+
{
|
|
149
|
+
description: "Get test case details by numeric ID",
|
|
150
|
+
parameters: { tcID: 4468020 },
|
|
151
|
+
expectedOutput: "Detailed test case information including summary, description, status",
|
|
152
|
+
},
|
|
153
|
+
],
|
|
154
|
+
hints: [
|
|
155
|
+
"⚠️ This API requires a numeric tcID parameter",
|
|
156
|
+
"If user provides entityKey (e.g., MAC-TC-1684), first call FETCH_TEST_CASES with a filter on entityKeyId to resolve the tcID",
|
|
157
|
+
"After resolving entityKey → tcID, call this tool with the resolved numeric tcID",
|
|
158
|
+
"This tool provides metadata and properties; use FETCH_TEST_CASE_STEPS for step-level details",
|
|
159
|
+
],
|
|
160
|
+
outputFormat: "JSON object with test case details including ID, key, summary, description, and metadata",
|
|
161
|
+
readOnly: true,
|
|
162
|
+
idempotent: true,
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
title: "Fetch Test Case Version Details",
|
|
166
|
+
summary: "Get QMetry test case details for a specific version by numeric ID",
|
|
167
|
+
handler: QMetryToolsHandlers.FETCH_TEST_CASE_VERSION_DETAILS,
|
|
168
|
+
zodSchema: TestCaseVersionDetailsArgsSchema,
|
|
169
|
+
purpose: "Retrieve version-specific information for a test case including history and changes",
|
|
170
|
+
useCases: [
|
|
171
|
+
"Get specific version details of a test case",
|
|
172
|
+
"Compare different versions of a test case",
|
|
173
|
+
"Retrieve version history information",
|
|
174
|
+
"Audit changes made across test case versions",
|
|
175
|
+
],
|
|
176
|
+
examples: [
|
|
177
|
+
{
|
|
178
|
+
description: "Get version 2 details for test case ID 123",
|
|
179
|
+
parameters: { id: 123, version: 2 },
|
|
180
|
+
expectedOutput: "Version 2 details for test case 123",
|
|
181
|
+
},
|
|
182
|
+
],
|
|
183
|
+
hints: [
|
|
184
|
+
"⚠️ Requires numeric ID, not entityKey",
|
|
185
|
+
"If user provides entityKey (e.g., MAC-TC-1684), first resolve it to numeric ID using FETCH_TEST_CASES",
|
|
186
|
+
"Version defaults to 1 if not specified",
|
|
187
|
+
"Provides version-specific metadata and history",
|
|
188
|
+
],
|
|
189
|
+
outputFormat: "JSON object with version-specific test case details",
|
|
190
|
+
readOnly: true,
|
|
191
|
+
idempotent: true,
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
title: "Fetch Test Case Steps",
|
|
195
|
+
summary: "Get detailed test case steps for a specific test case by numeric ID",
|
|
196
|
+
handler: QMetryToolsHandlers.FETCH_TEST_CASE_STEPS,
|
|
197
|
+
zodSchema: TestCaseStepsArgsSchema,
|
|
198
|
+
purpose: "Retrieve step-by-step instructions and expected results for manual execution of a test case",
|
|
199
|
+
useCases: [
|
|
200
|
+
"Get step-by-step instructions with expected results",
|
|
201
|
+
"Retrieve test case execution procedure for manual runs",
|
|
202
|
+
"Export or display detailed test steps for documentation",
|
|
203
|
+
"Fetch steps before automation mapping",
|
|
204
|
+
],
|
|
205
|
+
examples: [
|
|
206
|
+
{
|
|
207
|
+
description: "Get steps for test case ID 123",
|
|
208
|
+
parameters: { id: 123 },
|
|
209
|
+
expectedOutput: "Detailed steps with actions and expected results for test case 123",
|
|
210
|
+
},
|
|
211
|
+
],
|
|
212
|
+
hints: [
|
|
213
|
+
"⚠️ Requires numeric ID, not entityKey",
|
|
214
|
+
"If user provides entityKey (e.g., MAC-TC-1684), resolve it first via FETCH_TEST_CASES to get the numeric ID",
|
|
215
|
+
"Version defaults to 1 if not specified",
|
|
216
|
+
"Use pagination for test cases with many steps",
|
|
217
|
+
],
|
|
218
|
+
outputFormat: "JSON object with array of test steps including step description, expected result, and order",
|
|
219
|
+
readOnly: true,
|
|
220
|
+
idempotent: true,
|
|
221
|
+
},
|
|
222
|
+
];
|