slop-scorer
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@slop-scorerscan my codebase for template tells and propose fixes"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
slop-scorer-mcp
An MCP server that tells a coding agent, with citations, which parts of a codebase or a rendered web page read as machine-generated template output, and hands back the exact edit that would remove each one.
No model is involved anywhere in this server. Every finding cites a file and a line number, or a CSS selector and a computed style value, that you can go and check yourself. Seventy rules, all of them data, all of them carrying their own rebuttal.
claude mcp add slop-scorer -- npx -y github:Yuve21/slop-scorer-mcpNode 20 or newer and nothing else. Not on npm yet, so that line installs from this repository; the first launch compiles and takes about a minute. Full detail, and the two other ways in, are under Install.
Why this exists
The interesting half is not detection. It is list_rules, which returns the whole corpus so an
agent can read what not to produce before it produces anything.
Detection is a race that eventually gets lost: generators improve, tells decay, and a corpus of tells is a depreciating asset. Prevention does not decay, because an agent that reads the corpus first writes work the corpus does not fire on. It is the only loop here that gets stronger the more it is used.
Related MCP server: Sensory-Grounding MCP
What it will not do
This matters more than the feature list, so it is first.
It never says a person used AI. Every sentence it can emit has the same grammar: the subject is the server, the object is the artifact, the verb is something the server did. "We checked this artifact against 15 deterministic rules and 4 matched, on 88% coverage." Not "this is AI." The formatter cannot express the other sentence, and a test in
@slop/corefails the build if a forbidden phrase appears in the source.Abstention is a result, not a failure.
statusisassessed,inconclusive(we looked and will not publish a number) ornot_assessed(we did not look). Both abstentions carryscore: nulland a coded reason. They are not low scores and they are not clean bills of health. Branch onstatus, never onscore.The score stops at 99. There is no arrangement of evidence that reaches certainty.
A thin read withholds the score entirely. A page that only half rendered looks exactly like a clean page, and printing a confident number over a partial read would be the most dishonest thing this server could do.
It publishes no accuracy figure, anywhere.
packages/core/test/no-claims.test.tsscans every shipped source file and every README on each test run and fails the build if one appears. An accuracy claim about an inference is a substantiation-bearing claim under FTC Act section 5 (In re Workado, 2026), so the only defensible way to hold one is to compute it from a named corpus at test time. If you want a number, runnpm run backtestand read your own.It never writes to anything it scanned. No
exec, no request for write access, and no code path that modifies the artifact. Fixes come back as data for your agent to apply under your normal approval flow. The package contains exactly one filesystem write, the local observation log described below, and it is off until you switch it on, writes only to a directory you named, and never touches the target.
The local observation log (off by default)
Nothing here phones home. There is no telemetry, no usage beacon, no update check and no upload path in any package, and there is no code that could be pointed at one: the sink type takes a directory and cannot express a URL, so a future caller cannot quietly supply one.
If you want to help the corpus get better, you can switch on a local log:
SLOP_OBSERVATIONS_DIR=/an/absolute/path/you/choseUnset, nothing is written and no file is created. That is the default and there is no remote
default behind it. Set, each scan appends one JSON line to observations-YYYY-MM.jsonl in that
directory, recording the shape of the read and nothing else: the corpus version, a day rather than
a timestamp, the modality, the status and band, a score bucket rather than the score, the coverage
ratio, which rules fired, which rules were evaluated and did NOT fire, the per-probe denominators,
and a size bucket with markers from a closed vocabulary.
It records no path, no URL, no hostname, no file name, no dependency name and no observed value
out of any finding. The record is built by allowlist, field by field, rather than by stripping
fields, because a denylist fails open for every field somebody adds later. A guard then runs on the
finished record before it reaches disk, and it REFUSES rather than sanitises: a rule id the corpus
does not know, an extension that is not a bare extension, a marker outside the vocabulary or a
timestamp where a day belongs throws instead of being quietly rewritten.
The file is newline-delimited JSON. Read it, grep it, delete it. Getting an observation off your machine is your job and nobody else's: there is no service to send it to.
Install
slop-scorer-mcp is not on the npm registry yet, so every command here installs from this
repository. That works fine, with one caveat worth reading before you decide the install broke.
The first launch compiles from source. Measured cold, with an empty npm cache: 69 seconds.
Most MCP clients give a server 30 seconds to answer, so the first connect can report
connection timed out even though nothing is wrong. Reconnect, or warm the cache first:
npx -y github:Yuve21/slop-scorer-mcp # wait for "listening on stdio", then Ctrl-CEvery launch after that is off the cache and immediate.
Claude Code
claude mcp add slop-scorer -- npx -y github:Yuve21/slop-scorer-mcpClaude Desktop
~/Library/Application Support/Claude/claude_desktop_config.json on macOS,
%APPDATA%\Claude\claude_desktop_config.json on Windows. Restart the app afterwards.
{ "mcpServers": { "slop-scorer": { "command": "npx", "args": ["-y", "github:Yuve21/slop-scorer-mcp"] } } }Cursor
~/.cursor/mcp.json globally, or .cursor/mcp.json per project. Same block. Reload the window.
{ "mcpServers": { "slop-scorer": { "command": "npx", "args": ["-y", "github:Yuve21/slop-scorer-mcp"] } } }From a checkout
Use this if you are going to change a rule and watch it fire.
git clone https://github.com/Yuve21/slop-scorer-mcp
cd slop-scorer-mcp
npm install # `prepare` builds it for you
npm run install:local --workspace=packages/mcp-serverThat last line registers the built binary with claude mcp add by absolute path. If the claude
CLI is not on your PATH it prints the JSON block to paste by hand instead of failing silently.
Once it is on npm
claude mcp add slop-scorer -- npx -y slop-scorer-mcpNo build step, no cold start, because a registry tarball ships compiled. This 404s today.
How the GitHub install works
npx -y github:... resolves the repository ROOT, and npm has no way to point a git spec at a
subdirectory. So the root package.json here carries a bin (a single entry, which is what lets
npx pick an executable), a prepare script (the only lifecycle hook npm runs for a git
dependency: prepack is not run there), a files list naming packages/mcp-server/dist
(because dist/ is gitignored and npm falls back to .gitignore when packing, which would ship
the tarball with the binary removed), and the two runtime dependencies the esbuild bundle leaves
external. A root .npmignore sits beside them so .gitignore is never consulted at all.
scan_ui needs a browser
npx playwright install chromiumWithout it, scan_ui returns status: "not_assessed" and says why. It will not fall back to
reading the server HTML: a fetch-only read produces confident findings about a document nobody
sees, which is how a heading check once reported "this route has 0 H1s" for a route whose heading
does not exist until hydration.
The five tools
tool | what it does |
| The whole corpus, compact. Read before you generate. |
| Static analysis of a checkout, plus git history. |
| Renders a URL or a dev server in Chromium and measures the rendered document. |
| Re-scans and returns each finding as a precise, caveated edit. |
| Re-scans and puts the before and after finding sets side by side. |
list_rules
Seventy rules across two corpora: 51 web, 19 code. Compact by default, because a tool that asks to be called before you generate anything cannot cost a fifth of a small context window.
index every rule, one line: id | family | polarity/severity | weight | rationale
fullEntries why it reads as generated, the counter-evidence that rebuts it, how to avoid it
retrieval a sentence on every response saying how to get what was left outmodality: "web" | "code" | "all" optional
family: e.g. "agent-artifact" optional
ruleIds: ["craft.no-og-image", ...] full entries for these only
verbose: true, full entries for every rule costs roughly 18k tokensThe usual pattern after a scan is ruleIds: [...the ids that fired], which costs a few hundred
tokens and returns exactly the rebuttal and the prevention note you need to act on them.
scan_codebase
path: absolute path to the repository root
include: optional patterns, e.g. ["src/**/*.ts"]
readHistory: read git history for the commit-shape rules (default true)
maxFiles: cap on files walked (default 5000)A real reading, produced by pointing this server at its own checkout. Nothing here is typed by hand: it is the response bytes, lightly trimmed for length. Note that it finds things in us.
SLOP RECEIPT 20 / 99 band: few known signals
corpus code-corpus-2026.09 | coverage 100% | families fired 2
evidence kind: deterministic | detectors: code.static-rules
We checked this artifact against 19 deterministic rules from code-corpus-2026.09 and 4
matched, on 100% coverage. That places it in the "few known signals" band at 20 of 99.
Every match below cites what we read and where.
15 base rate before any evidence
+26 scaffold.placeholder-markers [scaffold-residue / medium] -> 41
Unfilled placeholders left in shipped source
evidence: line packages/detectors-code/src/artifact.ts:73 = "lorem ipsum"
(expected "a real value")
evidence: line packages/detectors-code/src/artifact.ts:109 = "TODO: implement"
(expected "a real value")
evidence: line packages/detectors-code/src/suppression.ts:18 = "lorem ipsum"
(expected "a real value")
... and 4 more, 5 counted toward the weight
caveat: TODOs are a normal and healthy way to record known gaps, and a mature
codebase carries plenty. Markers that name an owner or a ticket are
excluded as tracked work. Suppressed by suppress.self-defining-pattern:
19 match(es) were withdrawn as self-referential: the cited lines in
packages/detectors-code/src/scan.ts are the DEFINITION of the patterns
that matched them, not uses of them.
+23 verify.tautological-tests [verification-floor / high] -> 64
Tests that cannot fail
evidence: file packages/detectors-code/test/corpus/index.ts
= "93 lines, 0 assertions" (expected "at least one assertion")
evidence: file packages/detectors-web/test/corpus/index.ts
= "60 lines, 0 assertions" (expected "at least one assertion")
caveat: A deliberate smoke test that only checks a module imports without
throwing looks like this, and an assertion helper the scanner does not
recognise reads as zero assertions.
-42 counter.rationale-comments [counter-evidence / counter] -> 22
Comments that record a reason, not a restatement
evidence: line packages/core/src/assessment.ts:61 = "We read the artifact cleanly
and it carried no declared provenance either way. ABSENCE OF PROVENANCE
IS NOT EVIDENCE OF GENERATION."
... and 3 more, 4 counted toward the weight
caveat: A rationale comment can be copied along with the code it explains, and an
agent prompted to explain its reasoning will write plausible ones. The
signal is real but it is not proof.
-2 counter.real-test-coverage [counter-evidence / counter] -> 20
Tests dense enough to actually fail
evidence: metric "26 test files against 63 source files"
= "606 assertions, 0 tautologies"
20 TOTAL (base 15 + 4 contribution(s))
FAMILY CAPS
Scaffold residue +26 pts logit 1.37 of 1.60 allowed
Verification floor +23 pts logit 1.35 of 0.96 allowed AT CAP
Global counter-evidence logit -2.00 AT CAP (bypasses family caps)
WHAT WOULD CHANGE THIS SCORE
-26 scaffold.placeholder-markers: Fail the build on the placeholder strings your
scaffold ships with. They are the ones nobody notices.
-23 verify.tautological-tests: Mutation-test the test: break the code it covers and
confirm it goes red. If it stays green, delete it or fix it.
A high score means this artifact resembles generated-template output. It is not proof that
a tool made it, and it is not a judgement of the person who made it.Four things in that output are worth pointing at, because they are the design:
Counter-evidence subtracts. Two of the four matches argue for the artifact and together they are worth more than everything against it. A corpus that could only accuse would score every careful codebase as generated.
Families are capped. "Verification floor" contributed 23 points and is marked
AT CAP, so no single correlated group of tells can carry a verdict alone.The receipt reconciles. Base rate plus every printed contribution equals the printed score, in integers, exactly. You can recompute the number by hand.
A suppressor fired and said so. Nineteen placeholder matches were withdrawn because the lines that matched are the pattern table that defines the pattern. A phase-two suppressor may withdraw evidence about the detector rather than about the artifact, but it has to name the rule, the file and the reason, in the receipt, where you can see it.
In the real response every value copied out of the scanned artifact is wrapped in an
untrusted fence and the payload closes with a prompt-injection warning, because a scanner
that quotes a stranger's source into an agent's context is an injection vector. The fences are
stripped above only so the example reads.
scan_ui
url: https://example.com/pricing
port: 3000 (instead of url, for a local dev server)
viewportWidth: default 390
viewportHeight: default 844Renders in Chromium and measures the rendered document: builder fingerprints, default visual language, craft-floor defects, structural and motion uniformity, copy tells. A real finding:
+7 css.crushed-tracking [visual-default / medium] -> 34
Headline letter-spacing is crushed at a heavy weight
evidence: css letter-spacing on h1 = "-0.04em at weight 800"
(expected "-0.02em or looser at weight 700+")
caveat: Some faces genuinely want negative tracking at display size, and a
type-literate designer may choose exactly this. It is a taste signal,
not a provenance signal.One capability is missing from this build and it is missing on purpose. Text rendered
inside an image is not recovered here: the recoverer lives in a private half of the project,
so packages/ocr-text in this repository always abstains, with a stated reason attached to
every image it was asked about. The three imgtext.* rules therefore cannot fire on a live
scan_ui run. They remain in list_rules (the advice not to bury your copy in a JPEG is worth
reading either way) and they still score the frozen corpus artifacts, so archived numbers do not
move. This is written down rather than quietly omitted because an unread image is not an image
with no words in it.
propose_fixes
Re-scans the target and returns every finding as a proposal, grouped by family and split four ways so your agent can present it as "apply these N, skip these M".
bucket | meaning |
|
|
|
|
|
|
|
|
{
"id": "agent.instruction-file-committed#1",
"ruleId": "agent.instruction-file-committed",
"applicability": "confirm",
"destructive": true,
"blastRadius": "file",
"remediation": {
"kind": "delete_file",
"path": "CLAUDE.md",
"bytes": 4200,
"summary": "Remove CLAUDE.md from the repository and from the index.",
"rebuttal": "This says how the repository was worked on, not who wrote any given line.",
"doNotApplyIf": "this file is a deliberate part of how the team works. In that case keep it and say so in the README, which answers the finding without deleting anything."
}
}Most rules propose manual on purpose. A page title, a meta description, an alt attribute and a
brand palette are all things a machine can produce instantly, and all things whose machine
production is the defect this corpus measures. Generated alt text is the clearest case: it
satisfies the checker and tells a screen reader user, confidently, about an image nobody looked
at. So those rules name the gap and stop.
Two constraints hold everywhere in this tool:
Only a deterministic read may propose a patch. Where a detector abstains from certainty, the fix cannot assert it.
Every fix carries the rule's own rebuttal and an explicit
doNotApplyIf, injected from the rule'sfalsePositiveNoteso the fix and the argument against it can never drift apart.Counter-evidence is never remediable. A counter finding argues for the artifact. The corpus throws at load if anyone attaches a fix to one.
verify_fix
Before: 9 finding(s). After: 6. 3 no longer present, 6 still present, 0 newly present.
Score moved by -14 point(s).It does not report success. noLongerPresent is a list of rules that no longer match at those
locators, which is a fact about a re-scan and not a claim that a problem was solved.
newlyPresent is stated first and sets regression: true, because a change that resolves two
findings and introduces one has broken something, and a verifier that reported the net would
call that an improvement. If no earlier reading of the target is held in this session it says so
rather than comparing against nothing: a first run is never an all-clear.
The loop
scan, propose, apply, verify. The report was never the point; the change was.
1. scan_codebase / scan_ui what is there, with a locator on every claim
2. propose_fixes the same findings as precise, caveated edits
3. YOUR agent applies them with its own edit tools, under the user's normal approval
4. verify_fix re-scan, and show which findings are no longer presentStep 3 is deliberately not this server's job. Your agent already has edit tools, an approval prompt and a user who trusts them. A second, worse copy of that machinery inside an MCP server would be more code, more risk and less control.
What is in this repository
package | what it is |
| The server. Five tools, the stdio binary, the publish-shape bundle. |
| The rule contract, the scoring engine, the receipt, the calibration harness. |
| The code corpus: 19 rules, plus 16 labelled repositories (10 human, 4 generated, 2 synthetic). |
| The web corpus: 51 rules, plus 9 labelled pages (5 human, 4 generated). |
| The abstaining image-text stand-in described under |
The published npm package is self-contained: npm run build bundles the three internal
workspace packages into dist/bin.js and dist/index.js with esbuild and vendors their type
declarations, so nothing in the tarball points at a package that was never published.
@modelcontextprotocol/sdk and zod stay real external dependencies; playwright stays an
optional peer.
Build it yourself
npm install
npm run build # tsc -b, then the esbuild bundle
npm test # the rule suite, both calibration corpora, the security and FTC guards
npm run backtest # the gate: replay both frozen corpora and diff against the baselinenpm test never launches a browser. The web tests run against stored artifacts, which is also
what makes the calibration reproducible.
Contributing
Read CONTRIBUTING.md first. The short version: a rule in this repository is an
accusation with arithmetic behind it, so the bar is npm run backtest, not "the tests are
green". Every rule ships a positive fixture that must fire and a mutated fixture that must not,
and the meta-suite runs both on every test run. Adding a corpus member with stated, checkable
provenance is a bigger contribution than adding a rule.
Licence
MIT. See LICENSE.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Connectors
Lints + auto-fixes how AI coding agents discover any new product. 24 rules, 6 tools, score 0-100.
Trust signals for AI agents: an open agent-readiness standard and developer tool guide. Read-only.
Browser-backed QA with evidence and fix-ready reports for coding agents.
Evidence-backed x402 web verification for AI agents, with auditable decisions for every condition.
Related MCP Servers
- AlicenseNot gradedqualityAmaintenanceEnables AI agents to scan code for security and quality issues and receive machine-readable reports with suggested fixes and verification criteria.722MIT
- AlicenseNot gradedqualityBmaintenanceGives AI coding agents a closed-loop verification cycle for visual, audio, and video output, with enforcement hooks that make verification mandatory.Apache 2.0
- AlicenseNot gradedqualityDmaintenanceEnables AI coding agents to record auditable work ledgers with evidence chains, from contract to proof packet, via MCP tools for file scanning, code review, and issue triage.48MIT
- AlicenseNot gradedqualityAmaintenanceEnables AI coding agents like Claude and Cursor to audit websites for AI agent readiness, checking 199 rules across agentic discovery, content structure, and technical SEO.17Apache 2.0
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/Yuve21/slop-scorer-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server