Skip to main content

fraser-gate — the Experience Merge-Gate for AI coding agents

PyPI

Point FRASER at a build and it generates ICP personas from your product, drives a real browser as each one, and hands back grounded, machine-readable usability findings — what real users felt, where they hesitated, misread, or lost trust — then re-runs the identical cast to gate your fix pass | fail. FRASER is the loop's brain and eyes: it finds where real users break and verifies the fix helped. It never writes or merges your code; your agent does that — after validating each finding.

Why this exists: a build can pass its own typecheck + unit tests and still have a user drop off. That gap — experience ≠ correctness — is what the gate catches.

This package is a thin, dependency-light client (an MCP server + a stdlib CLI) that talks to the hosted FRASER API. The real work runs on FRASER's servers.

Setup (one paste)

Add this to your agent's .mcp.json (Claude Code, Cursor, Windsurf, Copilot, …). uvx fetches and runs fraser-gate on demand — nothing to install first:

{
  "mcpServers": {
    "fraser": {
      "command": "uvx",
      "args": ["--from", "fraser-gate", "fraser-gate-mcp"],
      "env": {
        "FRASER_API_KEY": "<your token — see below>",
        "FRASER_API_URL": "https://fraser.lythe.ai"
      }
    }
  }
}

Don't have uvx? It ships with uv (curl -LsSf https://astral.sh/uv/install.sh | sh, or pip install uv). Prefer pip? pipx install fraser-gate (or uv tool install fraser-gate) puts fraser-gate-mcp and fraser-gate on your PATH; then set "command": "fraser-gate-mcp" with no args.

Get your token

Sign up at fraser.lythe.ai, then mint a long-lived MCP token from the API (two calls — works anywhere, no dashboard needed):

# 1) log in → a short-lived session token   (or POST /api/auth/signup to make an account)
SESSION=$(curl -s -X POST https://fraser.lythe.ai/api/auth/login \
  -H 'Content-Type: application/json' \
  -d '{"email":"you@example.com","password":"your-password"}' \
  | python -c "import sys,json;print(json.load(sys.stdin)['token'])")

# 2) exchange it for the long-lived MCP token you paste into .mcp.json
curl -s -X POST https://fraser.lythe.ai/api/auth/mcp-token -H "Authorization: Bearer $SESSION"
# → {"token":"eyJ…  ← this is FRASER_API_KEY", "base_url":"https://fraser.lythe.ai"}

