chefcohen/corroborate-mcp
๐ ๐ - Tells AI agents how independently a claim is being reported: syndication-aware source counting (a wire story reprinted by 40 outlets counts as one origin), 0-1 confidence scores, and published error rates in-repo. Measures corroboration, not truth โ no stance detection. Keyless, no accounts. Install: npx -y corroborate-mcp.
Quick Install
{
"mcpServers": {
"chefcohen-corroborate-mcp": {
"command": "npx",
"args": [
"-y",
"chefcohen-corroborate-mcp"
]
}
}
}Using an AI coding agent (Claude Code, Cursor, etc.)? Copy a ready-made prompt that tells it to fetch the setup instructions and install this server for you.
Documentation Overview
corroborate-mcp
The honest corroboration layer for AI agents. Hand it a claim; it tells you how independently that claim is actually being reported โ and shows its work.
An AP story echoed by 40 outlets is one origin, not 40 citations. Other verifiers count citations; Corroborate counts independent origins โ syndication-aware โ then publishes its own error rates and discloses coverage gaps. That's the judgment layer raw search and citation-checkers skip.
corroborate_claim("NASA delayed the Artemis III landing") โ CONFIRMED 4 independent origins ยท conf 0.9
corroborate_claim("Zorblatt Corp acquired Portugal") โ UNCORROBORATED 0 origins ยท conf 0.05
When an agent should call this
- Before relying on a current-event / news claim โ is it independently reported, or one source echoed everywhere?
- To rank or filter sources by genuine independence, with a confidence score.
- When you need a verdict you can defend โ every answer ships its evidence, its confidence, and its own caveats.
Not a web search engine (use Exa/Tavily/Brave for that) and not a human fact-checking network. It's the narrow, honest primitive in between: how independently is this reported?
Why it's trustworthy โ by design
| ๐ Zero API keys, zero accounts | Keyless engines (Google News RSS ยท GDELT ยท Hacker News). Nothing to sign up for. |
| ๐ Read-only | It reads public news. It cannot act, write, spend, or touch your data. |
| ๐ฏ Deterministic | No LLM in the loop โ same evidence in, same verdict out. p50 ~0.4s. |
| ๐ Open source (MIT) | Read every line of the judgment. |
| ๐ Published error rates | We measure our own false-CONFIRMED / false-UNCORROBORATED rates on a public benchmark and print them below. Most "fact-check" tools don't. |
| ๐ซ Never lies by omission | If sources are unreachable it returns an error, never a fake "UNCORROBORATED." |
What it measures โ and what it doesn't
Reporting corroboration, not truth. A claim every outlet syndicated from one wire story comes back SINGLE_SOURCE. A claim nobody covers comes back UNCORROBORATED even if true. The verdict states exactly what was measured, flags its weaknesses in notes, and sets coverage:"degraded" when an engine was down โ so absence of evidence is never quietly sold as evidence of absence.
Quickstart
Requires Node โฅ 18. No keys, no config.
Claude Code
claude mcp add corroborate -- npx -y corroborate-mcp
Claude Desktop / Cursor / any MCP client (claude_desktop_config.json, .cursor/mcp.json, โฆ)
{ "mcpServers": { "corroborate": { "command": "npx", "args": ["-y", "corroborate-mcp"] } } }
Try it with no MCP client
npx corroborate-mcp # starts the stdio server (silent = healthy)
# or from a checkout: node cli.js "The Federal Reserve held interest rates steady"
Tools
corroborate_claim โ the verdict
{ claim, window_days?=7, max_sources?=8 } โ
{
"claim": "Acme Corp acquires Widget Industries for $2 billion",
"corroboration": "CONFIRMED",
"n_independent_sources": 3,
"confidence": 0.9,
"coverage": "full",
"sources": [
{ "headline": "Acme Corp to acquire Widget Industries in $2 billion deal",
"outlet": "Financial Times", "domain": "ft.com", "url": "https://โฆ",
"published_at": "2026-07-09T14:02:00Z", "age_days": 0.9,
"wire": false, "echoed_by_n_domains": 1 }
],
"notes": ["syndication detected: near-identical headlines across multiple domains collapsed into one origin"],
"method": "reporting-corroboration: counts INDEPENDENT story origins (syndication-aware), not raw article count; does not adjudicate truth"
}
| Field | Meaning |
|---|---|
corroboration | CONFIRMED (โฅ2 independent origins) ยท SINGLE_SOURCE (1) ยท UNCORROBORATED (0) |
n_independent_sources | Distinct story origins after syndication clustering โ not article count |
confidence | 0โ1. Rises with origins + cross-engine agreement; penalized for wide single-origin echo |
coverage | full, or degraded when an engine failed this call โ a weak verdict may then reflect the outage, not the claim |
sources[].wire | Origin is a wire service (AP/Reuters/AFP/UPI) |
sources[].echoed_by_n_domains | How many domains carried this same story |
notes | Honest caveats: syndication collapse, breaking-news cascade (<24h), relevance-gate discards, engine outages |
find_sources โ the raw evidence
{ query, window_days?=7, max_sources?=10 } โ deduped multi-engine list (outlet, domain, url, date), no verdict. The cheaper primitive when you want the coverage and your own judgment.
Measured accuracy โ because you should demand it from any fact-check tool
Against a public 40-claim labeled benchmark (benchmark/ โ claims, labels, category definitions, and harness are all public; run it yourself with npm run benchmark). The time-sensitive claims are refreshed from current news so the numbers reflect real accuracy, not staleness. Latest run 2026-07-22:
| Metric | Result |
|---|---|
| Fabricated claims falsely CONFIRMED | 0/10 |
| Widely-reported true claims missed | 0/12 (11 CONFIRMED, 1 single-source) |
| Niche true claims detected | 7/8 |
| Stale claims correctly windowed | 2/5 |
| Distorted claims falsely CONFIRMED | 6/6 โ read the warning โ |
| Latency | p50 0.4s ยท p95 <1s (โค8s worst case when the slow engine is up) |
โ ๏ธ Respect the distorted-claim number โ it's the honest ceiling of this tool's blind spot. In the latest run all 6 distorted claims came back CONFIRMED (6/6), because each one distorts a currently-reported event, and this tool checks whether independent reporting exists around a claim's topic and entities โ it does not do stance detection. So "Samsung cancelled the Galaxy Z Fold8" (they unveiled it) or "OpenAI released GPT-6" confirms off the real coverage of the true story. The rule to live by: if a claim's topic is being reported, a distorted version of it will very likely CONFIRM. Read CONFIRMED as "this topic has independent coverage โ now verify the specific facts against the returned sources," never as "this exact claim is true." Stance detection is the v1.1 roadmap item. (Recurring events like championships/elections can likewise match the current cycle's coverage โ see the stale-claim leaks.)
How the judgment works
- Search Google News RSS, GDELT, Hacker News in parallel (keywords extracted from the claim; quoted phrases preserved).
- Relevance gate โ an article counts only if its headline genuinely addresses the claim (โฅ2 core-token hits AND โฅ35% of core tokens). Loose topical echoes are discarded; the discard count is reported.
- Syndication clustering โ near-identical headlines across domains (Jaccard โฅ 0.55) collapse into one origin; wire domains flagged.
- Verdict โ independent origins = distinct clusters; confidence rises with origins + cross-engine agreement, falls for wide single-origin echoes; every known weakness goes in
notes.
Honest limitations
- No stance detection (the 6/6 above). CONFIRMED = independently covered, not verified in every detail.
- English-language, headline-level. Paywalled body text isn't fetched.
- Recency-windowed (default 7 days, max 90). Old claims read
UNCORROBORATEDโ a window statement, not a falsity verdict. - Independent rewrites of one wire story can occasionally slip clustering; distinct phrasings of one origin can occasionally count as two.
- All engines down โ error, never a fake
UNCORROBORATED.
Development
npm test # golden-claim suite โ deterministic, no network
npm run test:mcp # MCP stdio handshake + tools/list
npm run test:live # live invariants against real engines
npm run benchmark # 40-claim labeled accuracy benchmark (live, ~2 min)
npm run test:release # all of the above โ required green before every release