Skip to main content
Glama

citeguard

DOI PyPI npm License: MIT

Retracted papers get cited for years after retraction. Andrew Wakefield's fraudulent 1998 paper linking the MMR vaccine to autism — retracted in 2010 — has been cited well over a thousand times since its retraction, by researchers who had no easy way to know. A 2026 JMIR study found that freely available AI tools "cannot reliably flag retracted literature," right as AI-assisted research and writing has exploded. citeguard closes that gap: check a citation against Crossref — free, no API key — before it goes in a paper, a summary, or a review.

Three surfaces, one verified detection algorithm:

Surface

For

Location

Python library + CLI

academic writers, scripts

src/citeguard/

MCP server

AI agents doing research/writing

mcp-server/

GitHub Action

CI on a lab's or journal's repo

action.yml

Why this is built the way it is

The detection logic isn't guessed at from documentation — it was built by querying Crossref's real API for known cases and reading the actual response shapes, then writing tests against the saved real responses (committed in tests/fixtures/, shared by both the Python and TypeScript implementations). Two real papers anchor the ground truth:

  • Wakefield et al., 1998, The Lancet (the MMR-autism paper): the publisher's own Crossref metadata has no structured retraction data — an update-to check alone would silently miss it. What actually carries the signal is a separate updated-by field, where Crossref has backfilled the retraction (and an earlier 2004 correction) from the Retraction Watch database itself. Missing this field would have meant missing the single most famous retracted paper in medicine.

  • Mehra et al., 2020, The Lancet (the Surgisphere-linked COVID/hydroxychloroquine paper): here the publisher did attach structured data directly, via update-to. A different field, a different provenance, same underlying fact.

  • Watson & Crick, 1953, Nature serves as the clean control in every test suite — a definitely-real, definitely-not-retracted paper that must never be flagged.

So the checker looks at three independent signals, in order: update-to (publisher-asserted), updated-by (often Retraction-Watch-sourced, catching what publishers miss), and a title-prefix fallback ("RETRACTED:", "WITHDRAWN:", etc.) for older or unlinked cases with no structured metadata on either field. Each signal is mapped to the right severity — an "Expression of Concern:" title is not the same thing as a retraction, and earlier versions of this code collapsing that distinction was itself a bug caught by testing against real titles, not just synthetic ones. See src/citeguard/analyze.py for the fully-commented implementation.

Related MCP server: Citation Guard

Quick start

CLI:

pip install citeguard-cli
citeguard doi 10.1016/S0140-6736(97)11096-0
citeguard file references.bib --fail-on retracted   # for CI, see below

The distribution is named citeguard-cli on PyPI because the plain citeguard name there belongs to an unrelated project. The command and the import are still citeguard.

MCP server (add to your client's config, e.g. .mcp.json):

{ "mcpServers": { "citeguard": { "command": "npx", "args": ["-y", "citeguard-mcp-server"] } } }

Published on npm as citeguard-mcp-server — nothing to clone or build. To run it from a local checkout instead, point command at node and args at mcp-server/dist/index.js.

GitHub Action — on the GitHub Marketplace. Add to any repository that holds a manuscript and its bibliography:

- uses: wedo911/citeguard@v0.1.2
  with:
    path: references.bib
    fail-on: concern   # never | retracted | concern | corrected

What this is not

  • Not proof a paper's content is correct. It only checks retraction status, not whether a non-retracted paper's findings hold up.

  • Not exhaustive. The title-prefix heuristic only catches the publisher conventions it's been tested against; a clean result means "no known signal found," not "guaranteed never retracted."

  • Not a bulk-scraping tool. It's built for the size of a real bibliography (tens of citations), with a small fixed delay between Crossref requests and an optional persistent cache (src/citeguard/cache.py) — good API citizenship for a free public service, not a tool for scanning millions of DOIs.

Running the tests

# Python (55 tests, including against the real fixtures above)
pip install -e ".[dev]" && pytest -v

# MCP server (17 tests against the same real fixtures)
cd mcp-server && npm install && npm run build && npm test

Both suites are network-free and deterministic — they run against the committed real API responses, not live calls, so they're fast and don't depend on Crossref being reachable. Live end-to-end behavior (the actual CLI, the actual MCP tool, hitting the real API) was separately verified by hand during development; the GitHub Action additionally has its own CI job (action-smoke-test) that runs the real composite action against a known-retracted and a known-clean bibliography on every push, so the Action itself — not just the underlying library — is continuously verified against the live API.

Contributing

New signal types, additional publisher title conventions, and false-positive reports are all welcome. If you add a case, prefer adding it as a real, cited Crossref fixture over a synthetic one where possible — that's what caught the two real bugs this project's own test suite found during development (a URL-encoding bug in the DOI request path, and a BibTeX parser that could swallow an adjacent entry when parsing a malformed @comment block).

Citing citeguard

Archived on Zenodo with a DOI, so it can be cited in a paper:

Bajaman, W. (2026). citeguard: retraction, correction, and expression-of-concern checking for citations. Zenodo. https://doi.org/10.5281/zenodo.22115109

That DOI always resolves to the latest archived version. GitHub's Cite this repository button generates BibTeX and APA from CITATION.cff.

License

MIT — see LICENSE.

Available Tools

2 tools
check_citationCheck a Citation for RetractionA
Read-onlyIdempotent

Check a single DOI against Crossref for retraction, correction, or expression-of-concern status. Use this before citing a paper in a document, summary, or literature review -- retracted papers (including fraudulent or debunked ones) continue to be cited for years after retraction, often because whoever is citing them has no easy way to check. This tool checks three independent signal sources: publisher-asserted updates, Crossref's ingested Retraction Watch data (which catches many retractions the publisher's own metadata misses), and a title-prefix fallback ("RETRACTED:", etc.) for older or unlinked cases.

