Skip to main content
Glama
README.md
# wikicitation-mcp

[![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.23458590.svg)](https://doi.org/10.5281/zenodo.23458590)

> Wikipedia citation-science tools for Claude — pure Python, no R required.

An [MCP](https://modelcontextprotocol.io) server that exposes **38 tools** to
Claude Code, Claude Desktop, and claude.ai.  
Ask Claude to fetch Wikipedia edit histories, analyse citations, score
scientific quality, and annotate DOIs and ISBNs — all without leaving the chat.

---

<!-- mcp-name: io.github.jsobel1/wikicitation-mcp -->

## What you can do

| Category | Examples |
|---|---|
| **Edit history** | Full revision history, first/latest wikitext, category-wide sweeps |
| **Citation analysis** | Count DOIs/refs/ISBNs, extract CS1 templates, compute SciScore |
| **DOI & ISBN annotation** | Enrich DOIs via CrossRef / EuropePMC, fetch BibTeX; books via Open Library / Google Books |
| **Multilingual** | Every article-level tool takes `lang` (en, fr, de, es, it, pt citation templates are recognised natively) |

---

## Installation

### Option A — PyPI (recommended)

No clone needed. `uvx` downloads and runs the package in one step:

```bash
# Requires uv (https://docs.astral.sh/uv/)
uvx wikicitation-mcp
```

Or install permanently with pip:

```bash
pip install wikicitation-mcp
wikicitation-mcp          # starts the stdio server
```

### Option B — From source

```bash
git clone https://github.com/jsobel1/wikicitation-mcp.git
cd wikicitation-mcp
uv sync
uv run python server.py
```

---

## Connect to Claude

### Claude Code (CLI) — PyPI

```bash
claude mcp add wikicitation -- uvx wikicitation-mcp
claude mcp list
```

### Claude Code (CLI) — from source

```bash
# Run from inside the cloned directory
claude mcp add wikicitation -- uv run python server.py
```

### Claude Desktop — PyPI

Edit `~/.claude/claude_desktop_config.json`
(Windows: `%APPDATA%\Claude\claude_desktop_config.json`):

```json
{
  "mcpServers": {
    "wikicitation": {
      "command": "uvx",
      "args": ["wikicitation-mcp"]
    }
  }
}
```

### Claude Desktop — from source

```json
{
  "mcpServers": {
    "wikicitation": {
      "command": "uv",
      "args": [
        "run", "python",
        "/ABSOLUTE/PATH/TO/wikicitation-mcp/server.py"
      ],
      "cwd": "/ABSOLUTE/PATH/TO/wikicitation-mcp"
    }
  }
}
```

Restart Claude Desktop — the **wikicitation** server appears in the toolbar.

### claude.ai (web, HTTP transport)

```bash
uvx wikicitation-mcp --transport streamable-http --port 8000
# (from a clone: uv run python server.py --transport streamable-http --port 8000)
# Then in claude.ai → Settings → Connections → Add MCP server
# URL: http://localhost:8000/mcp
```

---

## Quick start

Once connected, try these prompts in Claude:

```
Fetch all edits to the Wikipedia article "Circadian clock" up to today.
```

```
Count all DOIs, references, and ISBNs in this wikitext: [paste wikitext]
```

```
Calculate the SciScore for "Sleep deprivation".
```

```
Extract all citations from "CRISPR" — how many are journals vs websites vs books?
```

```
Show me the top 20 most-cited papers in the Wikipedia article "mRNA vaccine".
```

```
Annotate these DOIs with CrossRef metadata:
10.1038/nature12373, 10.1016/j.cell.2020.01.001
```

---

## Usage notes

### `date_limit` is an upper bound

`date_limit` controls the **newest** revision returned, not the oldest.
It defaults to *now*. To retrieve edits "since 2023", leave it unset and filter
the returned rows by timestamp. A plain date (`2024-01-01`) means the end of that
day (UTC).

### Languages

Article-level tools accept `lang` (default `"en"`), e.g. `lang="fr"`. Citation
templates are recognised natively for en, fr, de, es, it and pt, and mapped to
the same canonical types so SciScore is comparable across editions.

### SciScore

`get_sci_score` returns `sci_score` = journal citations ÷ total citation
templates, from 0 to 1. Higher means more scientifically sourced.

### Optional environment variables

| Variable | Effect |
|---|---|
| `GOOGLE_BOOKS_API_KEY` | Gives `annotate_isbn_google` its own quota. Without it, Google often answers HTTP 429; `annotate_isbn_openlib` needs no key. |
| `CROSSREF_MAILTO` | Your e-mail address, sent to CrossRef's polite pool. |

### Input limits

Arguments are validated before any network call: up to 100 articles, 20
categories and 200 DOIs per call, regular expressions up to 500 characters, text
up to 5 million characters, subcategory depth 1–5, and `lang` must look like a
Wikipedia language code.

---

## Tool reference

### Group 1 — History & metadata (14 tools)

| Tool | Key arguments | Returns |
|---|---|---|
| `get_article_history` | `article_name`, `date_limit` | revision table (revid, user, timestamp, size, comment) |
| `get_article_recent` | `article_name`, `date_limit` | metadata + full wikitext |
| `get_article_initial` | `article_name` | first-ever revision metadata + wikitext |
| `get_article_info` | `article_name` | pageid, title, byte length (`missing: true` if absent) |
| `get_tables_all` | `article_name`, `date_limit` | initial + recent + history + info in one call |
| `get_category_pages` | `category` | list of article titles |
| `get_pages_in_cat_table` | `category` | structured page table (pageid, title, type) |
| `get_subcat_table` | `catname`, `replacement` | direct subcategories |
| `get_subcat_multiple` | `catname_list` | subcategories for N categories |
| `get_subcat_with_depth` | `catname`, `depth`, `replacement` | recursive subcategories |
| `get_page_in_cat_multiple` | `catname_list` | pages for N categories |
| `get_category_history` | `article_list` | revision table for a list of articles |
| `get_category_recent` | `article_list`, `date_limit` | metadata + wikitext for a list of articles |
| `get_category_creation` | `article_list` | creation-revision metadata for a list of articles |

### Group 2 — Citation counting, extraction & quality (19 tools)

| Tool | Key arguments | Returns |
|---|---|---|
| `get_doi_count` | `text` | `{"count": N}` |
| `get_ref_count` | `text` | `{"count": N}` |
| `get_url_count` | `text` | `{"count": N}` |
| `get_isbn_count` | `text` | `{"count": N}` |
| `get_hyperlink_count` | `text` | `{"count": N}` |
| `get_any_count` | `text`, `regexp` | `{"count": N}` |
| `extract_citations` | `text` | list of CS1 template strings |
| `extract_wikihypelinks` | `text` | list of `[[...]]` wikilink strings |
| `replace_wikihypelinks` | `text` | `{"cleaned_text": ...}` with links replaced by display text |
| `parse_cite_type` | `text` | `{"cite_type": ..., "fields": {...}}` |
| `extract_with_regex` | `article_name`, `regexp`, `date_limit` | list of regex matches from live article |
| `extract_all_regex` | `article_name`, `date_limit` | all built-in pattern matches (doi, url, isbn, pmid, …) |
| `parse_citations` | `article_name`, `date_limit` | structured citation rows (cite_type, doi, author, year, title) |
| `parse_all_citations` | `article_name`, `date_limit` | long-form table: one row per citation field |
| `get_citation_types` | `article_name`, `date_limit` | citation counts by CS1 type |
| `get_source_type_counts` | `text` | citation counts by CS1 type from raw wikitext |
| `get_sci_score` | `article_name`, `date_limit` | `sci_score` (see above) |
| `get_top_cited_papers` | `article_name`, `date_limit` | top 40 DOIs annotated via EuropePMC |
| `get_revert_counts` | `start`, `end` | revert-tagged edit counts by article (defaults to the last 7 days; ≤30-day window; can take a minute) |

### Group 3 — DOI & ISBN annotation (5 tools)

| Tool | Key arguments | Returns |
|---|---|---|
| `annotate_dois_europmc` | `doi_list` | title, journal, year, citation_count, is_open_access, PMID/PMCID |
| `annotate_dois_crossref` | `doi_list` | title, authors, journal, year, publisher, citation count |
| `annotate_dois_bibtex` | `doi_list` | `{"bibtex_entries": [...]}` |
| `annotate_isbn_google` | `isbn` | title, authors, publisher, publishedDate, description (see `GOOGLE_BOOKS_API_KEY`) |
| `annotate_isbn_openlib` | `isbn` | title, authors, publishers, publish_date, page count |

---

## Running tests

```bash
uv sync --dev
uv run pytest                              # unit tests, no network
uv run pytest --run-integration            # adds live API tests
```

Integration tests call public APIs (Wikipedia, EuropePMC, CrossRef, Open Library,
Google Books) and are skipped by default.

---

## Troubleshooting

**`uvx: command not found`**

Install uv first:
```bash
# macOS / Linux
curl -LsSf https://astral.sh/uv/install.sh | sh

# Windows (PowerShell)
powershell -ExecutionPolicy Bypass -c "irm https://astral.sh/uv/install.ps1 | iex"
```

**Server not appearing in Claude Code**

```bash
claude mcp remove wikicitation
claude mcp add wikicitation -- uvx wikicitation-mcp
claude mcp list
```

**`ModuleNotFoundError: No module named 'mwparserfromhell'`**

The venv is missing a dependency. Run `uv sync` inside the cloned repo, or
reinstall via `pip install wikicitation-mcp`.

**`get_revert_counts` returns no results**

The Wikipedia `recentchanges` API only retains ~30 days of history. Requests
for date ranges older than that will return empty results.

---

## Citing

If you use `wikicitation-mcp` in research, please cite it using [`CITATION.cff`](CITATION.cff) (GitHub's "Cite this repository" button) or the paper in [`paper/`](paper/paper.md).

## Related projects

These three projects share the same core analysis logic at three different
levels of interactivity — pick the one that matches how you want to work.

| Project | Interface | When to use |
|---|---|---|
| **wikicitation-mcp** *(this repo)* | MCP server for Claude Desktop / Claude Code / claude.ai | You want to ask Claude questions in natural language and have it call the tools for you. Pure Python, no R required. |
| [**wikilite**](https://github.com/jsobel1/wikilite) | R package | You want a programmable pipeline in R / R Markdown / Quarto with full access to revision tables, citation parsers, and ggplot2 / plotly / visNetwork output. |
| [**wikiliteApp**](https://github.com/jsobel1/wikiliteApp) | Shiny web app (built on `wikilite`) | You want a point-and-click GUI: per-article tabs (History Flow, Citations, Authorship, Stability with SciScore-over-time, Vandalism & Wars, WikiWho-powered Revision Inspector) and a Corpus Analysis mode (multi-corpus timeline, per-article + per-corpus SciScore, co-citation / publication / wikilink networks, batched EuropePMC + Google Books annotation, multi-sheet XLSX exports). |

## External references

- [Model Context Protocol](https://modelcontextprotocol.io) — MCP specification
- [FastMCP](https://gofastmcp.com) — Python MCP framework used here
- [mwparserfromhell](https://github.com/earwig/mwparserfromhell) — wikitext parser

TDQS

C2.9/5.0

Scored across 38 tools

Disambiguation3/5

Several clusters of tools overlap significantly: get_subcat_table / get_subcat_multiple / get_subcat_with_depth all fetch subcategories, and get_category_pages / get_pages_in_cat_table / get_page_in_cat_multiple all list pages in categories. Count and parsing tools (get_any_count vs the specific *_count tools, parse_cite_type vs parse_citations vs parse_all_citations) also blur boundaries, though descriptions provide some differentiation.

Naming Consistency4/5

Most names follow a consistent snake_case verb_noun pattern (get_*, extract_*, parse_*, annotate_*). Minor deviations like mixing 'table', 'multiple', and 'with_depth' suffixes for parallel operations introduce slight unpredictability but overall naming is coherent.

Tool Count2/5

38 tools is heavy for the scope, and much of the surface is redundant parallel variants (three subcategory getters, three page-in-category getters, two ISBN annotators, multiple citation parsers). Consolidation would reduce cognitive load.

Completeness4/5

Coverage is strong across the domain: extraction, parsing, counting, history at multiple levels (subcategory/page), external annotation via EuropePMC/CrossRef/OpenLibrary/Google Books, and derived metrics like sci_score. Minor gaps only in areas like write/update operations, which may be out of scope.

Maintenance

ActivityMaintained
ResponsivenessNo issues