Skip to main content
Glama
README.md
# visualdiff

visualdiff wraps the SnapDiff visual-testing API: verify that a UI
change matches what you meant to do, diff two pages (or a page
against a stored baseline), capture a screenshot of any URL, and
render raw HTML/CSS straight to an image. One binary serves a
command-line interface and a stdio MCP server.

An API key is required. Set `SNAPDIFF_API_KEY` to a key from the
SnapDiff dashboard; calls fail before any request is made when it
is missing. The API base URL can be overridden with
`SNAPDIFF_API_URL`; when it is not set, calls go to
`https://api.snapdiff.ai/v1`.

## Install

```bash
npx @reneturcios/visualdiff screenshot https://example.com
```

Or run the container image:

```bash
docker run -i -e SNAPDIFF_API_KEY=sk_live_... ghcr.io/cloudcorprecords/visualdiff
```

The container defaults to the MCP server (see below). Pass CLI
arguments to use it as a CLI instead.

## CLI

```bash
export SNAPDIFF_API_KEY=sk_live_...

visualdiff verify myproj pricing https://staging.example.com/pricing "added a yearly billing toggle"
visualdiff compare https://staging.example.com --before https://www.example.com
visualdiff compare http://localhost:3000 --project myproj --page-name homepage
visualdiff screenshot https://example.com --width 800 --full-page
visualdiff html-to-image "<h1>Hello</h1>" --css "h1 { color: navy; }" --width 1200 --height 630

visualdiff --version
```

Commands print the upstream JSON payload. Exit codes: `0` on
success, `1` when a call fails (including a missing API key or an
upstream API error), `2` for usage errors.

## MCP server

One bin serves both faces. The `mcp` subcommand starts a stdio MCP
server:

```bash
npx -y @reneturcios/visualdiff mcp
```

MCP client configuration:

```json
{
  "mcpServers": {
    "visualdiff": {
      "command": "npx",
      "args": ["-y", "@reneturcios/visualdiff", "mcp"],
      "env": { "SNAPDIFF_API_KEY": "sk_live_..." }
    }
  }
}
```

### Tools

| Tool | Input | What it returns |
| --- | --- | --- |
| `snapdiff_verify_ui_change` | `project`, `page_name`, `after`, `intent` (all required); optional `intent_regions`, `branch`, `threshold`, `match_tolerance_percent`, `ignore_selectors`, `full_page` | A verdict (`pass`, `expected_change_detected`, `unexpected_regression`, `no_change_detected`, or `needs_human_review`), a `next_action`, and a `review_url`. Requires a project with a stored page baseline. |
| `snapdiff_compare_pages` | `after` (required); plus `before`, or `project` + `page_name` for baseline mode; optional `branch`, `threshold`, `ignore_selectors`, `full_page` | A diff percentage plus a highlighted diff image. |
| `snapdiff_capture_screenshot` | `url` (required); optional `full_page`, `dark_mode`, `selector`, `width` | A screenshot of the page. |
| `snapdiff_html_to_image` | `html` (required); optional `css`, `width`, `height` | The rendered image (OG cards, social images, email headers). |

Each tool is sent as a POST to a per-tool endpoint under the base
URL with the API key as a Bearer token. Optional arguments that
are not supplied are omitted from the request body entirely.

## Development

```bash
npm install
npm run build   # tsc -> dist/
npm test        # node test runner over test/*.test.ts, no network (fake fetch fixtures)
npm run dev     # run the CLI from source via tsx
```

## License

MIT — see `LICENSE`.