Args:

Returns: For JSON format: { "doi": string, "verdict": "retracted" | "concern" | "corrected" | "clean" | "not_found" | "error", "title": string | null, "signals": [ { "type": string, "source": string, "label": string | null, "noticeDoi": string | null, "date": string | null } ], "error": string | null }

Examples:

  • Use when: about to cite a paper in a summary, report, or literature review -- check it first

  • Use when: reviewing someone else's bibliography for outdated or unreliable sources

  • Don't use when: you need to verify a claim's accuracy generally -- this only checks retraction status, not whether the paper's content is correct

Error Handling:

  • A DOI Crossref doesn't recognize returns verdict "not_found", not an error -- it may still be a real paper Crossref just doesn't index (e.g. some preprints), not necessarily an invalid citation.

Inputs: doi (required).

check_citationsCheck All Citations in a Block of TextA
Read-onlyIdempotent

Extract every DOI found in a block of text (a reference list, a paper draft, an exported bibliography) and check each one for retraction/correction/expression-of-concern status via Crossref. Use this to sanity-check a whole reference list at once before finalizing a document, rather than checking citations one at a time.

Finds DOIs in any form: bare ("10.1016/S0140-6736(97)11096-0"), as a doi.org URL, or embedded in a formatted citation. Up to 50 unique DOIs are checked per call; if more are found, only the first 50 (in order of first appearance) are checked and the response says so.

Args:

  • text (string, 1-200000 chars): the text to scan.

Returns: For JSON format: { "totalFound": number, "checked": number, "truncated": boolean, "results": [ { "doi": string, "verdict": string, "title": string|null, "signals": [...], "error": string|null } ] }

Examples:

  • Use when: finishing a literature review or research summary -- paste the whole reference list to check it in one call

  • Don't use when: checking just one citation you already have the DOI for -- use check_citation instead, it's simpler

Error Handling:

  • Returns an error if text is empty or exceeds 200000 characters. Text with no DOI-shaped substrings returns totalFound: 0, not an error.

Inputs: text (required).

Tool Schema Changelog

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

Loading tool schema changelog…

    TDQS

    A4.4/5.0

    Scored across 2 tools

    Disambiguation4/5

    check_citations (bulk scan of text) and check_citation (single DOI) have overlapping purposes, and the near-identical singular/plural names invite misselection. However, each description explicitly cross-references the other with 'use X instead' guidance, so an agent can reliably choose the right one.

    Naming Consistency4/5

    Both tools use consistent snake_case with a clear verb_noun shape, and the singular/plural convention neatly signals single vs. bulk behavior. The only downside is that the two names differ by a single character, which is a mild collision risk.

    Tool Count4/5

    Two tools is on the thin side, but the domain (checking retraction status of DOIs) is genuinely narrow and the single/bulk split is the one meaningful axis of variation. Nothing feels gratuitous, though a third tool would be hard to justify.

    Completeness4/5

    The surface covers the core lifecycle: check one DOI, check many DOIs in a block of text, with defined verdicts and error handling. A minor gap is the absence of any way to resolve a citation lacking a DOI (e.g. by title/author) into a checkable identifier.

    Maintenance

    ActivityInactive
    ResponsivenessNo recent community issues

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      Not graded
      quality
      B
      maintenance
      Ground citations before your agent emits them by checking references against public scholarly registries and flagging hallucinated or retracted ones.
      MIT
    • A
      license
      A
      quality
      C
      maintenance
      Provides MCP tools to check reference lists for retracted or problematic citations, retrieve nuanced editorial statuses with evidence, and monitor institutions for newly flagged papers using open data from OpenAlex, Retraction Watch, and Crossref.
      3
      22 PyPI
      MIT