pubmed-proxy-mcp
by dingyifei
README.md
# pubmed-proxy-mcp
MCP server and CLI for NCBI PubMed E-utilities with built-in rate limiting.
## Features
- **PubMed search** — Full query syntax with field tags, boolean operators, date ranges, pagination, and sorting
- **Article metadata** — Fetch title, authors, journal, year, DOI, and abstract for any PMID
- **Batch lookup** — Fetch metadata for up to 20 PMIDs at once
- **Full-text retrieval** — Get full article text from PubMed Central (when available)
- **BibTeX citations** — Generate BibTeX entries from PMID or DOI (with CrossRef fallback)
- **Bibliography management** — Add citations to .bib files, search, list, validate, and batch-initialize
- **BibTeX correction** — Correct .bib files against authoritative NCBI/CrossRef metadata with smart field comparison
- **Rate limiting** — Built-in token bucket (10 req/s NCBI, 1 req/s CrossRef)
## Quick Start for LLM Agents
If you are an LLM agent that has been pointed to this tool for literature research, follow these steps:
**1. Check that the NCBI API key is configured (recommended but optional):**
```bash
echo $NCBI_API_KEY
```
If empty, commands will still work but at a lower rate limit (3 req/s instead of 10 req/s).
To get the faster rate, set an API key:
```bash
export NCBI_API_KEY="your-api-key"
```
Or pass it per-command with `--api-key <key>`.
To obtain a key: create an account at https://www.ncbi.nlm.nih.gov/account/, then go to
Settings > API Key Management > Create an API Key.
**2. If using MCP tools** (e.g. via Claude Desktop), the MCP server must be configured
in the client. The CLI commands below work standalone without the MCP server.
**3. Typical research workflow:**
```bash
# Search for relevant articles
pubmed-proxy search "your topic here" --summaries --max 10
# Get full metadata + abstract for interesting PMIDs
pubmed-proxy lookup 23381990
# Get full-text if available (PMC only)
pubmed-proxy lookup --full-text 23381990
# Generate BibTeX citations for references
pubmed-proxy citation 23381990 16855256 >> references.bib
```
## Installation
```bash
pip install -e . # Core (MCP server + basic CLI)
pip install -e ".[bib]" # With bibliography management (bibtexparser)
```
Requires Python 3.10+. An NCBI API key is recommended for higher rate limits (see Quick Start above).
## MCP Server
Run as an MCP server (stdio transport). Works without an API key (3 req/s) or with one (10 req/s):
```bash
python -m pubmed_proxy_mcp
```
### MCP Tools
| Tool | Description |
|------|-------------|
| `search_articles` | Search PubMed with full query syntax, filters, pagination, optional summaries |
| `get_article_metadata` | Get metadata + abstract for a single PMID |
| `batch_get_metadata` | Batch fetch metadata for up to 20 PMIDs |
| `get_full_text` | Get full-text article content via PMC |
| `get_citation_bibtex` | Get BibTeX entry for a PMID |
| `get_citation_by_doi` | Get BibTeX entry for a DOI |
| `search_doi_to_pmid` | Convert DOI to PMID |
| `bib_add_citation` | Fetch BibTeX and add to a .bib file |
| `bib_search` | Search across all .bib files for a keyword |
| `bib_list` | List entries in .bib files |
| `bib_show` | Show full BibTeX for a citekey |
| `bib_validate` | Validate citekeys in markdown against .bib |
| `bib_init` | Scan markdown for PMIDs and create .bib files |
| `bib_stats` | Summary statistics for .bib files |
| `bib_correct` | Correct a .bib file against NCBI/CrossRef metadata |
## CLI Reference
An NCBI API key is optional but recommended. Without one, the rate limit is 3 req/s
instead of 10 req/s. If not set, a warning is printed to stderr but commands still work.
```bash
echo $NCBI_API_KEY # If empty, commands work at 3 req/s
```
To use a key, either `export NCBI_API_KEY=<key>` or pass `--api-key <key>` to any command.
### `pubmed-proxy server` — Shared Rate-Limiting Proxy
**Note:** The proxy server is only needed when running multiple CLI commands in parallel
(e.g. from multiple subagents). For single-command usage, the CLI works fine on its own
with its built-in rate limiter — no server required.
When running parallel commands, each process has its own rate limiter. To share a single
rate limiter across all processes, start the proxy server first:
```bash
# Terminal 1: start the shared proxy server
pubmed-proxy server # default port 8765
pubmed-proxy server --port 9000 # custom port
```
Then point CLI commands at it with `--server`:
```bash
# Terminal 2+: commands share the proxy's rate limiter
pubmed-proxy --server http://localhost:8765 search "CRISPR" --max 10
pubmed-proxy --server http://localhost:8765 lookup 23381990
```
The MCP server also auto-detects the proxy: if `pubmed-proxy server` is running on
port 8765 when the MCP server starts, it will use the shared rate limiter automatically.
Set `PUBMED_PROXY_URL` to override the proxy URL for the MCP server.
**Arguments:**
| Argument | Required | Description |
|----------|----------|-------------|
| `--port PORT` | No | Server port (default: 8765) |
**Global `--server` flag:**
| Argument | Required | Description |
|----------|----------|-------------|
| `--server URL` | No | Proxy server URL (e.g. `http://localhost:8765`). When set, all NCBI requests go through the proxy for shared rate limiting |
---
### `pubmed-proxy search` — Search PubMed
Search PubMed using full NCBI query syntax. Supports field-specific tags, boolean
operators (`AND`, `OR`, `NOT`), and MeSH terms.
**Arguments:**
| Argument | Required | Description |
|----------|----------|-------------|
| `query` | Yes | PubMed search query string |
| `--max N` | No | Maximum results to return (default: 20, max: 10000) |
| `--page N` | No | Page number, 0-indexed (default: 0). Page 1 with --max 10 starts at result 11 |
| `--sort ORDER` | No | Sort order: `relevance` (default), `pub_date`, `author`, `journal` |
| `--date-type TYPE` | No | Date field to filter: `pdat` (publication date) or `edat` (Entrez date) |
| `--min-date DATE` | No | Start date filter, format `YYYY/MM/DD` or `YYYY` |
| `--max-date DATE` | No | End date filter, format `YYYY/MM/DD` or `YYYY` |
| `--summaries` | No | Include title, authors, journal, and year for each result |
**Query syntax examples:**
- `"E. coli iron regulation"` — keyword search
- `"Smith[Author] AND cancer[Title]"` — field-specific search
- `"CRISPR[Title] AND 2024[PDAT]"` — with publication date tag
- `"breast cancer AND review[PT]"` — filter by publication type
- `"Nature[Journal] AND gene editing"` — journal-specific search
**Output without `--summaries`:**
```
Found 1543 results (showing 1-20):
38471234
38469012
...
```
**Output with `--summaries`:**
```
Found 1543 results (showing 1-5):
PMID: 38471234
Title: CRISPR-Cas9 gene editing in E. coli
Authors: Smith, JD; Doe, A; Lee, B (+4 more)
Journal: Nature Biotechnology (2024)
PMID: 38469012
Title: Another relevant paper
Authors: Jones, C; Wang, X
Journal: Science (2024)
```
**Examples:**
```bash
# Basic keyword search
pubmed-proxy search "antibiotic resistance"
# Field-specific with boolean operators
pubmed-proxy search "Smith[Author] AND cancer[Title]"
# Filter by date range (publication date)
pubmed-proxy search "CRISPR" --min-date 2023/01/01 --max-date 2024/12/31 --date-type pdat
# Sort by publication date, get 10 results
pubmed-proxy search "biofilm" --max 10 --sort pub_date
# Page 2 of results (results 11-20)
pubmed-proxy search "biofilm" --max 10 --page 1
# Include metadata summaries for quick scanning
pubmed-proxy search "antibiotic resistance" --summaries --max 5
```
---
### `pubmed-proxy lookup` — Get Article Metadata
Fetch metadata and abstract for one or more PubMed articles by PMID.
**Arguments:**
| Argument | Required | Description |
|----------|----------|-------------|
| `pmids` | Yes | One or more PubMed IDs (space-separated) |
| `--full-text` | No | Fetch full article text from PMC instead of just metadata. Only works for the first PMID. Not all articles have free full text |
**Output (metadata mode):**
```
PMID: 23381990
Valid: Yes
Title: The Fur regulon of pathogenic Neisseria
Authors: Smith, JD, Doe, A, Lee, B, Wang, X, Jones, C
Journal: Journal of Bacteriology (2013)
DOI: 10.1128/JB.01411-12
Abstract: The ferric uptake regulator (Fur) is a transcription factor...
```
For multiple PMIDs, each article's metadata is separated by a blank line.
Invalid PMIDs return `Valid: No` with an error message.
**Output (full-text mode):**
```
PMID: 23381990 (Full Text)
PMCID: PMC3697534
## Introduction
The ferric uptake regulator (Fur) is a global...
## Results
We identified 47 genes...
```
If the article is not available in PMC, shows: `Error: Full text not available in PMC`
**Examples:**
```bash
# Single article metadata + abstract
pubmed-proxy lookup 23381990
# Batch lookup (up to 20 PMIDs)
pubmed-proxy lookup 23381990 16855256 25157846
# Full article text (PMC only, first PMID only)
pubmed-proxy lookup --full-text 23381990
```
---
### `pubmed-proxy citation` — Fetch BibTeX Citations
Generate BibTeX `@article` entries for PubMed articles. Output goes to stdout and can
be piped or appended to .bib files. Citekeys use the format `pmid_XXXXXXXX`.
**Arguments:**
| Argument | Required | Description |
|----------|----------|-------------|
| `pmids` | No* | One or more PubMed IDs (space-separated) |
| `--doi DOI` | No* | Fetch BibTeX by DOI instead. Tries NCBI first for better metadata, falls back to CrossRef |
*Provide either pmids or --doi.
**Output:**
```bibtex
@article{pmid_23381990,
author = {Smith, JD and Doe, A},
title = {{The Fur regulon of pathogenic Neisseria}},
journal = {J Bacteriol},
year = {2013},
volume = {195},
number = {14},
pages = {3159-3170},
doi = {10.1128/JB.01411-12},
pmid = {23381990}
}
```
Multiple PMIDs produce multiple entries separated by blank lines.
DOI-only entries (not in PubMed) use citekey format `doi_<sanitized_doi>`.
**Examples:**
```bash
# Single PMID to stdout
pubmed-proxy citation 23381990
# Multiple PMIDs, append to a file
pubmed-proxy citation 23381990 16855256 25157846 >> references.bib
# By DOI (tries NCBI first, falls back to CrossRef)
pubmed-proxy citation --doi 10.1128/JB.01411-12
```
---
### `pubmed-proxy doi2pmid` — Convert DOI to PMID
Look up the PubMed ID for a given DOI.
**Arguments:**
| Argument | Required | Description |
|----------|----------|-------------|
| `doi` | Yes | Digital Object Identifier |
**Output:**
```
DOI: 10.1038/s41467-019-13483-w
PMID: 31784527
```
If no PMID is found: `PMID: not found`
**Example:**
```bash
pubmed-proxy doi2pmid 10.1038/s41467-019-13483-w
```
---
### `pubmed-proxy correct` — Correct BibTeX Files
Correct a .bib file by verifying each entry against authoritative NCBI/CrossRef
metadata. For each entry, fetches correct metadata by DOI, existing PMID, or
title search (with author-overlap validation to avoid wrong matches).
Requires `pip install -e ".[bib]"`.
**Smart comparison rules:**
- Authors: never replaces full names with initials; expands "and others"; flags mismatches for review
- Pages: normalizes abbreviated ranges (e.g. `735-47` to `735-747`); preserves LaTeX en-dashes
- Journal: prefers longer (more complete) journal name
- Title: never touched (may contain LaTeX formatting)
- Year/volume/number/DOI: trusts authoritative source
**Arguments:**
| Argument | Required | Description |
|----------|----------|-------------|
| `-i, --input FILE` | Yes | Input .bib file path |
| `-o, --output FILE` | No | Output .bib file path. If omitted, corrects in-place and creates a `.bib.bak` backup |
**Output:**
```
entry1_key: OK via DOI
entry2_key: FIXED 2 field(s) via DOI
year: 2012 -> 2013
pages: 735-47 -> 735-747
entry3_key: FAIL could not fetch (no DOI/PMID)
Summary: 15 entries processed
Verified OK: 12
Corrected: 2
Failed: 1
Skipped: 0
Output: corrected.bib
```
**Examples:**
```bash
# Correct to a new output file
pubmed-proxy correct -i references.bib -o corrected.bib
# Correct in-place (creates references.bib.bak backup)
pubmed-proxy correct -i references.bib
```
---
### `pubmed-proxy bib` — Bibliography Management
Manage .bib files: add citations, search, list, validate, and batch-initialize.
All `bib` subcommands accept `--dir DIR` to specify the directory containing .bib
files (default: current directory).
Requires `pip install -e ".[bib]"` (bibtexparser).
#### `bib fetch` — Add Citation to .bib File
Fetch a BibTeX citation by PMID or DOI and add it to a .bib file. Automatically
deduplicates — skips entries whose citekey already exists.
| Argument | Required | Description |
|----------|----------|-------------|
| `identifiers` | No* | One or more PubMed IDs |
| `--doi DOI` | No* | Fetch by DOI instead |
| `--bib FILE` | Yes | Target .bib filename (e.g. `Fur.bib`) |
*Provide either identifiers or --doi.
```bash
pubmed-proxy bib fetch 23381990 --bib Fur.bib
pubmed-proxy bib fetch 23381990 16855256 --bib refs.bib
pubmed-proxy bib fetch --doi 10.1128/JB.01411-12 --bib Fur.bib
pubmed-proxy bib fetch 23381990 --bib refs.bib --dir ./citations
```
Output: `Added: pmid_23381990` or `Already exists: pmid_23381990`
#### `bib search` — Search Across .bib Files
Search all .bib files in a directory for a keyword. Searches citekeys and all fields
(title, author, journal, etc.). Case-insensitive.
| Argument | Required | Description |
|----------|----------|-------------|
| `keyword` | Yes | Search term |
```bash
pubmed-proxy bib search "Fur"
pubmed-proxy bib search "iron regulation" --dir ./citations
```
Output:
```
Found 3 entries matching 'Fur':
[Fur.bib] @pmid_23381990: The Fur regulon of pathogenic Neisseria
[Fur.bib] @pmid_16855256: Fur-mediated iron transport in E. coli
[iron.bib] @pmid_25157846: Iron homeostasis and Fur regulation
```
#### `bib list` — List Entries in .bib Files
List all entries in all .bib files, or in a specific file.
| Argument | Required | Description |
|----------|----------|-------------|
| `--bib FILE` | No | List only this .bib file (default: all files in directory) |
```bash
pubmed-proxy bib list --dir ./citations
pubmed-proxy bib list --bib Fur.bib --dir ./citations
```
Output:
```
Fur.bib (3 entries):
@pmid_23381990: The Fur regulon of pathogenic Neisseria (2013)
@pmid_16855256: Fur-mediated iron transport in E. coli (2006)
@pmid_25157846: Iron homeostasis and Fur regulation (2014)
```
#### `bib show` — Show Full BibTeX for a Citekey
Look up a citekey across all .bib files and print the complete BibTeX entry.
| Argument | Required | Description |
|----------|----------|-------------|
| `citekey` | Yes | Citation key (e.g. `pmid_23381990`) |
```bash
pubmed-proxy bib show pmid_23381990
pubmed-proxy bib show pmid_23381990 --dir ./citations
```
Output: the full `@article{...}` BibTeX block, preceded by `# From <filename>`.
#### `bib validate` — Validate Citekeys in Markdown
Check that all `@citekey` references and `PMID:XXXXX` patterns in a markdown file
exist in the specified .bib file.
| Argument | Required | Description |
|----------|----------|-------------|
| `file` | Yes | Markdown file to validate |
| `--bib FILE` | Yes | .bib file to validate against |
```bash
pubmed-proxy bib validate evidence.md --bib evidence.bib --dir ./citations
```
Output on success: `OK: 5 citekeys validated, 2 raw PMIDs all in bib`
Output on failure (exits with code 1):
```
MISSING citekeys (not in evidence.bib):
@pmid_99999999
UNCONVERTED PMIDs (still using PMID:XXXXX format):
PMID:12345678
```
#### `bib validate-all` — Validate All Evidence Files
Scan all markdown files in a directory and validate each one against its
corresponding .bib file (e.g. `Fur.md` against `Fur.bib`).
| Argument | Required | Description |
|----------|----------|-------------|
| `--scan-dir DIR` | No | Directory containing markdown files (default: current directory) |
```bash
pubmed-proxy bib validate-all --scan-dir ./evidence --dir ./citations
```
Output on success: `All evidence files validated (12 bib files)`
Output on failure (exits with code 1):
```
Fur.md: 2 missing citekey(s) — pmid_99999999, pmid_88888888
iron.md: 1 unconverted PMID(s)
Total: 3 issues across 2 files
```
#### `bib init` — Batch-Create .bib Files from Markdown
Scan markdown files for `PMID:XXXXX` references, fetch BibTeX from PubMed for each,
and create per-file .bib files (e.g. `Fur.md` produces `Fur.bib`).
| Argument | Required | Description |
|----------|----------|-------------|
| `--scan-dir DIR` | No | Directory containing markdown files (default: current directory) |
```bash
pubmed-proxy bib init --scan-dir ./evidence --dir ./citations
```
Output:
```
Found 47 unique PMIDs across 12 files
Fetching PMID: 23381990...
Fetching PMID: 16855256...
...
Fetched 45/47 PMIDs
Created/updated 12 .bib files in ./citations
```
#### `bib stats` — Citation Database Statistics
Print summary statistics for all .bib files in a directory.
```bash
pubmed-proxy bib stats --dir ./citations
```
Output:
```
Citation Database Statistics:
.bib files: 12
Total entries: 47
Unique citekeys: 45
```
---
## Claude Desktop Configuration
Add to your Claude Desktop config (`claude_desktop_config.json`):
```json
{
"mcpServers": {
"pubmed": {
"command": "python",
"args": ["-m", "pubmed_proxy_mcp"],
"env": {
"NCBI_API_KEY": "your-api-key"
}
}
}
}
```
The `NCBI_API_KEY` in the `env` block is optional but recommended for the higher 10 req/s
rate limit. Without it the server works at 3 req/s. Replace `"your-api-key"` with your
actual key from https://www.ncbi.nlm.nih.gov/account/settings/.
This server cannot be deployed
Maintenance
ActivityInactive
ResponsivenessNo issues