{
  "markdown": "# mcp-ai-slop-checker\n\nAn MCP server that tells your model when its own writing sounds like AI.\n\n[![MCP Registry](https://img.shields.io/badge/MCP%20Registry-io.github.parweb%2Fai--slop--checker-0b7285)](https://registry.modelcontextprotocol.io/v0/servers?search=ai-slop-checker)\n[![test](https://github.com/parweb/mcp-ai-slop-checker/actions/workflows/test.yml/badge.svg)](https://github.com/parweb/mcp-ai-slop-checker/actions/workflows/test.yml)\n[![license](https://img.shields.io/badge/license-MIT-555)](LICENSE)\n\n```bash\nclaude mcp add ai-slop-checker -- npx -y github:parweb/mcp-ai-slop-checker\n```\n\nThree tools, all **deterministic, local and offline**: no LLM call, no API key, no network request, no telemetry. The same input always returns the same number, so you can put a score in a test and assert on it.\n\n```\ncheck_ai_slop(text)                          -> 0-100, 6 dimensions, named tells, fixes\ngrade_landing_copy(headline, subhead, cta)   -> 0-100, 5 dimensions, flags, rewrites\nget_slop_stats()                             -> benchmark stats from 239 real landing pages\n```\n\n## Why\n\nEvery \"AI detector\" is a probabilistic classifier that guesses at authorship and gets it wrong on both sides. This does the opposite and says so plainly: it **counts style tells** — em-dash density, `delve`/`tapestry`/`furthermore` frequency, \"not only… but also\" scaffolds, suspiciously even sentence lengths, missing specifics, over-parallel bullet lists — and hands back the raw counts that produced each sub-score.\n\nThat makes it useful in a loop an agent can actually close: write → score → see which count is high → fix that specific thing → re-score. A classifier's \"87% likely AI\" gives an agent nothing to act on. `\"hype\": 5` does.\n\n**A score is a style measurement, not an authorship claim.** `stripe.com` scores 61 and was obviously written by professionals. Low score means *reads generic*, never *was generated*.\n\n## Install\n\nListed in the official [MCP Registry](https://registry.modelcontextprotocol.io) as `io.github.parweb/ai-slop-checker`.\n\nInstalls straight from GitHub — not on npm yet, so use the `github:` spec:\n\n```bash\nclaude mcp add ai-slop-checker -- npx -y github:parweb/mcp-ai-slop-checker\n```\n\nOr in any MCP client config (`claude_desktop_config.json`, `.mcp.json`, Cursor, etc.):\n\n```json\n{\n  \"mcpServers\": {\n    \"ai-slop-checker\": {\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"github:parweb/mcp-ai-slop-checker\"]\n    }\n  }\n}\n```\n\nOr install the self-contained MCPB bundle (dependencies included, no install step) from the\n[v1.0.2 release](https://github.com/parweb/mcp-ai-slop-checker/releases/tag/v1.0.2) —\n`mcp-ai-slop-checker.mcpb`, SHA-256 `6b13eb6d19be99553ab4551c7b6f9fc159a0db854c20718c611bfa0cc30f43f8`.\nRebuild it yourself and compare: `./scripts/build-mcpb.sh`.\n\nFrom source:\n\n```bash\ngit clone https://github.com/parweb/mcp-ai-slop-checker\ncd mcp-ai-slop-checker && npm install && npm test\n# then point your client at:  node /abs/path/mcp-ai-slop-checker/src/index.js\n```\n\nNode >= 18. One runtime dependency (`@modelcontextprotocol/sdk`) plus `zod`.\n\n## Tools\n\n### `check_ai_slop(text)`\n\nScores prose 0-100, where **100 reads human**. Six dimensions: LLM-word density (30), em-dash density (20), formulaic structures (15), sentence rhythm (15), specificity (10), list perfection (10). ~200+ characters gives a reliable read.\n\nReal output, trimmed to the parts that matter:\n\n```jsonc\n// input: a 74-word paragraph of \"In today's fast-paced world… delve… Moreover… seamless…\"\n{\n  \"score\": 34,\n  \"verdict\": \"This sounds AI-generated.\",\n  \"words\": 74,\n  \"dimensions\": [\n    { \"key\": \"LLM-word density\",     \"max\": 30, \"score\": 0,  \"notes\": { \"phrases\": 5, \"words\": 14 } },\n    { \"key\": \"Em-dash density\",      \"max\": 20, \"score\": 8,  \"notes\": { \"dashes\": 1 } },\n    { \"key\": \"Formulaic structures\", \"max\": 15, \"score\": 10, \"notes\": { \"hits\": 1, \"triads\": 1 } },\n    { \"key\": \"Sentence rhythm\",      \"max\": 15, \"score\": 6,  \"notes\": { \"sentences\": 5, \"cv\": 0.2 } },\n    { \"key\": \"Specificity\",          \"max\": 10, \"score\": 0,  \"notes\": { \"number\": false, \"propers\": 0 } },\n    { \"key\": \"List perfection\",      \"max\": 10, \"score\": 10, \"notes\": { \"bullets\": 0, \"bold\": 0 } }\n  ],\n  \"flags\": [\"llmwords\", \"emdash\", \"formulaic\", \"uniform\", \"nospec\"],\n  \"fixes\": [\n    { \"title\": \"Cut the LLM words\",\n      \"detail\": \"Found 19 (\\\"delve/tapestry/furthermore/it's important to note\\\"…). Each one is a known model tell. Replace with the plain word you'd say out loud.\" },\n    { \"title\": \"Vary sentence length\",\n      \"detail\": \"Your sentences are suspiciously even (5 sentences, low variance). Humans write long, then short. Like this.\" }\n  ]\n}\n```\n\nThe hand-written paragraph in [`test/engine.test.js`](test/engine.test.js) — same subject, same rough length — scores **92, \"Reads human.\"**\n\n### `grade_landing_copy(headline, subhead, cta)`\n\nScores a hero block 0-100 across Anti-hype (25), Specificity (25), Clarity (25), Headline shape (13), CTA (12). `subhead` and `cta` are optional, but an empty CTA scores 0 on that dimension.\n\nThree exclusions are worth knowing, because each one was a measured false positive rather than a preference: **a digit that is part of a name, a version, a year or a list index is not a quantified claim** (`Auth0`, `Framer 3.0`, `B2C`, `© 2026`); **an arrow or a check mark is not an emoji** (`Get started →` was losing 4 points for a button glyph); and **an acronym is not shouting** — `SQL`, `MCP`, `CLI`, `API` no longer count as ALL-CAPS. Byte-for-byte the same rules as the browser grader in [parweb/landing-copy-grader](https://github.com/parweb/landing-copy-grader) and the live one; verified identical on all 239 corpus pages.\n\nReal output:\n\n```jsonc\n// headline: \"Revolutionize your workflow with our seamless, cutting-edge platform\"\n// subhead:  \"Unlock powerful solutions that transform your business\"\n// cta:      \"Learn more\"\n{\n  \"score\": 32,\n  \"verdict\": \"This reads AI-generated.\",\n  \"dimensions\": [\n    { \"key\": \"Anti-hype\",      \"max\": 25, \"score\": 0,  \"notes\": { \"hype\": 5, \"exclamations\": 0, \"emoji\": 0, \"allcaps\": 0 } },\n    { \"key\": \"Specificity\",    \"max\": 25, \"score\": 8,  \"notes\": { \"number\": false } },\n    { \"key\": \"Clarity\",        \"max\": 25, \"score\": 7,  \"notes\": { \"filler\": 3 } },\n    { \"key\": \"Headline shape\", \"max\": 13, \"score\": 13, \"notes\": { \"words\": 8 } },\n    { \"key\": \"CTA\",            \"max\": 12, \"score\": 4,  \"notes\": { \"weak\": true, \"empty\": false } }\n  ],\n  \"flags\": [\"hype\", \"filler\", \"weakcta\", \"nonum\"],\n  \"fixes\": [\n    { \"title\": \"Cut the hype words\", \"detail\": \"Found 5 (\\\"revolutionize/unlock/seamless/leverage\\\"…). Replace each with a plain, concrete verb.\" },\n    { \"title\": \"Add one number\",     \"detail\": \"No concrete figure anywhere. …82% of the 239 pages in our dataset fail this one.\" },\n    { \"title\": \"Rewrite the CTA\",    \"detail\": \"\\\"Learn more\\\" is generic. Use an action + outcome…\" }\n  ]\n}\n```\n\nFix all four and the same offer scores **100**:\n\n```\nheadline: \"Cut invoice time from 3 days to 20 minutes\"\nsubhead:  \"Turn your spreadsheet into a client-ready invoice, no template hunting.\"\ncta:      \"Start your first invoice\"\n-> { \"score\": 100, \"verdict\": \"Reads human & sharp.\", \"flags\": [] }\n```\n\nBoth numbers are asserted in [`test/engine.test.js`](test/engine.test.js), so they can't silently drift.\n\n### `get_slop_stats()`\n\nWithout a baseline, \"your copy scored 74\" is meaningless. This returns the reference distribution so the model can say *\"that's below the median of 239 real landing pages.\"*\n\nThese are the figures of the **deposited corpus**, scored with `static-fetch-regex-v1`. Three rules were tightened on 2026-07-25 — a digit inside a name/version/year is not a claim, an arrow is not an emoji, an acronym is not shouting — and `grade_landing_copy` applies them, so **a page re-scored today can differ from its row in this table**. The corpus deliberately keeps its original scoring: it is an archived object with a DOI, not a live view.\n\n| | |\n|---|---|\n| pages | 239 (303 attempted, 64 excluded) |\n| score | min **41** · median **79** · mean **80.1** · **19** perfect · **31** below 70 |\n| extracted | 2026-07-24, raw HTML, no JS execution, no LLM |\n\nHow often each tell fires:\n\n| flag | pages | % | meaning |\n|---|---:|---:|---|\n| `nonum` | **195** | **82%** | not a single digit in the hero |\n| `filler` | 82 | 34% | ≥1 filler word |\n| `weakcta` | 35 | 15% | CTA is a stock verb phrase |\n| `caps` | 33 | 14% | ALL-CAPS word in headline/sub |\n| `hype` | 16 | 7% | ≥1 hype word |\n| `shorthl` | 13 | 5% | headline under 3 words |\n| `longhl` | 9 | 4% | headline over 12 words |\n| `excl` | 7 | 3% | exclamation mark |\n| `emoji` | 7 | 3% | emoji in the hero |\n\n**The most common tell is not the em-dash and not \"delve\" — it's the absence of a number.** Four landing pages in five make a claim with zero quantity attached to it.\n\nFull CSV with the extracted hero text of every page, methodology and the exclusion list:\n[`landing-copy-grader/data/landing-pages-scores.csv`](https://github.com/parweb/landing-copy-grader/blob/main/data/landing-pages-scores.csv).\n`node scripts/verify-dataset.js` in that repo re-scores all 239 rows offline and fails on any disagreement —\nthe table above is pinned to its output by [`test/engine.test.js`](test/engine.test.js).\n\n> **Correction, 2026-07-25.** These counts were wrong in v1.0.0 and are fixed on `main`. The CSV they were\n> computed from stored only the first three flags per row, so every page with four or more tells lost one:\n> `nonum` read **194 / 81%** instead of **195 / 82%**, and `caps`, `shorthl`, `longhl` and `emoji` were low too.\n> Scores, median, mean and the perfect-100 list were never affected. **If you saw 194 / 81% from us anywhere,\n> 195 / 82% is the correct figure.**\n\n## Tests\n\n```bash\nnpm test\n```\n\n18 tests: the scoring engines against published fixtures, plus 6 that spawn the real server over stdio and drive it through an actual MCP client (`listTools`, three `callTool` round-trips, optional-argument handling, and a validation error that must not kill the process).\n\n```\n# tests 18\n# pass 18\n# fail 0\n```\n\n## Related\n\nSame engines, other surfaces:\n\n- [Does this sound AI?](https://1h-money-store.vercel.app/sounds-ai?utm_source=mcp) — `check_ai_slop` in the browser\n- [Landing-page leaderboard](https://1h-money-store.vercel.app/leaderboard?utm_source=mcp) — all 239 pages, scored, with the hero text\n- [parweb/landing-copy-grader](https://github.com/parweb/landing-copy-grader) — the single-file browser grader and the dataset\n\n## Project status\n\n**First published 2026-07-25.** Small and young — stated plainly so you can judge it.\n\n- **Stable:** the three tool signatures, the JSON shape they return, and the two scoring engines. Their outputs are asserted in the test suite, so a change that moves a score fails CI rather than surprising you.\n- **Opinionated and expected to change:** the word lists. English only.\n- **Known gap:** the `v1.0.0` bundle ships wrong benchmark numbers and `v1.0.0`/`v1.0.1` both ship the pre-correction scoring rules. Use `v1.0.2`, or the `npx github:` install, which tracks `main`.\n\nIssues and PRs welcome, particularly on the word lists — \"this term is wrong, here's a counter-example\" is a reproducible bug report against a deterministic scorer, which is most of the point of building it this way.\n\n## Honesty notes\n\n- This counts style tells. It does not detect authorship, and nothing reliably does.\n- The word lists are opinionated and English-only. They are plain arrays at the top of [`src/slop.js`](src/slop.js) and [`src/copy.js`](src/copy.js) — read them, disagree, fork.\n- Scores are comparable over time only because nothing here is stochastic. That's the whole point.\n- Built and maintained by an autonomous agent org. The code, the dataset and these numbers are real and reproducible; run `npm test` and check.\n\n## License\n\nMIT\n",
  "bytes": 11890,
  "sha": "5b46b25e8fb9d02b2958b0e1809aa53721c230d0321ba0a441e8ec27e512815c",
  "repo_slug": "parweb/mcp-ai-slop-checker",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_parweb_ai_slop_checker_2db567fd/readme"
}