Review a SnapVisor build
Walk the user through the visual changes SnapVisor detected in a build, then approve or reject it on their say-so. An approval is a real decision for the whole team: the build is accepted and its changed screenshots become the new baseline.
Connecting and calling the tools
Every operation in this skill comes from the snapvisor MCP server, the SnapVisor connector. The server defaults to Code Mode, so the operations below are written as the external_* functions you call inside snapvisor:execute_typescript. On a server set to the per-tool surface, the same operation is listed as snapvisor:<name> without the external_ prefix, for example snapvisor:listBuilds.
- Only
snapvisor:search_toolsandsnapvisor:execute_typescriptlisted (Code Mode, the default): callsnapvisor:search_toolsfirst, then call each tool insidesnapvisor:execute_typescriptasexternal_<tool>(for exampleexternal_listBuilds); the program mustreturnits result. Scopes, confirmations and gates are identical on both surfaces. - Operations listed by name: call them directly, for example
snapvisor:listBuilds. - "Tool not found": switch to the Code Mode route instead of retrying the direct call. If
snapvisor:search_toolsdoes not return the tool either, its OAuth scope was not granted; the user reconnects SnapVisor and approves that scope. - No
snapvisortools at all: SnapVisor is not connected yet. Install the SnapVisor power, which adds the server; it signs in with OAuth on first use.
Tools you will use
The snapvisor MCP server has two tools. Everything below goes through them.
snapvisor:search_tools: find the operations you need and their exact input shape. Call it before your firstsnapvisor:execute_typescript, for example with the queriesbuild,diff,review.snapvisor:execute_typescript: run a short program that calls theexternal_*functionssnapvisor:search_toolsdeclared. They are already in scope; never import or redeclare them. The program mustreturnits result.
If the connection lists the operations by name instead (the per-tool surface), call them directly without the external_ prefix.
Operations this skill uses (confirm names and inputs with snapvisor:search_tools):
external_getMe: the signed-in user and the accounts (withslug) this connection can reach.external_listProjects: projects in an account.external_listBuilds: builds of a project, newest first; filter by branch (head), commit (headSha) orsearch.external_getBuild: one build with status, conclusion, stats, branch and commit.external_listBuildDiffs: one entry per screenshot withname,status(added,changed,removed,failure,ignoredand more), and base and head image links. PassneedsReviewto see only what needs a decision.external_listReviews: reviews already filed on the build.external_createReview: file a review witheventAPPROVEorREJECTand an optionalbody.
Workflow
- If you do not know the account and project, run one program:
external_getMe({}), thenexternal_listProjectsfor the account the user means. Ask once if more than one project fits. - Find the build. With no build number given, take the newest from
external_listBuilds(or the newest on the branch the user named). - In one
snapvisor:execute_typescriptcall, fetchexternal_getBuild,external_listBuildDiffsandexternal_listReviewstogether withawait Promise.all([...])and return a compact summary. - Present the build number, branch, commit and status, then the changed screenshots grouped by status (changed, added, removed, failed). Give each its before and after links. Mention reviews already on the build.
- If the user wants a decision, restate it ("Approve build 42 in project web; 6 changed screenshots become the baseline") and wait for a clear yes.
- Call
external_createReviewwithevent: "APPROVE"orevent: "REJECT"and the user's reason asbody. Report the new review, and confirm the build's status withexternal_getBuild.
Code Mode pattern
const [build, diffs, reviews] = await Promise.all([
external_getBuild({ owner, project, buildNumber }),
external_listBuildDiffs({ owner, project, buildNumber, needsReview: "true" }),
external_listReviews({ owner, project, buildNumber }),
]);
return { build, diffs: diffs.results.map((d) => ({ name: d.name, status: d.status })), reviews };
Wrap a call in try { ... } catch (e) { return String(e) } when it may fail. Errors read <CODE>: <details>; relay the code and details instead of guessing.
Rules
- Never file a review without the user's explicit confirmation in this conversation, and never approve or reject several builds in a loop. One build, one confirmation.
- Always show the diffs before asking for a decision.
- Image links are signed and expire after about an hour. Hand them over; do not store or archive them.
- A 403 saying MCP access requires a paid plan means the account is on the Free plan. Say so plainly and do not retry.
- SnapVisor compares screenshots the user's CI uploaded. Do not offer to capture, upload or edit a screenshot; no operation on this surface can.
