@browserstack/mcp-server 1.2.15-beta.2 → 1.2.16-beta.1
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
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
import { percyGet } from "../../../lib/percy-api/percy-auth.js";
|
|
2
|
-
export async function percyGetComparison(args, config) {
|
|
3
|
-
const response = await percyGet(`/comparisons/${args.comparison_id}`, config, {
|
|
4
|
-
include: [
|
|
5
|
-
"head-screenshot.image",
|
|
6
|
-
"base-screenshot.image",
|
|
7
|
-
"diff-image",
|
|
8
|
-
"ai-diff-image",
|
|
9
|
-
"browser.browser-family",
|
|
10
|
-
"comparison-tag",
|
|
11
|
-
].join(","),
|
|
12
|
-
});
|
|
13
|
-
const comp = response?.data || {};
|
|
14
|
-
const attrs = comp.attributes || {};
|
|
15
|
-
const included = response?.included || [];
|
|
16
|
-
const ai = attrs["ai-details"] || {};
|
|
17
|
-
// Resolve browser name
|
|
18
|
-
const browserId = comp.relationships?.browser?.data?.id;
|
|
19
|
-
const browser = included.find((i) => i.type === "browsers" && i.id === browserId);
|
|
20
|
-
const familyId = browser?.relationships?.["browser-family"]?.data?.id;
|
|
21
|
-
const family = included.find((i) => i.type === "browser-families" && i.id === familyId);
|
|
22
|
-
const browserName = `${family?.attributes?.name || "?"} ${browser?.attributes?.version || ""}`;
|
|
23
|
-
let output = `## Comparison #${args.comparison_id}\n\n`;
|
|
24
|
-
output += `| Field | Value |\n|---|---|\n`;
|
|
25
|
-
output += `| **Browser** | ${browserName} |\n`;
|
|
26
|
-
output += `| **Width** | ${attrs.width || "?"}px |\n`;
|
|
27
|
-
output += `| **State** | ${attrs.state || "?"} |\n`;
|
|
28
|
-
output += `| **Diff ratio** | ${attrs["diff-ratio"] != null ? (attrs["diff-ratio"] * 100).toFixed(2) + "%" : "—"} |\n`;
|
|
29
|
-
output += `| **AI diff ratio** | ${attrs["ai-diff-ratio"] != null ? (attrs["ai-diff-ratio"] * 100).toFixed(2) + "%" : "—"} |\n`;
|
|
30
|
-
output += `| **AI state** | ${attrs["ai-processing-state"] || "—"} |\n`;
|
|
31
|
-
output += `| **Potential bugs** | ${ai["total-potential-bugs"] ?? "—"} |\n`;
|
|
32
|
-
output += `| **AI visual diffs** | ${ai["total-ai-visual-diffs"] ?? "—"} |\n`;
|
|
33
|
-
output += `| **Diffs reduced** | ${ai["total-diffs-reduced-capped"] ?? "—"} |\n`;
|
|
34
|
-
// AI regions (the detailed change descriptions)
|
|
35
|
-
const regions = attrs["applied-regions"];
|
|
36
|
-
if (Array.isArray(regions) && regions.length > 0) {
|
|
37
|
-
output += `\n### AI Detected Changes (${regions.length})\n\n`;
|
|
38
|
-
regions.forEach((r, i) => {
|
|
39
|
-
const ignored = r.ignored ? " ~~ignored~~" : "";
|
|
40
|
-
output += `${i + 1}. **${r.change_title || r.change_type || "Change"}** (${r.change_type || "?"})${ignored}\n`;
|
|
41
|
-
if (r.change_description)
|
|
42
|
-
output += ` ${r.change_description}\n`;
|
|
43
|
-
if (r.change_reason)
|
|
44
|
-
output += ` *Reason: ${r.change_reason}*\n`;
|
|
45
|
-
if (r.coordinates) {
|
|
46
|
-
const c = r.coordinates;
|
|
47
|
-
output += ` Region: (${c.x || c.left || 0}, ${c.y || c.top || 0}) → (${c.x2 || c.right || c.x + c.width || 0}, ${c.y2 || c.bottom || c.y + c.height || 0})\n`;
|
|
48
|
-
}
|
|
49
|
-
output += "\n";
|
|
50
|
-
});
|
|
51
|
-
}
|
|
52
|
-
// Image URLs
|
|
53
|
-
const resolveImageUrl = (relName) => {
|
|
54
|
-
const screenshotId = comp.relationships?.[relName]?.data?.id;
|
|
55
|
-
if (!screenshotId)
|
|
56
|
-
return null;
|
|
57
|
-
// Direct image relationship
|
|
58
|
-
const directImage = included.find((i) => i.type === "images" && i.id === screenshotId);
|
|
59
|
-
if (directImage?.attributes?.url)
|
|
60
|
-
return directImage.attributes.url;
|
|
61
|
-
// Screenshot → image relationship
|
|
62
|
-
const screenshot = included.find((i) => i.type === "screenshots" && i.id === screenshotId);
|
|
63
|
-
const imageId = screenshot?.relationships?.image?.data?.id;
|
|
64
|
-
if (imageId) {
|
|
65
|
-
const image = included.find((i) => i.type === "images" && i.id === imageId);
|
|
66
|
-
return image?.attributes?.url || null;
|
|
67
|
-
}
|
|
68
|
-
return null;
|
|
69
|
-
};
|
|
70
|
-
output += `### Images\n\n`;
|
|
71
|
-
const headUrl = resolveImageUrl("head-screenshot");
|
|
72
|
-
const baseUrl = resolveImageUrl("base-screenshot");
|
|
73
|
-
const diffUrl = resolveImageUrl("diff-image");
|
|
74
|
-
const aiDiffUrl = resolveImageUrl("ai-diff-image");
|
|
75
|
-
if (headUrl)
|
|
76
|
-
output += `**Head:** ${headUrl}\n`;
|
|
77
|
-
if (baseUrl)
|
|
78
|
-
output += `**Base:** ${baseUrl}\n`;
|
|
79
|
-
if (diffUrl)
|
|
80
|
-
output += `**Diff:** ${diffUrl}\n`;
|
|
81
|
-
if (aiDiffUrl)
|
|
82
|
-
output += `**AI Diff:** ${aiDiffUrl}\n`;
|
|
83
|
-
if (!headUrl && !baseUrl && !diffUrl)
|
|
84
|
-
output += `No images available.\n`;
|
|
85
|
-
// Error info
|
|
86
|
-
if (attrs["error-buckets-exists"]) {
|
|
87
|
-
output += `\n### Errors\n\n`;
|
|
88
|
-
const assetFailures = attrs["asset-failure-category-counts"];
|
|
89
|
-
if (assetFailures) {
|
|
90
|
-
output += `**Asset failures:** ${JSON.stringify(assetFailures)}\n`;
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
return { content: [{ type: "text", text: output }] };
|
|
94
|
-
}
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import { percyGet } from "../../../lib/percy-api/percy-auth.js";
|
|
2
|
-
export async function percyGetDevices(args, config) {
|
|
3
|
-
// Get browser families
|
|
4
|
-
const families = await percyGet("/browser-families", config);
|
|
5
|
-
const familyList = families?.data || [];
|
|
6
|
-
let output = `## Percy Browsers & Devices\n\n`;
|
|
7
|
-
output += `### Browser Families\n\n`;
|
|
8
|
-
output += `| Name | Slug | ID |\n|---|---|---|\n`;
|
|
9
|
-
familyList.forEach((f) => {
|
|
10
|
-
output += `| ${f.attributes?.name || "?"} | ${f.attributes?.slug || "?"} | ${f.id} |\n`;
|
|
11
|
-
});
|
|
12
|
-
// Get device details if build_id provided
|
|
13
|
-
if (args.build_id) {
|
|
14
|
-
try {
|
|
15
|
-
const devices = await percyGet("/discovery/device-details", config, {
|
|
16
|
-
build_id: args.build_id,
|
|
17
|
-
});
|
|
18
|
-
const deviceList = devices?.data || devices || [];
|
|
19
|
-
if (Array.isArray(deviceList) && deviceList.length) {
|
|
20
|
-
output += `\n### Devices for Build ${args.build_id}\n\n`;
|
|
21
|
-
output += `| Device | Width | Height |\n|---|---|---|\n`;
|
|
22
|
-
deviceList.forEach((d) => {
|
|
23
|
-
const attrs = d.attributes || d;
|
|
24
|
-
output += `| ${attrs.name || "?"} | ${attrs.width || "?"} | ${attrs.height || "?"} |\n`;
|
|
25
|
-
});
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
catch {
|
|
29
|
-
/* device details may not be available */
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
return { content: [{ type: "text", text: output }] };
|
|
33
|
-
}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import { BrowserStackConfig } from "../../../lib/types.js";
|
|
2
|
-
import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
|
|
3
|
-
export declare function percyGetInsights(args: {
|
|
4
|
-
org_slug: string;
|
|
5
|
-
period?: string;
|
|
6
|
-
product?: string;
|
|
7
|
-
}, config: BrowserStackConfig): Promise<CallToolResult>;
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
import { percyGet } from "../../../lib/percy-api/percy-auth.js";
|
|
2
|
-
export async function percyGetInsights(args, config) {
|
|
3
|
-
const params = {
|
|
4
|
-
organization_id: args.org_slug,
|
|
5
|
-
period: args.period || "last_30_days",
|
|
6
|
-
product: args.product || "web",
|
|
7
|
-
};
|
|
8
|
-
const response = await percyGet("/insights/metrics", config, params);
|
|
9
|
-
const data = response?.data?.attributes || response?.data || {};
|
|
10
|
-
let output = `## Percy Testing Insights — ${args.org_slug}\n\n`;
|
|
11
|
-
output += `**Period:** ${params.period}\n**Product:** ${params.product}\n\n`;
|
|
12
|
-
// Review efficiency
|
|
13
|
-
const review = data.reviewEfficiency || data["review-efficiency"] || {};
|
|
14
|
-
if (review) {
|
|
15
|
-
output += `### Review Efficiency\n`;
|
|
16
|
-
output += `| Metric | Value |\n|---|---|\n`;
|
|
17
|
-
if (review.meaningfulReviewTimeRatio != null)
|
|
18
|
-
output += `| Meaningful review ratio | ${(review.meaningfulReviewTimeRatio * 100).toFixed(0)}% |\n`;
|
|
19
|
-
if (review.totalReviews != null)
|
|
20
|
-
output += `| Total reviews | ${review.totalReviews} |\n`;
|
|
21
|
-
if (review.noisyReviews != null)
|
|
22
|
-
output += `| Noisy reviews | ${review.noisyReviews} |\n`;
|
|
23
|
-
if (review.medianReviewTimeSeconds != null)
|
|
24
|
-
output += `| Median review time | ${review.medianReviewTimeSeconds}s |\n`;
|
|
25
|
-
output += "\n";
|
|
26
|
-
}
|
|
27
|
-
// ROI
|
|
28
|
-
const roi = data.roiTimeSavings || data["roi-time-savings"] || {};
|
|
29
|
-
if (roi) {
|
|
30
|
-
output += `### ROI & Time Savings\n`;
|
|
31
|
-
output += `| Metric | Value |\n|---|---|\n`;
|
|
32
|
-
if (roi.totalTimeSaved != null)
|
|
33
|
-
output += `| Total time saved | ${roi.totalTimeSaved} min |\n`;
|
|
34
|
-
if (roi.noDiffPercentage != null)
|
|
35
|
-
output += `| No-diff percentage | ${(roi.noDiffPercentage * 100).toFixed(0)}% |\n`;
|
|
36
|
-
if (roi.buildsCount != null)
|
|
37
|
-
output += `| Builds | ${roi.buildsCount} |\n`;
|
|
38
|
-
output += "\n";
|
|
39
|
-
}
|
|
40
|
-
// Coverage
|
|
41
|
-
const coverage = data.coverage || {};
|
|
42
|
-
if (coverage) {
|
|
43
|
-
output += `### Coverage\n`;
|
|
44
|
-
output += `| Metric | Value |\n|---|---|\n`;
|
|
45
|
-
if (coverage.coveragePercentage != null)
|
|
46
|
-
output += `| Coverage | ${coverage.coveragePercentage.toFixed(0)}% |\n`;
|
|
47
|
-
if (coverage.activeSnapshotsCount != null)
|
|
48
|
-
output += `| Active snapshots | ${coverage.activeSnapshotsCount} |\n`;
|
|
49
|
-
output += "\n";
|
|
50
|
-
}
|
|
51
|
-
return { content: [{ type: "text", text: output }] };
|
|
52
|
-
}
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import { BrowserStackConfig } from "../../../lib/types.js";
|
|
2
|
-
import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
|
|
3
|
-
export declare function percyGetProjectsV2(args: {
|
|
4
|
-
search?: string;
|
|
5
|
-
limit?: number;
|
|
6
|
-
}, config: BrowserStackConfig): Promise<CallToolResult>;
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import { percyGet } from "../../../lib/percy-api/percy-auth.js";
|
|
2
|
-
import { setOrg } from "../../../lib/percy-api/percy-session.js";
|
|
3
|
-
export async function percyGetProjectsV2(args, config) {
|
|
4
|
-
const params = {};
|
|
5
|
-
if (args.search)
|
|
6
|
-
params["filter[search]"] = args.search;
|
|
7
|
-
params["page[limit]"] = String(args.limit || 20);
|
|
8
|
-
const response = await percyGet("/projects", config, params);
|
|
9
|
-
const projects = response?.data || [];
|
|
10
|
-
if (projects.length === 0) {
|
|
11
|
-
return {
|
|
12
|
-
content: [
|
|
13
|
-
{
|
|
14
|
-
type: "text",
|
|
15
|
-
text: "No projects found. Use `percy_create_project` to create one.",
|
|
16
|
-
},
|
|
17
|
-
],
|
|
18
|
-
};
|
|
19
|
-
}
|
|
20
|
-
// Extract org slug from the first project's full-slug
|
|
21
|
-
const firstSlug = projects[0]?.attributes?.["full-slug"] || "";
|
|
22
|
-
const orgSlug = firstSlug.split("/")[0] || "";
|
|
23
|
-
if (orgSlug)
|
|
24
|
-
setOrg({ slug: orgSlug });
|
|
25
|
-
let output = `## Percy Projects (${projects.length})\n\n`;
|
|
26
|
-
output += `| # | Name | ID | Type | Slug (for builds) |\n|---|---|---|---|---|\n`;
|
|
27
|
-
projects.forEach((p, i) => {
|
|
28
|
-
const name = p.attributes?.name || "?";
|
|
29
|
-
const type = p.attributes?.type || "?";
|
|
30
|
-
const fullSlug = p.attributes?.["full-slug"] || p.attributes?.slug || "?";
|
|
31
|
-
output += `| ${i + 1} | ${name} | ${p.id} | ${type} | \`${fullSlug}\` |\n`;
|
|
32
|
-
});
|
|
33
|
-
if (orgSlug) {
|
|
34
|
-
output += `\n**Organization:** ${orgSlug}\n`;
|
|
35
|
-
}
|
|
36
|
-
output += `\n### Usage\n\n`;
|
|
37
|
-
output += `- \`percy_get_builds\` with project_slug "${firstSlug}" — List builds for a project\n`;
|
|
38
|
-
output += `- \`percy_create_project\` with name "my-project" — Create new project & activate token\n`;
|
|
39
|
-
output += `- \`percy_create_build\` with project_name "my-project" — Create a build\n`;
|
|
40
|
-
return { content: [{ type: "text", text: output }] };
|
|
41
|
-
}
|
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
import { percyGet } from "../../../lib/percy-api/percy-auth.js";
|
|
2
|
-
export async function percyGetSnapshot(args, config) {
|
|
3
|
-
const response = await percyGet(`/snapshots/${args.snapshot_id}`, config, {
|
|
4
|
-
include: [
|
|
5
|
-
"comparisons.head-screenshot.image",
|
|
6
|
-
"comparisons.base-screenshot.image",
|
|
7
|
-
"comparisons.diff-image",
|
|
8
|
-
"comparisons.ai-diff-image",
|
|
9
|
-
"comparisons.browser.browser-family",
|
|
10
|
-
"comparisons.comparison-tag",
|
|
11
|
-
].join(","),
|
|
12
|
-
});
|
|
13
|
-
const snap = response?.data || {};
|
|
14
|
-
const attrs = snap.attributes || {};
|
|
15
|
-
const included = response?.included || [];
|
|
16
|
-
let output = `## Snapshot: ${attrs.name || args.snapshot_id}\n\n`;
|
|
17
|
-
if (attrs["display-name"] && attrs["display-name"] !== attrs.name) {
|
|
18
|
-
output += `**Display name:** ${attrs["display-name"]}\n`;
|
|
19
|
-
}
|
|
20
|
-
output += `| Field | Value |\n|---|---|\n`;
|
|
21
|
-
output += `| **Review** | ${attrs["review-state"] || "—"} (${attrs["review-state-reason"] || "—"}) |\n`;
|
|
22
|
-
output += `| **Diff ratio** | ${attrs["diff-ratio"] != null ? (attrs["diff-ratio"] * 100).toFixed(2) + "%" : "—"} |\n`;
|
|
23
|
-
output += `| **Test case** | ${attrs["test-case-name"] || "none"} |\n`;
|
|
24
|
-
output += `| **Comments** | ${attrs["total-open-comments"] ?? 0} |\n`;
|
|
25
|
-
output += `| **Layout** | ${attrs["enable-layout"] ? "enabled" : "disabled"} |\n`;
|
|
26
|
-
// Comparisons table
|
|
27
|
-
const comps = included.filter((i) => i.type === "comparisons");
|
|
28
|
-
const browsers = new Map(included
|
|
29
|
-
.filter((i) => i.type === "browsers")
|
|
30
|
-
.map((b) => {
|
|
31
|
-
const family = included.find((f) => f.type === "browser-families" &&
|
|
32
|
-
f.id === b.relationships?.["browser-family"]?.data?.id);
|
|
33
|
-
return [
|
|
34
|
-
b.id,
|
|
35
|
-
`${family?.attributes?.name || "?"} ${b.attributes?.version || ""}`,
|
|
36
|
-
];
|
|
37
|
-
}));
|
|
38
|
-
const images = new Map(included
|
|
39
|
-
.filter((i) => i.type === "images")
|
|
40
|
-
.map((img) => [img.id, img.attributes]));
|
|
41
|
-
if (comps.length > 0) {
|
|
42
|
-
output += `\n### Comparisons (${comps.length})\n\n`;
|
|
43
|
-
output += `| Browser | Width | Diff | AI Diff | AI State | Bugs |\n|---|---|---|---|---|---|\n`;
|
|
44
|
-
comps.forEach((c) => {
|
|
45
|
-
const ca = c.attributes || {};
|
|
46
|
-
const browserId = c.relationships?.browser?.data?.id;
|
|
47
|
-
const browserName = browsers.get(browserId) || "?";
|
|
48
|
-
const diff = ca["diff-ratio"] != null
|
|
49
|
-
? (ca["diff-ratio"] * 100).toFixed(1) + "%"
|
|
50
|
-
: "—";
|
|
51
|
-
const aiDiff = ca["ai-diff-ratio"] != null
|
|
52
|
-
? (ca["ai-diff-ratio"] * 100).toFixed(1) + "%"
|
|
53
|
-
: "—";
|
|
54
|
-
const aiState = ca["ai-processing-state"] || "—";
|
|
55
|
-
const bugs = ca["ai-details"]?.["total-potential-bugs"] ?? "—";
|
|
56
|
-
output += `| ${browserName} | ${ca.width || "?"}px | ${diff} | ${aiDiff} | ${aiState} | ${bugs} |\n`;
|
|
57
|
-
});
|
|
58
|
-
// Show AI regions for comparisons that have them
|
|
59
|
-
const compsWithRegions = comps.filter((c) => c.attributes?.["applied-regions"]?.length > 0);
|
|
60
|
-
if (compsWithRegions.length > 0) {
|
|
61
|
-
output += `\n### AI Detected Changes\n\n`;
|
|
62
|
-
for (const c of compsWithRegions) {
|
|
63
|
-
const regions = c.attributes["applied-regions"];
|
|
64
|
-
regions.forEach((r) => {
|
|
65
|
-
const ignored = r.ignored ? " *(ignored)*" : "";
|
|
66
|
-
output += `- **${r.change_title || r.change_type || "Change"}** (${r.change_type || "?"})${ignored}\n`;
|
|
67
|
-
if (r.change_description)
|
|
68
|
-
output += ` ${r.change_description}\n`;
|
|
69
|
-
if (r.change_reason)
|
|
70
|
-
output += ` *Reason: ${r.change_reason}*\n`;
|
|
71
|
-
});
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
// Image URLs for first comparison
|
|
75
|
-
const firstComp = comps[0];
|
|
76
|
-
const headScreenshotId = firstComp?.relationships?.["head-screenshot"]?.data?.id;
|
|
77
|
-
const headScreenshot = included.find((i) => i.type === "screenshots" && i.id === headScreenshotId);
|
|
78
|
-
const headImageId = headScreenshot?.relationships?.image?.data?.id;
|
|
79
|
-
const headImage = headImageId ? images.get(headImageId) : null;
|
|
80
|
-
if (headImage?.url) {
|
|
81
|
-
output += `\n### Images (first comparison)\n\n`;
|
|
82
|
-
output += `**Head:** ${headImage.url}\n`;
|
|
83
|
-
const baseScreenshotId = firstComp?.relationships?.["base-screenshot"]?.data?.id;
|
|
84
|
-
const baseScreenshot = included.find((i) => i.type === "screenshots" && i.id === baseScreenshotId);
|
|
85
|
-
const baseImageId = baseScreenshot?.relationships?.image?.data?.id;
|
|
86
|
-
const baseImage = baseImageId ? images.get(baseImageId) : null;
|
|
87
|
-
if (baseImage?.url)
|
|
88
|
-
output += `**Base:** ${baseImage.url}\n`;
|
|
89
|
-
const diffImageId = firstComp?.relationships?.["diff-image"]?.data?.id;
|
|
90
|
-
const diffImage = diffImageId ? images.get(diffImageId) : null;
|
|
91
|
-
if (diffImage?.url)
|
|
92
|
-
output += `**Diff:** ${diffImage.url}\n`;
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
return { content: [{ type: "text", text: output }] };
|
|
96
|
-
}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { percyGet } from "../../../lib/percy-api/percy-auth.js";
|
|
2
|
-
export async function percyGetTestCaseHistory(args, config) {
|
|
3
|
-
const response = await percyGet("/test-case-histories", config, {
|
|
4
|
-
test_case_id: args.test_case_id,
|
|
5
|
-
});
|
|
6
|
-
const history = response?.data || [];
|
|
7
|
-
if (!history.length) {
|
|
8
|
-
return {
|
|
9
|
-
content: [{ type: "text", text: "No history found for this test case." }],
|
|
10
|
-
};
|
|
11
|
-
}
|
|
12
|
-
let output = `## Test Case History\n\n`;
|
|
13
|
-
output += `| # | Build | State | Total | Failed | Unreviewed |\n|---|---|---|---|---|---|\n`;
|
|
14
|
-
history.forEach((entry, i) => {
|
|
15
|
-
const attrs = entry.attributes || {};
|
|
16
|
-
const buildId = entry.relationships?.build?.data?.id || "?";
|
|
17
|
-
output += `| ${i + 1} | #${buildId} | ${attrs["review-state"] ?? "?"} | ${attrs["total-snapshots"] ?? "?"} | ${attrs["failed-snapshots"] ?? "?"} | ${attrs["unreviewed-snapshots"] ?? "?"} |\n`;
|
|
18
|
-
});
|
|
19
|
-
return { content: [{ type: "text", text: output }] };
|
|
20
|
-
}
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import { BrowserStackConfig } from "../../../lib/types.js";
|
|
2
|
-
import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
|
|
3
|
-
export declare function percyGetTestCases(args: {
|
|
4
|
-
project_id: string;
|
|
5
|
-
build_id?: string;
|
|
6
|
-
}, config: BrowserStackConfig): Promise<CallToolResult>;
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import { percyGet } from "../../../lib/percy-api/percy-auth.js";
|
|
2
|
-
export async function percyGetTestCases(args, config) {
|
|
3
|
-
// Get test cases
|
|
4
|
-
const params = { project_id: args.project_id };
|
|
5
|
-
const response = await percyGet("/test-cases", config, params);
|
|
6
|
-
const testCases = response?.data || [];
|
|
7
|
-
if (!testCases.length) {
|
|
8
|
-
return {
|
|
9
|
-
content: [
|
|
10
|
-
{ type: "text", text: "No test cases found for this project." },
|
|
11
|
-
],
|
|
12
|
-
};
|
|
13
|
-
}
|
|
14
|
-
let output = `## Test Cases (${testCases.length})\n\n`;
|
|
15
|
-
output += `| # | Name | ID |\n|---|---|---|\n`;
|
|
16
|
-
testCases.forEach((tc, i) => {
|
|
17
|
-
const name = tc.attributes?.name || tc.name || "?";
|
|
18
|
-
output += `| ${i + 1} | ${name} | ${tc.id} |\n`;
|
|
19
|
-
});
|
|
20
|
-
// If build_id provided, get executions
|
|
21
|
-
if (args.build_id) {
|
|
22
|
-
const execResponse = await percyGet("/test-case-executions", config, {
|
|
23
|
-
build_id: args.build_id,
|
|
24
|
-
});
|
|
25
|
-
const executions = execResponse?.data || [];
|
|
26
|
-
if (executions.length) {
|
|
27
|
-
output += `\n### Executions for Build ${args.build_id}\n\n`;
|
|
28
|
-
output += `| Test Case | Total | Failed | Unreviewed | State |\n|---|---|---|---|---|\n`;
|
|
29
|
-
executions.forEach((exec) => {
|
|
30
|
-
const attrs = exec.attributes || {};
|
|
31
|
-
output += `| ${attrs["test-case-name"] || exec.id} | ${attrs["total-snapshots"] ?? "?"} | ${attrs["failed-snapshots"] ?? "?"} | ${attrs["unreviewed-snapshots"] ?? "?"} | ${attrs["review-state"] ?? "?"} |\n`;
|
|
32
|
-
});
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
return { content: [{ type: "text", text: output }] };
|
|
36
|
-
}
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Percy MCP Tools v2 — Simplified, production-ready tools.
|
|
3
|
-
*
|
|
4
|
-
* Key changes from v1:
|
|
5
|
-
* - ALL read operations use BrowserStack Basic Auth (not Percy Token)
|
|
6
|
-
* - Fewer, more powerful tools (quality > quantity)
|
|
7
|
-
* - Every tool tested against real Percy API
|
|
8
|
-
*
|
|
9
|
-
* Tools (21 total):
|
|
10
|
-
* percy_create_project — Create/get a Percy project
|
|
11
|
-
* percy_create_build — Create build (URL snapshot / screenshot upload / test wrap)
|
|
12
|
-
* percy_get_projects — List projects
|
|
13
|
-
* percy_get_builds — List builds with filters
|
|
14
|
-
* percy_auth_status — Check auth
|
|
15
|
-
* percy_figma_build — Create build from Figma designs
|
|
16
|
-
* percy_figma_baseline — Update Figma design baseline
|
|
17
|
-
* percy_figma_link — Get Figma link for snapshot/comparison
|
|
18
|
-
* percy_get_insights — Testing health metrics
|
|
19
|
-
* percy_manage_insights_email — Configure insights email recipients
|
|
20
|
-
* percy_get_test_cases — List test cases for a project
|
|
21
|
-
* percy_get_test_case_history — Test case execution history
|
|
22
|
-
* percy_discover_urls — Discover URLs from sitemaps
|
|
23
|
-
* percy_get_devices — List browsers/devices/viewports
|
|
24
|
-
* percy_manage_domains — Get/update allowed/error domains
|
|
25
|
-
* percy_manage_usage_alerts — Configure usage alert thresholds
|
|
26
|
-
* percy_preview_comparison — Trigger on-demand diff recomputation
|
|
27
|
-
* percy_search_builds — Advanced build item search
|
|
28
|
-
* percy_list_integrations — List org integrations
|
|
29
|
-
* percy_migrate_integrations — Migrate integrations between orgs
|
|
30
|
-
* percy_create_app_build — Create App Percy BYOS build from device screenshots
|
|
31
|
-
*/
|
|
32
|
-
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
33
|
-
import { BrowserStackConfig } from "../../../lib/types.js";
|
|
34
|
-
export declare function registerPercyMcpToolsV2(server: McpServer, config: BrowserStackConfig): Record<string, any>;
|
|
35
|
-
export default registerPercyMcpToolsV2;
|