Server data from the Official MCP Registry
Catch bad animations before they ship. Deterministic motion audit + vision-LLM design review.
Catch bad animations before they ship. Deterministic motion audit + vision-LLM design review.
MotionLint is a well-structured CLI/MCP tool for animation and UX auditing with reasonable security posture. The codebase properly handles API keys through environment variables and `.env` files rather than hardcoding. However, there are several moderate-severity concerns: the MCP server accepts arbitrary URLs without validation, subprocess calls to Playwright lack comprehensive shell-injection protection, sensitive environment variables could be logged in error scenarios, and there's insufficient input validation on user-supplied file paths. The permissions granted (file I/O, network access, environment variables) are appropriate for the tool's stated purpose, but the implementation has edge cases that could be exploited. Supply chain analysis found 3 known vulnerabilities in dependencies (0 critical, 3 high severity). Package verification found 1 issue.
3 files analyzed · 12 issues found
Security scores are indicators to help you make informed decisions, not guarantees. Always review permissions before connecting any MCP server.
This plugin requests these system permissions. Most are normal for its category.
Set these up before or after installing:
Environment variable: ANTHROPIC_API_KEY
Environment variable: OPENAI_API_KEY
Environment variable: GOOGLE_API_KEY
Environment variable: OPENAI_BASE_URL
Add this to your MCP configuration file:
{
"mcpServers": {
"io-github-bobaba99-motionlint": {
"env": {
"GOOGLE_API_KEY": "your-google-api-key-here",
"OPENAI_API_KEY": "your-openai-api-key-here",
"OPENAI_BASE_URL": "your-openai-base-url-here",
"ANTHROPIC_API_KEY": "your-anthropic-api-key-here"
},
"args": [
"-y",
"motionlint"
],
"command": "npx"
}
}
}From the project's GitHub README.
Score any page's animation quality in one command. No API key, no config.
npx motionlint audit http://localhost:3000 --open
MotionLint measures the motion your app actually ships — durations, easing curves, stagger intervals, exit timing, reduced-motion support — and scores it against a published set of animation standards. Ease-in on a dropdown, a 600ms modal, a card that scales from 0, hover motion that fires on touch: all caught, all with the measured value and a concrete fix.
The audit is free and offline. Add an API key and MotionLint also does vision-LLM design review — multi-viewport screenshots and 50ms frame bursts of real user journeys, judged by a model and handed back to your coding agent as ranked findings. It runs as an MCP server inside Claude Code and Cursor.
AI coding agents read JSX, HTML, and CSS — they're blind to what the user actually sees, clicks, and watches animate. Rules in a prompt tell the agent what should happen; nothing checks what did. Modals that should slide in just pop; loading states get omitted; focus rings disappear. Code review can't catch any of this before merge, because none of it is visible in the diff.
MotionLint closes that loop: it measures the running app and feeds the verdict back.
| MotionLint | Visual regression tools (Percy, Chromatic, Playwright snapshots) | AI design generators (v0, Galileo, Claude Design, Stitch) | |
|---|---|---|---|
| Deterministic motion audit | 13 checks, measured from the live page — no API key, $0 | ✗ | ✗ |
| Multi-viewport UX review | ranked findings across 12 dimensions | pixel diffs only | generates new layouts from prompts |
| Animation review | 50ms frame bursts via CDP screencast → contact sheet → LLM | ✗ | ✗ |
| Live animation tuning | Shadow-DOM previews + sliders + Claude Code export | ✗ | generates new motion, doesn't tune what's there |
| Native MCP server | ✓ stdio MCP for Claude Code / Cursor | ✗ | varies |
| CI gate | ✓ SARIF + exit codes for code scanning | ✓ image diff thresholds | ✗ |
| Validated quality | 100% recall · 0% FPR on 24-fixture stress test | n/a | n/a |
The conceptual gap MotionLint closes: visual-regression tools catch what changed but not whether the new pixels are good; AI design tools generate from scratch but don't review what's already running. MotionLint reviews live behavior with a vision LLM and feeds the verdict back into the coding loop.
npx playwright install chromium # one-time per machine (~300MB)
npx motionlint audit http://localhost:3000 --open
That's the whole setup for the audit. It's deterministic, runs offline, costs nothing, and works on any URL you can load — your dev server, a staging deploy, or someone else's site. Requires Node 18+.
The rules it checks are published in docs/STANDARDS.md — read them before you install anything.
Set one API key (ANTHROPIC_API_KEY, OPENAI_API_KEY, or GOOGLE_API_KEY — or run Ollama locally for free) and three more commands unlock:
npm install -g motionlint
# Multi-viewport UX review of a page → ranked findings across 12 dimensions.
motionlint review http://localhost:3000
# Animation review of a scripted user journey → frame contact sheet + report.
motionlint flow --spec flows/signup.json
# Interactive HTML tuner — every animation on the page, with live sliders.
motionlint tune http://localhost:3000
claude mcp add motionlint -- npx -y motionlint mcp
# CI mode — non-zero exit on critical issues, SARIF output for code scanning.
motionlint review https://staging.acme.dev --ci --threshold critical --format sarif -o ux.sarif
# Polished, shareable HTML review with embedded screenshots + before/after fixes.
motionlint review http://localhost:3000 --format html -o review.html
# Review every route the site knows about (sitemap.xml + Next.js app/ directory).
motionlint review http://localhost:3000 --discover-routes
# Storybook mode — discover stories from /index.json, review each story iframe as its own route.
motionlint review http://localhost:6006 --storybook
# Color-scheme sweep — light and dark modes, plus Windows High Contrast.
motionlint review http://localhost:3000 --schemes --forced-colors --format html -o review.html
# Interaction affordances — grid each element's default/hover/focus/active states.
motionlint review http://localhost:3000 --state-grid
# Agent focus — keep only the top 5 findings, and only ones not seen in prior runs.
motionlint review http://localhost:3000 --max-findings 5 --new-only
# Before/after comparison — PR preview vs. production baseline.
motionlint review https://pr-123.preview.example.com --against https://prod.example.com
# Reviewer focus — cap the SARIF upload at 10 annotations per report.
motionlint review https://staging.acme.dev --format sarif -o ux.sarif --max-pr-annotations 10
# Pick a provider explicitly (auto-detect picks the first reachable one).
motionlint review http://localhost:3000 --provider anthropic --model claude-sonnet-5
# Track provider quality across runs + teach the reviewer from eval misses.
motionlint eval --provider anthropic --evolve
Package on npm: motionlint.
Sample terminal output for a flow review:
$ motionlint flow --spec flows/signup.json --provider anthropic
→ Running flow "signup-happy-path" against http://localhost:3000/signup (11 steps, 50ms intervals × 750ms window)
provider: anthropic (claude-sonnet-5)
capturing flow…
✓ step 1: 16 frames ✓ step 2: 16 frames ✓ step 3: 16 frames …
captured 176 frames in 31s
contact sheet → .motionlint/flows/signup-happy-path-…png
analyzing flow…
report → .motionlint/flows/signup-happy-path.md
Score: 4/10 · 3 critical findings
[critical] interaction — input focus rings missing across steps 2/4/6
[critical] interaction — submit button has no pressed state
[critical] loading_state — 1.4s wait with no spinner during submit
A multi-route TS animation showcase ships in demo/ — covering Motion One, GSAP, anime.js, @formkit/auto-animate, and lottie-web — including a cat-themed one-pager that exercises every MotionLint capability in a single URL:
node demo/server.mjs # http://localhost:4173
motionlint review http://localhost:4173/cat --record --embed
motionlint flow --spec flows/signup.json
motionlint tune http://localhost:4173/dashboard
Routes available: /, /pricing, /signup, /dashboard, /loading, /cat. Reports go to .motionlint/reports/, screenshots to .motionlint/screenshots/, videos to .motionlint/videos/.
MotionLint auto-loads a .env file from the working directory at startup:
# .env (gitignored)
ANTHROPIC_API_KEY=sk-ant-...
# or
OPENAI_API_KEY=sk-...
# or
GOOGLE_API_KEY=...
# or run a local Ollama (no key needed) — auto-detected on http://localhost:11434
Real environment variables take precedence over .env. With no key set and no Ollama running, MotionLint falls back to a deterministic mock provider so the full pipeline (capture → analysis → report) still runs end-to-end for smoke tests.
MotionLint auto-detects in this order: Ollama (local) → Anthropic → OpenAI → Google. The first one with a working API key (or running service) wins. Override with --provider <name> and --model <id>. See Providers in depth for the per-provider quality scorecard and how to pick.
Everything below is for readers who want to understand how MotionLint works under the hood, pick the right provider for their workflow, or wire it into CI.
The flow-review pipeline was stress-tested across 12 popular web-app animation patterns × 2 variants (24 fixtures total) — staggered entrances, hover/press/focus, modal entrances, loading skeletons, form errors, toasts, counter ramps, multi-animation dashboards, modal-with-content stagger, rich form feedback (focus + press + spinner + success), and scroll-driven animations (progress bar + IntersectionObserver reveal + parallax).
Run on 2026-04-29 against the latest model from each major provider plus three Ollama-served local models:
| Provider · model | Recall (broken caught) | FPR (clean flagged) | Score gap | Wall time¹ |
|---|---|---|---|---|
| OpenAI · gpt-5.5 | 100% (12/12) | 0% (0/12) | +4.2 | 11.6 min |
| Anthropic · claude-opus-4-7 | 100% (12/12) | 8% (1/12) | +4.2 | 9.6 min |
| Anthropic · claude-sonnet-4-6 | 100% (12/12) | 8% (1/12) | +5.1 | 14.2 min |
| Ollama · nemotron3:33b (local, 27 GB) | 100% (12/12) | 25% (3/12) | +5.1 | 7.7 min |
| Google · gemini-3.1-pro-preview | 92% (11/12) | 0% (0/12) | +5.5 | 5.3 min |
| Ollama · gemma3:4b (local, 3.3 GB) | 83% (10/12) | 17% (2/12) | +1.1 | 3.6 min |
| Ollama · glm-ocr (local, 2.2 GB) | 33% (4/12) | 33% (4/12) | +0.3 | 11.3 min |
¹ Local-model wall times measured on an Apple M4 Max (128 GB unified). Cloud-provider times reflect API latency, not local compute.
Read this as: six of the seven model combinations are shippable for at least one workflow. OpenAI gpt-5.5 remains the only provider with 100% recall AND 0% FPR — the safest hard CI gate. The standout new result: nemotron3:33b is the first 100%-recall local model, ties Sonnet 4.6 on score gap, runs entirely on-device, and costs $0 — but its 25% false-positive rate (3 clean fixtures flagged critical) means it's better as an iteration-loop reviewer than a merge-blocker on a powerful local machine. Both Anthropic models match on recall and flag the same one clean fixture; Sonnet 4.6 is the better Anthropic value (~5× cheaper per token than Opus, equivalent quality on this test). Gemini 3.1 Pro is ~3× faster and 5× cheaper than Sonnet, at the cost of one missed broken pattern. glm-ocr is too weak for this task (33% recall, 33% FPR — barely above coin-flip) and is documented here only so future readers don't try the same path.
Full per-provider scorecards in .motionlint/stress/ after running scripts/run-all-benchmarks.mjs.
| Provider | Default model | Setup | Quality (24 fixtures) | Cost per review¹ |
|---|---|---|---|---|
openai | gpt-5.5 | OPENAI_API_KEY=… | 100% recall · 0% FPR · +4.2 gap | ~$0.005 |
anthropic | claude-opus-4-7 | ANTHROPIC_API_KEY=… | 100% recall · 8% FPR · +4.2 gap | ~$0.025 |
anthropic | claude-sonnet-4-6 | ANTHROPIC_API_KEY=… | 100% recall · 8% FPR · +5.1 gap | ~$0.005 |
ollama | nemotron3:33b (local, 27 GB) | ollama serve + ollama pull nemotron3:33b | 100% recall · 25% FPR · +5.1 gap | $0 |
google | gemini-3.1-pro-preview | GOOGLE_API_KEY=… | 92% recall · 0% FPR · +5.5 gap | ~$0.001 |
ollama | gemma3:4b (local, 3.3 GB) | ollama serve + ollama pull gemma3:4b | 83% recall · 17% FPR · +1.1 gap | $0 |
ollama | glm-ocr (local, 2.2 GB) | ollama serve + ollama pull glm-ocr | 33% recall · 33% FPR · +0.3 gap | $0 |
mock | heuristic stub | (auto fallback) | n/a — deterministic stub for CI smoke tests | $0 |
¹ Order-of-magnitude estimate per static review at the default 2 viewports. Flow review is one composite image per flow but the contact sheet is bigger. The Animation Tuner makes 0 LLM calls.
gpt-5.5 — the only provider that hit 100% recall and 0% FPR.claude-sonnet-4-6 ties Opus 4.7 on recall and FPR (both 100% / 8%) and is 5× cheaper per token. Pass --model claude-opus-4-7 for the Opus tier; otherwise Sonnet 4.6.nemotron3:33b — first local model at 100% recall, ties Sonnet 4.6 on score gap. 25% FPR keeps it out of hard CI gates, but it's the right pick for iteration loops and air-gapped reviews when you have ≥32 GB unified memory and don't want to pay per-call.gemini-3.1-pro-preview — 5× cheaper than Anthropic Sonnet, ~3× faster, 0% FPR, missed one broken pattern. Run the stress test on your own flows before relying on it as a hard merge gate.gemma3:4b (3.3 GB). 83% recall, 17% FPR. Use when nemotron3:33b doesn't fit in memory or when you need faster turn-around per fixture.glm-ocr is OCR-tuned and too weak for general design review (33% recall / 33% FPR).Every command honours --provider and --model:
motionlint review http://localhost:3000 --provider openai --model gpt-5.5
motionlint flow --spec flows/signup.json --provider google --model gemini-3.6-flash
motionlint review http://localhost:3000 --provider ollama --model llava:13b
To compare a new provider against the same 24-fixture stress test:
node -e "
import('./dist/config/env.js').then(async ({ loadEnv }) => {
loadEnv();
const { runStress, renderStressMarkdown } = await import('./dist/flow/stress.js');
const { writeFile, mkdir } = await import('node:fs/promises');
const { resolve } = await import('node:path');
await mkdir('.motionlint/stress', { recursive: true });
const r = await runStress({
stressPath: resolve('eval/animation-stress.json'),
fixturesDir: resolve('eval/animation-fixtures'),
artifactDir: resolve('.motionlint/stress'),
provider: 'YOUR_PROVIDER', // 'openai' | 'google' | 'ollama'
});
await writeFile('.motionlint/stress/SCORECARD.md', renderStressMarkdown(r), 'utf8');
console.error('Recall:', (r.broken_recall*100).toFixed(0)+'%, FPR:', (r.good_false_positive_rate*100).toFixed(0)+'%, gap:', r.avg_score_gap.toFixed(1));
});
"
Open .motionlint/stress/SCORECARD.md for the per-pattern breakdown.
motionlint flow worksStatic screenshots can't tell you whether a flow's animations and interaction states work — only whether the final frame looks right. motionlint flow fills that gap.
Given a scripted user journey, it:
Page.captureScreenshot JPEG, ~8ms per shot). 50ms is half the human visual-detection threshold and below the industry-typical 100ms minimum animation interval — short animations like 100ms button presses get caught with 2-3 mid-state frames. Every interaction burst is also pixel-diffed for input→feedback latency — interactions with no visible acknowledgment within the burst window are flagged deterministically.A single recording can capture and analyze multiple concurrent animations. Validated on:
The LLM correctly identifies which animations are broken without false-flagging the working ones — see the validated-quality table.
For sites with scroll-linked animations, scroll <px> steps animate the scroll over the burst window via requestAnimationFrame so each frame shows progressive scroll position and the LLM sees the timing as the page scrolls.
# Inline DSL — semicolon-separated steps
motionlint flow \
--url http://localhost:3000 \
--steps "navigate /signup; click input#email; type input#email=ada@example.com; click button[type=submit]; wait 2000; capture \"post-submit\"" \
--name signup-happy-path
# Or load a structured spec with expected_animations[] hints
motionlint flow --spec flows/signup.json --provider anthropic
# Pass team motion preferences (philosophy + inspirations + accepted defaults)
# Embedded into the prompt AND the report's CC handoff block.
motionlint flow --spec flows/signup.json --preferences flows/preferences.md
# Tighten the interval below 50ms for fine-grained timing review
motionlint flow --spec flows/signup.json --interval 30 --burst-ms 600
# Auto-detect: scan the page's animations, pick an interval that captures
# the shortest one with 4 frames inside it (clamped to [20, 100]ms).
motionlint flow --spec flows/signup.json --auto-interval
| Action | Form | Notes |
|---|---|---|
| navigate | navigate /pricing | path or full URL |
| click | click button#start | CSS selector |
| hover | hover .feature | CSS selector |
| type | type input#email=ada@example.com | selector=value |
| press | press Enter | keyboard key |
| scroll | scroll 800 | pixels; animates over the burst window |
| wait | wait 500 | ms |
| capture | capture "post-submit" | take an explicit burst with optional label |
Defaults: a frame burst is taken after every interaction. Pass --no-implicit-bursts to only burst on explicit capture steps. Pass --no-record to skip video.
Three ready-to-run sample flows ship in the repo: flows/signup.json, flows/loading-state.json, and flows/preferences.md.
Most AI coding tools generate animations from scratch. The Tuner lets you tune the animations that are already running on your page, in real time, and hand the changes back to your coding agent as a structured prompt.
motionlint tune http://localhost:3000 --open
This:
@keyframes running on the page..motionlint/tuner/index.html (auto-opens with --open):
changes[] JSON block. Paste that into CC and it edits your codebase to apply the new parameters.$ motionlint tune http://localhost:3000
→ Capturing animations on http://localhost:3000…
detected 15 animation(s)
tuner → /Users/you/proj/.motionlint/tuner/index.html
open with: file:///Users/you/proj/.motionlint/tuner/index.html
motionlint auditMotionLint encodes Emil Kowalski's design-engineering standards as a deterministic linter — no vision model, no API key, no cost. motionlint audit instruments the page, reads the real timing/easing/transform values every animation is running, and grades them:
| Category | What it catches | The standard |
|---|---|---|
| Easing | ease-in on UI; weak built-in curves on deliberate entrances | Entering/exiting → strong ease-out cubic-bezier(0.23, 1, 0.32, 1); never ease-in |
| Duration | UI motion over the 300ms ceiling (modals/drawers get 200–500ms) | A 180ms transition feels snappier than a 400ms one; exits ~20% faster |
| Physicality | scale(0) entrances | Nothing appears from nothing — start from scale(0.95) + opacity: 0 |
| Performance | transition: all, animating layout properties, stray infinite loops | Animate transform and opacity only — they skip layout/paint |
| Cohesion | Hand-rolled easing-curve sprawl; stagger intervals outside the 30–80ms band | Curves and durations should live as shared tokens; grouped entrances stagger 30–80ms apart |
| Duration (pairs) | Exits that aren't faster than their entrance (fadeIn 300ms / fadeOut 300ms) | Exits run ~20% faster than the matching entrance |
motionlint audit http://localhost:3000 --open # polished HTML report, scored 0–100
motionlint audit http://localhost:3000 --json audit.json --ci # machine-readable; non-zero on critical
Add --layout to also lint layout (tap targets, text size, contrast, overflow) from live DOM measurements — still deterministic, still no API key.
Add --watch [dir] to re-run the audit on file changes under [dir] (default: cwd) and print the score with a delta after each run — a live readout while you iterate. Recursive watching requires macOS, Windows, or Linux with Node 20+.
The report pairs every finding with a before → after panel; easing findings render a live cubic-bezier curve comparison so the fix is visible, not just described. The same standards feed the flow review prompt (so vision findings cite concrete rules) and appear inline in the Animation Tuner.
MotionLint ships an MCP server over stdio so an LLM agent can drive it directly inside a chat. The motionlint mcp subcommand boots it; the agent client spawns the process when a tool is called.
Published-npm version (recommended):
claude mcp add motionlint -- npx -y motionlint mcp
Local checkout (handy while developing):
claude mcp add motionlint -- node /absolute/path/to/motionlint/dist/index.js mcp
After registration:
claude mcp list — motionlint should show as running or available..env file in the project directory you're working from — MotionLint auto-loads it on startup.npx playwright install chromium if you haven't already.Then in Claude Code:
"Use motionlint to review the local app at mobile and desktop and tell me the top 3 issues to fix."
"Run motionlint review_flow on
http://localhost:3000/signupwith stepsclick input#email; type input#email=test@test.com; click button[type=submit]; wait 2000; captureand check the animations.""Run motionlint tune_animations on
http://localhost:3000/pricing— I want to fine-tune the card hover animations."
| Tool | What it does |
|---|---|
review_url(url, viewports?, provider?, model?, wait_for?, record?, format?, max_findings?, max_pr_annotations?, new_only?) | Static UX review of a URL at multiple viewports. Returns a markdown / JSON / SARIF report. |
review_routes(base_url, routes, viewports?, ..., max_findings?, max_pr_annotations?, new_only?) | Same review across multiple routes of one app. |
review_flow(url, steps?|spec_path?, preferences_path?, provider?, ...) | Animation/interaction review of a scripted user journey. Returns a flow report with the structured CC handoff block. |
tune_animations(url, viewport_*?, settle_ms?, output?) | Detects every animation on a page and writes an interactive HTML tuner. Returns the file path. |
get_latest_report(format?) | Returns the most recent review/flow report content. |
Resources: motionlint://reports/latest — the most recent report content.
Before deploying or sharing the MCP server with other users:
npm run build then verify dist/index.js exists. Without this, motionlint mcp won't start.npx playwright install chromium. The postinstall hook reminds you, but it's not enforced (we don't auto-download a 300 MB binary on npm install)..env file in the working directory the MCP client launches from.npm test includes an MCP smoke test that boots the server, lists tools, and asserts the expected tool surface..env is gitignored; .env.example should be a placeholder. Worth a final git diff --cached | grep -i 'sk-\|api_key' before pushing.claude mcp list that the server shows up and isn't erroring at startup.# .github/workflows/ux.yml
- run: npm ci
- run: npx playwright install chromium
- run: npx motionlint review $STAGING_URL --ci --threshold critical --format sarif -o ux.sarif
- uses: github/codeql-action/upload-sarif@v3
with: { sarif_file: ux.sarif }
MotionLint exits with 1 when critical issues exceed the configured threshold (failOnCritical) — wire it as a status check.
Captures:
--no-full-page.--record (Playwright .webm).click, hover, type, scroll, wait.localStorage, and a beforeNavigate script — all configurable in .motionlintrc.json.Analyzes: each screenshot is sent to a vision model with an opinionated UX-review system prompt covering twelve dimensions (hierarchy, spacing, alignment, typography, color, contrast, responsiveness, interaction, content, navigation, consistency, loading_state). For each issue the model returns:
{
"category": "hierarchy",
"severity": "critical | warning | suggestion",
"location": "above-the-fold hero",
"issue": "Primary CTA blends into the background gradient.",
"why_it_matters": "Users miss the conversion path on first scroll.",
"fix": "Increase background contrast or use a solid surface behind the button."
}
Override the prompt with --rules path/to/your-design-rules.md to inject project-specific heuristics.
Every review capture also takes a DOM snapshot: notable elements (headings, CTAs, inputs) get stable refs (E1, E2, …) with measured pixel rects, listed in the prompt so the model can ground a finding with "element_ref": "E3". Cited refs resolve back to their rects and are drawn as severity-colored bounding boxes on the screenshot in the HTML report (and reported as Where: E3 at (x, y) w×h in markdown). Refs the page never listed are dropped — the model can't annotate what it wasn't shown.
With --format html the findings render as a single shareable report — score ring, per-dimension breakdown, and an issue → fix panel per finding with the annotated screenshot:
Drop a .motionlintrc.json in your repo root (or use motionlint.config.js / a "motionlint" key in package.json):
{
"provider": "auto",
"fallbackProvider": "anthropic",
"fallbackModel": "claude-sonnet-5",
"viewports": {
"mobile": { "width": 375, "height": 812 },
"tablet": { "width": 768, "height": 1024 },
"desktop": { "width": 1440, "height": 900 }
},
"defaultViewports": ["mobile", "desktop"],
"waitFor": "networkidle",
"waitTimeout": 10000,
"screenshotDir": ".motionlint/screenshots",
"videoDir": ".motionlint/videos",
"reportDir": ".motionlint/reports",
"rules": null,
"record": false,
"maxFindings": null,
"maxPrAnnotations": null,
"memory": {
"enabled": true,
"path": ".motionlint/memory.json",
"baseline": ".motionlintignore",
"newOnly": false
},
"resources": { "maxConcurrentReviews": null, "providerCallsPerMinute": null, "maxTokensPerRun": null },
"ci": { "threshold": "warning", "failOnCritical": true },
"auth": { "cookies": null, "localStorage": null, "beforeNavigate": null }
}
Re-running review on the same routes used to surface the same findings every run. Two mechanisms keep the output focused:
--max-findings N (or maxFindings in config) keeps only the top N findings per run, severity-ordered, so an agent works on what matters most first. The report's Omitted line says how many were capped.--max-pr-annotations N (or maxPrAnnotations in config; SARIF only) emits at most N results per report, severity-ordered, so a code-scanning upload doesn't flood a PR with annotations. The dropped count lands in the SARIF run's omitted_by_pr_cap property.resources.maxConcurrentReviews bounds how many reviews run at once in one process (an MCP server fielding several agents in flight), and resources.providerCallsPerMinute is a process-wide sliding-window ceiling on vision-LLM calls (provider quota / spend control; also applies to flow reviews, where each --consistency sample counts). Both default to unlimited; both are config-only. Note they compose: a review holding a concurrency slot also waits out the rate limiter, so tight values on both multiply latency.Tokens: line in reports, token_usage in SARIF run properties). --max-tokens N (or resources.maxTokensPerRun in config) sets a per-run token budget: once the running total crosses it, remaining viewports are skipped and the report lists them under skipped_viewports. Providers that report no usage still count calls but consume no budget..motionlint/memory.json; recurring findings are annotated with seen in N prior runs rather than silently dropped. Opt into deltas-only with --new-only. To permanently wave off a finding, copy its id into .motionlintignore (one hash per line, # comments and trailing notes allowed). Disable everything with --no-memory.SARIF output carries the finding id as a partialFingerprint, so GitHub code scanning dedups the same finding across runs and PRs natively.
Concurrent reviews of the same project are safe: the memory store is updated under a stale-aware file lock (memory.json.lock), so parallel runs don't clobber each other's recorded sightings. A wedged lock never fails a review — after a short wait the run warns and proceeds without it.
motionlint review https://pr-123.preview.example.com --ci --threshold critical in CI; warning-or-worse blocks the merge until you've at least seen the issues.motionlint review https://prod.example.com --format sarif -o ux.sarif) and surface SARIF in your code-scanning dashboard so production regressions get caught the morning after.motionlint flow runs a scripted user journey through Playwright like a human would, captures frame bursts at every interaction, records video, and asks the LLM to review the animation behavior across the captured frames.src/
capture/ Playwright capture (screenshot, mosaic, DOM snapshot) + interaction sequences
providers/ Vision LLM providers (ollama, anthropic, openai, google, mock) + self-consistency wrapper
analysis/ Rubric-style UX prompt + JSON parser + rule injection
report/ Markdown / JSON / SARIF report generators
eval/ Tiered eval harness (L1/L2/L3 fixtures, scorer, runner, report)
flow/ Flow runner — spec parser, capture orchestrator, animation-aware report
tuner/ Animation Tuner — extractor, instrumentation script, Shadow-DOM render
mcp/ MCP server for Claude Code
cli/ Commander.js commands + terminal output
config/ cosmiconfig loader + .env loader
demo/ TS animation showcase used as a review target
flows/ Sample flow specs (signup, loading-state) for `motionlint flow`
eval/fixtures/ Labelled HTML pages with seeded UX faults at three complexity levels
test/ Node test runner unit + integration tests
v0.1 (this release) — shipped:
review, flow, tune + MCP server (motionlint mcp).next_actions[] JSON for downstream LLM coding tools.--preferences) embedded into the LLM rubric and the CC handoff block.--auto-interval) that picks an inter-frame interval based on the shortest animation detected on the page.v0.2 (in progress) — shipped so far:
--max-tokens / resources.maxTokensPerRun; Tokens: line in every report).--discover-routes: sitemap.xml + Next.js app directory).--state-grid: default/hover/focus/active per element, one labeled image)..motionlint/eval-history.json).next_actions (eval --evolve → learned heuristics in review prompts).v0.2 (next):
motionlint-action).MotionLint stands on other people's work:
motionlint audit, the tuner's easing presets, and the flow-review rubric are distilled from his design-engineering writing and his animations.dev course. His open-source UI libraries — sonner (toasts) and vaul (drawers) — are living reference implementations of the motion these rules describe. MotionLint is an independent project, not affiliated with or endorsed by Emil.MIT © Resila Technologies Inc.
Be the first to review this server!
by Modelcontextprotocol · Developer Tools
Web content fetching and conversion for efficient LLM usage
by Modelcontextprotocol · Developer Tools
Read, search, and manipulate Git repositories programmatically
by Toleno · Developer Tools
Toleno Network MCP Server — Manage your Toleno mining account with Claude AI using natural language.