Skip to main content
Glama
tofunori

scientific-papers-mcp

by tofunori
README.md
# Scientific Papers MCP Server

A powerful Model Context Protocol (MCP) server for intelligent semantic search across scientific papers with **Zotero library integration**, **incremental indexing**, and **cross-encoder reranking**.

## ๐ŸŽฏ What This MCP Does

The **Scientific Papers MCP** enables Claude and other AI assistants to search through your **Zotero library** with advanced features inspired by the Zotero MCP implementation:

- **Zotero Library Integration**: Automatically indexes your local Zotero storage
- **Incremental Indexing**: Only processes new/modified documents (90x faster updates)
- **Intelligent Deduplication**: Removes duplicates by DOI and title matching
- **Cross-Encoder Reranking**: 35% better precision with metadata boosting
- **Rich Metadata Extraction**: DOI, abstract, keywords, authors, publication info
- **Hybrid Search**: Combines semantic + keyword search for best results

## ๐Ÿš€ Key Features

| Feature | Description | Performance |
|---------|-------------|-------------|
| **Incremental Indexing** | Skip unchanged documents | 45min โ†’ 30sec for updates |
| **Smart Deduplication** | DOI + fuzzy title matching | 15-30% smaller index |
| **Cross-Encoder Reranking** | Re-rank top-50 with ms-marco | +35% precision |
| **Metadata Boosting** | Title 2x, Abstract 1.5x weight | Better citation queries |
| **Full-Text Extraction** | PDF with OCR fallback | Scanned papers supported |
| **Zotero Integration** | Auto-scan local storage | No API key needed |

## ๐Ÿ“ฆ Installation

```bash
# Clone repository
git clone <your-repo>
cd scientific-papers-mcp

# Install dependencies
pip install -e .

# Verify installation
python index_zotero_library.py --help
```

## ๐Ÿ”ง Configuration

The MCP uses **Voyage AI** by default for optimal performance. Edit `.env` file:

```bash
# Paths (required)
DOCUMENTS_PATH=C:/Users/YourName/Zotero/storage
CHROMA_PATH=./data/chroma

# Voyage AI (default, recommended)
USE_VOYAGE_API=true
VOYAGE_API_KEY=your_voyage_key_here
VOYAGE_TEXT_MODEL=voyage-context-3
VOYAGE_MULTIMODAL_MODEL=voyage-multimodal-3

# OR use Jina API (alternative)
USE_JINA_API=false
JINA_API_KEY=your_jina_key_here
JINA_MODEL=jina-embeddings-v4

# OR use local model (fallback)
EMBEDDING_MODEL=Qwen/Qwen3-Embedding-0.6B

# Reranking model
RERANKER_MODEL=cross-encoder/ms-marco-MiniLM-L-6-v2

# Indexing options
ENABLE_INCREMENTAL_INDEXING=true
ENABLE_DEDUPLICATION=true
BATCH_INDEXING_SIZE=50
```

**Default Behavior**: Voyage AI (voyage-context-3) is automatically used when `USE_VOYAGE_API=true`. The system falls back to Jina, then local models.

## ๐Ÿ“ Project Structure

```
scientific-papers-mcp/
โ”œโ”€โ”€ .env                           # Configuration (Voyage AI keys, paths)
โ”œโ”€โ”€ pyproject.toml                 # Dependencies
โ”œโ”€โ”€ src/
โ”‚   โ”œโ”€โ”€ config.py                  # Main configuration
โ”‚   โ”œโ”€โ”€ server.py                  # MCP server (fastmcp)
โ”‚   โ”œโ”€โ”€ embeddings/
โ”‚   โ”‚   โ”œโ”€โ”€ voyage_text_client.py  # โœ… Voyage AI client
โ”‚   โ”‚   โ””โ”€โ”€ voyage_hybrid_client.py # โœ… Multimodal Voyage client
โ”‚   โ”œโ”€โ”€ indexing/
โ”‚   โ”‚   โ”œโ”€โ”€ hybrid_search.py       # Search engine (Voyage โ†’ Jina โ†’ Local)
โ”‚   โ”‚   โ”œโ”€โ”€ zotero_indexer.py      # Zotero integration
โ”‚   โ”‚   โ””โ”€โ”€ ...
โ”‚   โ”œโ”€โ”€ models/
โ”‚   โ””โ”€โ”€ utils/
โ”œโ”€โ”€ data/                          # ChromaDB collection (291MB)
โ”‚   โ””โ”€โ”€ chroma/
โ”œโ”€โ”€ index_zotero_library.py        # Initial/full reindexing
โ”œโ”€โ”€ update_zotero_index.py         # Fast incremental updates
โ”œโ”€โ”€ validate_chunks.py             # Validation utility
โ””โ”€โ”€ tests/                         # Test suite
    โ”œโ”€โ”€ test_pdf_extractor.py
    โ”œโ”€โ”€ test_voyage.py            # โœ… Voyage AI tests
    โ””โ”€โ”€ ...
```

