@browserstack/mcp-server 1.2.15-beta.1 → 1.2.15-beta.2
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/lib/percy-api/auth.d.ts +41 -0
- package/dist/lib/percy-api/auth.js +96 -0
- package/dist/lib/percy-api/cache.d.ts +28 -0
- package/dist/lib/percy-api/cache.js +48 -0
- package/dist/lib/percy-api/client.d.ts +69 -0
- package/dist/lib/percy-api/client.js +275 -0
- package/dist/lib/percy-api/errors.d.ts +15 -0
- package/dist/lib/percy-api/errors.js +52 -0
- package/dist/lib/percy-api/formatter.d.ts +16 -0
- package/dist/lib/percy-api/formatter.js +344 -0
- package/dist/lib/percy-api/percy-auth.d.ts +43 -0
- package/dist/lib/percy-api/percy-auth.js +137 -0
- package/dist/lib/percy-api/percy-error-handler.d.ts +24 -0
- package/dist/lib/percy-api/percy-error-handler.js +302 -0
- package/dist/lib/percy-api/percy-session.d.ts +42 -0
- package/dist/lib/percy-api/percy-session.js +87 -0
- package/dist/lib/percy-api/polling.d.ts +26 -0
- package/dist/lib/percy-api/polling.js +42 -0
- package/dist/lib/percy-api/types.d.ts +56 -0
- package/dist/lib/percy-api/types.js +76 -0
- package/dist/server-factory.js +4 -0
- package/dist/tools/percy-mcp/advanced/branchline-operations.d.ts +16 -0
- package/dist/tools/percy-mcp/advanced/branchline-operations.js +81 -0
- package/dist/tools/percy-mcp/advanced/manage-variants.d.ts +16 -0
- package/dist/tools/percy-mcp/advanced/manage-variants.js +155 -0
- package/dist/tools/percy-mcp/advanced/manage-visual-monitoring.d.ts +16 -0
- package/dist/tools/percy-mcp/advanced/manage-visual-monitoring.js +171 -0
- package/dist/tools/percy-mcp/auth/auth-status.d.ts +3 -0
- package/dist/tools/percy-mcp/auth/auth-status.js +131 -0
- package/dist/tools/percy-mcp/core/approve-build.d.ts +14 -0
- package/dist/tools/percy-mcp/core/approve-build.js +97 -0
- package/dist/tools/percy-mcp/core/get-build-items.d.ts +13 -0
- package/dist/tools/percy-mcp/core/get-build-items.js +65 -0
- package/dist/tools/percy-mcp/core/get-build.d.ts +10 -0
- package/dist/tools/percy-mcp/core/get-build.js +16 -0
- package/dist/tools/percy-mcp/core/get-comparison.d.ts +11 -0
- package/dist/tools/percy-mcp/core/get-comparison.js +59 -0
- package/dist/tools/percy-mcp/core/get-snapshot.d.ts +10 -0
- package/dist/tools/percy-mcp/core/get-snapshot.js +40 -0
- package/dist/tools/percy-mcp/core/list-builds.d.ts +14 -0
- package/dist/tools/percy-mcp/core/list-builds.js +45 -0
- package/dist/tools/percy-mcp/core/list-projects.d.ts +12 -0
- package/dist/tools/percy-mcp/core/list-projects.js +51 -0
- package/dist/tools/percy-mcp/creation/create-app-snapshot.d.ts +12 -0
- package/dist/tools/percy-mcp/creation/create-app-snapshot.js +29 -0
- package/dist/tools/percy-mcp/creation/create-build.d.ts +19 -0
- package/dist/tools/percy-mcp/creation/create-build.js +68 -0
- package/dist/tools/percy-mcp/creation/create-comparison.d.ts +18 -0
- package/dist/tools/percy-mcp/creation/create-comparison.js +90 -0
- package/dist/tools/percy-mcp/creation/create-snapshot.d.ts +17 -0
- package/dist/tools/percy-mcp/creation/create-snapshot.js +99 -0
- package/dist/tools/percy-mcp/creation/finalize-build.d.ts +12 -0
- package/dist/tools/percy-mcp/creation/finalize-build.js +33 -0
- package/dist/tools/percy-mcp/creation/finalize-comparison.d.ts +10 -0
- package/dist/tools/percy-mcp/creation/finalize-comparison.js +16 -0
- package/dist/tools/percy-mcp/creation/finalize-snapshot.d.ts +12 -0
- package/dist/tools/percy-mcp/creation/finalize-snapshot.js +33 -0
- package/dist/tools/percy-mcp/creation/upload-resource.d.ts +15 -0
- package/dist/tools/percy-mcp/creation/upload-resource.js +43 -0
- package/dist/tools/percy-mcp/creation/upload-tile.d.ts +11 -0
- package/dist/tools/percy-mcp/creation/upload-tile.js +53 -0
- package/dist/tools/percy-mcp/diagnostics/analyze-logs-realtime.d.ts +13 -0
- package/dist/tools/percy-mcp/diagnostics/analyze-logs-realtime.js +65 -0
- package/dist/tools/percy-mcp/diagnostics/get-build-logs.d.ts +17 -0
- package/dist/tools/percy-mcp/diagnostics/get-build-logs.js +74 -0
- package/dist/tools/percy-mcp/diagnostics/get-network-logs.d.ts +5 -0
- package/dist/tools/percy-mcp/diagnostics/get-network-logs.js +21 -0
- package/dist/tools/percy-mcp/diagnostics/get-suggestions.d.ts +7 -0
- package/dist/tools/percy-mcp/diagnostics/get-suggestions.js +24 -0
- package/dist/tools/percy-mcp/index.d.ts +36 -0
- package/dist/tools/percy-mcp/index.js +1137 -0
- package/dist/tools/percy-mcp/intelligence/get-ai-analysis.d.ts +15 -0
- package/dist/tools/percy-mcp/intelligence/get-ai-analysis.js +166 -0
- package/dist/tools/percy-mcp/intelligence/get-ai-quota.d.ts +9 -0
- package/dist/tools/percy-mcp/intelligence/get-ai-quota.js +73 -0
- package/dist/tools/percy-mcp/intelligence/get-build-summary.d.ts +11 -0
- package/dist/tools/percy-mcp/intelligence/get-build-summary.js +78 -0
- package/dist/tools/percy-mcp/intelligence/get-rca.d.ts +6 -0
- package/dist/tools/percy-mcp/intelligence/get-rca.js +153 -0
- package/dist/tools/percy-mcp/intelligence/suggest-prompt.d.ts +15 -0
- package/dist/tools/percy-mcp/intelligence/suggest-prompt.js +86 -0
- package/dist/tools/percy-mcp/intelligence/trigger-ai-recompute.d.ts +16 -0
- package/dist/tools/percy-mcp/intelligence/trigger-ai-recompute.js +64 -0
- package/dist/tools/percy-mcp/management/create-project.d.ts +14 -0
- package/dist/tools/percy-mcp/management/create-project.js +52 -0
- package/dist/tools/percy-mcp/management/get-usage-stats.d.ts +12 -0
- package/dist/tools/percy-mcp/management/get-usage-stats.js +61 -0
- package/dist/tools/percy-mcp/management/manage-browser-targets.d.ts +12 -0
- package/dist/tools/percy-mcp/management/manage-browser-targets.js +136 -0
- package/dist/tools/percy-mcp/management/manage-comments.d.ts +14 -0
- package/dist/tools/percy-mcp/management/manage-comments.js +147 -0
- package/dist/tools/percy-mcp/management/manage-ignored-regions.d.ts +18 -0
- package/dist/tools/percy-mcp/management/manage-ignored-regions.js +182 -0
- package/dist/tools/percy-mcp/management/manage-project-settings.d.ts +16 -0
- package/dist/tools/percy-mcp/management/manage-project-settings.js +97 -0
- package/dist/tools/percy-mcp/management/manage-tokens.d.ts +14 -0
- package/dist/tools/percy-mcp/management/manage-tokens.js +90 -0
- package/dist/tools/percy-mcp/management/manage-webhooks.d.ts +15 -0
- package/dist/tools/percy-mcp/management/manage-webhooks.js +180 -0
- package/dist/tools/percy-mcp/v2/auth-status.d.ts +3 -0
- package/dist/tools/percy-mcp/v2/auth-status.js +80 -0
- package/dist/tools/percy-mcp/v2/clone-build.d.ts +24 -0
- package/dist/tools/percy-mcp/v2/clone-build.js +539 -0
- package/dist/tools/percy-mcp/v2/create-app-build.d.ts +28 -0
- package/dist/tools/percy-mcp/v2/create-app-build.js +442 -0
- package/dist/tools/percy-mcp/v2/create-build.d.ts +16 -0
- package/dist/tools/percy-mcp/v2/create-build.js +601 -0
- package/dist/tools/percy-mcp/v2/create-project.d.ts +8 -0
- package/dist/tools/percy-mcp/v2/create-project.js +33 -0
- package/dist/tools/percy-mcp/v2/discover-urls.d.ts +7 -0
- package/dist/tools/percy-mcp/v2/discover-urls.js +38 -0
- package/dist/tools/percy-mcp/v2/figma-baseline.d.ts +7 -0
- package/dist/tools/percy-mcp/v2/figma-baseline.js +18 -0
- package/dist/tools/percy-mcp/v2/figma-build.d.ts +7 -0
- package/dist/tools/percy-mcp/v2/figma-build.js +39 -0
- package/dist/tools/percy-mcp/v2/figma-link.d.ts +6 -0
- package/dist/tools/percy-mcp/v2/figma-link.js +27 -0
- package/dist/tools/percy-mcp/v2/get-ai-summary.d.ts +5 -0
- package/dist/tools/percy-mcp/v2/get-ai-summary.js +109 -0
- package/dist/tools/percy-mcp/v2/get-build-detail.d.ts +22 -0
- package/dist/tools/percy-mcp/v2/get-build-detail.js +567 -0
- package/dist/tools/percy-mcp/v2/get-builds.d.ts +8 -0
- package/dist/tools/percy-mcp/v2/get-builds.js +63 -0
- package/dist/tools/percy-mcp/v2/get-comparison.d.ts +5 -0
- package/dist/tools/percy-mcp/v2/get-comparison.js +94 -0
- package/dist/tools/percy-mcp/v2/get-devices.d.ts +5 -0
- package/dist/tools/percy-mcp/v2/get-devices.js +33 -0
- package/dist/tools/percy-mcp/v2/get-insights.d.ts +7 -0
- package/dist/tools/percy-mcp/v2/get-insights.js +52 -0
- package/dist/tools/percy-mcp/v2/get-projects.d.ts +6 -0
- package/dist/tools/percy-mcp/v2/get-projects.js +41 -0
- package/dist/tools/percy-mcp/v2/get-snapshot.d.ts +5 -0
- package/dist/tools/percy-mcp/v2/get-snapshot.js +96 -0
- package/dist/tools/percy-mcp/v2/get-test-case-history.d.ts +5 -0
- package/dist/tools/percy-mcp/v2/get-test-case-history.js +20 -0
- package/dist/tools/percy-mcp/v2/get-test-cases.d.ts +6 -0
- package/dist/tools/percy-mcp/v2/get-test-cases.js +36 -0
- package/dist/tools/percy-mcp/v2/index.d.ts +35 -0
- package/dist/tools/percy-mcp/v2/index.js +544 -0
- package/dist/tools/percy-mcp/v2/list-integrations.d.ts +5 -0
- package/dist/tools/percy-mcp/v2/list-integrations.js +41 -0
- package/dist/tools/percy-mcp/v2/manage-domains.d.ts +8 -0
- package/dist/tools/percy-mcp/v2/manage-domains.js +33 -0
- package/dist/tools/percy-mcp/v2/manage-insights-email.d.ts +8 -0
- package/dist/tools/percy-mcp/v2/manage-insights-email.js +49 -0
- package/dist/tools/percy-mcp/v2/manage-usage-alerts.d.ts +10 -0
- package/dist/tools/percy-mcp/v2/manage-usage-alerts.js +43 -0
- package/dist/tools/percy-mcp/v2/migrate-integrations.d.ts +6 -0
- package/dist/tools/percy-mcp/v2/migrate-integrations.js +20 -0
- package/dist/tools/percy-mcp/v2/preview-comparison.d.ts +5 -0
- package/dist/tools/percy-mcp/v2/preview-comparison.js +17 -0
- package/dist/tools/percy-mcp/v2/search-build-items.d.ts +12 -0
- package/dist/tools/percy-mcp/v2/search-build-items.js +45 -0
- package/dist/tools/percy-mcp/workflows/auto-triage.d.ts +7 -0
- package/dist/tools/percy-mcp/workflows/auto-triage.js +82 -0
- package/dist/tools/percy-mcp/workflows/clone-build.d.ts +22 -0
- package/dist/tools/percy-mcp/workflows/clone-build.js +414 -0
- package/dist/tools/percy-mcp/workflows/create-percy-build.d.ts +32 -0
- package/dist/tools/percy-mcp/workflows/create-percy-build.js +434 -0
- package/dist/tools/percy-mcp/workflows/debug-failed-build.d.ts +5 -0
- package/dist/tools/percy-mcp/workflows/debug-failed-build.js +122 -0
- package/dist/tools/percy-mcp/workflows/diff-explain.d.ts +6 -0
- package/dist/tools/percy-mcp/workflows/diff-explain.js +147 -0
- package/dist/tools/percy-mcp/workflows/pr-visual-report.d.ts +8 -0
- package/dist/tools/percy-mcp/workflows/pr-visual-report.js +184 -0
- package/dist/tools/percy-mcp/workflows/run-tests.d.ts +17 -0
- package/dist/tools/percy-mcp/workflows/run-tests.js +107 -0
- package/dist/tools/percy-mcp/workflows/snapshot-urls.d.ts +18 -0
- package/dist/tools/percy-mcp/workflows/snapshot-urls.js +197 -0
- package/package.json +3 -2
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* percy_get_build_items — List snapshots in a Percy build filtered by category.
|
|
3
|
+
*/
|
|
4
|
+
import { BrowserStackConfig } from "../../../lib/types.js";
|
|
5
|
+
import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
|
|
6
|
+
interface GetBuildItemsArgs {
|
|
7
|
+
build_id: string;
|
|
8
|
+
category?: string;
|
|
9
|
+
sort_by?: string;
|
|
10
|
+
limit?: number;
|
|
11
|
+
}
|
|
12
|
+
export declare function percyGetBuildItems(args: GetBuildItemsArgs, config: BrowserStackConfig): Promise<CallToolResult>;
|
|
13
|
+
export {};
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* percy_get_build_items — List snapshots in a Percy build filtered by category.
|
|
3
|
+
*/
|
|
4
|
+
import { PercyClient } from "../../../lib/percy-api/client.js";
|
|
5
|
+
function na(value) {
|
|
6
|
+
if (value == null || value === "")
|
|
7
|
+
return "N/A";
|
|
8
|
+
return String(value);
|
|
9
|
+
}
|
|
10
|
+
function pct(value) {
|
|
11
|
+
if (value == null)
|
|
12
|
+
return "N/A";
|
|
13
|
+
return `${(value * 100).toFixed(1)}%`;
|
|
14
|
+
}
|
|
15
|
+
export async function percyGetBuildItems(args, config) {
|
|
16
|
+
const client = new PercyClient(config, { scope: "project" });
|
|
17
|
+
const limit = Math.min(args.limit ?? 20, 100);
|
|
18
|
+
const params = {
|
|
19
|
+
"filter[build-id]": args.build_id,
|
|
20
|
+
"page[limit]": String(limit),
|
|
21
|
+
};
|
|
22
|
+
if (args.category) {
|
|
23
|
+
params["filter[category]"] = args.category;
|
|
24
|
+
}
|
|
25
|
+
if (args.sort_by) {
|
|
26
|
+
params["sort"] = args.sort_by;
|
|
27
|
+
}
|
|
28
|
+
const response = await client.get("/build-items", params);
|
|
29
|
+
const items = Array.isArray(response.data) ? response.data : [];
|
|
30
|
+
if (items.length === 0) {
|
|
31
|
+
const category = args.category ? ` in category "${args.category}"` : "";
|
|
32
|
+
return {
|
|
33
|
+
content: [
|
|
34
|
+
{
|
|
35
|
+
type: "text",
|
|
36
|
+
text: `_No snapshots found${category} for build ${args.build_id}._`,
|
|
37
|
+
},
|
|
38
|
+
],
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
const lines = [];
|
|
42
|
+
const category = args.category ? ` (${args.category})` : "";
|
|
43
|
+
lines.push(`## Build Snapshots${category} — ${items.length} items`);
|
|
44
|
+
lines.push("");
|
|
45
|
+
lines.push("| # | Snapshot Name | ID | Diff | AI Diff | Status |");
|
|
46
|
+
lines.push("|---|---------------|----|----- |---------|--------|");
|
|
47
|
+
items.forEach((item, i) => {
|
|
48
|
+
const name = na(item.name ?? item.snapshotName);
|
|
49
|
+
const id = na(item.id ?? item.snapshotId);
|
|
50
|
+
const diff = pct(item.diffRatio);
|
|
51
|
+
const aiDiff = pct(item.aiDiffRatio);
|
|
52
|
+
const status = na(item.reviewState ?? item.state);
|
|
53
|
+
lines.push(`| ${i + 1} | ${name} | ${id} | ${diff} | ${aiDiff} | ${status} |`);
|
|
54
|
+
});
|
|
55
|
+
if (response.meta) {
|
|
56
|
+
const total = response.meta.totalEntries ?? response.meta.total;
|
|
57
|
+
if (total != null && total > items.length) {
|
|
58
|
+
lines.push("");
|
|
59
|
+
lines.push(`_Showing ${items.length} of ${total} snapshots._`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return {
|
|
63
|
+
content: [{ type: "text", text: lines.join("\n") }],
|
|
64
|
+
};
|
|
65
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* percy_get_build — Get detailed Percy build information.
|
|
3
|
+
*/
|
|
4
|
+
import { BrowserStackConfig } from "../../../lib/types.js";
|
|
5
|
+
import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
|
|
6
|
+
interface GetBuildArgs {
|
|
7
|
+
build_id: string;
|
|
8
|
+
}
|
|
9
|
+
export declare function percyGetBuild(args: GetBuildArgs, config: BrowserStackConfig): Promise<CallToolResult>;
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* percy_get_build — Get detailed Percy build information.
|
|
3
|
+
*/
|
|
4
|
+
import { PercyClient } from "../../../lib/percy-api/client.js";
|
|
5
|
+
import { formatBuild } from "../../../lib/percy-api/formatter.js";
|
|
6
|
+
export async function percyGetBuild(args, config) {
|
|
7
|
+
const client = new PercyClient(config, { scope: "project" });
|
|
8
|
+
const response = await client.get(`/builds/${args.build_id}`, { "include-metadata": "true" }, [
|
|
9
|
+
"build-summary",
|
|
10
|
+
"browsers",
|
|
11
|
+
]);
|
|
12
|
+
const build = response.data;
|
|
13
|
+
return {
|
|
14
|
+
content: [{ type: "text", text: formatBuild(build) }],
|
|
15
|
+
};
|
|
16
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* percy_get_comparison — Get detailed Percy comparison data.
|
|
3
|
+
*/
|
|
4
|
+
import { BrowserStackConfig } from "../../../lib/types.js";
|
|
5
|
+
import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
|
|
6
|
+
interface GetComparisonArgs {
|
|
7
|
+
comparison_id: string;
|
|
8
|
+
include_images?: boolean;
|
|
9
|
+
}
|
|
10
|
+
export declare function percyGetComparison(args: GetComparisonArgs, config: BrowserStackConfig): Promise<CallToolResult>;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* percy_get_comparison — Get detailed Percy comparison data.
|
|
3
|
+
*/
|
|
4
|
+
import { PercyClient } from "../../../lib/percy-api/client.js";
|
|
5
|
+
import { formatComparison } from "../../../lib/percy-api/formatter.js";
|
|
6
|
+
export async function percyGetComparison(args, config) {
|
|
7
|
+
const client = new PercyClient(config, { scope: "project" });
|
|
8
|
+
const includes = [
|
|
9
|
+
"head-screenshot.image",
|
|
10
|
+
"base-screenshot.image",
|
|
11
|
+
"diff-image",
|
|
12
|
+
"ai-diff-image",
|
|
13
|
+
"browser.browser-family",
|
|
14
|
+
"comparison-tag",
|
|
15
|
+
];
|
|
16
|
+
const response = await client.get(`/comparisons/${args.comparison_id}`, undefined, includes);
|
|
17
|
+
const comparison = response.data;
|
|
18
|
+
if (!comparison) {
|
|
19
|
+
return {
|
|
20
|
+
content: [
|
|
21
|
+
{
|
|
22
|
+
type: "text",
|
|
23
|
+
text: `_Comparison ${args.comparison_id} not found._`,
|
|
24
|
+
},
|
|
25
|
+
],
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
const contentParts = [];
|
|
29
|
+
// Always include the formatted text
|
|
30
|
+
contentParts.push({
|
|
31
|
+
type: "text",
|
|
32
|
+
text: formatComparison(comparison, { includeRegions: true }),
|
|
33
|
+
});
|
|
34
|
+
// If include_images is requested, fetch and include image URLs as text
|
|
35
|
+
if (args.include_images) {
|
|
36
|
+
const imageLines = [];
|
|
37
|
+
imageLines.push("");
|
|
38
|
+
imageLines.push("### Screenshot URLs");
|
|
39
|
+
const baseUrl = comparison.baseScreenshot?.image?.url ?? comparison.baseScreenshot?.url;
|
|
40
|
+
const headUrl = comparison.headScreenshot?.image?.url ?? comparison.headScreenshot?.url;
|
|
41
|
+
const diffUrl = comparison.diffImage?.url;
|
|
42
|
+
const aiDiffUrl = comparison.aiDiffImage?.url;
|
|
43
|
+
if (baseUrl)
|
|
44
|
+
imageLines.push(`- **Base:** ${baseUrl}`);
|
|
45
|
+
if (headUrl)
|
|
46
|
+
imageLines.push(`- **Head:** ${headUrl}`);
|
|
47
|
+
if (diffUrl)
|
|
48
|
+
imageLines.push(`- **Diff:** ${diffUrl}`);
|
|
49
|
+
if (aiDiffUrl)
|
|
50
|
+
imageLines.push(`- **AI Diff:** ${aiDiffUrl}`);
|
|
51
|
+
if (imageLines.length > 2) {
|
|
52
|
+
contentParts.push({
|
|
53
|
+
type: "text",
|
|
54
|
+
text: imageLines.join("\n"),
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return { content: contentParts };
|
|
59
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* percy_get_snapshot — Get a Percy snapshot with all comparisons and screenshots.
|
|
3
|
+
*/
|
|
4
|
+
import { BrowserStackConfig } from "../../../lib/types.js";
|
|
5
|
+
import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
|
|
6
|
+
interface GetSnapshotArgs {
|
|
7
|
+
snapshot_id: string;
|
|
8
|
+
}
|
|
9
|
+
export declare function percyGetSnapshot(args: GetSnapshotArgs, config: BrowserStackConfig): Promise<CallToolResult>;
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* percy_get_snapshot — Get a Percy snapshot with all comparisons and screenshots.
|
|
3
|
+
*/
|
|
4
|
+
import { PercyClient } from "../../../lib/percy-api/client.js";
|
|
5
|
+
import { formatSnapshot, formatComparison, } from "../../../lib/percy-api/formatter.js";
|
|
6
|
+
export async function percyGetSnapshot(args, config) {
|
|
7
|
+
const client = new PercyClient(config, { scope: "project" });
|
|
8
|
+
const includes = [
|
|
9
|
+
"comparisons.head-screenshot.image",
|
|
10
|
+
"comparisons.base-screenshot.lossy-image",
|
|
11
|
+
"comparisons.diff-image",
|
|
12
|
+
"comparisons.browser.browser-family",
|
|
13
|
+
"comparisons.comparison-tag",
|
|
14
|
+
];
|
|
15
|
+
const response = await client.get(`/snapshots/${args.snapshot_id}`, undefined, includes);
|
|
16
|
+
const snapshot = response.data;
|
|
17
|
+
if (!snapshot) {
|
|
18
|
+
return {
|
|
19
|
+
content: [
|
|
20
|
+
{ type: "text", text: `_Snapshot ${args.snapshot_id} not found._` },
|
|
21
|
+
],
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
const comparisons = snapshot.comparisons ?? [];
|
|
25
|
+
const lines = [];
|
|
26
|
+
lines.push(formatSnapshot(snapshot, comparisons));
|
|
27
|
+
if (comparisons.length > 0) {
|
|
28
|
+
lines.push("");
|
|
29
|
+
lines.push("---");
|
|
30
|
+
lines.push("");
|
|
31
|
+
lines.push("### Comparison Details");
|
|
32
|
+
for (const comparison of comparisons) {
|
|
33
|
+
lines.push("");
|
|
34
|
+
lines.push(formatComparison(comparison, { includeRegions: true }));
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return {
|
|
38
|
+
content: [{ type: "text", text: lines.join("\n") }],
|
|
39
|
+
};
|
|
40
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* percy_list_builds — List Percy builds for a project with filtering.
|
|
3
|
+
*/
|
|
4
|
+
import { BrowserStackConfig } from "../../../lib/types.js";
|
|
5
|
+
import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
|
|
6
|
+
interface ListBuildsArgs {
|
|
7
|
+
project_id?: string;
|
|
8
|
+
branch?: string;
|
|
9
|
+
state?: string;
|
|
10
|
+
sha?: string;
|
|
11
|
+
limit?: number;
|
|
12
|
+
}
|
|
13
|
+
export declare function percyListBuilds(args: ListBuildsArgs, config: BrowserStackConfig): Promise<CallToolResult>;
|
|
14
|
+
export {};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* percy_list_builds — List Percy builds for a project with filtering.
|
|
3
|
+
*/
|
|
4
|
+
import { PercyClient } from "../../../lib/percy-api/client.js";
|
|
5
|
+
import { formatBuildStatus } from "../../../lib/percy-api/formatter.js";
|
|
6
|
+
export async function percyListBuilds(args, config) {
|
|
7
|
+
const client = new PercyClient(config, { scope: "project" });
|
|
8
|
+
const limit = Math.min(args.limit ?? 10, 30);
|
|
9
|
+
const params = {
|
|
10
|
+
"page[limit]": String(limit),
|
|
11
|
+
};
|
|
12
|
+
if (args.branch) {
|
|
13
|
+
params["filter[branch]"] = args.branch;
|
|
14
|
+
}
|
|
15
|
+
if (args.state) {
|
|
16
|
+
params["filter[state]"] = args.state;
|
|
17
|
+
}
|
|
18
|
+
if (args.sha) {
|
|
19
|
+
params["filter[sha]"] = args.sha;
|
|
20
|
+
}
|
|
21
|
+
const path = args.project_id
|
|
22
|
+
? `/projects/${args.project_id}/builds`
|
|
23
|
+
: "/builds";
|
|
24
|
+
const response = await client.get(path, params);
|
|
25
|
+
const builds = Array.isArray(response.data) ? response.data : [];
|
|
26
|
+
if (builds.length === 0) {
|
|
27
|
+
return {
|
|
28
|
+
content: [
|
|
29
|
+
{
|
|
30
|
+
type: "text",
|
|
31
|
+
text: "_No builds found matching the specified filters._",
|
|
32
|
+
},
|
|
33
|
+
],
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
const lines = [];
|
|
37
|
+
lines.push(`## Percy Builds (${builds.length})`);
|
|
38
|
+
lines.push("");
|
|
39
|
+
for (const build of builds) {
|
|
40
|
+
lines.push(`- ${formatBuildStatus(build)} (ID: ${build.id})`);
|
|
41
|
+
}
|
|
42
|
+
return {
|
|
43
|
+
content: [{ type: "text", text: lines.join("\n") }],
|
|
44
|
+
};
|
|
45
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* percy_list_projects — List Percy projects in an organization.
|
|
3
|
+
*/
|
|
4
|
+
import { BrowserStackConfig } from "../../../lib/types.js";
|
|
5
|
+
import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
|
|
6
|
+
interface ListProjectsArgs {
|
|
7
|
+
org_id?: string;
|
|
8
|
+
search?: string;
|
|
9
|
+
limit?: number;
|
|
10
|
+
}
|
|
11
|
+
export declare function percyListProjects(args: ListProjectsArgs, config: BrowserStackConfig): Promise<CallToolResult>;
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* percy_list_projects — List Percy projects in an organization.
|
|
3
|
+
*/
|
|
4
|
+
import { PercyClient } from "../../../lib/percy-api/client.js";
|
|
5
|
+
export async function percyListProjects(args, config) {
|
|
6
|
+
const client = new PercyClient(config, { scope: "auto" });
|
|
7
|
+
const limit = Math.min(args.limit ?? 10, 50);
|
|
8
|
+
const params = {
|
|
9
|
+
"page[limit]": String(limit),
|
|
10
|
+
};
|
|
11
|
+
if (args.search) {
|
|
12
|
+
params["filter[name]"] = args.search;
|
|
13
|
+
}
|
|
14
|
+
const path = args.org_id
|
|
15
|
+
? `/organizations/${args.org_id}/projects`
|
|
16
|
+
: "/projects";
|
|
17
|
+
const response = await client.get(path, params);
|
|
18
|
+
const projects = Array.isArray(response.data) ? response.data : [];
|
|
19
|
+
if (projects.length === 0) {
|
|
20
|
+
return {
|
|
21
|
+
content: [
|
|
22
|
+
{
|
|
23
|
+
type: "text",
|
|
24
|
+
text: "_No projects found._",
|
|
25
|
+
},
|
|
26
|
+
],
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
const lines = [];
|
|
30
|
+
lines.push(`## Percy Projects (${projects.length})`);
|
|
31
|
+
lines.push("");
|
|
32
|
+
lines.push("| # | Name | ID | Type | Default Branch |");
|
|
33
|
+
lines.push("|---|------|----|------|----------------|");
|
|
34
|
+
projects.forEach((project, i) => {
|
|
35
|
+
const name = project.name ?? "Unnamed";
|
|
36
|
+
const id = project.id ?? "?";
|
|
37
|
+
const type = project.type ?? "web";
|
|
38
|
+
const branch = project.defaultBaseBranch ?? "main";
|
|
39
|
+
lines.push(`| ${i + 1} | ${name} | ${id} | ${type} | ${branch} |`);
|
|
40
|
+
});
|
|
41
|
+
if (response.meta) {
|
|
42
|
+
const total = response.meta.totalPages ?? response.meta.total;
|
|
43
|
+
if (total != null) {
|
|
44
|
+
lines.push("");
|
|
45
|
+
lines.push(`_Showing ${projects.length} of ${total} projects._`);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return {
|
|
49
|
+
content: [{ type: "text", text: lines.join("\n") }],
|
|
50
|
+
};
|
|
51
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* percy_create_app_snapshot — Create a snapshot for App Percy or BYOS builds.
|
|
3
|
+
*/
|
|
4
|
+
import { BrowserStackConfig } from "../../../lib/types.js";
|
|
5
|
+
import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
|
|
6
|
+
interface CreateAppSnapshotArgs {
|
|
7
|
+
build_id: string;
|
|
8
|
+
name: string;
|
|
9
|
+
test_case?: string;
|
|
10
|
+
}
|
|
11
|
+
export declare function percyCreateAppSnapshot(args: CreateAppSnapshotArgs, config: BrowserStackConfig): Promise<CallToolResult>;
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* percy_create_app_snapshot — Create a snapshot for App Percy or BYOS builds.
|
|
3
|
+
*/
|
|
4
|
+
import { PercyClient } from "../../../lib/percy-api/client.js";
|
|
5
|
+
export async function percyCreateAppSnapshot(args, config) {
|
|
6
|
+
const client = new PercyClient(config, { scope: "auto" });
|
|
7
|
+
const attributes = {
|
|
8
|
+
name: args.name,
|
|
9
|
+
};
|
|
10
|
+
if (args.test_case) {
|
|
11
|
+
attributes["test-case"] = args.test_case;
|
|
12
|
+
}
|
|
13
|
+
const body = {
|
|
14
|
+
data: {
|
|
15
|
+
type: "snapshots",
|
|
16
|
+
attributes,
|
|
17
|
+
},
|
|
18
|
+
};
|
|
19
|
+
const response = await client.post(`/builds/${args.build_id}/snapshots`, body);
|
|
20
|
+
const id = response.data?.id ?? "unknown";
|
|
21
|
+
return {
|
|
22
|
+
content: [
|
|
23
|
+
{
|
|
24
|
+
type: "text",
|
|
25
|
+
text: `App snapshot '${args.name}' created (ID: ${id}). Create comparisons with percy_create_comparison.`,
|
|
26
|
+
},
|
|
27
|
+
],
|
|
28
|
+
};
|
|
29
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* percy_create_build — Create a new Percy build for visual testing.
|
|
3
|
+
*
|
|
4
|
+
* Supports two modes:
|
|
5
|
+
* 1. With project_id: POST /projects/{project_id}/builds
|
|
6
|
+
* 2. Without project_id: POST /builds (uses PERCY_TOKEN project scope)
|
|
7
|
+
*/
|
|
8
|
+
import { BrowserStackConfig } from "../../../lib/types.js";
|
|
9
|
+
import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
|
|
10
|
+
interface CreateBuildArgs {
|
|
11
|
+
project_id?: string;
|
|
12
|
+
branch: string;
|
|
13
|
+
commit_sha: string;
|
|
14
|
+
commit_message?: string;
|
|
15
|
+
pull_request_number?: string;
|
|
16
|
+
type?: string;
|
|
17
|
+
}
|
|
18
|
+
export declare function percyCreateBuild(args: CreateBuildArgs, config: BrowserStackConfig): Promise<CallToolResult>;
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* percy_create_build — Create a new Percy build for visual testing.
|
|
3
|
+
*
|
|
4
|
+
* Supports two modes:
|
|
5
|
+
* 1. With project_id: POST /projects/{project_id}/builds
|
|
6
|
+
* 2. Without project_id: POST /builds (uses PERCY_TOKEN project scope)
|
|
7
|
+
*/
|
|
8
|
+
import { PercyClient } from "../../../lib/percy-api/client.js";
|
|
9
|
+
export async function percyCreateBuild(args, config) {
|
|
10
|
+
const { project_id, branch, commit_sha, commit_message, pull_request_number, type, } = args;
|
|
11
|
+
const body = {
|
|
12
|
+
data: {
|
|
13
|
+
type: "builds",
|
|
14
|
+
attributes: {
|
|
15
|
+
branch,
|
|
16
|
+
"commit-sha": commit_sha,
|
|
17
|
+
...(commit_message ? { "commit-message": commit_message } : {}),
|
|
18
|
+
...(pull_request_number
|
|
19
|
+
? { "pull-request-number": pull_request_number }
|
|
20
|
+
: {}),
|
|
21
|
+
...(type ? { type } : {}),
|
|
22
|
+
},
|
|
23
|
+
relationships: {
|
|
24
|
+
resources: {
|
|
25
|
+
data: [],
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
try {
|
|
31
|
+
const client = new PercyClient(config);
|
|
32
|
+
// Use project-scoped endpoint if project_id given, otherwise token-scoped
|
|
33
|
+
const endpoint = project_id ? `/projects/${project_id}/builds` : "/builds";
|
|
34
|
+
const result = await client.post(endpoint, body);
|
|
35
|
+
// Handle both raw JSON:API response and deserialized response
|
|
36
|
+
const buildData = result?.data || result;
|
|
37
|
+
const buildId = buildData?.id ?? (typeof buildData === "object" ? "created" : "unknown");
|
|
38
|
+
const buildNumber = buildData?.buildNumber || buildData?.["build-number"] || "";
|
|
39
|
+
const webUrl = buildData?.webUrl || buildData?.["web-url"] || "";
|
|
40
|
+
let output = `## Percy Build Created\n\n`;
|
|
41
|
+
output += `| Field | Value |\n`;
|
|
42
|
+
output += `|-------|-------|\n`;
|
|
43
|
+
output += `| **Build ID** | ${buildId} |\n`;
|
|
44
|
+
if (buildNumber)
|
|
45
|
+
output += `| **Build Number** | ${buildNumber} |\n`;
|
|
46
|
+
output += `| **Branch** | ${branch} |\n`;
|
|
47
|
+
output += `| **Commit** | ${commit_sha} |\n`;
|
|
48
|
+
if (webUrl)
|
|
49
|
+
output += `| **URL** | ${webUrl} |\n`;
|
|
50
|
+
output += `\n### Next Steps\n\n`;
|
|
51
|
+
output += `1. Create snapshots: \`percy_create_snapshot\` with build_id \`${buildId}\`\n`;
|
|
52
|
+
output += `2. Upload resources: \`percy_upload_resource\` for each missing resource\n`;
|
|
53
|
+
output += `3. Finalize: \`percy_finalize_build\` with build_id \`${buildId}\`\n`;
|
|
54
|
+
return { content: [{ type: "text", text: output }] };
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
58
|
+
return {
|
|
59
|
+
content: [
|
|
60
|
+
{
|
|
61
|
+
type: "text",
|
|
62
|
+
text: `Failed to create build: ${message}`,
|
|
63
|
+
},
|
|
64
|
+
],
|
|
65
|
+
isError: true,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* percy_create_comparison — Create a comparison with device/browser tag and tile metadata.
|
|
3
|
+
*/
|
|
4
|
+
import { BrowserStackConfig } from "../../../lib/types.js";
|
|
5
|
+
import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
|
|
6
|
+
interface CreateComparisonArgs {
|
|
7
|
+
snapshot_id: string;
|
|
8
|
+
tag_name: string;
|
|
9
|
+
tag_width: number;
|
|
10
|
+
tag_height: number;
|
|
11
|
+
tag_os_name?: string;
|
|
12
|
+
tag_os_version?: string;
|
|
13
|
+
tag_browser_name?: string;
|
|
14
|
+
tag_orientation?: string;
|
|
15
|
+
tiles: string;
|
|
16
|
+
}
|
|
17
|
+
export declare function percyCreateComparison(args: CreateComparisonArgs, config: BrowserStackConfig): Promise<CallToolResult>;
|
|
18
|
+
export {};
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* percy_create_comparison — Create a comparison with device/browser tag and tile metadata.
|
|
3
|
+
*/
|
|
4
|
+
import { PercyClient } from "../../../lib/percy-api/client.js";
|
|
5
|
+
export async function percyCreateComparison(args, config) {
|
|
6
|
+
const client = new PercyClient(config, { scope: "auto" });
|
|
7
|
+
// Parse tiles JSON string
|
|
8
|
+
let tilesArray;
|
|
9
|
+
try {
|
|
10
|
+
tilesArray = JSON.parse(args.tiles);
|
|
11
|
+
if (!Array.isArray(tilesArray)) {
|
|
12
|
+
return {
|
|
13
|
+
content: [
|
|
14
|
+
{
|
|
15
|
+
type: "text",
|
|
16
|
+
text: "Error: 'tiles' must be a JSON array of tile objects.",
|
|
17
|
+
},
|
|
18
|
+
],
|
|
19
|
+
isError: true,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return {
|
|
25
|
+
content: [
|
|
26
|
+
{
|
|
27
|
+
type: "text",
|
|
28
|
+
text: "Error: 'tiles' is not valid JSON. Expected a JSON array of tile objects.",
|
|
29
|
+
},
|
|
30
|
+
],
|
|
31
|
+
isError: true,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
// Build tag attributes
|
|
35
|
+
const tagAttributes = {
|
|
36
|
+
name: args.tag_name,
|
|
37
|
+
width: args.tag_width,
|
|
38
|
+
height: args.tag_height,
|
|
39
|
+
};
|
|
40
|
+
if (args.tag_os_name)
|
|
41
|
+
tagAttributes["os-name"] = args.tag_os_name;
|
|
42
|
+
if (args.tag_os_version)
|
|
43
|
+
tagAttributes["os-version"] = args.tag_os_version;
|
|
44
|
+
if (args.tag_browser_name)
|
|
45
|
+
tagAttributes["browser-name"] = args.tag_browser_name;
|
|
46
|
+
if (args.tag_orientation)
|
|
47
|
+
tagAttributes["orientation"] = args.tag_orientation;
|
|
48
|
+
// Build tiles data
|
|
49
|
+
const tilesData = tilesArray.map((tile) => {
|
|
50
|
+
const tileAttributes = {
|
|
51
|
+
sha: tile.sha,
|
|
52
|
+
};
|
|
53
|
+
if (tile["status-bar-height"] != null) {
|
|
54
|
+
tileAttributes["status-bar-height"] = tile["status-bar-height"];
|
|
55
|
+
}
|
|
56
|
+
if (tile["nav-bar-height"] != null) {
|
|
57
|
+
tileAttributes["nav-bar-height"] = tile["nav-bar-height"];
|
|
58
|
+
}
|
|
59
|
+
return {
|
|
60
|
+
type: "tiles",
|
|
61
|
+
attributes: tileAttributes,
|
|
62
|
+
};
|
|
63
|
+
});
|
|
64
|
+
const body = {
|
|
65
|
+
data: {
|
|
66
|
+
type: "comparisons",
|
|
67
|
+
relationships: {
|
|
68
|
+
tag: {
|
|
69
|
+
data: {
|
|
70
|
+
type: "tag",
|
|
71
|
+
attributes: tagAttributes,
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
tiles: {
|
|
75
|
+
data: tilesData,
|
|
76
|
+
},
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
const response = await client.post(`/snapshots/${args.snapshot_id}/comparisons`, body);
|
|
81
|
+
const id = response.data?.id ?? "unknown";
|
|
82
|
+
return {
|
|
83
|
+
content: [
|
|
84
|
+
{
|
|
85
|
+
type: "text",
|
|
86
|
+
text: `Comparison created (ID: ${id}). Upload tiles with percy_upload_tile.`,
|
|
87
|
+
},
|
|
88
|
+
],
|
|
89
|
+
};
|
|
90
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* percy_create_snapshot — Create a snapshot in a Percy build with DOM resources.
|
|
3
|
+
*
|
|
4
|
+
* POST /builds/{build_id}/snapshots with JSON:API body.
|
|
5
|
+
* Returns snapshot ID and list of missing resources for upload.
|
|
6
|
+
*/
|
|
7
|
+
import { BrowserStackConfig } from "../../../lib/types.js";
|
|
8
|
+
import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
|
|
9
|
+
interface CreateSnapshotArgs {
|
|
10
|
+
build_id: string;
|
|
11
|
+
name: string;
|
|
12
|
+
widths?: string;
|
|
13
|
+
enable_javascript?: boolean;
|
|
14
|
+
resources?: string;
|
|
15
|
+
}
|
|
16
|
+
export declare function percyCreateSnapshot(args: CreateSnapshotArgs, config: BrowserStackConfig): Promise<CallToolResult>;
|
|
17
|
+
export {};
|