MCP server + CLI that lets an AI coding agent run FRASER's persona-based usability review against a build, read grounded experience findings, and gate the fix pass/fail
Project description
fraser-gate — the Experience Merge-Gate for AI coding agents
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.
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) orbattle(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.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file fraser_gate-0.2.0.tar.gz.
File metadata
- Download URL: fraser_gate-0.2.0.tar.gz
- Upload date:
- Size: 13.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
55e8e6a185d4150a1d5f747738c8ba5c361f2ed0c9e7481a89df091305cf94dd
|
|
| MD5 |
d73481151f85a1665894bee26d11b11f
|
|
| BLAKE2b-256 |
fed51b506bdf3f442ea9657478d82047393e76e2397056563c47fece58f4c72c
|
File details
Details for the file fraser_gate-0.2.0-py3-none-any.whl.
File metadata
- Download URL: fraser_gate-0.2.0-py3-none-any.whl
- Upload date:
- Size: 16.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9c27fbe87b5ebcc0cc0edb9fda1e384744f0d1174198640c5dfeb7ac60a81ab2
|
|
| MD5 |
af3d3d7a4a347c22523e42eecd8eefa2
|
|
| BLAKE2b-256 |
53333d5a1f54ac06399209ab57bd15b060552e33e5cf81aa309ceb081a131f3c
|