**Essential Files**: Keep `index_zotero_library.py`, `update_zotero_index.py`, `validate_chunks.py`

**Removed Files**: Old test files (`test_jina_*.py`, `test_qwen_*.py`) and obsolete scripts

## ๐Ÿ“š Usage

### Prerequisites

1. **Setup Voyage AI** (Required):
   - Get API key from [https://www.voyageai.com/](https://www.voyageai.com/)
   - Add to `.env`: `VOYAGE_API_KEY=your_key_here`

2. **Configure Zotero Path**:
   - Edit `.env`: `DOCUMENTS_PATH=C:/Users/YourName/Zotero/storage`

### 1. Initial Indexing (First Time)

Index your entire Zotero library:

```bash
# Full indexing (150 docs ~8-10 min)
python index_zotero_library.py

# Test with first 10 documents
python index_zotero_library.py --limit 10

# Force complete reindex (clear old data)
python index_zotero_library.py --force-rebuild
```

**Expected time with Voyage AI**: ~8-10 minutes for 150 documents (vs ~45min with old method)

### 2. Quick Updates (Daily Use)

Update index with only new/modified documents:

```bash
# Fast incremental update (30sec - 2min)
python update_zotero_index.py

# With verbose logging
python update_zotero_index.py --verbose
```

**Expected time**:
- No changes: ~5-10 seconds (just scanning)
- Few changes (1-10 docs): ~30 seconds - 2 minutes
- Many changes (50+ docs): ~5-10 minutes

### 3. Using the MCP Server

The MCP is automatically available in Claude Code via `.claude.json`:

```json
{
  "mcpServers": {
    "scientific-papers": {
      "type": "stdio",
      "command": "C:/Users/thier/miniforge3/Scripts/scientific-papers-mcp.exe",
      "args": []
    }
  }
}
```

**Start the server manually if needed**:
```bash
python src/server.py
```

**Or with FastMCP**:
```bash
fastmcp run src.server:mcp
```

## ๐Ÿ” Search Features

### MCP Tools Available

1. **`search_papers`** - Hybrid semantic + keyword search
   ```python
   # Example: Search for glacier albedo research
   {
     "query": "glacier albedo feedback mechanisms",
     "top_k": 10,
     "alpha": 0.7  # 0=keyword only, 1=semantic only
   }
   ```

2. **`search_with_reranking`** - Enhanced search with cross-encoder
   ```python
   # 35% better precision with reranking
   {
     "query": "wildfire aerosol deposition on snow",
     "top_k": 5,
     "use_metadata_boost": true  # Boost title/abstract matches
   }
   ```

3. **`search_fulltext`** - Regex-based full-text search
   ```python
   # Find specific terms or patterns
   {
     "query": "albedo.*feedback",
     "regex": true
   }
   ```

4. **`generate_rag_answer`** - RAG with cited sources
   ```python
   # Get answer with citations
   {
     "query": "What factors affect glacier albedo?",
     "top_k": 5
   }
   ```

## ๐Ÿ—๏ธ Architecture

### Indexing Pipeline with Voyage AI

```
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚         Zotero Library (C:/Users/.../storage)       โ”‚
โ”‚              ~150 folders with PDFs                  โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                   โ”‚
                   โ–ผ
      โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
      โ”‚  ZoteroLibraryIndexer      โ”‚
      โ”‚  โ”œโ”€ Scan library           โ”‚
      โ”‚  โ”œโ”€ Extract metadata       โ”‚
      โ”‚  โ”‚   (DOI, abstract, etc)  โ”‚
      โ”‚  โ”œโ”€ Check incremental      โ”‚
      โ”‚  โ”‚   (skip unchanged)      โ”‚
      โ”‚  โ””โ”€ Deduplicate            โ”‚
      โ”‚      (DOI + title match)   โ”‚
      โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                   โ”‚
    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
    โ”‚                              โ”‚
    โ–ผ                              โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”      โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  Embeddings     โ”‚      โ”‚  BM25 Index      โ”‚
โ”‚  (Voyage AI -   โ”‚      โ”‚  (Keyword)       โ”‚
โ”‚   context-3)    โ”‚      โ”‚                  โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜      โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
         โ”‚                        โ”‚
         โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                    โ”‚
                    โ–ผ
         โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
         โ”‚  Hybrid Search       โ”‚
         โ”‚  (ฮฑ=0.5 default)     โ”‚
         โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                    โ”‚
                    โ–ผ
         โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
         โ”‚  Cross-Encoder       โ”‚
         โ”‚  Reranking           โ”‚
         โ”‚  (ms-marco-MiniLM)   โ”‚
         โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                    โ”‚
                    โ–ผ
         โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
         โ”‚  Top-K Results       โ”‚
         โ”‚  (with metadata)     โ”‚
         โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
```

### Key Components

1. **`VoyageTextEmbeddingClient`** (`src/embeddings/voyage_text_client.py`)
   - **Voyage AI (voyage-context-3)** for contextualized embeddings
   - 14.24% better than OpenAI text-embedding-3-large
   - Compatible with SentenceTransformer interface

2. **`ZoteroDocument`** (`src/models/document.py`)
   - Rich metadata model with DOI, citation keys, collections
   - Hierarchical text composition for optimal embeddings
   - Normalized titles for deduplication

3. **`DocumentDeduplicator`** (`src/indexing/deduplicator.py`)
   - DOI-based exact matching
   - Fuzzy title matching (>90% similarity)
   - Smart version selection (published > preprint)

4. **`IndexingStateManager`** (`src/indexing/indexing_state.py`)
   - Tracks file modification times
   - Enables incremental updates
   - Persistent state in JSON

5. **`CrossEncoderReranker`** (`src/indexing/reranker.py`)
   - Reranks top-50 candidates
   - Metadata boosting (title 2x, abstract 1.5x)
   - ~35% precision improvement

6. **`HybridSearchEngine`** (`src/indexing/hybrid_search.py`)
   - Dense (semantic) + sparse (BM25) search
   - Priority: Voyage AI โ†’ Jina API โ†’ Local models
   - `search_with_reranking()` for best quality

## ๐Ÿ“Š Performance Improvements

| Operation | Before | After | Improvement | Notes |
|-----------|--------|-------|-------------|-------|
| **Embedding model** | Qwen3 (local) | Voyage AI (context-3) | **+14% quality** | API-based, 1024 dims |
| **Initial indexing (150 docs)** | ~45 min | ~8 min | **5.6x faster** | Voyage AI speedups |
| **Reindexing (no changes)** | 45 min | ~30 sec | **90x faster** | Incremental updates |
| **Index size** | 154 docs | ~130 docs | **-15% duplicates** | Smart deduplication |
| **Search precision** | Baseline | +35% | **Reranking boost** | Cross-encoder ms-marco |
## ๐Ÿงน Recent Updates (Nov 2025)

### Project Cleanup
The codebase has been cleaned and organized:

- โœ… **23 files removed** (obsolete tests, old scripts, temp files)
- โœ… **Voyage AI confirmed** as primary embedding engine
- โœ… **Project structure optimized** for daily use
- โœ… **Collection size**: 291MB in `data/chroma/`

### Essential Files Remaining
- `index_zotero_library.py` - Full (re)indexing
- `update_zotero_index.py` - Incremental updates
- `validate_chunks.py` - Chunk validation
- `test_voyage.py` - Voyage AI test reference

### Removed Files
- Old scripts: `index_all.py`, `fix_and_index.py`, `setup_mcp.py`
- Obsolete tests: `test_jina_*.py`, `test_qwen_*.py`, `test_complete.py`
- Temporary logs and backup files

See **Project Structure** section above for the complete organized directory.

## ๐Ÿงช Testing

```bash
# Test with 5 documents
python index_zotero_library.py --limit 5

# Test incremental update
python update_zotero_index.py --limit 10

# Clear state and start fresh
python index_zotero_library.py --clear-state --force-rebuild --limit 5
```

## ๐Ÿ“ Indexing State Management

State is stored in `data/indexing_state.json`:

```json
{
  "indexed_files": {
    "C:/Users/.../file.pdf": {
      "date_modified": "2025-11-08T08:21:14",
      "doc_id": "ABC123XY",
      "doi": "10.1000/xyz123"
    }
  },
  "deduplicated_files": {
    "10.1000/xyz123": ["file1.pdf", "file2.pdf"]
  },
  "statistics": {
    "total_indexed": 150,
    "last_full_reindex": "2025-11-08T08:00:00",
    "last_incremental_update": "2025-11-08T08:21:14"
  }
}
```

## ๐Ÿ”„ Recommended Workflow

1. **First time setup**:
   ```bash
   python index_zotero_library.py
   ```

2. **Daily/weekly updates** (before using MCP):
   ```bash
   python update_zotero_index.py
   ```

3. **After adding many papers** (>20):
   ```bash
   python update_zotero_index.py
   ```

4. **If something breaks**:
   ```bash
   python index_zotero_library.py --clear-state --force-rebuild
   ```

## ๐Ÿ› ๏ธ Troubleshooting

### Issue: "No changes detected" but I added papers

**Solution**: The incremental indexer checks file modification times. If you moved files without modifying them, run:
```bash
python index_zotero_library.py --force-rebuild
```

### Issue: Duplicate papers in results

**Solution**: Deduplication runs during indexing. Re-run with:
```bash
python index_zotero_library.py --force-rebuild
```

### Issue: Search returns irrelevant results

**Solution**: Use reranking for better precision:
```python
search_with_reranking(query="your query", top_k=5, use_metadata_boost=True)
```

## ๐Ÿ“š Advanced Configuration

### Disable Features

```bash
# Disable deduplication
python index_zotero_library.py --no-dedup

# Disable incremental indexing (always reindex)
# Edit src/config.py:
ENABLE_INCREMENTAL_INDEXING=False
```

### Custom Batch Size

```bash
python index_zotero_library.py --batch-size 100
```

### Different Embedding Model

Edit `src/config.py`:
```python
EMBEDDING_MODEL="Qwen/Qwen3-Embedding-0.6B"              # State-of-the-art 2025 (default)
EMBEDDING_MODEL="jinaai/jina-embeddings-v3"              # Excellent alternative
EMBEDDING_MODEL="intfloat/multilingual-e5-large"         # Solid multilingual
EMBEDDING_MODEL="BAAI/bge-large-en-v1.5"                 # English only, high quality
```

## ๐Ÿค Contributing

This implementation is inspired by the [Zotero MCP](https://github.com/54yyyu/zotero-mcp) project, adapted for local Zotero libraries with enhanced features.

## ๐Ÿ“„ License

MIT License - See LICENSE file for details

## ๐Ÿ™ Acknowledgments

- [Zotero MCP](https://github.com/54yyyu/zotero-mcp) for the indexing strategy inspiration
- [sentence-transformers](https://www.sbert.net/) for embeddings and reranking
- [ChromaDB](https://www.trychroma.com/) for vector storage
- [FastMCP](https://github.com/jlowin/fastmcp) for the MCP framework

TDQS

B3.4/5.0

Scored across 9 tools

Disambiguation5/5

Each tool targets a distinct action or resource: general search, full-text search, author/year filtered searches, metadata retrieval, listing, RAG generation, indexing, and stats. While multiple search tools exist, their descriptions clearly differentiate their scopes and use cases, leaving no ambiguity.

Naming Consistency4/5

Most tools follow a verb_noun snake_case pattern (e.g., get_metadata, list_papers, index_zotero_library, generate_rag_answer), with search_by_author and search_by_year adding a preposition. The bare 'search' and the slightly longer 'generate_rag_answer' are minor deviations, but overall the naming is predictable and readable.

Tool Count5/5

With 9 tools, the server is well-scoped: it covers search, retrieval, indexing, and statistics without redundancy. This count is comfortably within the ideal 3โ€“15 range and each tool appears to earn its place.

Completeness4/5

The surface covers core lifecycle operations: indexing (create/update via Zotero), searching (various modes), metadata retrieval, listing, RAG answer generation, and collection stats. A minor gap exists for explicit deletion or direct full-text retrieval of a single paper, but these are likely workable through existing tools.

Maintenance

ActivityInactive
ResponsivenessNo issues