Skip to main content
Glama

doi2ris MCP

Servidor MCP que hace de intermediario entre Claude y las APIs bibliográficas.

Por qué existe

El entorno de claude.ai no tiene salida a internet. Un script que corra ahí no puede consultar Crossref ni OpenAlex. Este servidor corre en tu VPS, que sí tiene red, y Claude le habla a él.

   ┌──────────────────── entorno de Claude (sin red) ─────────────────────┐
   │                                                                      │
   │   referencias.txt                                                    │
   │        │                                                             │
   │        ▼                                                             │
   │   doi2ris.py plan ──────────────────► consultas.json                 │
   │                                            │                         │
   │                                            ▼                         │
   │                                    tool `resolver`                   │
   └────────────────────────────────────────────┼─────────────────────────┘
                                                │  HTTPS
                                                ▼
                    ┌─────────── tu VPS: mcpdoi.systempiura.com ──────────┐
                    │                                                     │
                    │   nginx (TLS · token en la URL · limit_req)         │
                    │        │                                            │
                    │        ▼                                            │
                    │   server.py ── caché sqlite ──► ¿ya lo sé? ──► sí ──┼──┐
                    │        │                                            │  │
                    │        ▼ no                                         │  │
                    │   token bucket (auto-ajustado) ──► normalizar       │  │
                    └────────┼────────────────────────────────────────────┘  │
                             ▼                                               │
                    api.crossref.org · api.openalex.org                      │
                                                                             │
   ┌─────────────────────────────────────────────────────────────────────────┘
   │  respuestas.json  (registros ya normalizados, ~1KB c/u)
   ▼
   doi2ris.py resolve ──► ¿faltan búsquedas por título?
        │                      │
        │ no                   └── sí ──► consultas-2.json ──► (otra ronda)
        ▼
   referencias.ris  +  reporte.md

Related MCP server: BibTeX MCP Server

Estrategia contra el bloqueo de Crossref

Crossref no cobra ni pide clave, pero estrangula y termina bloqueando por IP a quien abusa. Como aquí todos los usuarios comparten la IP de la VPS, un solo usuario descuidado los tumbaría a todos. Cinco capas, de más a menos efectiva:

  1. Caché compartida en disco (30 días). Es la defensa principal. Vive en un volumen Docker y sobrevive a los redeploys. Una referencia que ya pidió otro usuario no genera ninguna petición. En una comunidad que cita la misma literatura, el ahorro es enorme.

  2. Pool cortés. DOI2RIS_MAILTO mete tu correo en el User-Agent y en cada petición. Crossref te mueve al pool cortés: mejor latencia, límites más generosos, y te escriben antes de bloquearte en vez de cortarte en seco. Sin esto caés al pool anónimo, que es el primero en ser estrangulado.

  3. Token bucket que se auto-ajusta. El servidor lee las cabeceras X-Rate-Limit-Limit / X-Rate-Limit-Interval que Crossref devuelve en cada respuesta y se ajusta al 70% de lo que el propio Crossref declara. Nunca pide más rápido de lo permitido, aunque Crossref cambie sus límites.

  4. Backoff que obedece. Ante un 429 o 503 respeta Retry-After al pie de la letra y además reduce a la mitad su propio ritmo de forma permanente. No reintenta los 404: un DOI que no existe es una respuesta, no un fallo.

  5. Menos peticiones desde el diseño. Deduplicación dentro del lote, single-flight (dos usuarios pidiendo lo mismo a la vez generan una sola petición upstream), y el esquema de dos rondas: las búsquedas por título solo se piden para las referencias cuyo DOI falló, no para todas.

El token en nginx y el limit_req son la capa de arriba: impiden que un tercero convierta tu servidor en un scraper y se lleve puesta tu IP.

Desplegar

git clone <tu-repo> && cd mcp-doi2ris
cp .env.example .env
nano .env                      # poné tu DOI2RIS_MAILTO — no lo dejes vacío
docker compose up -d --build
curl -sN -X POST localhost:8080/mcp \
  -H 'Content-Type: application/json' -H 'Accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"curl","version":"0"}}}'

Si eso devuelve un result, el contenedor está bien. Después viene nginx, y va en dos fases: primero HTTP, y que certbot añada el TLS él mismo.

Fase 1 — nginx en el puerto 80

openssl rand -hex 24                                    # tu token
cp nginx.conf.example /etc/nginx/sites-available/mcpdoi.systempiura.com
nano /etc/nginx/sites-available/mcpdoi.systempiura.com  # pegá el token
ln -s /etc/nginx/sites-available/mcpdoi.systempiura.com /etc/nginx/sites-enabled/
nginx -t && systemctl reload nginx

Acordate de la línea limit_req_zone en el bloque http { } de /etc/nginx/nginx.conf, o nginx -t falla.

Fase 2 — certbot pone el TLS

certbot --nginx -d mcpdoi.systempiura.com

Certbot edita ese mismo archivo: te añade el bloque listen 443 ssl, los ssl_certificate, los parámetros SSL y el redirect de 80 a 443. No escribas nada de eso a mano — si ya hay un bloque 443 escrito por vos, certbot se confunde o duplica directivas. Y como ssl_certificate apuntando a un archivo inexistente impide que nginx arranque, escribirlo antes de tener el certificado te deja el servidor caído.

Comprobá que la renovación automática quedó puesta:

certbot renew --dry-run

Fase 3 — probá desde fuera

curl -sN -X POST https://mcpdoi.systempiura.com/t/<TOKEN>/mcp \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"curl","version":"0"}}}'

Si responde Invalid Host header, te falta el dominio en DOI2RIS_ALLOWED_HOSTS del .env. Si da 404, el token de la URL no coincide con el de nginx.

La URL que repartís es:

https://mcpdoi.systempiura.com/t/<TOKEN>/mcp

Operación

  • Logs: docker compose logs -f

  • Salud: la tool estado reporta el status de ambas APIs, los aciertos de caché y el ritmo actual hacia Crossref. Si rate_crossref bajó mucho, es que Crossref te estuvo frenando.

  • Vaciar la caché: docker compose down && docker volume rm mcp-doi2ris_doi2ris-cache

  • Rotar el token: cambiás la línea en nginx, systemctl reload nginx, y repartís la URL nueva. Los conectores viejos empiezan a recibir 404.

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    D
    maintenance
    Searches academic references from arXiv, DBLP, Semantic Scholar, and OpenAlex concurrently and generates BibTeX citations.
    4
    11
    MIT
  • A
    license
    Not graded
    quality
    C
    maintenance
    Enables searching academic papers, journals, and citations via the Crossref API, and resolving DOIs to canonical metadata with authors, references, and citation graphs.
    5 npm
    MIT