Back to the catalog

io.github.jtl-neo/crossref-mcp

MCP server wrapping the Crossref REST API for scholarly metadata.

Open source Open in the app JSON README (API)

About

MCP server wrapping the Crossref REST API for scholarly metadata.

Details

Kind
MCP servers
Topic
Developer tools
Publisher
jtl-neo
Origin
official
Category
ferramentas
Transport
local
Version
0.1.0
Open pull requests
4
Last push
2026-06-24T11:35:20Z
Repository state
ativo
Language
Python
License
MIT
Added
2026-08-29 04:00:17
Updated
2026-08-29 04:00:17
Origin id
io.github.jtl-neo/crossref-mcp

README

<!-- mcp-name: io.github.jtl-neo/crossref-mcp -->

# crossref-mcp

[![CI](https://github.com/jtl-neo/crossref-api-MCP/actions/workflows/ci.yml/badge.svg)](https://github.com/jtl-neo/crossref-api-MCP/actions/workflows/ci.yml)
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](./LICENSE)
[![Docker](https://img.shields.io/badge/docker-heyinnaneo%2Fcrossref--mcp-blue?logo=docker)](https://hub.docker.com/r/heyinnaneo/crossref-mcp)

**English** | [中文](#中文)

An [MCP](https://modelcontextprotocol.io) server wrapping the
[Crossref REST API](https://api.crossref.org), exposing scholarly-metadata
lookups (works, members, journals, funders, types, licenses, prefixes) as tools
an LLM client can call.

> Not affiliated with or endorsed by Crossref. Data is served live from the
> public Crossref API. Set `CROSSREF_MAILTO` to join Crossref's
> [polite pool](https://api.crossref.org/swagger-ui/index.html).

## Features

- All major Crossref resource endpoints as **18 read-only tools**.
- Dual transport: **stdio** (local) and **Streamable HTTP** (container).
- Polite pool (User-Agent + `mailto`), optional Crossref Plus token.
- Token-bucket rate limiting (auto-tuned from `X-Rate-Limit-*` headers) with
  exponential backoff honoring `Retry-After`.
- Cursor deep paging, field `select`, `raw` vs simplified output.
- Optional `X-API-Key` HTTP auth (`/health` exempt), `/health` endpoint.
- Optional Redis response cache + cross-replica rate limiting.
- Optional Caddy TLS reverse proxy for public deployment.

## Quick start (local, stdio)

```bash
uv sync
export CROSSREF_MAILTO="you@example.com"   # recommended (polite pool)
uv run crossref-mcp                         # starts a stdio MCP server
```

Inspect with the MCP Inspector: `uv run mcp dev src/crossref_mcp/server.py`.

## Configuration

| Env var | Required | Default | Description |
|---------|----------|---------|-------------|
| `CROSSREF_MAILTO` | recommended | — | Email for Crossref's polite pool. |
| `CROSSREF_PLUS_TOKEN` | no | — | Crossref Plus API token (sent as a header). |
| `CROSSREF_BASE_URL` | no | `https://api.crossref.org` | API base URL. |
| `CROSSREF_TIMEOUT` | no | `30` | Per-request timeout (seconds). |
| `MCP_TRANSPORT` | no | `stdio` | `stdio` or `http`. |
| `MCP_API_KEY` | no | — | If set, HTTP requests need a matching `X-API-Key` header (`/health` exempt). |
| `LOG_LEVEL` | no | `INFO` | Log level (logs go to stderr). |
| `REDIS_URL` | no | — | Enable Redis cache + shared rate limiting (optional). |
| `CACHE_TTL` | no | `86400` | Cache TTL in seconds. |
| `RATELIMIT_BACKEND` | no | `in-memory` | `in-memory` or `redis`. |

## Tools

**Works** — `search_works`, `get_work` (by DOI), `get_work_references` (capped
at 50), `get_work_quality` (registration agency).
**Members / Journals / Funders** — `search_members` · `get_member` ·
`get_member_works`; `search_journals` · `get_journal` (ISSN) ·
`get_journal_works`; `search_funders` · `get_funder` · `get_funder_works`.
**Types / Licenses / Prefixes** — `list_types` · `get_type`; `list_licenses`;
`get_prefix`. Plus `ping`.

**Shared parameters.** Search/list tools take `query` (and
`query_bibliographic` / `query_author` / `query_title` on works), `filter`
(Crossref `key:value,…` syntax), `sort` + `order`, `rows` (≤1000) + `offset`,
and `select`. Deep paging: set `cursor="*"` then reuse the returned
`next_cursor`. All tools return simplified fields by default; pass `raw=true`
for the full Crossref JSON.

## Connecting an MCP client

**stdio (Claude Desktop / Cursor)** — `claude_desktop_config.json`:

```json
{
  "mcpServers": {
    "crossref": {
      "command": "uvx",
      "args": ["crossref-mcp"],
      "env": { "CROSSREF_MAILTO": "you@example.com" }
    }
  }
}
```

Or run the container over stdio: `"command": "docker"`, `"args": ["run", "-i",
"--rm", "-e", "CROSSREF_MAILTO", "heyinnaneo/crossref-mcp"]`.

**Streamable HTTP** — once the container is up, point an HTTP-capable client at
`http://localhost:8000/mcp`. If `MCP_API_KEY` is set, send it as `X-API-Key`.

## Use as a skill (no server)

Prefer not to run a server? [`skill.md`](./skill.md) is a self-contained skill
that teaches an agent to call the Crossref REST API directly — it only needs an
HTTP/fetch capability (no install, no key, no container). Trade-off: the agent
issues raw HTTP and parses Crossref JSON itself, without the MCP server's
polite-pool throttling, field simplification, or structured errors.

**Claude Code (Agent Skill):**

```bash
mkdir -p ~/.claude/skills/crossref-api
curl -fsSL https://raw.githubusercontent.com/jtl-neo/crossref-api-MCP/main/skill.md \
  -o ~/.claude/skills/crossref-api/SKILL.md
```

(Project-scoped instead: place it at `.claude/skills/crossref-api/SKILL.md` in
your repo.)

**Other agents (Cursor, Claude Projects, custom):** paste the contents of
`skill.md` into your system prompt / project knowledge / rules file.

## Docker / HTTP deployment

```bash
cp .env.example .env          # set CROSSREF_MAILTO
docker compose up -d          # pulls heyinnaneo/crossref-mcp, HTTP on :8000
curl http://localhost:8000/health
```

Serves Streamable HTTP at `/mcp` and `/health` (compose health check). Set
`MCP_API_KEY` to require `X-API-Key` on `/mcp`. To build locally, uncomment
`build: .` in `docker-compose.yml`.

## Public deployment (TLS / reverse proxy) — optional

Put it behind the bundled Caddy proxy (`Caddyfile` + `docker-compose.proxy.yml`):
TLS termination (auto Let's Encrypt), HTTP→HTTPS, SSE streaming, backend not
published on the host.

```bash
cp .env.example .env   # add DOMAIN=, ACME_EMAIL=, MCP_API_KEY=
docker compose -f docker-compose.proxy.yml up -d
curl https://your-domain/health
```

Defense in depth: proxy edge (optional IP allowlist / Basic auth) + `MCP_API_KEY`
app layer; both leave `/health` open. Needs a domain with DNS pointing at the
host and ports 80/443 reachable. Caddy rate limiting needs the `caddy-ratelimit`
plugin (custom build).

## Redis cache + cross-replica rate limiting — optional

Set `REDIS_URL` for a response cache (cache-aside on the raw envelope; `mailto`/
secrets excluded from keys) and, with `RATELIMIT_BACKEND=redis`, a shared token
bucket so replicas stay within Crossref's polite-pool rate.

```bash
docker compose -f docker-compose.redis.yml up -d
curl http://localhost:8000/health   # cache_enabled + ratelimit_backend + redis: up
```

Redis failures degrade gracefully (no cache, in-memory limiting); `/health` stays
200 with `redis: down`.

## CI / publishing

`.github/workflows/ci.yml`: **test** (ruff + format + pytest with coverage on
every push/PR), **security** (Trivy fs scan), **build-push** (multi-arch image to
`heyinnaneo/crossref-mcp` on `main` / `v*` tags), and on tags **publish-pypi**
(OIDC Trusted Publisher) + **publish-registry** (MCP registry).

Repository secrets (**Settings → Secrets and variables → Actions**):
`DOCKERHUB_USERNAME` = `heyinnaneo`, `DOCKERHUB_TOKEN` = a Docker Hub Access
Token (Read/Write). Tag a release (`git tag v0.1.0 && git push origin v0.1.0`);
the tag must match `pyproject.toml`.

## Install from the MCP registry

Published as `io.github.jtl-neo/crossref-mcp` (see [`server.json`](./server.json)),
with a PyPI package (`uvx crossref-mcp`) and an OCI image. Publishing is automated
on version tags (`publish-pypi` + `publish-registry`); requires a public repo and
a configured PyPI Trusted Publisher.

## Security & trust

- **Read-only.** Every tool is a lookup, annotated `readOnlyHint`.
- **Rate limiting.** Single-process in-memory by default — don't run as a public
  proxy for heavy traffic; for replicas use `RATELIMIT_BACKEND=redis`.
- **Polite pool.** Use your own `CROSSREF_MAILTO`.
- **Public HTTP.** Set `MCP_API_KEY` and front with TLS.
- **No bundled secrets.** `.env` is excluded from image and git.

## License

[MIT](./LICENSE). Bibliographic data comes from the public
[Crossref REST API](https://api.crossref.org); not affiliated with Crossref.

---

# 中文

[English](#crossref-mcp) | **中文**

把 [Crossref REST API](https://api.crossref.org) 封裝成 [MCP](https://modelcontextprotocol.io)
server,將學術文獻 metadata 查詢(works、members、journals、funders、types、
licenses、prefixes)包成 LLM client 可呼叫的工具。

> 非 Crossref 官方、未經其背書。資料即時取自公開的 Crossref API。請設定
> `CROSSREF_MAILTO` 以加入 Crossref 的
> [polite pool](https://api.crossref.org/swagger-ui/index.html)。

## 特色

- 主要 Crossref 資源端點,共 **18 個唯讀工具**。
- 雙傳輸:**stdio**(本機)與 **Streamable HTTP**(容器)。
- Polite pool(User-Agent + `mailto`)、可選 Crossref Plus token。
- Token-bucket 速率控制(依 `X-Rate-Limit-*` header 自動調整),429 指數退避並
  尊重 `Retry-After`。
- cursor 深分頁、欄位 `select`、`raw` 與精簡輸出切換。
- 可選 `X-API-Key` HTTP 認證(`/health` 豁免)、`/health` 端點。
- 可選 Redis 回應快取 + 跨副本速率控制。
- 可選 Caddy TLS 反向代理,供公開部署。

## 快速開始(本機 stdio)

```bash
uv sync
export CROSSREF_MAILTO="you@example.com"   # 建議(polite pool)
uv run crossref-mcp                         # 啟動 stdio MCP server
```

用 MCP Inspector 檢視:`uv run mcp dev src/crossref_mcp/server.py`。

## 設定

| 環境變數 | 必填 | 預設 | 說明 |
|---------|------|------|------|
| `CROSSREF_MAILTO` | 建議 | — | Crossref polite pool 用的 email。 |
| `CROSSREF_PLUS_TOKEN` | 否 | — | Crossref Plus API token(以 header 送出)。 |
| `CROSSREF_BASE_URL` | 否 | `https://api.crossref.org` | API base URL。 |
| `CROSSREF_TIMEOUT` | 否 | `30` | 每請求逾時(秒)。 |
| `MCP_TRANSPORT` | 否 | `stdio` | `stdio` 或 `http`。 |
| `MCP_API_KEY` | 否 | — | 設了則 HTTP 請求需帶相符的 `X-API-Key` header(`/health` 豁免)。 |
| `LOG_LEVEL` | 否 | `INFO` | 日誌層級(日誌走 stderr)。 |
| `REDIS_URL` | 否 | — | 啟用 Redis 快取 + 共享速率控制(選用)。 |
| `CACHE_TTL` | 否 | `86400` | 快取 TTL(秒)。 |
| `RATELIMIT_BACKEND` | 否 | `in-memory` | `in-memory` 或 `redis`。 |

## 工具

**Works** — `search_works`、`get_work`(依 DOI)、`get_work_references`(上限
50)、`get_work_quality`(註冊機構)。
**Members / Journals / Funders** — `search_members`/`get_member`/
`get_member_works`;`search_journals`/`get_journal`(ISSN)/`get_journal_works`;
`search_funders`/`get_funder`/`get_funder_works`。
**Types / Licenses / Prefixes** — `list_types`/`get_type`;`list_licenses`;
`get_prefix`。另有 `ping`。

**共用參數。** 搜尋/列表工具接受 `query`(works 另有 `query_bibliographic`/
`query_author`/`query_title`)、`filter`(Crossref `key:value,…` 語法)、`sort`
+ `order`、`rows`(≤1000)+ `offset`、`select`。深分頁:設 `cursor="*"`,再把回傳
的 `next_cursor` 傳回續抓。所有工具預設回精簡欄位;傳 `raw=true` 取完整 Crossref
JSON。

## 連接 MCP client

**stdio(Claude Desktop / Cursor)** — `claude_desktop_config.json`:

```json
{
  "mcpServers": {
    "crossref": {
      "command": "uvx",
      "args": ["crossref-mcp"],
      "env": { "CROSSREF_MAILTO": "you@example.com" }
    }
  }
}
```

或以容器跑 stdio:`"command": "docker"`、`"args": ["run", "-i", "--rm", "-e",
"CROSSREF_MAILTO", "heyinnaneo/crossref-mcp"]`。

**Streamable HTTP** — 容器啟動後,將支援 HTTP 的 client 指向
`http://localhost:8000/mcp`。若設了 `MCP_API_KEY`,以 `X-API-Key` header 帶上。

## 當作 skill 使用(免 server)

不想架 server?[`skill.md`](./skill.md) 是自包含 skill,教 agent **直接呼叫
Crossref REST API** — 只需 agent 有 HTTP/fetch 能力(免安裝、免 key、免容器)。
取捨:agent 自己發 raw HTTP、自己解析 Crossref JSON,沒有 MCP server 的
polite-pool 限速、欄位精簡、結構化錯誤。

**Claude Code(Agent Skill):**

```bash
mkdir -p ~/.claude/skills/crossref-api
curl -fsSL https://raw.githubusercontent.com/jtl-neo/crossref-api-MCP/main/skill.md \
  -o ~/.claude/skills/crossref-api/SKILL.md
```

(專案層級:放 repo 內 `.claude/skills/crossref-api/SKILL.md`。)

**其他 agent(Cursor / Claude Projects / 自製):** 把 `skill.md` 內容貼進
system prompt / project knowledge / rules 檔。

## Docker / HTTP 部署

```bash
cp .env.example .env          # 設定 CROSSREF_MAILTO
docker compose up -d          # 拉 heyinnaneo/crossref-mcp,HTTP 在 :8000
curl http://localhost:8000/health
```

於 `/mcp` 提供 Streamable HTTP,`/health` 供 compose 健康檢查。設 `MCP_API_KEY`
可要求 `/mcp` 帶 `X-API-Key`。要本地 build,取消 `docker-compose.yml` 內
`build: .` 註解。

## 公開部署(TLS / 反向代理)— 選用

放在內附的 Caddy proxy 後(`Caddyfile` + `docker-compose.proxy.yml`):TLS 終結
(自動 Let's Encrypt)、HTTP→HTTPS、SSE 串流、後端不對 host 公開。

```bash
cp .env.example .env   # 加上 DOMAIN=、ACME_EMAIL=、MCP_API_KEY=
docker compose -f docker-compose.proxy.yml up -d
curl https://your-domain/health
```

縱深防禦:proxy 邊界(可選 IP allowlist / Basic auth)+ `MCP_API_KEY` 應用層;
兩者皆留 `/health` 開放。需網域 DNS 指向主機、80/443 可達。Caddy 限流需
`caddy-ratelimit` plugin(自 build)。

## Redis 快取 + 跨副本速率控制 — 選用

設 `REDIS_URL` 啟用回應快取(cache-aside 存原始 envelope;key 排除 `mailto`/
密鑰),並以 `RATELIMIT_BACKEND=redis` 啟用共享 token bucket,讓多副本維持在
Crossref polite-pool 速率內。

```bash
docker compose -f docker-compose.redis.yml up -d
curl http://localhost:8000/health   # cache_enabled + ratelimit_backend + redis: up
```

Redis 故障會優雅降級(不快取、改 in-memory 限速);`/health` 仍回 200 並標
`redis: down`。

## CI / 發佈

`.github/workflows/ci.yml`:**test**(每次 push/PR 跑 ruff + format + pytest 含
覆蓋率)、**security**(Trivy fs 掃描)、**build-push**(`main`/`v*` tag 時 push
multi-arch image 到 `heyinnaneo/crossref-mcp`),打 tag 時另跑 **publish-pypi**
(OIDC Trusted Publisher)+ **publish-registry**(MCP registry)。

Repository secrets(**Settings → Secrets and variables → Actions**):
`DOCKERHUB_USERNAME` = `heyinnaneo`、`DOCKERHUB_TOKEN` = Docker Hub Access Token
(Read/Write)。打 tag 發版(`git tag v0.1.0 && git push origin v0.1.0`);tag 須
與 `pyproject.toml` 版本一致。

## 從 MCP registry 安裝

以 `io.github.jtl-neo/crossref-mcp` 發佈(見 [`server.json`](./server.json)),含
PyPI 套件(`uvx crossref-mcp`)與 OCI image。發佈於版本 tag 時自動進行
(`publish-pypi` + `publish-registry`);需公開 repo 與已設定的 PyPI Trusted
Publisher。

## 安全與信任

- **唯讀。** 每個工具都是查詢,標註 `readOnlyHint`。
- **速率控制。** 預設單 process in-memory — 勿當公開代理承載大流量;多副本請用
  `RATELIMIT_BACKEND=redis`。
- **Polite pool。** 用你自己的 `CROSSREF_MAILTO`。
- **公開 HTTP。** 設 `MCP_API_KEY` 並前置 TLS。
- **不內含密鑰。** `.env` 不進 image、不進 git。

## 授權

[MIT](./LICENSE)。文獻資料來自公開的
[Crossref REST API](https://api.crossref.org);與 Crossref 無隸屬關係。

More