@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.
Files changed (170) hide show
  1. package/dist/lib/percy-api/auth.d.ts +41 -0
  2. package/dist/lib/percy-api/auth.js +96 -0
  3. package/dist/lib/percy-api/cache.d.ts +28 -0
  4. package/dist/lib/percy-api/cache.js +48 -0
  5. package/dist/lib/percy-api/client.d.ts +69 -0
  6. package/dist/lib/percy-api/client.js +275 -0
  7. package/dist/lib/percy-api/errors.d.ts +15 -0
  8. package/dist/lib/percy-api/errors.js +52 -0
  9. package/dist/lib/percy-api/formatter.d.ts +16 -0
  10. package/dist/lib/percy-api/formatter.js +344 -0
  11. package/dist/lib/percy-api/percy-auth.d.ts +43 -0
  12. package/dist/lib/percy-api/percy-auth.js +137 -0
  13. package/dist/lib/percy-api/percy-error-handler.d.ts +24 -0
  14. package/dist/lib/percy-api/percy-error-handler.js +302 -0
  15. package/dist/lib/percy-api/percy-session.d.ts +42 -0
  16. package/dist/lib/percy-api/percy-session.js +87 -0
  17. package/dist/lib/percy-api/polling.d.ts +26 -0
  18. package/dist/lib/percy-api/polling.js +42 -0
  19. package/dist/lib/percy-api/types.d.ts +56 -0
  20. package/dist/lib/percy-api/types.js +76 -0
  21. package/dist/server-factory.js +4 -0
  22. package/dist/tools/percy-mcp/advanced/branchline-operations.d.ts +16 -0
  23. package/dist/tools/percy-mcp/advanced/branchline-operations.js +81 -0
  24. package/dist/tools/percy-mcp/advanced/manage-variants.d.ts +16 -0
  25. package/dist/tools/percy-mcp/advanced/manage-variants.js +155 -0
  26. package/dist/tools/percy-mcp/advanced/manage-visual-monitoring.d.ts +16 -0
  27. package/dist/tools/percy-mcp/advanced/manage-visual-monitoring.js +171 -0
  28. package/dist/tools/percy-mcp/auth/auth-status.d.ts +3 -0
  29. package/dist/tools/percy-mcp/auth/auth-status.js +131 -0
  30. package/dist/tools/percy-mcp/core/approve-build.d.ts +14 -0
  31. package/dist/tools/percy-mcp/core/approve-build.js +97 -0
  32. package/dist/tools/percy-mcp/core/get-build-items.d.ts +13 -0
  33. package/dist/tools/percy-mcp/core/get-build-items.js +65 -0
  34. package/dist/tools/percy-mcp/core/get-build.d.ts +10 -0
  35. package/dist/tools/percy-mcp/core/get-build.js +16 -0
  36. package/dist/tools/percy-mcp/core/get-comparison.d.ts +11 -0
  37. package/dist/tools/percy-mcp/core/get-comparison.js +59 -0
  38. package/dist/tools/percy-mcp/core/get-snapshot.d.ts +10 -0
  39. package/dist/tools/percy-mcp/core/get-snapshot.js +40 -0
  40. package/dist/tools/percy-mcp/core/list-builds.d.ts +14 -0
  41. package/dist/tools/percy-mcp/core/list-builds.js +45 -0
  42. package/dist/tools/percy-mcp/core/list-projects.d.ts +12 -0
  43. package/dist/tools/percy-mcp/core/list-projects.js +51 -0
  44. package/dist/tools/percy-mcp/creation/create-app-snapshot.d.ts +12 -0
  45. package/dist/tools/percy-mcp/creation/create-app-snapshot.js +29 -0
  46. package/dist/tools/percy-mcp/creation/create-build.d.ts +19 -0
  47. package/dist/tools/percy-mcp/creation/create-build.js +68 -0
  48. package/dist/tools/percy-mcp/creation/create-comparison.d.ts +18 -0
  49. package/dist/tools/percy-mcp/creation/create-comparison.js +90 -0
  50. package/dist/tools/percy-mcp/creation/create-snapshot.d.ts +17 -0
  51. package/dist/tools/percy-mcp/creation/create-snapshot.js +99 -0
  52. package/dist/tools/percy-mcp/creation/finalize-build.d.ts +12 -0
  53. package/dist/tools/percy-mcp/creation/finalize-build.js +33 -0
  54. package/dist/tools/percy-mcp/creation/finalize-comparison.d.ts +10 -0
  55. package/dist/tools/percy-mcp/creation/finalize-comparison.js +16 -0
  56. package/dist/tools/percy-mcp/creation/finalize-snapshot.d.ts +12 -0
  57. package/dist/tools/percy-mcp/creation/finalize-snapshot.js +33 -0
  58. package/dist/tools/percy-mcp/creation/upload-resource.d.ts +15 -0
  59. package/dist/tools/percy-mcp/creation/upload-resource.js +43 -0
  60. package/dist/tools/percy-mcp/creation/upload-tile.d.ts +11 -0
  61. package/dist/tools/percy-mcp/creation/upload-tile.js +53 -0
  62. package/dist/tools/percy-mcp/diagnostics/analyze-logs-realtime.d.ts +13 -0
  63. package/dist/tools/percy-mcp/diagnostics/analyze-logs-realtime.js +65 -0
  64. package/dist/tools/percy-mcp/diagnostics/get-build-logs.d.ts +17 -0
  65. package/dist/tools/percy-mcp/diagnostics/get-build-logs.js +74 -0
  66. package/dist/tools/percy-mcp/diagnostics/get-network-logs.d.ts +5 -0
  67. package/dist/tools/percy-mcp/diagnostics/get-network-logs.js +21 -0
  68. package/dist/tools/percy-mcp/diagnostics/get-suggestions.d.ts +7 -0
  69. package/dist/tools/percy-mcp/diagnostics/get-suggestions.js +24 -0
  70. package/dist/tools/percy-mcp/index.d.ts +36 -0
  71. package/dist/tools/percy-mcp/index.js +1137 -0
  72. package/dist/tools/percy-mcp/intelligence/get-ai-analysis.d.ts +15 -0
  73. package/dist/tools/percy-mcp/intelligence/get-ai-analysis.js +166 -0
  74. package/dist/tools/percy-mcp/intelligence/get-ai-quota.d.ts +9 -0
  75. package/dist/tools/percy-mcp/intelligence/get-ai-quota.js +73 -0
  76. package/dist/tools/percy-mcp/intelligence/get-build-summary.d.ts +11 -0
  77. package/dist/tools/percy-mcp/intelligence/get-build-summary.js +78 -0
  78. package/dist/tools/percy-mcp/intelligence/get-rca.d.ts +6 -0
  79. package/dist/tools/percy-mcp/intelligence/get-rca.js +153 -0
  80. package/dist/tools/percy-mcp/intelligence/suggest-prompt.d.ts +15 -0
  81. package/dist/tools/percy-mcp/intelligence/suggest-prompt.js +86 -0
  82. package/dist/tools/percy-mcp/intelligence/trigger-ai-recompute.d.ts +16 -0
  83. package/dist/tools/percy-mcp/intelligence/trigger-ai-recompute.js +64 -0
  84. package/dist/tools/percy-mcp/management/create-project.d.ts +14 -0
  85. package/dist/tools/percy-mcp/management/create-project.js +52 -0
  86. package/dist/tools/percy-mcp/management/get-usage-stats.d.ts +12 -0
  87. package/dist/tools/percy-mcp/management/get-usage-stats.js +61 -0
  88. package/dist/tools/percy-mcp/management/manage-browser-targets.d.ts +12 -0
  89. package/dist/tools/percy-mcp/management/manage-browser-targets.js +136 -0
  90. package/dist/tools/percy-mcp/management/manage-comments.d.ts +14 -0
  91. package/dist/tools/percy-mcp/management/manage-comments.js +147 -0
  92. package/dist/tools/percy-mcp/management/manage-ignored-regions.d.ts +18 -0
  93. package/dist/tools/percy-mcp/management/manage-ignored-regions.js +182 -0
  94. package/dist/tools/percy-mcp/management/manage-project-settings.d.ts +16 -0
  95. package/dist/tools/percy-mcp/management/manage-project-settings.js +97 -0
  96. package/dist/tools/percy-mcp/management/manage-tokens.d.ts +14 -0
  97. package/dist/tools/percy-mcp/management/manage-tokens.js +90 -0
  98. package/dist/tools/percy-mcp/management/manage-webhooks.d.ts +15 -0
  99. package/dist/tools/percy-mcp/management/manage-webhooks.js +180 -0
  100. package/dist/tools/percy-mcp/v2/auth-status.d.ts +3 -0
  101. package/dist/tools/percy-mcp/v2/auth-status.js +80 -0
  102. package/dist/tools/percy-mcp/v2/clone-build.d.ts +24 -0
  103. package/dist/tools/percy-mcp/v2/clone-build.js +539 -0
  104. package/dist/tools/percy-mcp/v2/create-app-build.d.ts +28 -0
  105. package/dist/tools/percy-mcp/v2/create-app-build.js +442 -0
  106. package/dist/tools/percy-mcp/v2/create-build.d.ts +16 -0
  107. package/dist/tools/percy-mcp/v2/create-build.js +601 -0
  108. package/dist/tools/percy-mcp/v2/create-project.d.ts +8 -0
  109. package/dist/tools/percy-mcp/v2/create-project.js +33 -0
  110. package/dist/tools/percy-mcp/v2/discover-urls.d.ts +7 -0
  111. package/dist/tools/percy-mcp/v2/discover-urls.js +38 -0
  112. package/dist/tools/percy-mcp/v2/figma-baseline.d.ts +7 -0
  113. package/dist/tools/percy-mcp/v2/figma-baseline.js +18 -0
  114. package/dist/tools/percy-mcp/v2/figma-build.d.ts +7 -0
  115. package/dist/tools/percy-mcp/v2/figma-build.js +39 -0
  116. package/dist/tools/percy-mcp/v2/figma-link.d.ts +6 -0
  117. package/dist/tools/percy-mcp/v2/figma-link.js +27 -0
  118. package/dist/tools/percy-mcp/v2/get-ai-summary.d.ts +5 -0
  119. package/dist/tools/percy-mcp/v2/get-ai-summary.js +109 -0
  120. package/dist/tools/percy-mcp/v2/get-build-detail.d.ts +22 -0
  121. package/dist/tools/percy-mcp/v2/get-build-detail.js +567 -0
  122. package/dist/tools/percy-mcp/v2/get-builds.d.ts +8 -0
  123. package/dist/tools/percy-mcp/v2/get-builds.js +63 -0
  124. package/dist/tools/percy-mcp/v2/get-comparison.d.ts +5 -0
  125. package/dist/tools/percy-mcp/v2/get-comparison.js +94 -0
  126. package/dist/tools/percy-mcp/v2/get-devices.d.ts +5 -0
  127. package/dist/tools/percy-mcp/v2/get-devices.js +33 -0
  128. package/dist/tools/percy-mcp/v2/get-insights.d.ts +7 -0
  129. package/dist/tools/percy-mcp/v2/get-insights.js +52 -0
  130. package/dist/tools/percy-mcp/v2/get-projects.d.ts +6 -0
  131. package/dist/tools/percy-mcp/v2/get-projects.js +41 -0
  132. package/dist/tools/percy-mcp/v2/get-snapshot.d.ts +5 -0
  133. package/dist/tools/percy-mcp/v2/get-snapshot.js +96 -0
  134. package/dist/tools/percy-mcp/v2/get-test-case-history.d.ts +5 -0
  135. package/dist/tools/percy-mcp/v2/get-test-case-history.js +20 -0
  136. package/dist/tools/percy-mcp/v2/get-test-cases.d.ts +6 -0
  137. package/dist/tools/percy-mcp/v2/get-test-cases.js +36 -0
  138. package/dist/tools/percy-mcp/v2/index.d.ts +35 -0
  139. package/dist/tools/percy-mcp/v2/index.js +544 -0
  140. package/dist/tools/percy-mcp/v2/list-integrations.d.ts +5 -0
  141. package/dist/tools/percy-mcp/v2/list-integrations.js +41 -0
  142. package/dist/tools/percy-mcp/v2/manage-domains.d.ts +8 -0
  143. package/dist/tools/percy-mcp/v2/manage-domains.js +33 -0
  144. package/dist/tools/percy-mcp/v2/manage-insights-email.d.ts +8 -0
  145. package/dist/tools/percy-mcp/v2/manage-insights-email.js +49 -0
  146. package/dist/tools/percy-mcp/v2/manage-usage-alerts.d.ts +10 -0
  147. package/dist/tools/percy-mcp/v2/manage-usage-alerts.js +43 -0
  148. package/dist/tools/percy-mcp/v2/migrate-integrations.d.ts +6 -0
  149. package/dist/tools/percy-mcp/v2/migrate-integrations.js +20 -0
  150. package/dist/tools/percy-mcp/v2/preview-comparison.d.ts +5 -0
  151. package/dist/tools/percy-mcp/v2/preview-comparison.js +17 -0
  152. package/dist/tools/percy-mcp/v2/search-build-items.d.ts +12 -0
  153. package/dist/tools/percy-mcp/v2/search-build-items.js +45 -0
  154. package/dist/tools/percy-mcp/workflows/auto-triage.d.ts +7 -0
  155. package/dist/tools/percy-mcp/workflows/auto-triage.js +82 -0
  156. package/dist/tools/percy-mcp/workflows/clone-build.d.ts +22 -0
  157. package/dist/tools/percy-mcp/workflows/clone-build.js +414 -0
  158. package/dist/tools/percy-mcp/workflows/create-percy-build.d.ts +32 -0
  159. package/dist/tools/percy-mcp/workflows/create-percy-build.js +434 -0
  160. package/dist/tools/percy-mcp/workflows/debug-failed-build.d.ts +5 -0
  161. package/dist/tools/percy-mcp/workflows/debug-failed-build.js +122 -0
  162. package/dist/tools/percy-mcp/workflows/diff-explain.d.ts +6 -0
  163. package/dist/tools/percy-mcp/workflows/diff-explain.js +147 -0
  164. package/dist/tools/percy-mcp/workflows/pr-visual-report.d.ts +8 -0
  165. package/dist/tools/percy-mcp/workflows/pr-visual-report.js +184 -0
  166. package/dist/tools/percy-mcp/workflows/run-tests.d.ts +17 -0
  167. package/dist/tools/percy-mcp/workflows/run-tests.js +107 -0
  168. package/dist/tools/percy-mcp/workflows/snapshot-urls.d.ts +18 -0
  169. package/dist/tools/percy-mcp/workflows/snapshot-urls.js +197 -0
  170. package/package.json +3 -2