Signed in with Google (no password)? Grab the session token from your browser after logging in (it's the bearer token the web app sends), then run step 2. The minted token is a normal session JWT with a long TTL — revoke it by rotating your account, same as any session.

The loop

your agent builds + deploys ─▶ fraser_start_review(target_url)   → {run_id, project_id, flow_id}
                                     │  (async, minutes — poll)
                              fraser_get_status(run_id) ─▶ complete
                              fraser_get_findings(run_id)
                                     │  → findings[] {persona, sentiment, what_they_wish, evidence, screenshot, finding_id} + guidance + fix_prompt
   your agent validates + fixes ◀────┘   (FRASER never touches code)
                                     ▼
                              fraser_validate_fix(project_id, flow_id, baseline_run_id, new_url)
                              fraser_get_validation(run_id, baseline_run_id)
                                     │  → {gate: pass|fail, per-finding {fixed|still_present|regressed}, new_findings}
                              gate=fail ⇒ don't merge · gate=pass ⇒ ship

Each finding carries a stable finding_id (content-addressed, survives re-wording) so the gate maps fix→verify on the same defect across runs — no false "fixed" when the model just renames a bug.

Run it end-to-end (autonomous)

Every tool response carries a next_step string, so an agent drives the whole loop without a playbook:

  1. fraser_start_review(target_url) — no credentials needed up front: if the app is gated, FRASER gives each tester a disposable inbox and signs up on its own (the image-captcha + emailed code are handled for it).
  2. Poll fraser_get_status until terminal. If it returns a blocked object, FRASER couldn't get in on its own — blocked.ask says what's needed; ask the user for a test login and re-run fraser_start_review with auth_notes (or, for a bot_wall, retry with bypass_bot_protection).
  3. fraser_get_findings — validate each finding against the product yourself, make the smallest change that resolves it, and redeploy the preview.
  4. fraser_validate_fix → fraser_get_validation — re-runs the identical cast and gates the fix:
    • gate: pass ⇒ ship it — commit and push to main, or open a PR to main, per your repo's usual flow.
    • gate: fail ⇒ don't merge; fix the still_present/regressed findings and validate again.

Tools (6)

Tool Use
fraser_start_review(target_url, mode?, objective?, steps?, success_criteria?, personas?, context?, num_testers?, auth_notes?) Start a usability review. Async — returns a run_id. Steer the flow (objective/steps) or the cast (free-text personas).
fraser_get_status(run_id) Poll until terminal (complete/failed/timed_out). Runs take minutes.
fraser_get_findings(run_id) Persona-grounded usability findings + validate-first guidance + fix_prompt. Validate each before changing code.
fraser_validate_fix(project_id, flow_id, baseline_run_id, target_url) Re-run the identical cast on the fix. Async.
fraser_get_validation(run_id, baseline_run_id) The pass/fail gate verdict.
fraser_cancel(run_id) Stop a run.

It takes minutes. FRASER generates personas from your app, then drives a real browser as each one. Start the review, do other work, and poll fraser_get_status every few seconds.

Knobs (defaults are good — omit everything and you get the grounded ICP cast on open exploration):

  • mode — visual (persona usability review, default — this is the product) or battle (adversarial defect sweep).
  • personas — free-text descriptions to test AS specific people (["a 55-year-old first-time user on an old Android", "a skeptical enterprise buyer"]); omit for the grounded ICP cast.
  • objective / steps / success_criteria — pin a specific flow instead of open exploration.
  • context — one line on what the product is (anchors personas for a new/thin app).
  • auth_notes — app login creds if the core experience is behind a login. Ask the user for a test account and pass them here; secret, never echoed or logged.
  • num_testers — 0 = the full grounded cast.

localhost just works

Point a review at http://localhost:3000 and the MCP opens an ephemeral public tunnel automatically (via cloudflared, downloaded once) so the hosted engine can reach your machine. You set nothing up; the tunnel closes when the run finishes.

CLI (same loop, for CI without MCP)

The fraser CLI is stdlib-only — a CI job needs nothing but Python and this package:

export FRASER_API_KEY=<token> FRASER_API_URL=https://fraser.lythe.ai
fraser review https://myapp-pr42.vercel.app --wait          # prints findings JSON
fraser validate <project_id> <flow_id> <baseline_run_id> <new_url> --wait   # exits non-zero on a FAIL gate

fraser validate --wait exits 3 on a fail gate, so a CI step can block the merge on it directly.

Local development of this package

git clone <repo> && cd gate_client
pip install -e .
python -m fraser_gate.mcp_server     # stdio MCP server
python -m fraser_gate.cli --help     # the CLI

Publishing a new version: see PUBLISHING.md.


MIT licensed. FRASER is a product of Lythe.

Release files for fraser-gate 0.3.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for fraser-gate 0.3.0
File Size Uploaded
fraser_gate-0.3.0.tar.gz 14.1 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for fraser-gate 0.3.0
File Interpreter ABI Platform
fraser_gate-0.3.0-py3-none-any.whl Python 3 none any Details

Total release size: 31.3 kB

Release files / fraser_gate-0.3.0.tar.gz

Download URL fraser_gate-0.3.0.tar.gz
Size 14.1 kB
Tags Source
SHA-256 checksum
How to use checksums
9222665bad60a702ff1a64b72682aa597bbeee377189d53af82495999cb4db3c
BLAKE2b-256 checksum
How to use checksums
f85855b9286cc4ed09faaaedb107c78cb3db9e99ae1516a7b20075f55affe1c1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.12.13

Release files / fraser_gate-0.3.0-py3-none-any.whl

Download URL fraser_gate-0.3.0-py3-none-any.whl
Size 17.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
6b7e058bdf90582a6c61440fdcdd29784e65c5832bee119e6f78065b219a51e1
BLAKE2b-256 checksum
How to use checksums
96b5bea1b46b9409a0d3a04787d68470251431e6d71e4313a1814a5b739388f3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.12.13

Release history Release notifications | RSS feed

0.11.1

2 release files

0.11.0

2 release files

0.10.2

2 release files

0.10.1

2 release files

0.10.0

2 release files

0.9.0

2 release files

0.8.0

2 release files

0.7.0

2 release files

0.6.0

2 release files

0.5.0

2 release files

0.4.0

2 release files

This release

0.3.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page