Skip to main content
Glama
chefcohen

corroborate-mcp

by chefcohen

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.

npm downloads license node MCP API keys

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?

Related MCP server: Fact-Checker MCP Server

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

  1. Search Google News RSS, GDELT, Hacker News in parallel (keywords extracted from the claim; quoted phrases preserved).

  2. 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.

  3. Syndication clustering — near-identical headlines across domains (Jaccard ≥ 0.55) collapse into one origin; wire domains flagged.

  4. 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

MIT © Ezra Cohen · github · npm

Available Tools

1 tool
find_sourcesFind sourcesA

Multi-engine news/source search (Google News, GDELT, Hacker News) for a query: a deduped list of {outlet, domain, url, date}. Use when you want raw coverage to judge yourself, not a scored verdict — the cheaper primitive under corroborate_claim. Keyless, read-only.

ParametersJSON Schema
NameRequiredDescriptionDefault
queryYesSearch query or claim
max_sourcesNo
window_daysNo

TDQS

A4.3/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations provided, so description carries full burden. It discloses 'Keyless, read-only,' which are critical behavioral traits. Could mention rate limits or pagination, but the provided info is sufficient for safe use.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two sentences: first covers core functionality, second provides usage context. No filler, front-loaded with essential info.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given 3 parameters and no output schema, the description covers purpose, usage, and key behaviors. The deduped list fields are specified. Lacks a concrete example but still complete for a simple tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is only 33% (only query has a description). The description does not explain max_sources or window_days beyond their schema constraints. Low coverage with no compensation.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's function: multi-engine news/source search returning a deduped list of {outlet, domain, url, date}. It distinguishes itself from the sibling tool corroborate_claim by calling itself the cheaper primitive.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly states when to use ('when you want raw coverage to judge yourself, not a scored verdict') and provides the alternative sibling tool corroborate_claim. Clear guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 1 tool updatev0.1.2
    • Removedcorroborate_claim
  2. 2 tool updatesv0.1.1
    • First observedcorroborate_claim
    • First observedfind_sources

TDQS

A4/5.0

Scored across 1 tool

Disambiguation5/5

With only one tool, there is no possibility of ambiguity. The single tool has a clear purpose and cannot be confused with others.

Naming Consistency5/5

The tool name 'find_sources' follows a consistent verb_noun pattern, and since there is only one tool, there is no inconsistency.

Tool Count2/5

A single tool, 'find_sources', feels too few for a server named 'corroborate-mcp', which implies a broader scope including claim verification. The tool is described as a 'cheaper primitive', suggesting that a more complex tool ('corroborate_claim') is missing, making the count inappropriate.

Completeness1/5

The server's purpose appears to be corroboration, but it only provides a news search tool. The description explicitly references a missing 'corroborate_claim' primitive, leaving a significant gap. There are no tools for actual claim verification, making the surface severely incomplete.

Maintenance

ActivitySlowing
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers