Skip to main content
Glama
Eclipse-Cj

Paper Distill MCP Server

by Eclipse-Cj
README.md
# ๐Ÿ“š Paper Distill MCP Server

[![License: AGPL-3.0](https://img.shields.io/badge/License-AGPL--3.0-blue.svg)](LICENSE)
[![Python 3.10+](https://img.shields.io/badge/Python-3.10%2B-blue.svg)](https://www.python.org/)
[![PyPI version](https://img.shields.io/pypi/v/paper-distill-mcp.svg)](https://pypi.org/project/paper-distill-mcp/)
[![CI](https://github.com/Eclipse-Cj/paper-distill-mcp/actions/workflows/ci.yml/badge.svg)](https://github.com/Eclipse-Cj/paper-distill-mcp/actions/workflows/ci.yml)

<!-- mcp-name: io.github.Eclipse-Cj/paper-distill-mcp -->

Academic paper search, intelligent curation, and multi-platform delivery โ€” built on the [Model Context Protocol](https://modelcontextprotocol.io/).

Compatible with all MCP clients: Claude Desktop, Claude Code, Cursor, Trae, Codex CLI, Gemini CLI, OpenClaw, VS Code, Zed, and more.

> โš ๏ธ **Early development stage.** Many features are still being validated and may contain bugs or instabilities. Feedback and bug reports are warmly welcome!

---

## โœจ Features

- ๐Ÿ” **11-source parallel search** โ€” OpenAlex, Semantic Scholar, PubMed, arXiv, Papers with Code, CrossRef, Europe PMC, bioRxiv, DBLP, CORE, Unpaywall
- ๐Ÿค– **Adaptive AI delivery** โ€” the agent tracks your evolving research interests and automatically refines search keywords and recommendations over time
- ๐Ÿ“Š **4-dimensional weighted ranking** โ€” relevance ร— recency ร— impact ร— novelty, fully customizable weights
- ๐Ÿ‘ฅ **Dual-AI blind review** โ€” two AI reviewers independently shortlist papers; a chief reviewer synthesizes a final push/overflow/discard decision (optional)
- ๐Ÿงน **Scraper delegation** โ€” offload abstract extraction to a low-cost agent or API to cut token spend significantly
- ๐ŸŒ **Personal paper library site** โ€” Astro + Vercel auto-deploy; site updates within 30 seconds of each push
- ๐Ÿ“ฌ **Multi-platform delivery** โ€” Telegram / Discord / Feishu / WeCom
- ๐Ÿ“ฆ **Zotero integration** โ€” save papers to Zotero with one command
- ๐Ÿ“ **Obsidian integration** โ€” auto-generate paper note cards with Zotero backlinks; supports summary and template modes

---

## ๐Ÿš€ Quick Install

```bash
uvx paper-distill-mcp
```

That's it. Your AI client will discover all tools automatically. No API keys required for basic paper search.

> No `uv`? โ†’ `curl -LsSf https://astral.sh/uv/install.sh | sh` or `brew install uv`

<details>
<summary>Other installation methods (pip / Homebrew / Docker / source)</summary>

**pip:**

```bash
pip install paper-distill-mcp
```

**Homebrew:**

```bash
brew tap Eclipse-Cj/tap
brew install paper-distill-mcp
```

**Docker:**

```bash
docker run -i --rm ghcr.io/eclipse-cj/paper-distill-mcp
```

**From source (developers):**

```bash
git clone https://github.com/Eclipse-Cj/paper-distill-mcp.git
cd paper-distill-mcp
python3 -m venv .venv && .venv/bin/pip install --upgrade pip && .venv/bin/pip install -e .
```

</details>

---

## ๐Ÿ”— Connecting to AI Clients

### Claude Desktop

Add to `claude_desktop_config.json` (Settings โ†’ Developer โ†’ Edit Config):

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

### Claude Code

```bash
claude mcp add paper-distill -- uvx paper-distill-mcp
```

Or add to `.mcp.json`:

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

### Codex CLI (OpenAI)

Add to `~/.codex/config.toml`:

```toml
[mcp_servers.paper-distill]
command = "uvx"
args = ["paper-distill-mcp"]
```

### Gemini CLI (Google)

Add to `~/.gemini/settings.json`:

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

### OpenClaw

```bash
mcporter config add paper-distill --command uvx --scope home -- paper-distill-mcp
mcporter list  # verify
```

> To remove: `mcporter config remove paper-distill`

<details>
<summary>OpenClaw โ€” install from source</summary>

```bash
git clone https://github.com/Eclipse-Cj/paper-distill-mcp.git ~/.openclaw/tools/paper-distill-mcp
cd ~/.openclaw/tools/paper-distill-mcp
uv venv .venv && uv pip install .
mcporter config add paper-distill \
  --command ~/.openclaw/tools/paper-distill-mcp/.venv/bin/python3 \
  --scope home \
  -- -m mcp_server.server
mcporter list
```

> To remove: `rm -rf ~/.openclaw/tools/paper-distill-mcp && mcporter config remove paper-distill`

</details>

### Other clients (Cursor, VS Code, Windsurf, Zed, Trae)

Same JSON config, different config file paths:

| Client | Config path |
|--------|-------------|
| Claude Desktop | `claude_desktop_config.json` |
| Trae | Settings โ†’ MCP โ†’ Add |
| Cursor | `~/.cursor/mcp.json` |
| VS Code | `.vscode/mcp.json` |
| Windsurf | `~/.codeium/windsurf/mcp_config.json` |
| Zed | `settings.json` |

### HTTP transport (remote / hosted)

```bash
paper-distill-mcp --transport http --port 8765
```

---

## ๐ŸŽฏ Getting Started

After connecting your client, tell the agent **"initialize paper-distill"**. It will call `setup()` and walk you through:

1. **Research topics** โ€” describe your interests in plain language; the AI extracts keywords
2. **Delivery platform** โ€” set up Telegram / Discord / Feishu / WeCom (optional)
3. **Paper library site** โ€” build a personal paper library that updates automatically (optional)
4. **Scraper delegate** โ€” point to a low-cost agent or API for abstract extraction (recommended)
5. **Preferences** โ€” paper count, ranking weights, review mode, etc.
6. **First search** โ€” `pool_refresh()` populates the paper pool

All settings can be updated at any time through conversation:
- "Push 8 papers next time"
- "Add a new topic: RAG retrieval"
- "Enable dual-AI blind review"
- "Increase recency weight"

---

## โš™๏ธ Configuration Reference

All parameters are set via `configure()` or `add_topic()` โ€” no manual file editing needed.

### Research Topics (`add_topic` / `manage_topics`)

| Parameter | Description | Default |
|-----------|-------------|---------|
| `key` | Topic identifier (e.g. `"llm-reasoning"`) | โ€” |
| `label` | Display name (e.g. `"LLM Reasoning"`) | โ€” |
| `keywords` | Search keywords, 3โ€“5 recommended | โ€” |
| `weight` | Topic priority 0.0โ€“1.0 (higher = more papers) | `1.0` |
| `blocked` | Temporarily disable without deleting | `false` |

### Paper Count & Review (`configure`)

| Parameter | Options | Default | Description |
|-----------|---------|---------|-------------|
| `paper_count_value` | any integer | `6` | Papers per push |
| `paper_count_mode` | `"at_most"` / `"at_least"` / `"exactly"` | `"at_most"` | Count mode |
| `picks_per_reviewer` | any integer | `5` | Shortlist size per reviewer |
| `review_mode` | `"single"` / `"dual"` | `"single"` | Single AI or dual blind review |
| `custom_focus` | free text | `""` | Custom selection criteria |

> ๐Ÿ’ก **Dual blind review**: two independent AI reviewers each shortlist papers; a chief reviewer makes the final push/overflow/discard call. Papers that don't make the cut are held for the next cycle rather than discarded. Enable with `configure(review_mode="dual")`.

### Ranking Weights (`configure`)

Controls paper scoring. The four weights should sum to approximately 1.0.

| Parameter | Measures | Default |
|-----------|----------|---------|
| `w_relevance` | Keyword and topic match | `0.55` |
| `w_recency` | How recently the paper was published | `0.20` |
| `w_impact` | Citation count (log-normalized) | `0.15` |
| `w_novelty` | Whether this is the first appearance | `0.10` |

> Example: "Prioritize recent papers" โ†’ `configure(w_recency=0.35, w_relevance=0.40)`

### Scraper / Abstract Extraction Delegate (`configure`)

Abstract extraction is the most token-intensive step. It runs on the main agent by default, but can be delegated to a cheaper model to cut costs significantly.

| Parameter | Value | Description |
|-----------|-------|-------------|
| `summarizer` | `"self"` | Main agent handles extraction (most expensive) |
| | agent name (e.g. `"scraper"`) | Delegate to a low-cost sub-agent |
| | API URL | Call an external LLM API (DeepSeek, Ollama, etc.) |

> ๐Ÿ”ง **Strongly recommended**: for 30+ papers, frontier model costs add up fast. A $0.14/M-token model handles extraction just as well. Set this with `configure(summarizer="scraper")`.

### Paper Pool & Scan Batches (`configure`)

| Parameter | Description | Default |
|-----------|-------------|---------|
| `scan_batches` | Split the paper pool into N batches, reviewed over N+1 days | `2` (3 days) |

`pool_refresh()` searches all 11 APIs and fills the pool. The pool is then split into batches for daily AI review โ€” avoiding a single 60+ paper dump.

- `scan_batches=2` (default): review first half on day 1, second half on day 2, finalize on day 3
- `scan_batches=3`: review one-third per day, finalize on day 4

When all batches are reviewed, the pool is exhausted and the next run triggers a fresh API search automatically.

### Delivery Platforms (Environment Variables)

| Platform | Environment variables | `platform` value |
|----------|-----------------------|------------------|
| Telegram | `TELEGRAM_BOT_TOKEN` + `TELEGRAM_CHAT_ID` | `"telegram"` |
| Discord | `DISCORD_WEBHOOK_URL` | `"discord"` |
| Feishu | `FEISHU_WEBHOOK_URL` | `"feishu"` |
| WeCom | `WECOM_WEBHOOK_URL` | `"wecom"` |

> โš ๏ธ **Important**: set environment variables in the MCP client config `env` field, not as system environment variables. Otherwise `send_push()` cannot access the webhook URL and the AI may generate scripts that call webhooks directly, causing encoding issues.

Config example (WeCom + Claude Desktop):

```json
{
  "mcpServers": {
    "paper-distill": {
      "command": "uvx",
      "args": ["paper-distill-mcp"],
      "env": {
        "WECOM_WEBHOOK_URL": "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=YOUR_KEY"
      }
    }
  }
}
```

Restart the MCP client after editing the config.

Push message format (fixed):

```
1. Paper Title (Year)
   Journal Name
   - One-sentence summary
   - Why it was selected
   https://doi.org/...
```

### Paper Library Site (`configure`)

Personal paper library website, auto-updated on every push. Built on Astro + Vercel (free tier).

| Parameter | Description |
|-----------|-------------|
| `site_deploy_hook` | Vercel deploy hook URL (triggers site rebuild) |
| `site_repo_path` | Local path to the paper-library repository |

Setup steps (the AI agent will guide you):
1. Create a repo from the [paper-library-template](https://github.com/Eclipse-Cj/paper-library-template)
2. Connect to Vercel and deploy
3. Create a deploy hook in Vercel (Settings > Git > Deploy Hooks)
4. Tell the agent the hook URL โ†’ saved via `configure(site_deploy_hook=...)`

After setup, every `finalize_review()` call pushes the digest JSON to the site repo and triggers a Vercel rebuild. The site updates in ~30 seconds.

### Zotero Integration

Save papers to Zotero with one command. Requires a Zotero account and API key.

**Getting credentials:**
1. **API Key**: go to [zotero.org/settings/keys/new](https://www.zotero.org/settings/keys/new) โ†’ check "Allow library access" + "Allow write access" โ†’ Save Key
2. **Library ID**: go to [zotero.org/settings/keys](https://www.zotero.org/settings/keys) โ†’ your userID is shown at the top

**Add to MCP client config:**

```json
{
  "mcpServers": {
    "paper-distill": {
      "command": "uvx",
      "args": ["paper-distill-mcp"],
      "env": {
        "ZOTERO_LIBRARY_ID": "your userID",
        "ZOTERO_API_KEY": "your API key"
      }
    }
  }
}
```

After setup, reply `collect 1 3` after a push to save papers 1 and 3 to Zotero, automatically sorted into per-topic folders.

### All Environment Variables

| Variable | Description | Required |
|----------|-------------|----------|
| `OPENALEX_EMAIL` | Increases OpenAlex API rate limit; also used for Unpaywall | optional |
| `CORE_API_KEY` | CORE API key ([free registration](https://core.ac.uk/services/api)) | optional |
| `DEEPSEEK_API_KEY` | Enhanced search via DeepSeek | optional |
| `ZOTERO_LIBRARY_ID` + `ZOTERO_API_KEY` | Save papers to Zotero | optional |
| `SITE_URL` | Paper library website URL | optional |
| `PAPER_DISTILL_DATA_DIR` | Data directory | default: `~/.paper-distill/` |

---

## ๐Ÿ› ๏ธ Tools (19 total)

### Setup & Configuration

| Tool | Description |
|------|-------------|
| `setup()` | **First call** โ€” detects fresh install and returns guided initialization instructions |
| `add_topic(key, label, keywords)` | Add a research topic with search keywords |
| `configure(...)` | Update any setting: paper count, ranking weights, review mode, etc. |

### Search & Curation

| Tool | Description |
|------|-------------|
| `search_papers(query)` | Parallel search across 11 sources |
| `rank_papers(papers)` | 4-dimensional weighted scoring |
| `filter_duplicates(papers)` | Deduplicate against previously pushed papers |

### Daily Pipeline (paper pool mode)

| Tool | Description |
|------|-------------|
| `pool_refresh(topic?)` | Search all 11 APIs and build the paper pool |
| `prepare_summarize(custom_focus?)` | Generate AI abstract extraction prompt |
| `prepare_review(dual?)` | Generate review prompt โ€” AI makes push/overflow/discard decisions |
| `finalize_review(selections)` | Process AI decisions, update pool, output push message |
| `pool_status()` | Pool status: count, scan day, exhausted or not |
| `collect(paper_indices)` | Save papers to Zotero + generate Obsidian notes |

### Session & Output

| Tool | Description |
|------|-------------|
| `init_session` | Detect delivery platform and load research context |
| `load_session_context` | Load historical research context |
| `generate_digest(papers, date)` | Generate output files (JSONL, site, Obsidian) |
| `send_push(date, papers, platform)` | Deliver to Telegram / Discord / Feishu / WeCom |
| `collect_to_zotero(paper_ids)` | Save to Zotero via DOI |
| `manage_topics(action, topic)` | List / disable / enable / reweight topics |
| `ingest_research_context(text)` | Inherit research context across sessions |

---

## ๐Ÿ—๏ธ Architecture

```
AI client (Claude Code / Codex CLI / Gemini CLI / Cursor / ...)
    โ†“ MCP (stdio or HTTP)
paper-distill-mcp
    โ”œโ”€โ”€ search/         โ€” 11-source academic search (with OA full-text enrichment)
    โ”œโ”€โ”€ curate/         โ€” scoring + deduplication
    โ”œโ”€โ”€ generate/       โ€” output (JSONL, Obsidian, site)
    โ”œโ”€โ”€ bot/            โ€” push formatting (4 platforms)
    โ””โ”€โ”€ integrations/   โ€” Zotero API
```

The server does not call any LLM internally. Search, ranking, and deduplication are pure data operations. Intelligence comes from your AI client.

---

## ๐Ÿ“– Paywalled Papers & Open Access

The system searches all papers by default (including subscription journals) and maximizes free full-text access through:

1. **CORE** โ€” world's largest OA aggregator (200M+ papers), covering author self-archived versions from institutional repositories
2. **Unpaywall** โ€” after merging results, automatically looks up legal free PDFs via DOI (preprints, green OA, author versions)

For papers with no free version, the system returns a DOI link. If you have institutional VPN access, clicking the DOI link while connected is usually enough โ€” publishers identify your institution by IP.

> `open_access_url` priority: arXiv > CORE > Unpaywall > OpenAlex > Semantic Scholar > Papers with Code

---

## โ“ FAQ

### Review stage hangs / no response for 30+ minutes

**Symptom**: the review prompt generated by `prepare_review()` causes the AI client to hang or time out.

**Cause**: too many candidate papers in the pool (e.g. 80โ€“100), making the prompt exceed the client's context window or output token limit. VS Code Copilot and some IDE plugins have limited context capacity.

**Solutions** (pick one):
1. **Increase `scan_batches`** (recommended) โ€” split the pool into more batches:
   ```
   configure(scan_batches=5)
   ```
2. **Reduce topics or keywords** โ€” fewer topics โ†’ fewer search results โ†’ smaller pool.
3. **Switch to a higher-context client** โ€” Claude Code (200k), Claude Desktop (200k), or Cursor handle long prompts better.

### Install error: `Requires-Python >=3.10`

Python 3.10+ is required. macOS ships with Python 3.9 by default โ€” install a newer version with `brew install python@3.13` or use `uv`.

### Docker image fails to pull (mainland China)

`ghcr.io` is blocked in mainland China. Use pip with a Chinese mirror:

```bash
pip install paper-distill-mcp -i https://pypi.tuna.tsinghua.edu.cn/simple
```

---

## ๐Ÿง‘โ€๐Ÿ’ป Development

```bash
git clone https://github.com/Eclipse-Cj/paper-distill-mcp.git
cd paper-distill-mcp
python3 -m venv .venv && .venv/bin/pip install --upgrade pip && .venv/bin/pip install -e .
python tests/test_mcp_smoke.py   # 9 tests, no network required
```

---

## ๐Ÿ“„ License

This project is licensed under **AGPL-3.0**. See [LICENSE](LICENSE) for details.

**Unauthorized commercial use is prohibited.** For commercial licensing inquiries, contact the author.

---

## ๐Ÿ“ฌ Contact

- Email: vertex.cj@gmail.com
- GitHub Issues: [Eclipse-Cj/paper-distill-mcp/issues](https://github.com/Eclipse-Cj/paper-distill-mcp/issues)

Bug reports and feature requests are welcome. The project is in active early development โ€” thank you for your patience and support ๐Ÿ™

TDQS

A3.7/5.0

Scored across 19 tools

Disambiguation4/5

Most tools have distinct purposes, but 'collect' and 'collect_to_zotero' overlap in adding papers to Zotero. Descriptions help differentiate, but an agent might still misselect.

Naming Consistency4/5

Tool names mostly follow a verb_noun pattern (e.g., add_topic, search_papers), but a few are single verbs (collect, configure) or have longer phrases (load_session_context). Still readable and predictable.

Tool Count5/5

19 tools cover a complex research pipeline (search, review, collection, push) without being excessive. Each tool serves a clear step in the workflow.

Completeness5/5

The tool surface covers the full lifecycle: setup, session management, topic management, paper search, pool management, review, summarization, Zotero collection, and messaging push. No obvious gaps for the stated purpose.

Maintenance

ActivityInactive
ResponsivenessNo issues