@@ -0,0 +1,544 @@
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 { handleMCPError } from "../../../lib/utils.js";
33
+ import { trackMCP } from "../../../index.js";
34
+ import { z } from "zod";
35
+ import { handlePercyToolError, TOOL_HELP, } from "../../../lib/percy-api/percy-error-handler.js";
36
+ import { percyCreateProjectV2 } from "./create-project.js";
37
+ import { percyGetProjectsV2 } from "./get-projects.js";
38
+ import { percyGetBuildsV2 } from "./get-builds.js";
39
+ import { percyCreateBuildV2 } from "./create-build.js";
40
+ import { percyAuthStatusV2 } from "./auth-status.js";
41
+ import { percyGetBuildDetail } from "./get-build-detail.js";
42
+ import { percyCloneBuildV2 } from "./clone-build.js";
43
+ import { percyGetSnapshot } from "./get-snapshot.js";
44
+ import { percyGetComparison } from "./get-comparison.js";
45
+ import { percyFigmaBuild } from "./figma-build.js";
46
+ import { percyFigmaBaseline } from "./figma-baseline.js";
47
+ import { percyFigmaLink } from "./figma-link.js";
48
+ import { percyGetInsights } from "./get-insights.js";
49
+ import { percyManageInsightsEmail } from "./manage-insights-email.js";
50
+ import { percyGetTestCases } from "./get-test-cases.js";
51
+ import { percyGetTestCaseHistory } from "./get-test-case-history.js";
52
+ import { percyDiscoverUrls } from "./discover-urls.js";
53
+ import { percyGetDevices } from "./get-devices.js";
54
+ import { percyManageDomains } from "./manage-domains.js";
55
+ import { percyManageUsageAlerts } from "./manage-usage-alerts.js";
56
+ import { percyPreviewComparison } from "./preview-comparison.js";
57
+ import { percySearchBuildItems } from "./search-build-items.js";
58
+ import { percyListIntegrations } from "./list-integrations.js";
59
+ import { percyMigrateIntegrations } from "./migrate-integrations.js";
60
+ import { percyGetAiSummary } from "./get-ai-summary.js";
61
+ import { percyCreateAppBuildV2 } from "./create-app-build.js";
62
+ export function registerPercyMcpToolsV2(server, config) {
63
+ const tools = {};
64
+ // ── percy_create_project ────────────────────────────────────────────────
65
+ tools.percy_create_project = server.tool("percy_create_project", "Create a new Percy project (or get token for existing one). Returns project token for CLI use.", {
66
+ name: z.string().describe("Project name"),
67
+ type: z
68
+ .enum(["web", "automate"])
69
+ .optional()
70
+ .describe("Project type (default: auto-detect)"),
71
+ }, async (args) => {
72
+ try {
73
+ trackMCP("percy_create_project", server.server.getClientVersion(), config);
74
+ return await percyCreateProjectV2(args, config);
75
+ }
76
+ catch (error) {
77
+ return TOOL_HELP.percy_create_project
78
+ ? handlePercyToolError(error, TOOL_HELP.percy_create_project, args)
79
+ : handleMCPError("percy_create_project", server, config, error);
80
+ }
81
+ });
82
+ // ── percy_create_build ──────────────────────────────────────────────────
83
+ tools.percy_create_build = server.tool("percy_create_build", "Create a Percy build with snapshots. Handles URL snapshotting (launches real browser), screenshot upload, and test command wrapping — all in one tool. Auto-creates project if needed, auto-detects git branch.", {
84
+ project_name: z
85
+ .string()
86
+ .describe("Percy project name (auto-creates if doesn't exist)"),
87
+ urls: z
88
+ .string()
89
+ .optional()
90
+ .describe("Comma-separated URLs to snapshot (e.g., 'http://localhost:3000,http://localhost:3000/about')"),
91
+ screenshots_dir: z
92
+ .string()
93
+ .optional()
94
+ .describe("Directory path with PNG/JPG screenshots to upload"),
95
+ screenshot_files: z
96
+ .string()
97
+ .optional()
98
+ .describe("Comma-separated screenshot file paths"),
99
+ test_command: z
100
+ .string()
101
+ .optional()
102
+ .describe("Test command to wrap with Percy (e.g., 'npx cypress run')"),
103
+ branch: z.string().optional().describe("Git branch (auto-detected)"),
104
+ widths: z
105
+ .string()
106
+ .optional()
107
+ .describe("Viewport widths (default: '375,1280')"),
108
+ snapshot_names: z
109
+ .string()
110
+ .optional()
111
+ .describe("Custom snapshot names, comma-separated (e.g., 'Homepage,Login Page,Dashboard'). Maps 1:1 with urls or screenshot files."),
112
+ test_case: z
113
+ .string()
114
+ .optional()
115
+ .describe("Test case name to attach to all snapshots"),
116
+ type: z.enum(["web", "automate"]).optional().describe("Project type"),
117
+ }, async (args) => {
118
+ try {
119
+ trackMCP("percy_create_build", server.server.getClientVersion(), config);
120
+ return await percyCreateBuildV2(args, config);
121
+ }
122
+ catch (error) {
123
+ return TOOL_HELP.percy_create_build
124
+ ? handlePercyToolError(error, TOOL_HELP.percy_create_build, args)
125
+ : handleMCPError("percy_create_build", server, config, error);
126
+ }
127
+ });
128
+ // ── percy_get_projects ──────────────────────────────────────────────────
129
+ tools.percy_get_projects = server.tool("percy_get_projects", "List all Percy projects in your organization.", {
130
+ search: z.string().optional().describe("Search by project name"),
131
+ limit: z.number().optional().describe("Max results (default: 20)"),
132
+ }, async (args) => {
133
+ try {
134
+ trackMCP("percy_get_projects", server.server.getClientVersion(), config);
135
+ return await percyGetProjectsV2(args, config);
136
+ }
137
+ catch (error) {
138
+ return TOOL_HELP.percy_get_projects
139
+ ? handlePercyToolError(error, TOOL_HELP.percy_get_projects, args)
140
+ : handleMCPError("percy_get_projects", server, config, error);
141
+ }
142
+ });
143
+ // ── percy_get_builds ────────────────────────────────────────────────────
144
+ tools.percy_get_builds = server.tool("percy_get_builds", "List Percy builds. Provide project_slug (from percy_get_projects) to filter by project.", {
145
+ project_slug: z
146
+ .string()
147
+ .optional()
148
+ .describe("Project slug from percy_get_projects (e.g., 'org-id/project-slug')"),
149
+ branch: z.string().optional().describe("Filter by branch"),
150
+ state: z
151
+ .string()
152
+ .optional()
153
+ .describe("Filter: pending, processing, finished, failed"),
154
+ limit: z.number().optional().describe("Max results (default: 10)"),
155
+ }, async (args) => {
156
+ try {
157
+ trackMCP("percy_get_builds", server.server.getClientVersion(), config);
158
+ return await percyGetBuildsV2(args, config);
159
+ }
160
+ catch (error) {
161
+ return TOOL_HELP.percy_get_builds
162
+ ? handlePercyToolError(error, TOOL_HELP.percy_get_builds, args)
163
+ : handleMCPError("percy_get_builds", server, config, error);
164
+ }
165
+ });
166
+ // ── percy_auth_status ───────────────────────────────────────────────────
167
+ tools.percy_auth_status = server.tool("percy_auth_status", "Check Percy authentication — validates BrowserStack credentials and Percy API connectivity.", {}, async () => {
168
+ try {
169
+ trackMCP("percy_auth_status", server.server.getClientVersion(), config);
170
+ return await percyAuthStatusV2({}, config);
171
+ }
172
+ catch (error) {
173
+ return handleMCPError("percy_auth_status", server, config, error);
174
+ }
175
+ });
176
+ // ── Unified Build Details ─────────────────────────────────────────────────
177
+ tools.percy_get_build = server.tool("percy_get_build", "Get Percy build details. Supports multiple views: overview (default), ai_summary, changes, rca, logs, network, snapshots. One tool for all build data.", {
178
+ build_id: z.string().describe("Percy build ID"),
179
+ detail: z
180
+ .enum([
181
+ "overview",
182
+ "ai_summary",
183
+ "changes",
184
+ "rca",
185
+ "logs",
186
+ "network",
187
+ "snapshots",
188
+ ])
189
+ .optional()
190
+ .describe("What to show: overview (default), ai_summary, changes, rca, logs, network, snapshots"),
191
+ comparison_id: z
192
+ .string()
193
+ .optional()
194
+ .describe("Comparison ID (required for rca and network details)"),
195
+ snapshot_id: z
196
+ .string()
197
+ .optional()
198
+ .describe("Snapshot ID (for snapshot-specific details)"),
199
+ }, async (args) => {
200
+ try {
201
+ trackMCP("percy_get_build", server.server.getClientVersion(), config);
202
+ return await percyGetBuildDetail(args, config);
203
+ }
204
+ catch (error) {
205
+ return TOOL_HELP.percy_get_build
206
+ ? handlePercyToolError(error, TOOL_HELP.percy_get_build, args)
207
+ : handleMCPError("percy_get_build", server, config, error);
208
+ }
209
+ });
210
+ // ── Clone Build (Deep) ─────────────────────────────────────────────────────
211
+ tools.percy_clone_build = server.tool("percy_clone_build", "Deep clone a Percy build to another project. Downloads DOM resources and re-creates snapshots so Percy re-renders them. Falls back to screenshot cloning when DOM is unavailable. Works across projects.", {
212
+ source_build_id: z.string().describe("Build ID to clone FROM"),
213
+ target_project_name: z
214
+ .string()
215
+ .describe("Project name to clone INTO (auto-creates if new)"),
216
+ target_token: z
217
+ .string()
218
+ .optional()
219
+ .describe("Target project token (use for existing projects to avoid creating duplicates)"),
220
+ branch: z
221
+ .string()
222
+ .optional()
223
+ .describe("Branch for new build (auto-detected from git)"),
224
+ }, async (args) => {
225
+ try {
226
+ trackMCP("percy_clone_build", server.server.getClientVersion(), config);
227
+ return await percyCloneBuildV2(args, config);
228
+ }
229
+ catch (error) {
230
+ return TOOL_HELP.percy_clone_build
231
+ ? handlePercyToolError(error, TOOL_HELP.percy_clone_build, args)
232
+ : handleMCPError("percy_clone_build", server, config, error);
233
+ }
234
+ });
235
+ // ── Snapshot Details ───────────────────────────────────────────────────────
236
+ tools.percy_get_snapshot = server.tool("percy_get_snapshot", "Get Percy snapshot details: name, review state, all comparisons with diff ratios, AI analysis regions, and screenshot URLs.", {
237
+ snapshot_id: z.string().describe("Percy snapshot ID"),
238
+ }, async (args) => {
239
+ try {
240
+ trackMCP("percy_get_snapshot", server.server.getClientVersion(), config);
241
+ return await percyGetSnapshot(args, config);
242
+ }
243
+ catch (error) {
244
+ return TOOL_HELP.percy_get_snapshot
245
+ ? handlePercyToolError(error, TOOL_HELP.percy_get_snapshot, args)
246
+ : handleMCPError("percy_get_snapshot", server, config, error);
247
+ }
248
+ });
249
+ // ── Comparison Details ────────────────────────────────────────────────────
250
+ tools.percy_get_comparison = server.tool("percy_get_comparison", "Get Percy comparison details: diff ratios, AI change descriptions with coordinates, potential bugs, and head/base/diff image URLs.", {
251
+ comparison_id: z.string().describe("Percy comparison ID"),
252
+ }, async (args) => {
253
+ try {
254
+ trackMCP("percy_get_comparison", server.server.getClientVersion(), config);
255
+ return await percyGetComparison(args, config);
256
+ }
257
+ catch (error) {
258
+ return TOOL_HELP.percy_get_comparison
259
+ ? handlePercyToolError(error, TOOL_HELP.percy_get_comparison, args)
260
+ : handleMCPError("percy_get_comparison", server, config, error);
261
+ }
262
+ });
263
+ // ── Figma ─────────────────────────────────────────────────────────────────
264
+ tools.percy_figma_build = server.tool("percy_figma_build", "Create a Percy build from Figma design files. Extracts design nodes and creates visual comparisons.", {
265
+ project_slug: z
266
+ .string()
267
+ .describe("Project slug (e.g., 'org-id/project-slug')"),
268
+ branch: z.string().describe("Branch name"),
269
+ figma_url: z
270
+ .string()
271
+ .describe("Figma file URL (e.g., 'https://www.figma.com/file/...')"),
272
+ }, async (args) => {
273
+ try {
274
+ trackMCP("percy_figma_build", server.server.getClientVersion(), config);
275
+ return await percyFigmaBuild(args, config);
276
+ }
277
+ catch (error) {
278
+ return handleMCPError("percy_figma_build", server, config, error);
279
+ }
280
+ });
281
+ tools.percy_figma_baseline = server.tool("percy_figma_baseline", "Update the Figma design baseline for a project. Uses the latest Figma designs as the new baseline.", {
282
+ project_slug: z.string().describe("Project slug"),
283
+ branch: z.string().describe("Branch name"),
284
+ build_id: z.string().describe("Build ID to use as new baseline"),
285
+ }, async (args) => {
286
+ try {
287
+ trackMCP("percy_figma_baseline", server.server.getClientVersion(), config);
288
+ return await percyFigmaBaseline(args, config);
289
+ }
290
+ catch (error) {
291
+ return handleMCPError("percy_figma_baseline", server, config, error);
292
+ }
293
+ });
294
+ tools.percy_figma_link = server.tool("percy_figma_link", "Get the Figma design link for a snapshot or comparison.", {
295
+ snapshot_id: z.string().optional().describe("Snapshot ID"),
296
+ comparison_id: z.string().optional().describe("Comparison ID"),
297
+ }, async (args) => {
298
+ try {
299
+ trackMCP("percy_figma_link", server.server.getClientVersion(), config);
300
+ return await percyFigmaLink(args, config);
301
+ }
302
+ catch (error) {
303
+ return handleMCPError("percy_figma_link", server, config, error);
304
+ }
305
+ });
306
+ // ── Insights ──────────────────────────────────────────────────────────────
307
+ tools.percy_get_insights = server.tool("percy_get_insights", "Get testing health metrics: review efficiency, ROI, coverage, change quality. By period and product.", {
308
+ org_slug: z.string().describe("Organization slug"),
309
+ period: z
310
+ .enum(["last_7_days", "last_30_days", "last_90_days"])
311
+ .optional()
312
+ .describe("Time period (default: last_30_days)"),
313
+ product: z
314
+ .enum(["web", "app"])
315
+ .optional()
316
+ .describe("Product type (default: web)"),
317
+ }, async (args) => {
318
+ try {
319
+ trackMCP("percy_get_insights", server.server.getClientVersion(), config);
320
+ return await percyGetInsights(args, config);
321
+ }
322
+ catch (error) {
323
+ return handleMCPError("percy_get_insights", server, config, error);
324
+ }
325
+ });
326
+ tools.percy_manage_insights_email = server.tool("percy_manage_insights_email", "Configure weekly insights email recipients for an organization.", {
327
+ org_id: z.string().describe("Organization ID"),
328
+ action: z
329
+ .enum(["get", "create", "update"])
330
+ .optional()
331
+ .describe("Action (default: get)"),
332
+ emails: z.string().optional().describe("Comma-separated email addresses"),
333
+ enabled: z.boolean().optional().describe("Enable/disable emails"),
334
+ }, async (args) => {
335
+ try {
336
+ trackMCP("percy_manage_insights_email", server.server.getClientVersion(), config);
337
+ return await percyManageInsightsEmail(args, config);
338
+ }
339
+ catch (error) {
340
+ return handleMCPError("percy_manage_insights_email", server, config, error);
341
+ }
342
+ });
343
+ // ── Test Cases ────────────────────────────────────────────────────────────
344
+ tools.percy_get_test_cases = server.tool("percy_get_test_cases", "List test cases for a project with optional execution details per build.", {
345
+ project_id: z.string().describe("Project ID"),
346
+ build_id: z
347
+ .string()
348
+ .optional()
349
+ .describe("Build ID for execution details"),
350
+ }, async (args) => {
351
+ try {
352
+ trackMCP("percy_get_test_cases", server.server.getClientVersion(), config);
353
+ return await percyGetTestCases(args, config);
354
+ }
355
+ catch (error) {
356
+ return handleMCPError("percy_get_test_cases", server, config, error);
357
+ }
358
+ });
359
+ tools.percy_get_test_case_history = server.tool("percy_get_test_case_history", "Get full execution history of a test case across all builds.", {
360
+ test_case_id: z.string().describe("Test case ID"),
361
+ }, async (args) => {
362
+ try {
363
+ trackMCP("percy_get_test_case_history", server.server.getClientVersion(), config);
364
+ return await percyGetTestCaseHistory(args, config);
365
+ }
366
+ catch (error) {
367
+ return handleMCPError("percy_get_test_case_history", server, config, error);
368
+ }
369
+ });
370
+ // ── Discovery ─────────────────────────────────────────────────────────────
371
+ tools.percy_discover_urls = server.tool("percy_discover_urls", "Discover URLs from a sitemap for visual testing. Returns URLs to use with percy_create_build.", {
372
+ project_id: z.string().describe("Project ID"),
373
+ sitemap_url: z.string().optional().describe("Sitemap XML URL to crawl"),
374
+ action: z
375
+ .enum(["create", "list"])
376
+ .optional()
377
+ .describe("create = crawl new sitemap, list = show existing"),
378
+ }, async (args) => {
379
+ try {
380
+ trackMCP("percy_discover_urls", server.server.getClientVersion(), config);
381
+ return await percyDiscoverUrls(args, config);
382
+ }
383
+ catch (error) {
384
+ return handleMCPError("percy_discover_urls", server, config, error);
385
+ }
386
+ });
387
+ tools.percy_get_devices = server.tool("percy_get_devices", "List available browsers, devices, and viewport details for visual testing.", {
388
+ build_id: z.string().optional().describe("Build ID for device details"),
389
+ }, async (args) => {
390
+ try {
391
+ trackMCP("percy_get_devices", server.server.getClientVersion(), config);
392
+ return await percyGetDevices(args, config);
393
+ }
394
+ catch (error) {
395
+ return handleMCPError("percy_get_devices", server, config, error);
396
+ }
397
+ });
398
+ // ── Configuration ─────────────────────────────────────────────────────────
399
+ tools.percy_manage_domains = server.tool("percy_manage_domains", "Get or update allowed/error domain lists for a project.", {
400
+ project_id: z.string().describe("Project ID"),
401
+ action: z
402
+ .enum(["get", "update"])
403
+ .optional()
404
+ .describe("Action (default: get)"),
405
+ allowed_domains: z
406
+ .string()
407
+ .optional()
408
+ .describe("Comma-separated allowed domains"),
409
+ error_domains: z
410
+ .string()
411
+ .optional()
412
+ .describe("Comma-separated error domains"),
413
+ }, async (args) => {
414
+ try {
415
+ trackMCP("percy_manage_domains", server.server.getClientVersion(), config);
416
+ return await percyManageDomains(args, config);
417
+ }
418
+ catch (error) {
419
+ return handleMCPError("percy_manage_domains", server, config, error);
420
+ }
421
+ });
422
+ tools.percy_manage_usage_alerts = server.tool("percy_manage_usage_alerts", "Configure usage alert thresholds for billing notifications.", {
423
+ org_id: z.string().describe("Organization ID"),
424
+ action: z
425
+ .enum(["get", "create", "update"])
426
+ .optional()
427
+ .describe("Action (default: get)"),
428
+ threshold: z.number().optional().describe("Screenshot count threshold"),
429
+ emails: z.string().optional().describe("Comma-separated email addresses"),
430
+ enabled: z.boolean().optional().describe("Enable/disable alerts"),
431
+ product: z.enum(["web", "app"]).optional().describe("Product type"),
432
+ }, async (args) => {
433
+ try {
434
+ trackMCP("percy_manage_usage_alerts", server.server.getClientVersion(), config);
435
+ return await percyManageUsageAlerts(args, config);
436
+ }
437
+ catch (error) {
438
+ return handleMCPError("percy_manage_usage_alerts", server, config, error);
439
+ }
440
+ });
441
+ tools.percy_preview_comparison = server.tool("percy_preview_comparison", "Trigger on-demand diff recomputation for a comparison without full rebuild.", {
442
+ comparison_id: z.string().describe("Comparison ID to recompute"),
443
+ }, async (args) => {
444
+ try {
445
+ trackMCP("percy_preview_comparison", server.server.getClientVersion(), config);
446
+ return await percyPreviewComparison(args, config);
447
+ }
448
+ catch (error) {
449
+ return handleMCPError("percy_preview_comparison", server, config, error);
450
+ }
451
+ });
452
+ // ── Advanced Search ───────────────────────────────────────────────────────
453
+ tools.percy_search_builds = server.tool("percy_search_builds", "Advanced build item search with filters: category, browser, width, OS, device, resolution, orientation.", {
454
+ build_id: z.string().describe("Build ID to search within"),
455
+ category: z
456
+ .string()
457
+ .optional()
458
+ .describe("Filter: changed, new, removed, unchanged, failed"),
459
+ browser_ids: z
460
+ .string()
461
+ .optional()
462
+ .describe("Comma-separated browser IDs"),
463
+ widths: z.string().optional().describe("Comma-separated widths"),
464
+ os: z.string().optional().describe("Operating system filter"),
465
+ device_name: z.string().optional().describe("Device name filter"),
466
+ sort_by: z.string().optional().describe("Sort: diff_ratio or bug_count"),
467
+ limit: z.number().optional().describe("Max results"),
468
+ }, async (args) => {
469
+ try {
470
+ trackMCP("percy_search_builds", server.server.getClientVersion(), config);
471
+ return await percySearchBuildItems(args, config);
472
+ }
473
+ catch (error) {
474
+ return handleMCPError("percy_search_builds", server, config, error);
475
+ }
476
+ });
477
+ // ── Integrations ──────────────────────────────────────────────────────────
478
+ tools.percy_list_integrations = server.tool("percy_list_integrations", "List all integrations (VCS, Slack, Teams, Email) for an organization.", {
479
+ org_id: z.string().describe("Organization ID"),
480
+ }, async (args) => {
481
+ try {
482
+ trackMCP("percy_list_integrations", server.server.getClientVersion(), config);
483
+ return await percyListIntegrations(args, config);
484
+ }
485
+ catch (error) {
486
+ return handleMCPError("percy_list_integrations", server, config, error);
487
+ }
488
+ });
489
+ tools.percy_migrate_integrations = server.tool("percy_migrate_integrations", "Migrate integrations between organizations.", {
490
+ source_org_id: z.string().describe("Source organization ID"),
491
+ target_org_id: z.string().describe("Target organization ID"),
492
+ }, async (args) => {
493
+ try {
494
+ trackMCP("percy_migrate_integrations", server.server.getClientVersion(), config);
495
+ return await percyMigrateIntegrations(args, config);
496
+ }
497
+ catch (error) {
498
+ return handleMCPError("percy_migrate_integrations", server, config, error);
499
+ }
500
+ });
501
+ // ── AI Summary ─────────────────────────────────────────────────────────
502
+ tools.percy_get_ai_summary = server.tool("percy_get_ai_summary", "Get the AI-generated build summary: potential bugs, visual diffs, change descriptions with occurrence counts. Shows what changed and why.", {
503
+ build_id: z.string().describe("Percy build ID"),
504
+ }, async (args) => {
505
+ try {
506
+ trackMCP("percy_get_ai_summary", server.server.getClientVersion(), config);
507
+ return await percyGetAiSummary(args, config);
508
+ }
509
+ catch (error) {
510
+ return handleMCPError("percy_get_ai_summary", server, config, error);
511
+ }
512
+ });
513
+ // ── App Percy Build (BYOS) ──────────────────────────────────────────────
514
+ tools.percy_create_app_build = server.tool("percy_create_app_build", "Create an App Percy BYOS (Bring Your Own Screenshots) build. Works in two modes: (1) Sample mode (default) — auto-generates 3 devices × 2 screenshots for instant testing, no setup needed. (2) Custom mode — provide resources_dir with your own device folders (each with device.json + .png files). Validates dimensions, uploads with device tags, and finalizes the build.", {
515
+ project_name: z
516
+ .string()
517
+ .describe("App Percy project name (auto-creates if doesn't exist)"),
518
+ resources_dir: z
519
+ .string()
520
+ .optional()
521
+ .describe("Path to resources directory with device folders (each with device.json + .png files). Omit to use built-in sample data."),
522
+ use_sample_data: z
523
+ .boolean()
524
+ .optional()
525
+ .describe("Use built-in sample data (3 devices × 2 screenshots). Default: true when resources_dir is omitted."),
526
+ branch: z.string().optional().describe("Git branch (auto-detected)"),
527
+ test_case: z
528
+ .string()
529
+ .optional()
530
+ .describe("Test case name to attach to all snapshots"),
531
+ }, async (args) => {
532
+ try {
533
+ trackMCP("percy_create_app_build", server.server.getClientVersion(), config);
534
+ return await percyCreateAppBuildV2(args, config);
535
+ }
536
+ catch (error) {
537
+ return TOOL_HELP.percy_create_app_build
538
+ ? handlePercyToolError(error, TOOL_HELP.percy_create_app_build, args)
539
+ : handleMCPError("percy_create_app_build", server, config, error);
540
+ }
541
+ });
542
+ return tools;
543
+ }
544
+ export default registerPercyMcpToolsV2;
@@ -0,0 +1,5 @@
1
+ import { BrowserStackConfig } from "../../../lib/types.js";
2
+ import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
3
+ export declare function percyListIntegrations(args: {
4
+ org_id: string;
5
+ }, config: BrowserStackConfig): Promise<CallToolResult>;
@@ -0,0 +1,41 @@
1
+ import { percyGet } from "../../../lib/percy-api/percy-auth.js";
2
+ export async function percyListIntegrations(args, config) {
3
+ const response = await percyGet(`/organizations/${args.org_id}`, config, {
4
+ include: "version-control-integrations,slack-integrations,msteams-integrations,email-integration",
5
+ });
6
+ const included = response?.included || [];
7
+ let output = `## Integrations for Organization\n\n`;
8
+ const vcs = included.filter((i) => i.type === "version-control-integrations");
9
+ const slack = included.filter((i) => i.type === "slack-integrations");
10
+ const teams = included.filter((i) => i.type === "msteams-integrations");
11
+ const email = included.filter((i) => i.type === "email-integrations");
12
+ if (vcs.length) {
13
+ output += `### VCS Integrations (${vcs.length})\n`;
14
+ vcs.forEach((v) => {
15
+ const attrs = v.attributes || {};
16
+ output += `- **${attrs["integration-type"] || attrs.integrationType || "VCS"}** — ${attrs.status || "active"}\n`;
17
+ });
18
+ output += "\n";
19
+ }
20
+ if (slack.length) {
21
+ output += `### Slack (${slack.length})\n`;
22
+ slack.forEach((s) => {
23
+ output += `- ${s.attributes?.["channel-name"] || "channel"}\n`;
24
+ });
25
+ output += "\n";
26
+ }
27
+ if (teams.length) {
28
+ output += `### MS Teams (${teams.length})\n`;
29
+ teams.forEach((t) => {
30
+ output += `- ${t.attributes?.["channel-name"] || "channel"}\n`;
31
+ });
32
+ output += "\n";
33
+ }
34
+ if (email.length) {
35
+ output += `### Email\n- Configured\n\n`;
36
+ }
37
+ if (!vcs.length && !slack.length && !teams.length && !email.length) {
38
+ output += `No integrations found.\n`;
39
+ }
40
+ return { content: [{ type: "text", text: output }] };
41
+ }
@@ -0,0 +1,8 @@
1
+ import { BrowserStackConfig } from "../../../lib/types.js";
2
+ import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
3
+ export declare function percyManageDomains(args: {
4
+ project_id: string;
5
+ action?: string;
6
+ allowed_domains?: string;
7
+ error_domains?: string;
8
+ }, config: BrowserStackConfig): Promise<CallToolResult>;
@@ -0,0 +1,33 @@
1
+ import { percyGet, percyPatch } from "../../../lib/percy-api/percy-auth.js";
2
+ export async function percyManageDomains(args, config) {
3
+ if (!args.action || args.action === "get") {
4
+ const response = await percyGet(`/project-domain-configs/${args.project_id}`, config);
5
+ const attrs = response?.data?.attributes || {};
6
+ let output = `## Domain Configuration\n\n`;
7
+ output += `**Allowed domains:** ${attrs["allowed-domains"] || attrs.allowedDomains || "none"}\n`;
8
+ output += `**Error domains:** ${attrs["error-domains"] || attrs.errorDomains || "none"}\n`;
9
+ return { content: [{ type: "text", text: output }] };
10
+ }
11
+ if (args.action === "update") {
12
+ const body = {
13
+ data: { type: "project-domain-configs", attributes: {} },
14
+ };
15
+ if (args.allowed_domains)
16
+ body.data.attributes["allowed-domains"] = args.allowed_domains;
17
+ if (args.error_domains)
18
+ body.data.attributes["error-domains"] = args.error_domains;
19
+ await percyPatch(`/project-domain-configs/${args.project_id}`, config, body);
20
+ return {
21
+ content: [
22
+ {
23
+ type: "text",
24
+ text: `Domain configuration updated for project ${args.project_id}.`,
25
+ },
26
+ ],
27
+ };
28
+ }
29
+ return {
30
+ content: [{ type: "text", text: "Use action: get or update" }],
31
+ isError: true,
32
+ };
33
+ }
@@ -0,0 +1,8 @@
1
+ import { BrowserStackConfig } from "../../../lib/types.js";
2
+ import { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
3
+ export declare function percyManageInsightsEmail(args: {
4
+ org_id: string;
5
+ action?: string;
6
+ emails?: string;
7
+ enabled?: boolean;
8
+ }, config: BrowserStackConfig): Promise<CallToolResult>;