Integrations

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:

  1. Notice that the PR has a Frontguard check.
  2. Open the report URL.
  3. Either screenshot-OCR the report (lossy) or scrape the rendered HTML (brittle).
  4. 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/mcp
npm install -g @frontguard/mcp
frontguard-mcp
pnpm dlx @frontguard/mcp
bunx @frontguard/mcp

The 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 under experimental.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

FieldTypeRequiredNotes
pr_idnumber | stringyesA GitHub PR number, or a Frontguard run id (run_…).
repostringnoowner/name, disambiguates when the same PR number exists across multiple repos.
branchstringnoReserved 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

FieldTypeRequiredNotes
diff_idstringyesFormat: <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

FieldTypeRequired
diff_idstringyes

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

FieldTypeRequiredNotes
repostringnoowner/name filter.
branchstringnoMatched against commit SHA prefix or PR number (best-effort).
limitnumberno1–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

VariableDefaultRequired
FRONTGUARD_API_KEYnoneyes — enforced on each tool call
FRONTGUARD_API_URLhttps://api.frontguard.devno

A few details that catch people out:

  1. The server starts cleanly without a key. This is intentional — the MCP tools/list handshake 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.
  2. The key is read fresh on every tool call. You can rotate FRONTGUARD_API_KEY in the editor's env without restarting the server.
  3. Trailing slashes on FRONTGUARD_API_URL are stripped — http://localhost:8787/ and http://localhost:8787 are 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/mcp

Or, 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 --version

If 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/mcp

If 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_URL points at the wrong instance (e.g., a stale localhost).

Server starts but the editor doesn't see it

Verify the mcp.json path. Each editor has its own:

EditorConfig 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> to api.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.

On this page