# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Project Overview

Academic Search MCP is a Model Context Protocol (MCP) server that enables Claude and other LLM clients to search and download academic papers from 12 platforms: arXiv, PubMed, bioRxiv, medRxiv, Google Scholar, IACR, Semantic Scholar, CrossRef, OpenAlex, CORE, SSRN, and CyberLeninka.

Fork of [openags/paper-search-mcp](https://github.com/openags/paper-search-mcp) with additional platforms.

## Commands

```bash
# Install dependencies (editable mode)
uv venv && source .venv/bin/activate  # Windows: .venv\Scripts\activate
uv add -e .

# Run tests
pytest tests/

# Run specific test
pytest tests/test_arxiv.py

# Run MCP server
python -m paper_search_mcp.server
```

## Architecture

### Core Components

- **`server.py`** - MCP server entry point using FastMCP. Declares 18 tools with `@mcp.tool()` decorator
- **`paper.py`** - `Paper` dataclass for standardized output format across all platforms
- **`pdf_utils.py`** - PDF text extraction (pdftotext with PyPDF2 fallback)
- **`academic_platforms/`** - Platform-specific searchers, each inheriting from `PaperSource` base class

### Data Flow

1. MCP Tool receives request → 2. Platform searcher makes API call → 3. Response parsed into `Paper` objects → 4. Serialized via `paper.to_dict(abstract_limit)`

### Base Class Pattern

All platform searchers inherit from `PaperSource` and implement:
- `search(query, **kwargs) -> List[Paper]`
- `download_pdf(paper_id, save_path) -> str`
- `read_paper(paper_id, save_path) -> str`

### Tool Categories

- **Search**: `search_{platform}` for each of the 12 platforms
- **Metadata**: `get_paper_by_doi`, `get_crossref_paper_by_doi`, `get_openalex_work_by_id`
- **Citations**: `get_openalex_references`, `get_openalex_citations`
- **Authors**: `search_authors`, `get_author_papers`
- **Download/Read**: `download_paper`, `read_paper` (unified interface)

## Conventions

- **Date format**: Always YYYY-MM-DD for `date_from`/`date_to` parameters
- **Abstract limit**: `abstract_limit` param controls truncation (0=omit, -1=full, default=200)
- **Searcher naming**: Classes are `{Platform}Searcher` (e.g., `ArxivSearcher`)
- **Error handling**: Return empty lists on API errors, use logging, graceful degradation

## Adding a New Platform

1. Create `paper_search_mcp/academic_platforms/{platform}.py`
2. Create class inheriting from `PaperSource`
3. Implement `search()`, `download_pdf()`, `read_paper()` methods
4. Add searcher instance to `SEARCHERS` dict in `server.py`
5. Create `@mcp.tool()` function for `search_{platform}`
6. Add test file `tests/test_{platform}.py`
