@browserstack/mcp-server 1.2.15-beta.2 → 1.2.16
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/dist/server-factory.js +0 -4
- package/dist/tools/percy-sdk.js +20 -11
- package/dist/tools/testmanagement-utils/get-testplan.d.ts +16 -0
- package/dist/tools/testmanagement-utils/get-testplan.js +99 -0
- package/dist/tools/testmanagement-utils/list-folders.d.ts +16 -0
- package/dist/tools/testmanagement-utils/list-folders.js +77 -0
- package/dist/tools/testmanagement-utils/list-testcases.js +1 -1
- package/dist/tools/testmanagement-utils/list-testplans.d.ts +15 -0
- package/dist/tools/testmanagement-utils/list-testplans.js +75 -0
- package/dist/tools/testmanagement-utils/update-testcase.d.ts +16 -0
- package/dist/tools/testmanagement-utils/update-testcase.js +133 -10
- package/dist/tools/testmanagement.d.ts +15 -0
- package/dist/tools/testmanagement.js +73 -2
- package/package.json +2 -3
- package/dist/lib/percy-api/auth.d.ts +0 -41
- package/dist/lib/percy-api/auth.js +0 -96
- package/dist/lib/percy-api/cache.d.ts +0 -28
- package/dist/lib/percy-api/cache.js +0 -48
- package/dist/lib/percy-api/client.d.ts +0 -69
- package/dist/lib/percy-api/client.js +0 -275
- package/dist/lib/percy-api/errors.d.ts +0 -15
- package/dist/lib/percy-api/errors.js +0 -52
- package/dist/lib/percy-api/formatter.d.ts +0 -16
- package/dist/lib/percy-api/formatter.js +0 -344
- package/dist/lib/percy-api/percy-auth.d.ts +0 -43
- package/dist/lib/percy-api/percy-auth.js +0 -137
- package/dist/lib/percy-api/percy-error-handler.d.ts +0 -24
- package/dist/lib/percy-api/percy-error-handler.js +0 -302
- package/dist/lib/percy-api/percy-session.d.ts +0 -42
- package/dist/lib/percy-api/percy-session.js +0 -87
- package/dist/lib/percy-api/polling.d.ts +0 -26
- package/dist/lib/percy-api/polling.js +0 -42
- package/dist/lib/percy-api/types.d.ts +0 -56
- package/dist/lib/percy-api/types.js +0 -76
- package/dist/tools/percy-mcp/advanced/branchline-operations.d.ts +0 -16
- package/dist/tools/percy-mcp/advanced/branchline-operations.js +0 -81
- package/dist/tools/percy-mcp/advanced/manage-variants.d.ts +0 -16
- package/dist/tools/percy-mcp/advanced/manage-variants.js +0 -155
- package/dist/tools/percy-mcp/advanced/manage-visual-monitoring.d.ts +0 -16
- package/dist/tools/percy-mcp/advanced/manage-visual-monitoring.js +0 -171
- package/dist/tools/percy-mcp/auth/auth-status.d.ts +0 -3
- package/dist/tools/percy-mcp/auth/auth-status.js +0 -131
- package/dist/tools/percy-mcp/core/approve-build.d.ts +0 -14
- package/dist/tools/percy-mcp/core/approve-build.js +0 -97
- package/dist/tools/percy-mcp/core/get-build-items.d.ts +0 -13
- package/dist/tools/percy-mcp/core/get-build-items.js +0 -65
- package/dist/tools/percy-mcp/core/get-build.d.ts +0 -10
- package/dist/tools/percy-mcp/core/get-build.js +0 -16
- package/dist/tools/percy-mcp/core/get-comparison.d.ts +0 -11
- package/dist/tools/percy-mcp/core/get-comparison.js +0 -59
- package/dist/tools/percy-mcp/core/get-snapshot.d.ts +0 -10
- package/dist/tools/percy-mcp/core/get-snapshot.js +0 -40
- package/dist/tools/percy-mcp/core/list-builds.d.ts +0 -14
- package/dist/tools/percy-mcp/core/list-builds.js +0 -45
- package/dist/tools/percy-mcp/core/list-projects.d.ts +0 -12
- package/dist/tools/percy-mcp/core/list-projects.js +0 -51
- package/dist/tools/percy-mcp/creation/create-app-snapshot.d.ts +0 -12
- package/dist/tools/percy-mcp/creation/create-app-snapshot.js +0 -29
- package/dist/tools/percy-mcp/creation/create-build.d.ts +0 -19
- package/dist/tools/percy-mcp/creation/create-build.js +0 -68
- package/dist/tools/percy-mcp/creation/create-comparison.d.ts +0 -18
- package/dist/tools/percy-mcp/creation/create-comparison.js +0 -90
- package/dist/tools/percy-mcp/creation/create-snapshot.d.ts +0 -17
- package/dist/tools/percy-mcp/creation/create-snapshot.js +0 -99
- package/dist/tools/percy-mcp/creation/finalize-build.d.ts +0 -12
- package/dist/tools/percy-mcp/creation/finalize-build.js +0 -33
- package/dist/tools/percy-mcp/creation/finalize-comparison.d.ts +0 -10
- package/dist/tools/percy-mcp/creation/finalize-comparison.js +0 -16
- package/dist/tools/percy-mcp/creation/finalize-snapshot.d.ts +0 -12
- package/dist/tools/percy-mcp/creation/finalize-snapshot.js +0 -33
- package/dist/tools/percy-mcp/creation/upload-resource.d.ts +0 -15
- package/dist/tools/percy-mcp/creation/upload-resource.js +0 -43
- package/dist/tools/percy-mcp/creation/upload-tile.d.ts +0 -11
- package/dist/tools/percy-mcp/creation/upload-tile.js +0 -53
- package/dist/tools/percy-mcp/diagnostics/analyze-logs-realtime.d.ts +0 -13
- package/dist/tools/percy-mcp/diagnostics/analyze-logs-realtime.js +0 -65
- package/dist/tools/percy-mcp/diagnostics/get-build-logs.d.ts +0 -17
- package/dist/tools/percy-mcp/diagnostics/get-build-logs.js +0 -74
- package/dist/tools/percy-mcp/diagnostics/get-network-logs.d.ts +0 -5
- package/dist/tools/percy-mcp/diagnostics/get-network-logs.js +0 -21
- package/dist/tools/percy-mcp/diagnostics/get-suggestions.d.ts +0 -7
- package/dist/tools/percy-mcp/diagnostics/get-suggestions.js +0 -24
- package/dist/tools/percy-mcp/index.d.ts +0 -36
- package/dist/tools/percy-mcp/index.js +0 -1137
- package/dist/tools/percy-mcp/intelligence/get-ai-analysis.d.ts +0 -15
- package/dist/tools/percy-mcp/intelligence/get-ai-analysis.js +0 -166
- package/dist/tools/percy-mcp/intelligence/get-ai-quota.d.ts +0 -9
- package/dist/tools/percy-mcp/intelligence/get-ai-quota.js +0 -73
- package/dist/tools/percy-mcp/intelligence/get-build-summary.d.ts +0 -11
- package/dist/tools/percy-mcp/intelligence/get-build-summary.js +0 -78
- package/dist/tools/percy-mcp/intelligence/get-rca.d.ts +0 -6
- package/dist/tools/percy-mcp/intelligence/get-rca.js +0 -153
- package/dist/tools/percy-mcp/intelligence/suggest-prompt.d.ts +0 -15
- package/dist/tools/percy-mcp/intelligence/suggest-prompt.js +0 -86
- package/dist/tools/percy-mcp/intelligence/trigger-ai-recompute.d.ts +0 -16
- package/dist/tools/percy-mcp/intelligence/trigger-ai-recompute.js +0 -64
- package/dist/tools/percy-mcp/management/create-project.d.ts +0 -14
- package/dist/tools/percy-mcp/management/create-project.js +0 -52
- package/dist/tools/percy-mcp/management/get-usage-stats.d.ts +0 -12
- package/dist/tools/percy-mcp/management/get-usage-stats.js +0 -61
- package/dist/tools/percy-mcp/management/manage-browser-targets.d.ts +0 -12
- package/dist/tools/percy-mcp/management/manage-browser-targets.js +0 -136
- package/dist/tools/percy-mcp/management/manage-comments.d.ts +0 -14
- package/dist/tools/percy-mcp/management/manage-comments.js +0 -147
- package/dist/tools/percy-mcp/management/manage-ignored-regions.d.ts +0 -18
- package/dist/tools/percy-mcp/management/manage-ignored-regions.js +0 -182
- package/dist/tools/percy-mcp/management/manage-project-settings.d.ts +0 -16
- package/dist/tools/percy-mcp/management/manage-project-settings.js +0 -97
- package/dist/tools/percy-mcp/management/manage-tokens.d.ts +0 -14
- package/dist/tools/percy-mcp/management/manage-tokens.js +0 -90
- package/dist/tools/percy-mcp/management/manage-webhooks.d.ts +0 -15
- package/dist/tools/percy-mcp/management/manage-webhooks.js +0 -180
- package/dist/tools/percy-mcp/v2/auth-status.d.ts +0 -3
- package/dist/tools/percy-mcp/v2/auth-status.js +0 -80
- package/dist/tools/percy-mcp/v2/clone-build.d.ts +0 -24
- package/dist/tools/percy-mcp/v2/clone-build.js +0 -539
- package/dist/tools/percy-mcp/v2/create-app-build.d.ts +0 -28
- package/dist/tools/percy-mcp/v2/create-app-build.js +0 -442
- package/dist/tools/percy-mcp/v2/create-build.d.ts +0 -16
- package/dist/tools/percy-mcp/v2/create-build.js +0 -601
- package/dist/tools/percy-mcp/v2/create-project.d.ts +0 -8
- package/dist/tools/percy-mcp/v2/create-project.js +0 -33
- package/dist/tools/percy-mcp/v2/discover-urls.d.ts +0 -7
- package/dist/tools/percy-mcp/v2/discover-urls.js +0 -38
- package/dist/tools/percy-mcp/v2/figma-baseline.d.ts +0 -7
- package/dist/tools/percy-mcp/v2/figma-baseline.js +0 -18
- package/dist/tools/percy-mcp/v2/figma-build.d.ts +0 -7
- package/dist/tools/percy-mcp/v2/figma-build.js +0 -39
- package/dist/tools/percy-mcp/v2/figma-link.d.ts +0 -6
- package/dist/tools/percy-mcp/v2/figma-link.js +0 -27
- package/dist/tools/percy-mcp/v2/get-ai-summary.d.ts +0 -5
- package/dist/tools/percy-mcp/v2/get-ai-summary.js +0 -109
- package/dist/tools/percy-mcp/v2/get-build-detail.d.ts +0 -22
- package/dist/tools/percy-mcp/v2/get-build-detail.js +0 -567
- package/dist/tools/percy-mcp/v2/get-builds.d.ts +0 -8
- package/dist/tools/percy-mcp/v2/get-builds.js +0 -63
- package/dist/tools/percy-mcp/v2/get-comparison.d.ts +0 -5
- package/dist/tools/percy-mcp/v2/get-comparison.js +0 -94
- package/dist/tools/percy-mcp/v2/get-devices.d.ts +0 -5
- package/dist/tools/percy-mcp/v2/get-devices.js +0 -33
- package/dist/tools/percy-mcp/v2/get-insights.d.ts +0 -7
- package/dist/tools/percy-mcp/v2/get-insights.js +0 -52
- package/dist/tools/percy-mcp/v2/get-projects.d.ts +0 -6
- package/dist/tools/percy-mcp/v2/get-projects.js +0 -41
- package/dist/tools/percy-mcp/v2/get-snapshot.d.ts +0 -5
- package/dist/tools/percy-mcp/v2/get-snapshot.js +0 -96
- package/dist/tools/percy-mcp/v2/get-test-case-history.d.ts +0 -5
- package/dist/tools/percy-mcp/v2/get-test-case-history.js +0 -20
- package/dist/tools/percy-mcp/v2/get-test-cases.d.ts +0 -6
- package/dist/tools/percy-mcp/v2/get-test-cases.js +0 -36
- package/dist/tools/percy-mcp/v2/index.d.ts +0 -35
- package/dist/tools/percy-mcp/v2/index.js +0 -544
- package/dist/tools/percy-mcp/v2/list-integrations.d.ts +0 -5
- package/dist/tools/percy-mcp/v2/list-integrations.js +0 -41
- package/dist/tools/percy-mcp/v2/manage-domains.d.ts +0 -8
- package/dist/tools/percy-mcp/v2/manage-domains.js +0 -33
- package/dist/tools/percy-mcp/v2/manage-insights-email.d.ts +0 -8
- package/dist/tools/percy-mcp/v2/manage-insights-email.js +0 -49
- package/dist/tools/percy-mcp/v2/manage-usage-alerts.d.ts +0 -10
- package/dist/tools/percy-mcp/v2/manage-usage-alerts.js +0 -43
- package/dist/tools/percy-mcp/v2/migrate-integrations.d.ts +0 -6
- package/dist/tools/percy-mcp/v2/migrate-integrations.js +0 -20
- package/dist/tools/percy-mcp/v2/preview-comparison.d.ts +0 -5
- package/dist/tools/percy-mcp/v2/preview-comparison.js +0 -17
- package/dist/tools/percy-mcp/v2/search-build-items.d.ts +0 -12
- package/dist/tools/percy-mcp/v2/search-build-items.js +0 -45
- package/dist/tools/percy-mcp/workflows/auto-triage.d.ts +0 -7
- package/dist/tools/percy-mcp/workflows/auto-triage.js +0 -82
- package/dist/tools/percy-mcp/workflows/clone-build.d.ts +0 -22
- package/dist/tools/percy-mcp/workflows/clone-build.js +0 -414
- package/dist/tools/percy-mcp/workflows/create-percy-build.d.ts +0 -32
- package/dist/tools/percy-mcp/workflows/create-percy-build.js +0 -434
- package/dist/tools/percy-mcp/workflows/debug-failed-build.d.ts +0 -5
- package/dist/tools/percy-mcp/workflows/debug-failed-build.js +0 -122
- package/dist/tools/percy-mcp/workflows/diff-explain.d.ts +0 -6
- package/dist/tools/percy-mcp/workflows/diff-explain.js +0 -147
- package/dist/tools/percy-mcp/workflows/pr-visual-report.d.ts +0 -8
- package/dist/tools/percy-mcp/workflows/pr-visual-report.js +0 -184
- package/dist/tools/percy-mcp/workflows/run-tests.d.ts +0 -17
- package/dist/tools/percy-mcp/workflows/run-tests.js +0 -107
- package/dist/tools/percy-mcp/workflows/snapshot-urls.d.ts +0 -18
- package/dist/tools/percy-mcp/workflows/snapshot-urls.js +0 -197
package/dist/server-factory.js
CHANGED
|
@@ -16,8 +16,6 @@ import addAppLiveTools from "./tools/applive.js";
|
|
|
16
16
|
import addBuildInsightsTools from "./tools/build-insights.js";
|
|
17
17
|
import { setupOnInitialized } from "./oninitialized.js";
|
|
18
18
|
import addRCATools from "./tools/rca-agent.js";
|
|
19
|
-
// import addPercyMcpTools from "./tools/percy-mcp/index.js"; // v1 disabled
|
|
20
|
-
import addPercyMcpToolsV2 from "./tools/percy-mcp/v2/index.js";
|
|
21
19
|
/**
|
|
22
20
|
* Wrapper class for BrowserStack MCP Server
|
|
23
21
|
* Stores a map of registered tools by name
|
|
@@ -53,8 +51,6 @@ export class BrowserStackMcpServer {
|
|
|
53
51
|
addSelfHealTools,
|
|
54
52
|
addBuildInsightsTools,
|
|
55
53
|
addRCATools,
|
|
56
|
-
// addPercyMcpTools, // v1 — disabled, replaced by v2
|
|
57
|
-
addPercyMcpToolsV2,
|
|
58
54
|
];
|
|
59
55
|
toolAdders.forEach((adder) => {
|
|
60
56
|
// Each adder now returns a Record<string, Tool>
|
package/dist/tools/percy-sdk.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { trackMCP } from "../index.js";
|
|
2
2
|
import { fetchPercyChanges } from "./review-agent.js";
|
|
3
3
|
import { addListTestFiles } from "./list-test-files.js";
|
|
4
|
-
import { runPercyScan } from "./run-percy-scan.js";
|
|
5
4
|
import { SetUpPercyParamsShape } from "./sdk-utils/common/schema.js";
|
|
6
5
|
import { updateTestsWithPercyCommands } from "./add-percy-snapshots.js";
|
|
7
6
|
import { approveOrDeclinePercyBuild } from "./review-agent-utils/percy-approve-reject.js";
|
|
@@ -9,7 +8,10 @@ import { setUpPercyHandler, simulatePercyChangeHandler, } from "./sdk-utils/hand
|
|
|
9
8
|
import { z } from "zod";
|
|
10
9
|
import { SETUP_PERCY_DESCRIPTION, LIST_TEST_FILES_DESCRIPTION, PERCY_SNAPSHOT_COMMANDS_DESCRIPTION, SIMULATE_PERCY_CHANGE_DESCRIPTION, } from "./sdk-utils/common/constants.js";
|
|
11
10
|
import { UpdateTestFileWithInstructionsParams } from "./percy-snapshot-utils/constants.js";
|
|
12
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
// PMAA-100: kept commented so the registration block below is easy to restore once the proper fix lands.
|
|
13
|
+
// RunPercyScanParamsShape,
|
|
14
|
+
FetchPercyChangesParamsShape, ManagePercyBuildApprovalParamsShape, } from "./sdk-utils/common/schema.js";
|
|
13
15
|
import { handleMCPError } from "../lib/utils.js";
|
|
14
16
|
export function registerPercyTools(server, config) {
|
|
15
17
|
const tools = {};
|
|
@@ -66,15 +68,22 @@ export function registerPercyTools(server, config) {
|
|
|
66
68
|
return handleMCPError("listTestFiles", server, config, error);
|
|
67
69
|
}
|
|
68
70
|
});
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
71
|
+
// PMAA-100: runPercyScan temporarily disabled — fetched Percy token was being
|
|
72
|
+
// returned in plaintext within tool output (see HackerOne #3576387). Re-enable
|
|
73
|
+
// once the token is replaced with a placeholder in run-percy-scan.ts.
|
|
74
|
+
// tools.runPercyScan = server.tool(
|
|
75
|
+
// "runPercyScan",
|
|
76
|
+
// "Run a Percy visual test scan. Example prompts : Run this Percy build/scan. Never run percy scan/build without this tool",
|
|
77
|
+
// RunPercyScanParamsShape,
|
|
78
|
+
// async (args) => {
|
|
79
|
+
// try {
|
|
80
|
+
// trackMCP("runPercyScan", server.server.getClientVersion()!, config);
|
|
81
|
+
// return runPercyScan(args, config);
|
|
82
|
+
// } catch (error) {
|
|
83
|
+
// return handleMCPError("runPercyScan", server, config, error);
|
|
84
|
+
// }
|
|
85
|
+
// },
|
|
86
|
+
// );
|
|
78
87
|
tools.fetchPercyChanges = server.tool("fetchPercyChanges", "Retrieves and summarizes all visual changes detected by Percy AI between the latest and previous builds, helping quickly review what has changed in your project.", FetchPercyChangesParamsShape, async (args) => {
|
|
79
88
|
try {
|
|
80
89
|
trackMCP("fetchPercyChanges", server.server.getClientVersion(), config);
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
|
|
3
|
+
import { BrowserStackConfig } from "../../lib/types.js";
|
|
4
|
+
/**
|
|
5
|
+
* Schema for fetching a single test plan by identifier, including its linked test runs.
|
|
6
|
+
*/
|
|
7
|
+
export declare const GetTestPlanSchema: z.ZodObject<{
|
|
8
|
+
project_identifier: z.ZodString;
|
|
9
|
+
test_plan_identifier: z.ZodString;
|
|
10
|
+
}, z.core.$strip>;
|
|
11
|
+
export type GetTestPlanArgs = z.infer<typeof GetTestPlanSchema>;
|
|
12
|
+
/**
|
|
13
|
+
* Fetches a test plan by identifier and its linked test runs, returning a unified view
|
|
14
|
+
* suitable for generating documentation (metadata + linked runs + status summary + case count).
|
|
15
|
+
*/
|
|
16
|
+
export declare function getTestPlan(args: GetTestPlanArgs, config: BrowserStackConfig): Promise<CallToolResult>;
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { apiClient } from "../../lib/apiClient.js";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { formatAxiosError } from "../../lib/error.js";
|
|
4
|
+
import { getBrowserStackAuth } from "../../lib/get-auth.js";
|
|
5
|
+
import { getTMBaseURL } from "../../lib/tm-base-url.js";
|
|
6
|
+
/**
|
|
7
|
+
* Schema for fetching a single test plan by identifier, including its linked test runs.
|
|
8
|
+
*/
|
|
9
|
+
export const GetTestPlanSchema = z.object({
|
|
10
|
+
project_identifier: z
|
|
11
|
+
.string()
|
|
12
|
+
.describe("Identifier of the project (starts with PR- followed by a number)."),
|
|
13
|
+
test_plan_identifier: z
|
|
14
|
+
.string()
|
|
15
|
+
.describe("Identifier of the test plan (starts with TP- followed by a number)."),
|
|
16
|
+
});
|
|
17
|
+
/**
|
|
18
|
+
* Fetches a test plan by identifier and its linked test runs, returning a unified view
|
|
19
|
+
* suitable for generating documentation (metadata + linked runs + status summary + case count).
|
|
20
|
+
*/
|
|
21
|
+
export async function getTestPlan(args, config) {
|
|
22
|
+
try {
|
|
23
|
+
const tmBaseUrl = await getTMBaseURL(config);
|
|
24
|
+
const projectId = encodeURIComponent(args.project_identifier);
|
|
25
|
+
const planId = encodeURIComponent(args.test_plan_identifier);
|
|
26
|
+
const authString = getBrowserStackAuth(config);
|
|
27
|
+
const [username, password] = authString.split(":");
|
|
28
|
+
const authHeader = "Basic " + Buffer.from(`${username}:${password}`).toString("base64");
|
|
29
|
+
const planResp = await apiClient.get({
|
|
30
|
+
url: `${tmBaseUrl}/api/v2/projects/${projectId}/test-plans/${planId}`,
|
|
31
|
+
headers: { Authorization: authHeader },
|
|
32
|
+
});
|
|
33
|
+
if (!planResp.data?.success) {
|
|
34
|
+
return {
|
|
35
|
+
content: [
|
|
36
|
+
{
|
|
37
|
+
type: "text",
|
|
38
|
+
text: `Failed to fetch test plan: ${JSON.stringify(planResp.data)}`,
|
|
39
|
+
},
|
|
40
|
+
],
|
|
41
|
+
isError: true,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
const plan = planResp.data.test_plan;
|
|
45
|
+
const runsResp = await apiClient.get({
|
|
46
|
+
url: `${tmBaseUrl}/api/v2/projects/${projectId}/test-plans/${planId}/test-runs`,
|
|
47
|
+
headers: { Authorization: authHeader },
|
|
48
|
+
});
|
|
49
|
+
const runs = runsResp.data?.success
|
|
50
|
+
? (runsResp.data.test_runs ?? [])
|
|
51
|
+
: [];
|
|
52
|
+
const statusSummary = {};
|
|
53
|
+
let totalCases = 0;
|
|
54
|
+
for (const run of runs) {
|
|
55
|
+
statusSummary[run.run_state] = (statusSummary[run.run_state] ?? 0) + 1;
|
|
56
|
+
totalCases += run.test_cases_count ?? 0;
|
|
57
|
+
}
|
|
58
|
+
const header = [
|
|
59
|
+
`Test Plan ${plan.identifier}: ${plan.name}`,
|
|
60
|
+
`Status: ${plan.active_state}`,
|
|
61
|
+
plan.description ? `Description: ${plan.description}` : null,
|
|
62
|
+
plan.start_date || plan.end_date
|
|
63
|
+
? `Dates: ${plan.start_date ?? "—"} → ${plan.end_date ?? "—"}`
|
|
64
|
+
: null,
|
|
65
|
+
`Linked runs: ${runs.length} (plan counts — active ${plan.test_runs_count?.active ?? 0} / closed ${plan.test_runs_count?.closed ?? 0})`,
|
|
66
|
+
`Total test cases across runs: ${totalCases}`,
|
|
67
|
+
Object.keys(statusSummary).length > 0
|
|
68
|
+
? `Run-state breakdown: ${Object.entries(statusSummary)
|
|
69
|
+
.map(([s, n]) => `${s}=${n}`)
|
|
70
|
+
.join(", ")}`
|
|
71
|
+
: null,
|
|
72
|
+
]
|
|
73
|
+
.filter(Boolean)
|
|
74
|
+
.join("\n");
|
|
75
|
+
const runsBlock = runs.length
|
|
76
|
+
? "\n\nLinked test runs:\n" +
|
|
77
|
+
runs
|
|
78
|
+
.map((r) => `• ${r.identifier}: ${r.name} [${r.run_state}] — ${r.test_cases_count} case(s)${r.assignee ? ` (assignee: ${r.assignee})` : ""}`)
|
|
79
|
+
.join("\n")
|
|
80
|
+
: "\n\nNo test runs linked to this plan.";
|
|
81
|
+
return {
|
|
82
|
+
content: [
|
|
83
|
+
{ type: "text", text: header + runsBlock },
|
|
84
|
+
{
|
|
85
|
+
type: "text",
|
|
86
|
+
text: JSON.stringify({
|
|
87
|
+
test_plan: plan,
|
|
88
|
+
linked_test_runs: runs,
|
|
89
|
+
status_summary: statusSummary,
|
|
90
|
+
total_test_cases: totalCases,
|
|
91
|
+
}, null, 2),
|
|
92
|
+
},
|
|
93
|
+
],
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
catch (err) {
|
|
97
|
+
return formatAxiosError(err, "Failed to fetch test plan");
|
|
98
|
+
}
|
|
99
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
|
|
3
|
+
import { BrowserStackConfig } from "../../lib/types.js";
|
|
4
|
+
/**
|
|
5
|
+
* Schema for listing folders in a BrowserStack Test Management project.
|
|
6
|
+
*/
|
|
7
|
+
export declare const ListFoldersSchema: z.ZodObject<{
|
|
8
|
+
project_identifier: z.ZodString;
|
|
9
|
+
parent_id: z.ZodOptional<z.ZodNumber>;
|
|
10
|
+
p: z.ZodOptional<z.ZodNumber>;
|
|
11
|
+
}, z.core.$strip>;
|
|
12
|
+
export type ListFoldersArgs = z.infer<typeof ListFoldersSchema>;
|
|
13
|
+
/**
|
|
14
|
+
* Lists folders (or sub-folders) for a project in BrowserStack Test Management.
|
|
15
|
+
*/
|
|
16
|
+
export declare function listFolders(args: ListFoldersArgs, config: BrowserStackConfig): Promise<CallToolResult>;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { apiClient } from "../../lib/apiClient.js";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { formatAxiosError } from "../../lib/error.js";
|
|
4
|
+
import { getBrowserStackAuth } from "../../lib/get-auth.js";
|
|
5
|
+
import { getTMBaseURL } from "../../lib/tm-base-url.js";
|
|
6
|
+
/**
|
|
7
|
+
* Schema for listing folders in a BrowserStack Test Management project.
|
|
8
|
+
*/
|
|
9
|
+
export const ListFoldersSchema = z.object({
|
|
10
|
+
project_identifier: z
|
|
11
|
+
.string()
|
|
12
|
+
.describe("Identifier of the project to fetch folders from (starts with PR- followed by a number)."),
|
|
13
|
+
parent_id: z
|
|
14
|
+
.number()
|
|
15
|
+
.optional()
|
|
16
|
+
.describe("If provided, list sub-folders under this parent folder id. If omitted, lists top-level folders."),
|
|
17
|
+
p: z.number().optional().describe("Page number."),
|
|
18
|
+
});
|
|
19
|
+
/**
|
|
20
|
+
* Lists folders (or sub-folders) for a project in BrowserStack Test Management.
|
|
21
|
+
*/
|
|
22
|
+
export async function listFolders(args, config) {
|
|
23
|
+
try {
|
|
24
|
+
const params = new URLSearchParams();
|
|
25
|
+
if (args.p !== undefined)
|
|
26
|
+
params.append("p", args.p.toString());
|
|
27
|
+
const tmBaseUrl = await getTMBaseURL(config);
|
|
28
|
+
const projectId = encodeURIComponent(args.project_identifier);
|
|
29
|
+
// GET /api/v2/projects/{projectIdentifier}/folders
|
|
30
|
+
// or /api/v2/projects/{projectIdentifier}/folders/{parent_id}/sub-folders
|
|
31
|
+
const path = args.parent_id !== undefined
|
|
32
|
+
? `folders/${args.parent_id}/sub-folders`
|
|
33
|
+
: `folders`;
|
|
34
|
+
const url = `${tmBaseUrl}/api/v2/projects/${projectId}/${path}?${params.toString()}`;
|
|
35
|
+
const authString = getBrowserStackAuth(config);
|
|
36
|
+
const [username, password] = authString.split(":");
|
|
37
|
+
const resp = await apiClient.get({
|
|
38
|
+
url,
|
|
39
|
+
headers: {
|
|
40
|
+
Authorization: "Basic " + Buffer.from(`${username}:${password}`).toString("base64"),
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
const folders = resp.data?.folders ?? [];
|
|
44
|
+
const info = resp.data?.info ?? {};
|
|
45
|
+
const count = info.count ?? folders.length;
|
|
46
|
+
if (folders.length === 0) {
|
|
47
|
+
return {
|
|
48
|
+
content: [
|
|
49
|
+
{
|
|
50
|
+
type: "text",
|
|
51
|
+
text: args.parent_id !== undefined
|
|
52
|
+
? `No sub-folders found under folder ${args.parent_id} in project ${args.project_identifier}.`
|
|
53
|
+
: `No folders found in project ${args.project_identifier}.`,
|
|
54
|
+
},
|
|
55
|
+
],
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
const summary = folders
|
|
59
|
+
.map((f) => `• [id=${f.id}] ${f.name} — ${f.cases_count} case(s), ${f.sub_folders_count} sub-folder(s)${f.parent_id ? ` (parent=${f.parent_id})` : ""}`)
|
|
60
|
+
.join("\n");
|
|
61
|
+
return {
|
|
62
|
+
content: [
|
|
63
|
+
{
|
|
64
|
+
type: "text",
|
|
65
|
+
text: `Found ${count} folder(s) in project ${args.project_identifier}:\n\n${summary}`,
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
type: "text",
|
|
69
|
+
text: JSON.stringify(folders, null, 2),
|
|
70
|
+
},
|
|
71
|
+
],
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
catch (err) {
|
|
75
|
+
return formatAxiosError(err, "Failed to list folders");
|
|
76
|
+
}
|
|
77
|
+
}
|
|
@@ -13,7 +13,7 @@ export const ListTestCasesSchema = z.object({
|
|
|
13
13
|
folder_id: z
|
|
14
14
|
.string()
|
|
15
15
|
.optional()
|
|
16
|
-
.describe("If provided, only return cases in this folder."),
|
|
16
|
+
.describe("Optional. If provided, only return test cases in this folder. If omitted, returns all test cases in the project. Folder ids can be discovered via listFolders."),
|
|
17
17
|
case_type: z
|
|
18
18
|
.string()
|
|
19
19
|
.optional()
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
|
|
3
|
+
import { BrowserStackConfig } from "../../lib/types.js";
|
|
4
|
+
/**
|
|
5
|
+
* Schema for listing test plans in a BrowserStack Test Management project.
|
|
6
|
+
*/
|
|
7
|
+
export declare const ListTestPlansSchema: z.ZodObject<{
|
|
8
|
+
project_identifier: z.ZodString;
|
|
9
|
+
p: z.ZodOptional<z.ZodNumber>;
|
|
10
|
+
}, z.core.$strip>;
|
|
11
|
+
export type ListTestPlansArgs = z.infer<typeof ListTestPlansSchema>;
|
|
12
|
+
/**
|
|
13
|
+
* Lists test plans for a project in BrowserStack Test Management.
|
|
14
|
+
*/
|
|
15
|
+
export declare function listTestPlans(args: ListTestPlansArgs, config: BrowserStackConfig): Promise<CallToolResult>;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { apiClient } from "../../lib/apiClient.js";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { formatAxiosError } from "../../lib/error.js";
|
|
4
|
+
import { getBrowserStackAuth } from "../../lib/get-auth.js";
|
|
5
|
+
import { getTMBaseURL } from "../../lib/tm-base-url.js";
|
|
6
|
+
/**
|
|
7
|
+
* Schema for listing test plans in a BrowserStack Test Management project.
|
|
8
|
+
*/
|
|
9
|
+
export const ListTestPlansSchema = z.object({
|
|
10
|
+
project_identifier: z
|
|
11
|
+
.string()
|
|
12
|
+
.describe("Identifier of the project to fetch test plans from (starts with PR- followed by a number)."),
|
|
13
|
+
p: z.number().optional().describe("Page number."),
|
|
14
|
+
});
|
|
15
|
+
/**
|
|
16
|
+
* Lists test plans for a project in BrowserStack Test Management.
|
|
17
|
+
*/
|
|
18
|
+
export async function listTestPlans(args, config) {
|
|
19
|
+
try {
|
|
20
|
+
const params = new URLSearchParams();
|
|
21
|
+
if (args.p !== undefined)
|
|
22
|
+
params.append("p", args.p.toString());
|
|
23
|
+
const tmBaseUrl = await getTMBaseURL(config);
|
|
24
|
+
const projectId = encodeURIComponent(args.project_identifier);
|
|
25
|
+
const url = `${tmBaseUrl}/api/v2/projects/${projectId}/test-plans?${params.toString()}`;
|
|
26
|
+
const authString = getBrowserStackAuth(config);
|
|
27
|
+
const [username, password] = authString.split(":");
|
|
28
|
+
const resp = await apiClient.get({
|
|
29
|
+
url,
|
|
30
|
+
headers: {
|
|
31
|
+
Authorization: "Basic " + Buffer.from(`${username}:${password}`).toString("base64"),
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
const data = resp.data;
|
|
35
|
+
if (!data.success) {
|
|
36
|
+
return {
|
|
37
|
+
content: [
|
|
38
|
+
{
|
|
39
|
+
type: "text",
|
|
40
|
+
text: `Failed to list test plans: ${JSON.stringify(data)}`,
|
|
41
|
+
},
|
|
42
|
+
],
|
|
43
|
+
isError: true,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
const plans = data.test_plans ?? [];
|
|
47
|
+
const info = data.info ?? {};
|
|
48
|
+
const count = info.count ?? plans.length;
|
|
49
|
+
if (plans.length === 0) {
|
|
50
|
+
return {
|
|
51
|
+
content: [
|
|
52
|
+
{
|
|
53
|
+
type: "text",
|
|
54
|
+
text: `No test plans found in project ${args.project_identifier}.`,
|
|
55
|
+
},
|
|
56
|
+
],
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
const summary = plans
|
|
60
|
+
.map((p) => `• ${p.identifier}: ${p.name} [${p.active_state}] — ${p.test_runs_count?.active ?? 0} active / ${p.test_runs_count?.closed ?? 0} closed run(s)`)
|
|
61
|
+
.join("\n");
|
|
62
|
+
return {
|
|
63
|
+
content: [
|
|
64
|
+
{
|
|
65
|
+
type: "text",
|
|
66
|
+
text: `Found ${count} test plan(s) in project ${args.project_identifier}:\n\n${summary}`,
|
|
67
|
+
},
|
|
68
|
+
{ type: "text", text: JSON.stringify(plans, null, 2) },
|
|
69
|
+
],
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
catch (err) {
|
|
73
|
+
return formatAxiosError(err, "Failed to list test plans");
|
|
74
|
+
}
|
|
75
|
+
}
|
|
@@ -11,6 +11,14 @@ export interface TestCaseUpdateRequest {
|
|
|
11
11
|
step: string;
|
|
12
12
|
result: string;
|
|
13
13
|
}>;
|
|
14
|
+
owner?: string;
|
|
15
|
+
priority?: string;
|
|
16
|
+
case_type?: string;
|
|
17
|
+
automation_status?: string;
|
|
18
|
+
status?: string;
|
|
19
|
+
tags?: string[];
|
|
20
|
+
issues?: string[];
|
|
21
|
+
custom_fields?: Record<string, string | number | boolean>;
|
|
14
22
|
}
|
|
15
23
|
export declare const UpdateTestCaseSchema: z.ZodObject<{
|
|
16
24
|
project_identifier: z.ZodString;
|
|
@@ -22,6 +30,14 @@ export declare const UpdateTestCaseSchema: z.ZodObject<{
|
|
|
22
30
|
step: z.ZodString;
|
|
23
31
|
result: z.ZodString;
|
|
24
32
|
}, z.core.$strip>>>;
|
|
33
|
+
owner: z.ZodOptional<z.ZodString>;
|
|
34
|
+
priority: z.ZodOptional<z.ZodString>;
|
|
35
|
+
case_type: z.ZodOptional<z.ZodString>;
|
|
36
|
+
automation_status: z.ZodOptional<z.ZodString>;
|
|
37
|
+
status: z.ZodOptional<z.ZodString>;
|
|
38
|
+
tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
39
|
+
issues: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
40
|
+
custom_fields: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>>;
|
|
25
41
|
}, z.core.$strip>;
|
|
26
42
|
/**
|
|
27
43
|
* Updates an existing test case in BrowserStack Test Management.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { apiClient } from "../../lib/apiClient.js";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { formatAxiosError } from "../../lib/error.js";
|
|
4
|
-
import { projectIdentifierToId } from "./TCG-utils/api.js";
|
|
4
|
+
import { fetchFormFields, projectIdentifierToId } from "./TCG-utils/api.js";
|
|
5
5
|
import { getTMBaseURL } from "../../lib/tm-base-url.js";
|
|
6
6
|
import { getBrowserStackAuth } from "../../lib/get-auth.js";
|
|
7
7
|
import logger from "../../logger.js";
|
|
@@ -28,26 +28,148 @@ export const UpdateTestCaseSchema = z.object({
|
|
|
28
28
|
}))
|
|
29
29
|
.optional()
|
|
30
30
|
.describe("Updated list of test case steps with expected results."),
|
|
31
|
+
owner: z
|
|
32
|
+
.string()
|
|
33
|
+
.email()
|
|
34
|
+
.optional()
|
|
35
|
+
.describe("Email of the test case owner."),
|
|
36
|
+
priority: z
|
|
37
|
+
.string()
|
|
38
|
+
.optional()
|
|
39
|
+
.describe("Updated priority. Accepts either display name (e.g. 'Medium', 'Critical', 'High', 'Low') or internal name (e.g. 'medium'). Valid values are per-project and discoverable via the form-fields endpoint."),
|
|
40
|
+
case_type: z
|
|
41
|
+
.string()
|
|
42
|
+
.optional()
|
|
43
|
+
.describe("Updated test case type. Accepts either display name (e.g. 'Functional', 'Regression', 'Smoke & Sanity') or internal name (e.g. 'functional', 'smoke_sanity'). Valid values are per-project."),
|
|
44
|
+
automation_status: z
|
|
45
|
+
.string()
|
|
46
|
+
.optional()
|
|
47
|
+
.describe("Updated automation status. Use internal name such as 'not_automated', 'automated', 'automation_not_required', 'cannot_be_automated', or 'obsolete'."),
|
|
48
|
+
status: z
|
|
49
|
+
.string()
|
|
50
|
+
.optional()
|
|
51
|
+
.describe("Updated review status of the test case (e.g. 'active', 'draft', 'in_review', 'outdated', 'rejected')."),
|
|
52
|
+
tags: z
|
|
53
|
+
.array(z.string())
|
|
54
|
+
.optional()
|
|
55
|
+
.describe("Replacement list of tags for the test case."),
|
|
56
|
+
issues: z
|
|
57
|
+
.array(z.string())
|
|
58
|
+
.optional()
|
|
59
|
+
.describe("Replacement list of linked Jira/Asana/Azure issue IDs for the test case."),
|
|
60
|
+
custom_fields: z
|
|
61
|
+
.record(z.string(), z.union([z.string(), z.number(), z.boolean()]))
|
|
62
|
+
.optional()
|
|
63
|
+
.describe("Map of custom field name/id to value. Valid field names and value types are per-project; discover them via the project's form fields."),
|
|
31
64
|
});
|
|
65
|
+
/**
|
|
66
|
+
* Build a normalizer for a default field's accepted value.
|
|
67
|
+
* The TM PATCH endpoint accepts different casings for different default
|
|
68
|
+
* fields (Title-Case display name for priority/case_type, snake_case
|
|
69
|
+
* internal_name for automation_status). We accept either from the caller
|
|
70
|
+
* and emit the form the API actually wants.
|
|
71
|
+
*
|
|
72
|
+
* Returns undefined when no matching option is found — callers should
|
|
73
|
+
* pass the raw value through so the backend can surface its own error.
|
|
74
|
+
*/
|
|
75
|
+
function normalizeDefaultFieldValue(fieldValues, input, emit) {
|
|
76
|
+
const normalized = input.toLowerCase().trim();
|
|
77
|
+
const match = fieldValues.find((v) => (v.internal_name ?? "").toLowerCase() === normalized ||
|
|
78
|
+
(v.name ?? "").toLowerCase() === normalized);
|
|
79
|
+
if (!match)
|
|
80
|
+
return undefined;
|
|
81
|
+
if (emit === "name")
|
|
82
|
+
return match.name;
|
|
83
|
+
return match.internal_name ?? match.name;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Normalise default-field inputs (priority/case_type/automation_status) to
|
|
87
|
+
* what the TM PATCH endpoint accepts. Fetches the project's form-fields
|
|
88
|
+
* on demand; on failure, returns inputs unchanged and lets the backend
|
|
89
|
+
* surface validation errors.
|
|
90
|
+
*/
|
|
91
|
+
async function normalizeDefaultFields(params, config) {
|
|
92
|
+
const needsLookup = params.priority !== undefined ||
|
|
93
|
+
params.case_type !== undefined ||
|
|
94
|
+
params.automation_status !== undefined;
|
|
95
|
+
if (!needsLookup)
|
|
96
|
+
return {};
|
|
97
|
+
try {
|
|
98
|
+
const numericProjectId = await projectIdentifierToId(params.project_identifier, config);
|
|
99
|
+
const { default_fields } = await fetchFormFields(numericProjectId, config);
|
|
100
|
+
const out = {};
|
|
101
|
+
if (params.priority !== undefined) {
|
|
102
|
+
out.priority =
|
|
103
|
+
normalizeDefaultFieldValue(default_fields?.priority?.values ?? [], params.priority, "name") ?? params.priority;
|
|
104
|
+
}
|
|
105
|
+
if (params.case_type !== undefined) {
|
|
106
|
+
out.case_type =
|
|
107
|
+
normalizeDefaultFieldValue(default_fields?.case_type?.values ?? [], params.case_type, "name") ?? params.case_type;
|
|
108
|
+
}
|
|
109
|
+
if (params.automation_status !== undefined) {
|
|
110
|
+
// automation_status.values have null internal_name and the internal
|
|
111
|
+
// name is actually held in `value` (see API inspection). Accept
|
|
112
|
+
// either the display name or the internal snake_case form.
|
|
113
|
+
const values = default_fields?.automation_status?.values ?? [];
|
|
114
|
+
const input = params.automation_status.toLowerCase().trim();
|
|
115
|
+
const match = values.find((v) => (v.value ?? "").toLowerCase() === input ||
|
|
116
|
+
(v.name ?? "").toLowerCase() === input);
|
|
117
|
+
out.automation_status = match?.value ?? params.automation_status;
|
|
118
|
+
}
|
|
119
|
+
return out;
|
|
120
|
+
}
|
|
121
|
+
catch (err) {
|
|
122
|
+
logger.warn("Failed to normalize default field values; passing through as given: %s", err instanceof Error ? err.message : String(err));
|
|
123
|
+
return {
|
|
124
|
+
priority: params.priority,
|
|
125
|
+
case_type: params.case_type,
|
|
126
|
+
automation_status: params.automation_status,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
}
|
|
32
130
|
/**
|
|
33
131
|
* Updates an existing test case in BrowserStack Test Management.
|
|
34
132
|
*/
|
|
35
133
|
export async function updateTestCase(params, config) {
|
|
36
134
|
const authString = getBrowserStackAuth(config);
|
|
37
135
|
const [username, password] = authString.split(":");
|
|
38
|
-
// Build the request body with only the fields to update
|
|
39
136
|
const testCaseBody = {};
|
|
40
|
-
if (params.name !== undefined)
|
|
137
|
+
if (params.name !== undefined)
|
|
41
138
|
testCaseBody.name = params.name;
|
|
42
|
-
|
|
43
|
-
if (params.description !== undefined) {
|
|
139
|
+
if (params.description !== undefined)
|
|
44
140
|
testCaseBody.description = params.description;
|
|
45
|
-
|
|
46
|
-
if (params.preconditions !== undefined) {
|
|
141
|
+
if (params.preconditions !== undefined)
|
|
47
142
|
testCaseBody.preconditions = params.preconditions;
|
|
48
|
-
|
|
49
|
-
if (params.test_case_steps !== undefined) {
|
|
143
|
+
if (params.test_case_steps !== undefined)
|
|
50
144
|
testCaseBody.steps = params.test_case_steps;
|
|
145
|
+
if (params.owner !== undefined)
|
|
146
|
+
testCaseBody.owner = params.owner;
|
|
147
|
+
if (params.status !== undefined)
|
|
148
|
+
testCaseBody.status = params.status;
|
|
149
|
+
if (params.tags !== undefined)
|
|
150
|
+
testCaseBody.tags = params.tags;
|
|
151
|
+
if (params.issues !== undefined)
|
|
152
|
+
testCaseBody.issues = params.issues;
|
|
153
|
+
if (params.custom_fields !== undefined)
|
|
154
|
+
testCaseBody.custom_fields = params.custom_fields;
|
|
155
|
+
// Default fields need value normalization (see notes above the helper).
|
|
156
|
+
const normalized = await normalizeDefaultFields(params, config);
|
|
157
|
+
if (normalized.priority !== undefined)
|
|
158
|
+
testCaseBody.priority = normalized.priority;
|
|
159
|
+
if (normalized.case_type !== undefined)
|
|
160
|
+
testCaseBody.case_type = normalized.case_type;
|
|
161
|
+
if (normalized.automation_status !== undefined)
|
|
162
|
+
testCaseBody.automation_status = normalized.automation_status;
|
|
163
|
+
if (Object.keys(testCaseBody).length === 0) {
|
|
164
|
+
return {
|
|
165
|
+
content: [
|
|
166
|
+
{
|
|
167
|
+
type: "text",
|
|
168
|
+
text: "No updatable fields provided. Pass at least one of: name, description, preconditions, test_case_steps, owner, priority, case_type, automation_status, status, tags, issues, custom_fields.",
|
|
169
|
+
},
|
|
170
|
+
],
|
|
171
|
+
isError: true,
|
|
172
|
+
};
|
|
51
173
|
}
|
|
52
174
|
const body = { test_case: testCaseBody };
|
|
53
175
|
try {
|
|
@@ -80,7 +202,7 @@ export async function updateTestCase(params, config) {
|
|
|
80
202
|
{
|
|
81
203
|
type: "text",
|
|
82
204
|
text: `Test case successfully updated:
|
|
83
|
-
|
|
205
|
+
|
|
84
206
|
**Test Case Details:**
|
|
85
207
|
- **ID**: ${tc.identifier}
|
|
86
208
|
- **Name**: ${tc.title}
|
|
@@ -88,6 +210,7 @@ export async function updateTestCase(params, config) {
|
|
|
88
210
|
- **Case Type**: ${tc.case_type}
|
|
89
211
|
- **Priority**: ${tc.priority}
|
|
90
212
|
- **Status**: ${tc.status}
|
|
213
|
+
- **Automation Status**: ${tc.automation_status ?? "N/A"}
|
|
91
214
|
|
|
92
215
|
**View on BrowserStack Dashboard:**
|
|
93
216
|
https://test-management.browserstack.com/projects/${projectId}/folders/${tc.folder_id}/test-cases/${tc.identifier}
|
|
@@ -5,6 +5,7 @@ import { CreateProjFoldSchema } from "./testmanagement-utils/create-project-fold
|
|
|
5
5
|
import { TestCaseCreateRequest } from "./testmanagement-utils/create-testcase.js";
|
|
6
6
|
import { TestCaseUpdateRequest } from "./testmanagement-utils/update-testcase.js";
|
|
7
7
|
import { ListTestCasesSchema } from "./testmanagement-utils/list-testcases.js";
|
|
8
|
+
import { ListFoldersSchema } from "./testmanagement-utils/list-folders.js";
|
|
8
9
|
import { CreateTestRunSchema } from "./testmanagement-utils/create-testrun.js";
|
|
9
10
|
import { ListTestRunsSchema } from "./testmanagement-utils/list-testruns.js";
|
|
10
11
|
import { UpdateTestRunSchema } from "./testmanagement-utils/update-testrun.js";
|
|
@@ -12,6 +13,8 @@ import { AddTestResultSchema } from "./testmanagement-utils/add-test-result.js";
|
|
|
12
13
|
import { UploadFileSchema } from "./testmanagement-utils/upload-file.js";
|
|
13
14
|
import { CreateTestCasesFromFileSchema } from "./testmanagement-utils/TCG-utils/types.js";
|
|
14
15
|
import { CreateLCAStepsSchema } from "./testmanagement-utils/create-lca-steps.js";
|
|
16
|
+
import { ListTestPlansSchema } from "./testmanagement-utils/list-testplans.js";
|
|
17
|
+
import { GetTestPlanSchema } from "./testmanagement-utils/get-testplan.js";
|
|
15
18
|
import { BrowserStackConfig } from "../lib/types.js";
|
|
16
19
|
/**
|
|
17
20
|
* Wrapper to call createProjectOrFolder util.
|
|
@@ -29,6 +32,10 @@ export declare function updateTestCaseTool(args: TestCaseUpdateRequest, config:
|
|
|
29
32
|
* Lists test cases in a project with optional filters (status, priority, custom fields, etc.)
|
|
30
33
|
*/
|
|
31
34
|
export declare function listTestCasesTool(args: z.infer<typeof ListTestCasesSchema>, config: BrowserStackConfig, server: McpServer): Promise<CallToolResult>;
|
|
35
|
+
/**
|
|
36
|
+
* Lists folders in a project (or sub-folders under a parent folder).
|
|
37
|
+
*/
|
|
38
|
+
export declare function listFoldersTool(args: z.infer<typeof ListFoldersSchema>, config: BrowserStackConfig, server: McpServer): Promise<CallToolResult>;
|
|
32
39
|
/**
|
|
33
40
|
* Creates a test run in BrowserStack Test Management.
|
|
34
41
|
*/
|
|
@@ -59,6 +66,14 @@ export declare function createTestCasesFromFileTool(args: z.infer<typeof CreateT
|
|
|
59
66
|
* Creates LCA (Low Code Automation) steps for a test case in BrowserStack Test Management.
|
|
60
67
|
*/
|
|
61
68
|
export declare function createLCAStepsTool(args: z.infer<typeof CreateLCAStepsSchema>, context: any, config: BrowserStackConfig, server: McpServer): Promise<CallToolResult>;
|
|
69
|
+
/**
|
|
70
|
+
* Lists test plans in a project.
|
|
71
|
+
*/
|
|
72
|
+
export declare function listTestPlansTool(args: z.infer<typeof ListTestPlansSchema>, config: BrowserStackConfig, server: McpServer): Promise<CallToolResult>;
|
|
73
|
+
/**
|
|
74
|
+
* Fetches a test plan by identifier, with its linked runs and a derived status summary.
|
|
75
|
+
*/
|
|
76
|
+
export declare function getTestPlanTool(args: z.infer<typeof GetTestPlanSchema>, config: BrowserStackConfig, server: McpServer): Promise<CallToolResult>;
|
|
62
77
|
/**
|
|
63
78
|
* Registers both project/folder and test-case tools.
|
|
64
79
|
*/
|