{
  "markdown": "<!-- mcp-name: io.github.jtl-neo/crossref-mcp -->\n\n# crossref-mcp\n\n[![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)\n[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](./LICENSE)\n[![Docker](https://img.shields.io/badge/docker-heyinnaneo%2Fcrossref--mcp-blue?logo=docker)](https://hub.docker.com/r/heyinnaneo/crossref-mcp)\n\n**English** | [中文](#中文)\n\nAn [MCP](https://modelcontextprotocol.io) server wrapping the\n[Crossref REST API](https://api.crossref.org), exposing scholarly-metadata\nlookups (works, members, journals, funders, types, licenses, prefixes) as tools\nan LLM client can call.\n\n> Not affiliated with or endorsed by Crossref. Data is served live from the\n> public Crossref API. Set `CROSSREF_MAILTO` to join Crossref's\n> [polite pool](https://api.crossref.org/swagger-ui/index.html).\n\n## Features\n\n- All major Crossref resource endpoints as **18 read-only tools**.\n- Dual transport: **stdio** (local) and **Streamable HTTP** (container).\n- Polite pool (User-Agent + `mailto`), optional Crossref Plus token.\n- Token-bucket rate limiting (auto-tuned from `X-Rate-Limit-*` headers) with\n  exponential backoff honoring `Retry-After`.\n- Cursor deep paging, field `select`, `raw` vs simplified output.\n- Optional `X-API-Key` HTTP auth (`/health` exempt), `/health` endpoint.\n- Optional Redis response cache + cross-replica rate limiting.\n- Optional Caddy TLS reverse proxy for public deployment.\n\n## Quick start (local, stdio)\n\n```bash\nuv sync\nexport CROSSREF_MAILTO=\"you@example.com\"   # recommended (polite pool)\nuv run crossref-mcp                         # starts a stdio MCP server\n```\n\nInspect with the MCP Inspector: `uv run mcp dev src/crossref_mcp/server.py`.\n\n## Configuration\n\n| Env var | Required | Default | Description |\n|---------|----------|---------|-------------|\n| `CROSSREF_MAILTO` | recommended | — | Email for Crossref's polite pool. |\n| `CROSSREF_PLUS_TOKEN` | no | — | Crossref Plus API token (sent as a header). |\n| `CROSSREF_BASE_URL` | no | `https://api.crossref.org` | API base URL. |\n| `CROSSREF_TIMEOUT` | no | `30` | Per-request timeout (seconds). |\n| `MCP_TRANSPORT` | no | `stdio` | `stdio` or `http`. |\n| `MCP_API_KEY` | no | — | If set, HTTP requests need a matching `X-API-Key` header (`/health` exempt). |\n| `LOG_LEVEL` | no | `INFO` | Log level (logs go to stderr). |\n| `REDIS_URL` | no | — | Enable Redis cache + shared rate limiting (optional). |\n| `CACHE_TTL` | no | `86400` | Cache TTL in seconds. |\n| `RATELIMIT_BACKEND` | no | `in-memory` | `in-memory` or `redis`. |\n\n## Tools\n\n**Works** — `search_works`, `get_work` (by DOI), `get_work_references` (capped\nat 50), `get_work_quality` (registration agency).\n**Members / Journals / Funders** — `search_members` · `get_member` ·\n`get_member_works`; `search_journals` · `get_journal` (ISSN) ·\n`get_journal_works`; `search_funders` · `get_funder` · `get_funder_works`.\n**Types / Licenses / Prefixes** — `list_types` · `get_type`; `list_licenses`;\n`get_prefix`. Plus `ping`.\n\n**Shared parameters.** Search/list tools take `query` (and\n`query_bibliographic` / `query_author` / `query_title` on works), `filter`\n(Crossref `key:value,…` syntax), `sort` + `order`, `rows` (≤1000) + `offset`,\nand `select`. Deep paging: set `cursor=\"*\"` then reuse the returned\n`next_cursor`. All tools return simplified fields by default; pass `raw=true`\nfor the full Crossref JSON.\n\n## Connecting an MCP client\n\n**stdio (Claude Desktop / Cursor)** — `claude_desktop_config.json`:\n\n```json\n{\n  \"mcpServers\": {\n    \"crossref\": {\n      \"command\": \"uvx\",\n      \"args\": [\"crossref-mcp\"],\n      \"env\": { \"CROSSREF_MAILTO\": \"you@example.com\" }\n    }\n  }\n}\n```\n\nOr run the container over stdio: `\"command\": \"docker\"`, `\"args\": [\"run\", \"-i\",\n\"--rm\", \"-e\", \"CROSSREF_MAILTO\", \"heyinnaneo/crossref-mcp\"]`.\n\n**Streamable HTTP** — once the container is up, point an HTTP-capable client at\n`http://localhost:8000/mcp`. If `MCP_API_KEY` is set, send it as `X-API-Key`.\n\n## Use as a skill (no server)\n\nPrefer not to run a server? [`skill.md`](./skill.md) is a self-contained skill\nthat teaches an agent to call the Crossref REST API directly — it only needs an\nHTTP/fetch capability (no install, no key, no container). Trade-off: the agent\nissues raw HTTP and parses Crossref JSON itself, without the MCP server's\npolite-pool throttling, field simplification, or structured errors.\n\n**Claude Code (Agent Skill):**\n\n```bash\nmkdir -p ~/.claude/skills/crossref-api\ncurl -fsSL https://raw.githubusercontent.com/jtl-neo/crossref-api-MCP/main/skill.md \\\n  -o ~/.claude/skills/crossref-api/SKILL.md\n```\n\n(Project-scoped instead: place it at `.claude/skills/crossref-api/SKILL.md` in\nyour repo.)\n\n**Other agents (Cursor, Claude Projects, custom):** paste the contents of\n`skill.md` into your system prompt / project knowledge / rules file.\n\n## Docker / HTTP deployment\n\n```bash\ncp .env.example .env          # set CROSSREF_MAILTO\ndocker compose up -d          # pulls heyinnaneo/crossref-mcp, HTTP on :8000\ncurl http://localhost:8000/health\n```\n\nServes Streamable HTTP at `/mcp` and `/health` (compose health check). Set\n`MCP_API_KEY` to require `X-API-Key` on `/mcp`. To build locally, uncomment\n`build: .` in `docker-compose.yml`.\n\n## Public deployment (TLS / reverse proxy) — optional\n\nPut it behind the bundled Caddy proxy (`Caddyfile` + `docker-compose.proxy.yml`):\nTLS termination (auto Let's Encrypt), HTTP→HTTPS, SSE streaming, backend not\npublished on the host.\n\n```bash\ncp .env.example .env   # add DOMAIN=, ACME_EMAIL=, MCP_API_KEY=\ndocker compose -f docker-compose.proxy.yml up -d\ncurl https://your-domain/health\n```\n\nDefense in depth: proxy edge (optional IP allowlist / Basic auth) + `MCP_API_KEY`\napp layer; both leave `/health` open. Needs a domain with DNS pointing at the\nhost and ports 80/443 reachable. Caddy rate limiting needs the `caddy-ratelimit`\nplugin (custom build).\n\n## Redis cache + cross-replica rate limiting — optional\n\nSet `REDIS_URL` for a response cache (cache-aside on the raw envelope; `mailto`/\nsecrets excluded from keys) and, with `RATELIMIT_BACKEND=redis`, a shared token\nbucket so replicas stay within Crossref's polite-pool rate.\n\n```bash\ndocker compose -f docker-compose.redis.yml up -d\ncurl http://localhost:8000/health   # cache_enabled + ratelimit_backend + redis: up\n```\n\nRedis failures degrade gracefully (no cache, in-memory limiting); `/health` stays\n200 with `redis: down`.\n\n## CI / publishing\n\n`.github/workflows/ci.yml`: **test** (ruff + format + pytest with coverage on\nevery push/PR), **security** (Trivy fs scan), **build-push** (multi-arch image to\n`heyinnaneo/crossref-mcp` on `main` / `v*` tags), and on tags **publish-pypi**\n(OIDC Trusted Publisher) + **publish-registry** (MCP registry).\n\nRepository secrets (**Settings → Secrets and variables → Actions**):\n`DOCKERHUB_USERNAME` = `heyinnaneo`, `DOCKERHUB_TOKEN` = a Docker Hub Access\nToken (Read/Write). Tag a release (`git tag v0.1.0 && git push origin v0.1.0`);\nthe tag must match `pyproject.toml`.\n\n## Install from the MCP registry\n\nPublished as `io.github.jtl-neo/crossref-mcp` (see [`server.json`](./server.json)),\nwith a PyPI package (`uvx crossref-mcp`) and an OCI image. Publishing is automated\non version tags (`publish-pypi` + `publish-registry`); requires a public repo and\na configured PyPI Trusted Publisher.\n\n## Security & trust\n\n- **Read-only.** Every tool is a lookup, annotated `readOnlyHint`.\n- **Rate limiting.** Single-process in-memory by default — don't run as a public\n  proxy for heavy traffic; for replicas use `RATELIMIT_BACKEND=redis`.\n- **Polite pool.** Use your own `CROSSREF_MAILTO`.\n- **Public HTTP.** Set `MCP_API_KEY` and front with TLS.\n- **No bundled secrets.** `.env` is excluded from image and git.\n\n## License\n\n[MIT](./LICENSE). Bibliographic data comes from the public\n[Crossref REST API](https://api.crossref.org); not affiliated with Crossref.\n\n---\n\n# 中文\n\n[English](#crossref-mcp) | **中文**\n\n把 [Crossref REST API](https://api.crossref.org) 封裝成 [MCP](https://modelcontextprotocol.io)\nserver，將學術文獻 metadata 查詢（works、members、journals、funders、types、\nlicenses、prefixes）包成 LLM client 可呼叫的工具。\n\n> 非 Crossref 官方、未經其背書。資料即時取自公開的 Crossref API。請設定\n> `CROSSREF_MAILTO` 以加入 Crossref 的\n> [polite pool](https://api.crossref.org/swagger-ui/index.html)。\n\n## 特色\n\n- 主要 Crossref 資源端點，共 **18 個唯讀工具**。\n- 雙傳輸：**stdio**（本機）與 **Streamable HTTP**（容器）。\n- Polite pool（User-Agent + `mailto`）、可選 Crossref Plus token。\n- Token-bucket 速率控制（依 `X-Rate-Limit-*` header 自動調整），429 指數退避並\n  尊重 `Retry-After`。\n- cursor 深分頁、欄位 `select`、`raw` 與精簡輸出切換。\n- 可選 `X-API-Key` HTTP 認證（`/health` 豁免）、`/health` 端點。\n- 可選 Redis 回應快取 + 跨副本速率控制。\n- 可選 Caddy TLS 反向代理，供公開部署。\n\n## 快速開始（本機 stdio）\n\n```bash\nuv sync\nexport CROSSREF_MAILTO=\"you@example.com\"   # 建議（polite pool）\nuv run crossref-mcp                         # 啟動 stdio MCP server\n```\n\n用 MCP Inspector 檢視：`uv run mcp dev src/crossref_mcp/server.py`。\n\n## 設定\n\n| 環境變數 | 必填 | 預設 | 說明 |\n|---------|------|------|------|\n| `CROSSREF_MAILTO` | 建議 | — | Crossref polite pool 用的 email。 |\n| `CROSSREF_PLUS_TOKEN` | 否 | — | Crossref Plus API token（以 header 送出）。 |\n| `CROSSREF_BASE_URL` | 否 | `https://api.crossref.org` | API base URL。 |\n| `CROSSREF_TIMEOUT` | 否 | `30` | 每請求逾時（秒）。 |\n| `MCP_TRANSPORT` | 否 | `stdio` | `stdio` 或 `http`。 |\n| `MCP_API_KEY` | 否 | — | 設了則 HTTP 請求需帶相符的 `X-API-Key` header（`/health` 豁免）。 |\n| `LOG_LEVEL` | 否 | `INFO` | 日誌層級（日誌走 stderr）。 |\n| `REDIS_URL` | 否 | — | 啟用 Redis 快取 + 共享速率控制（選用）。 |\n| `CACHE_TTL` | 否 | `86400` | 快取 TTL（秒）。 |\n| `RATELIMIT_BACKEND` | 否 | `in-memory` | `in-memory` 或 `redis`。 |\n\n## 工具\n\n**Works** — `search_works`、`get_work`（依 DOI）、`get_work_references`（上限\n50）、`get_work_quality`（註冊機構）。\n**Members / Journals / Funders** — `search_members`／`get_member`／\n`get_member_works`；`search_journals`／`get_journal`（ISSN）／`get_journal_works`；\n`search_funders`／`get_funder`／`get_funder_works`。\n**Types / Licenses / Prefixes** — `list_types`／`get_type`；`list_licenses`；\n`get_prefix`。另有 `ping`。\n\n**共用參數。** 搜尋／列表工具接受 `query`（works 另有 `query_bibliographic`／\n`query_author`／`query_title`）、`filter`（Crossref `key:value,…` 語法）、`sort`\n+ `order`、`rows`（≤1000）+ `offset`、`select`。深分頁：設 `cursor=\"*\"`，再把回傳\n的 `next_cursor` 傳回續抓。所有工具預設回精簡欄位；傳 `raw=true` 取完整 Crossref\nJSON。\n\n## 連接 MCP client\n\n**stdio（Claude Desktop / Cursor）** — `claude_desktop_config.json`：\n\n```json\n{\n  \"mcpServers\": {\n    \"crossref\": {\n      \"command\": \"uvx\",\n      \"args\": [\"crossref-mcp\"],\n      \"env\": { \"CROSSREF_MAILTO\": \"you@example.com\" }\n    }\n  }\n}\n```\n\n或以容器跑 stdio：`\"command\": \"docker\"`、`\"args\": [\"run\", \"-i\", \"--rm\", \"-e\",\n\"CROSSREF_MAILTO\", \"heyinnaneo/crossref-mcp\"]`。\n\n**Streamable HTTP** — 容器啟動後，將支援 HTTP 的 client 指向\n`http://localhost:8000/mcp`。若設了 `MCP_API_KEY`，以 `X-API-Key` header 帶上。\n\n## 當作 skill 使用（免 server）\n\n不想架 server？[`skill.md`](./skill.md) 是自包含 skill，教 agent **直接呼叫\nCrossref REST API** — 只需 agent 有 HTTP/fetch 能力（免安裝、免 key、免容器）。\n取捨：agent 自己發 raw HTTP、自己解析 Crossref JSON，沒有 MCP server 的\npolite-pool 限速、欄位精簡、結構化錯誤。\n\n**Claude Code（Agent Skill）：**\n\n```bash\nmkdir -p ~/.claude/skills/crossref-api\ncurl -fsSL https://raw.githubusercontent.com/jtl-neo/crossref-api-MCP/main/skill.md \\\n  -o ~/.claude/skills/crossref-api/SKILL.md\n```\n\n（專案層級：放 repo 內 `.claude/skills/crossref-api/SKILL.md`。）\n\n**其他 agent（Cursor / Claude Projects / 自製）：** 把 `skill.md` 內容貼進\nsystem prompt / project knowledge / rules 檔。\n\n## Docker / HTTP 部署\n\n```bash\ncp .env.example .env          # 設定 CROSSREF_MAILTO\ndocker compose up -d          # 拉 heyinnaneo/crossref-mcp，HTTP 在 :8000\ncurl http://localhost:8000/health\n```\n\n於 `/mcp` 提供 Streamable HTTP，`/health` 供 compose 健康檢查。設 `MCP_API_KEY`\n可要求 `/mcp` 帶 `X-API-Key`。要本地 build，取消 `docker-compose.yml` 內\n`build: .` 註解。\n\n## 公開部署（TLS / 反向代理）— 選用\n\n放在內附的 Caddy proxy 後（`Caddyfile` + `docker-compose.proxy.yml`）：TLS 終結\n（自動 Let's Encrypt）、HTTP→HTTPS、SSE 串流、後端不對 host 公開。\n\n```bash\ncp .env.example .env   # 加上 DOMAIN=、ACME_EMAIL=、MCP_API_KEY=\ndocker compose -f docker-compose.proxy.yml up -d\ncurl https://your-domain/health\n```\n\n縱深防禦：proxy 邊界（可選 IP allowlist / Basic auth）+ `MCP_API_KEY` 應用層；\n兩者皆留 `/health` 開放。需網域 DNS 指向主機、80/443 可達。Caddy 限流需\n`caddy-ratelimit` plugin（自 build）。\n\n## Redis 快取 + 跨副本速率控制 — 選用\n\n設 `REDIS_URL` 啟用回應快取（cache-aside 存原始 envelope；key 排除 `mailto`／\n密鑰），並以 `RATELIMIT_BACKEND=redis` 啟用共享 token bucket，讓多副本維持在\nCrossref polite-pool 速率內。\n\n```bash\ndocker compose -f docker-compose.redis.yml up -d\ncurl http://localhost:8000/health   # cache_enabled + ratelimit_backend + redis: up\n```\n\nRedis 故障會優雅降級（不快取、改 in-memory 限速）；`/health` 仍回 200 並標\n`redis: down`。\n\n## CI / 發佈\n\n`.github/workflows/ci.yml`：**test**（每次 push/PR 跑 ruff + format + pytest 含\n覆蓋率）、**security**（Trivy fs 掃描）、**build-push**（`main`／`v*` tag 時 push\nmulti-arch image 到 `heyinnaneo/crossref-mcp`），打 tag 時另跑 **publish-pypi**\n（OIDC Trusted Publisher）+ **publish-registry**（MCP registry）。\n\nRepository secrets（**Settings → Secrets and variables → Actions**）：\n`DOCKERHUB_USERNAME` = `heyinnaneo`、`DOCKERHUB_TOKEN` = Docker Hub Access Token\n（Read/Write）。打 tag 發版（`git tag v0.1.0 && git push origin v0.1.0`）；tag 須\n與 `pyproject.toml` 版本一致。\n\n## 從 MCP registry 安裝\n\n以 `io.github.jtl-neo/crossref-mcp` 發佈（見 [`server.json`](./server.json)），含\nPyPI 套件（`uvx crossref-mcp`）與 OCI image。發佈於版本 tag 時自動進行\n（`publish-pypi` + `publish-registry`）；需公開 repo 與已設定的 PyPI Trusted\nPublisher。\n\n## 安全與信任\n\n- **唯讀。** 每個工具都是查詢，標註 `readOnlyHint`。\n- **速率控制。** 預設單 process in-memory — 勿當公開代理承載大流量；多副本請用\n  `RATELIMIT_BACKEND=redis`。\n- **Polite pool。** 用你自己的 `CROSSREF_MAILTO`。\n- **公開 HTTP。** 設 `MCP_API_KEY` 並前置 TLS。\n- **不內含密鑰。** `.env` 不進 image、不進 git。\n\n## 授權\n\n[MIT](./LICENSE)。文獻資料來自公開的\n[Crossref REST API](https://api.crossref.org)；與 Crossref 無隸屬關係。\n",
  "bytes": 13722,
  "sha": "16923197262c74aa49ec86b86e86b673026d8387e38be925767df5ead810a30c",
  "repo_slug": "jtl-neo/crossref-api-mcp",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_jtl_neo_crossref_mcp_cb418ec8/readme"
}