MCP server
Use Frontguard from inside your IDE. The @frontguard/mcp server exposes list_regressions, get_suggested_fix, accept_baseline, and recent_runs over the Model Context Protocol so Claude Code, Cursor, and Copilot can answer "what regressed on this PR?" without leaving the editor.
Frontguard MCP server
@frontguard/mcp is a Model Context Protocol server that exposes your Frontguard cloud account to in-IDE coding agents. The agent stays inside your editor — no terminal switch, no copy/pasting screenshot URLs, no scraping the PR comment for "which diff was the bad one." It calls four small tools, gets structured JSON back, and applies the suggested CSS patch.
This page covers why we ship an MCP server, the four tools it exposes, how to configure it for the popular editors, how to authenticate, and how to point it at a local cloud-api during development.
Already comfortable with MCP? Skip to Tools or Editor setup.
What is MCP?
The Model Context Protocol is an open spec for plugging tools into LLM-driven agents. Anthropic published the initial draft in November 2024; it has since landed in Claude Code, Cursor, GitHub Copilot, Windsurf, Continue, and the official OpenAI Responses API.
The mental model is simple:
- An MCP server speaks JSON-RPC over stdio (or, optionally, HTTP+SSE). It advertises a list of tools, resources, and prompts.
- An MCP client (the editor / agent) launches the server, lists what it exposes, and lets the LLM call those tools mid-conversation.
For Frontguard, "what regressed on PR 42?" is a question the LLM cannot answer from its training data and shouldn't have to scrape from a GitHub PR comment. With an MCP server we hand it a list_regressions(pr_id) tool — the agent calls it, parses JSON, and reasons over real data.
Why Frontguard ships one
Visual regression results have always been awkward to consume from inside an editor. The agent has to:
- Notice that the PR has a Frontguard check.
- Open the report URL.
- Either screenshot-OCR the report (lossy) or scrape the rendered HTML (brittle).
- Map a diff back to a CSS selector before suggesting a fix.
Two of our competitors (Applitools and Chromatic) shipped MCP servers in H1 2026. Not shipping one in mid-2026 looks dated; shipping one collapses the loop above into one round-trip:
agent → list_regressions(pr_id=42) → [
{ diffId: "run_pr42:/pricing:1280", route: "/pricing",
viewport: 1280, hasSuggestedFix: true, … }
]
agent → get_suggested_fix(diff_id="run_pr42:/pricing:1280") → {
fix: { fixType: "css", patch: ".pricing-card { overflow: hidden; }",
confidence: 0.82, explanation: "Pricing card overflowed at 1280px…" }
}The agent then writes the patch into your stylesheet, and (if you say "looks good") calls accept_baseline(diff_id) to promote the new screenshots.
Install
The server is published as @frontguard/mcp on npm. Most users invoke it via npx so the editor always launches the latest version — but a global install gives you a faster start.
npx -y @frontguard/mcpnpm install -g @frontguard/mcp
frontguard-mcppnpm dlx @frontguard/mcpbunx @frontguard/mcpThe server runs on stdio — your editor launches it as a subprocess and talks JSON-RPC. There is no port to forward and no HTTP server to keep alive.
Editor setup
Claude Code
Add this to ~/.claude/mcp.json for global use, or .mcp.json at your project root for repo-scoped use:
{
"mcpServers": {
"frontguard": {
"command": "npx",
"args": ["-y", "@frontguard/mcp"],
"env": {
"FRONTGUARD_API_KEY": "fg_live_xxx"
}
}
}
}Restart Claude Code. In a chat, run /mcp — you should see frontguard listed with four tools.
Don't commit the API key into a project-scoped .mcp.json. Use ${env:FRONTGUARD_API_KEY} and pass the value through your shell.
Cursor
~/.cursor/mcp.json:
{
"mcpServers": {
"frontguard": {
"command": "npx",
"args": ["-y", "@frontguard/mcp"],
"env": {
"FRONTGUARD_API_KEY": "fg_live_xxx"
}
}
}
}Then open Settings → MCP and toggle Frontguard on. Cursor will spawn the server the next time you start a chat.
GitHub Copilot (VS Code)
VS Code's Copilot Chat reads .vscode/mcp.json per workspace:
{
"servers": {
"frontguard": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@frontguard/mcp"],
"env": {
"FRONTGUARD_API_KEY": "${env:FRONTGUARD_API_KEY}"
}
}
}
}Export FRONTGUARD_API_KEY in your shell profile, restart VS Code, and Copilot will pick the server up.
Other clients
Anything that speaks MCP-over-stdio works the same way. The command is npx -y @frontguard/mcp and the only required env var is FRONTGUARD_API_KEY. Two examples:
- Windsurf:
~/.codeium/windsurf/mcp_config.json, same shape as Claude Code. - Continue: in
~/.continue/config.json, add an entry underexperimental.modelContextProtocolServers.
Tools
Every tool returns JSON inside the MCP text content channel — agents JSON.parse() the body directly.
list_regressions
Returns the visual regressions Frontguard detected for a PR (or a Frontguard run id).
Input
| Field | Type | Required | Notes |
|---|---|---|---|
pr_id | number | string | yes | A GitHub PR number, or a Frontguard run id (run_…). |
repo | string | no | owner/name, disambiguates when the same PR number exists across multiple repos. |
branch | string | no | Reserved for future use. |
Output
{
"count": 1,
"runId": "run_pr42",
"regressions": [
{
"diffId": "run_pr42:/pricing:1280",
"runId": "run_pr42",
"route": "/pricing",
"viewport": 1280,
"status": "regression",
"diffPercentage": 0.087,
"classification": "regression",
"hasSuggestedFix": true,
"reportUrl": "https://api.frontguard.dev/v1/reports/run_pr42",
"prNumber": 42,
"repo": "acme/shop",
"commitSha": "abc1234"
}
]
}When no run matches, the tool returns count: 0 and a notFound.reason string instead of erroring — the agent can surface that as a clean status message ("CI hasn't finished yet").
get_suggested_fix
Returns the AI-generated patch for a single diff. The diff_id is whatever list_regressions handed you.
Input
| Field | Type | Required | Notes |
|---|---|---|---|
diff_id | string | yes | Format: <runId>:<route>:<viewport>. |
Output
{
"diffId": "run_pr42:/pricing:1280",
"runId": "run_pr42",
"route": "/pricing",
"viewport": 1280,
"fix": {
"fixType": "css",
"category": "overflow-fix",
"patch": ".pricing-card { overflow: hidden; }",
"confidence": 0.82,
"explanation": "Pricing card content overflowed at 1280px; clip overflow.",
"target": ".pricing-card"
}
}When the diff exists but has no fix (the run wasn't configured with an AI provider, or the AI declined to suggest one), fix is null and reason carries a human-readable explanation.
accept_baseline
Promotes a run's current screenshots to the new baseline. The cloud-api scopes approvals to the whole run, so you can pass either a diff_id (the run id is extracted from the prefix) or a bare run id.
Input
| Field | Type | Required |
|---|---|---|
diff_id | string | yes |
Output
{ "approved": true, "runId": "run_pr42" }recent_runs
Lists the most recent runs the API key has access to. Useful when the agent wants a quick "what's running, what passed, what failed" snapshot before diving into a specific PR.
Input
| Field | Type | Required | Notes |
|---|---|---|---|
repo | string | no | owner/name filter. |
branch | string | no | Matched against commit SHA prefix or PR number (best-effort). |
limit | number | no | 1–50, defaults to 10. |
Output
{
"count": 2,
"runs": [
{
"runId": "run_pr99",
"status": "completed",
"url": "https://shop.example.com",
"createdAt": "2026-06-11T08:00:00.000Z",
"completedAt": "2026-06-11T08:00:30.000Z",
"durationMs": 30000,
"routesCount": 1,
"regressionsCount": 0,
"baselinesApproved": true,
"reportUrl": "https://api.frontguard.dev/v1/reports/run_pr99",
"repo": "acme/shop",
"prNumber": 99,
"commitSha": "def5678"
}
]
}Authentication
| Variable | Default | Required |
|---|---|---|
FRONTGUARD_API_KEY | none | yes — enforced on each tool call |
FRONTGUARD_API_URL | https://api.frontguard.dev | no |
A few details that catch people out:
- The server starts cleanly without a key. This is intentional — the MCP
tools/listhandshake completes so the editor can show the tool catalog. The missing-key error surfaces only when an agent actually calls a tool, and it surfaces as a tool error with an actionable message, not as a server crash. - The key is read fresh on every tool call. You can rotate
FRONTGUARD_API_KEYin the editor's env without restarting the server. - Trailing slashes on
FRONTGUARD_API_URLare stripped —http://localhost:8787/andhttp://localhost:8787are equivalent.
Generate a key from the Frontguard dashboard. Use a separate key for each editor / machine so you can revoke them independently.
Local-only mode
When you're hacking on @frontguard/cloud-api itself, point the MCP server at your local wrangler dev:
export FRONTGUARD_API_URL=http://localhost:8787
export FRONTGUARD_API_KEY=fg_dev_localkey
npx -y @frontguard/mcpOr, inside a per-editor mcp.json:
{
"mcpServers": {
"frontguard-local": {
"command": "npx",
"args": ["-y", "@frontguard/mcp"],
"env": {
"FRONTGUARD_API_URL": "http://localhost:8787",
"FRONTGUARD_API_KEY": "fg_dev_localkey"
}
}
}
}You can register both frontguard (prod) and frontguard-local side-by-side; the agent will see both and pick the right one based on the question.
Example agent prompts
These prompts exercise the four tools end-to-end. Paste them into Claude Code, Cursor, or Copilot Chat once the server is configured.
"What regressed on PR 42?"
What regressed on PR 42 in acme/shop? Use the Frontguard MCP server.
For each regression, show me the route, the viewport, and whether
there's a suggested fix available.The agent calls list_regressions({ pr_id: 42, repo: "acme/shop" }) and renders the rows into a table.
"Show me the fix for the first one"
For the first regression you listed, fetch the suggested fix from
Frontguard and explain in one paragraph what it does. Don't apply
it yet — I want to read it first.The agent calls get_suggested_fix({ diff_id }), summarizes the patch + explanation, and waits for your confirmation.
"Apply it and accept the baseline"
Looks good. Apply the patch to packages/web/src/pricing.css and then
call accept_baseline so the new screenshot is the canonical one.The agent writes the patch into pricing.css, runs your formatter, and calls accept_baseline({ diff_id }). You see a single confirmation: "Approved baseline for run_pr42."
"What's been failing this week?"
List the last 20 Frontguard runs in acme/shop. Group them by status
and tell me which routes are flaking most often.The agent calls recent_runs({ repo: "acme/shop", limit: 20 }), sorts by regressionsCount, and gives you a per-route breakdown.
"Pre-flight before merging"
Before I merge this PR, check Frontguard for any unapproved baselines
on the head commit. If there are any, summarize them — otherwise just
say "clean."The agent walks list_regressions and either lists the open items or returns "clean." Wire this into your pre-merge prompt template.
Troubleshooting
tools/list is empty
The server is running but failed to register the tools — almost always a Node version issue. Check that you're on Node 18+:
node --versionIf you're on Node 16 the SDK refuses to load (the MCP transport relies on globalThis.fetch).
Every tool returns a FRONTGUARD_API_KEY error
You either forgot the env var or your editor isn't propagating it. Double-check by launching the server in a terminal with the same env:
FRONTGUARD_API_KEY=fg_test_xxx npx -y @frontguard/mcpIf a tool call still fails, your key is invalid — regenerate one from the dashboard.
Frontguard cloud-api error (401)
Your key is valid syntactically but the cloud-api rejected it. Common causes:
- The key was revoked.
- The key belongs to a different team and the PR is in another team's project.
FRONTGUARD_API_URLpoints at the wrong instance (e.g., a stalelocalhost).
Server starts but the editor doesn't see it
Verify the mcp.json path. Each editor has its own:
| Editor | Config path |
|---|---|
| Claude Code | ~/.claude/mcp.json (global) or .mcp.json (project) |
| Cursor | ~/.cursor/mcp.json |
| GitHub Copilot (VS Code) | .vscode/mcp.json |
| Windsurf | ~/.codeium/windsurf/mcp_config.json |
Restart the editor after editing the file; most clients only re-read it on launch.
Security notes
- The server is a thin shell over the public cloud-api — it doesn't touch your disk and doesn't read source files. Everything sensitive (API key, run data) goes through
Authorization: Bearer <key>toapi.frontguard.dev. - Each tool call is a single HTTP round-trip; there is no caching layer that could leak run data across editor sessions.
- The server logs to stderr only so it doesn't corrupt the JSON-RPC stream on stdout. If you see stray output on stdout in your editor's MCP logs, please file an issue.
Source
The server lives in packages/mcp/ of the Frontguard monorepo. Issues and PRs welcome.
Migrate from Lost Pixel
A Lost Pixel migration guide and Lost Pixel alternative. Lost Pixel is archived — move to Frontguard for a free-forever CLI, Playwright rendering, and AI visual classification.
Storybook
Run Frontguard against a Storybook to catch component-level visual regressions — without paying for Chromatic.