Cinco servidores MCP para la pila académica: cómo construimos la familia OpenCódice
El detalle técnico de cómo cubrimos peer review, integridad, identidad de autores, bibliografía CS y artefactos de investigación, y por qué un agente con acceso a los cinco puede razonar sobre un paper como unidad de investigación.
Hace dos meses lanzamos openreview-mcp, el primer servidor MCP que expone peer review (las reseñas de OpenReview, los meta-reviews del area chair, las decisiones finales) a un modelo de lenguaje. Hoy publicamos cuatro más: retractionwatch-mcp, orcid-mcp, dblp-mcp y zenodo-mcp. Junto con el primero, la familia cierra un bucle: el LLM ya puede razonar sobre un paper como unidad de investigación, no como un montón de metadatos sueltos.
Este post explica cómo lo hicimos, qué aprendimos, y qué funcionó al primer intento (poco) y qué tuvimos que reconstruir (bastante).
El hueco que estábamos llenando
A día de hoy hay servidores MCP públicos para casi todo lo que un investigador toca a diario: arXiv, Semantic Scholar, Crossref, ACL Anthology, Hugging Face, GitHub. Todos comparten una hipótesis implícita: un agente académico es un agente de descubrimiento. La pregunta que responden es siempre la misma: «encuéntrame algo que leer».
Pero un investigador no solo descubre. También verifica, reproduce, cita con cuidado, evalúa coautores, escribe rebuttals, deposita datasets. Cuando un agente conectado a Claude (o a la API o a un cliente custom) intenta hacer cualquiera de esas cosas, se queda sin herramientas. Si el agente cita un paper retractado, no se entera. Si dos «J. Smith» son dos personas distintas, no se entera. Si un paper tiene un dataset acompañante en Zenodo bajo CC-BY, el agente solo sabe que existe el paper y, con suerte, su DOI.
La familia de cinco servidores que estamos terminando hoy llena ese hueco.
| Servidor | Cubre | Diferenciador |
|---|---|---|
openreview-mcp | Peer review (reseñas, meta-reviews, decisiones, rebuttals) | aggregate_weaknesses — clusteriza quejas recurrentes en rechazos |
retractionwatch-mcp | Estado de integridad post-publicación (retracciones, withdrawals, expressions of concern) | Vía Crossref + Retraction Watch CC-BY desde sept. 2023 |
orcid-mcp | Identidad canónica del autor (perfil, works, affiliations, búsqueda) | Heurística de desambiguación con score por solapamiento de tokens |
dblp-mcp | Bibliografía de CS hand-curated (autores, venues, publicaciones) | Parser XML para /pid/X/Y.xml (DBLP no expone JSON ahí) |
zenodo-mcp | Artefactos con DOI permanente (datasets, software, suplementarios) | Recorrido de versiones vía concept-recid |
Los cinco son MIT, los cinco se instalan con pip install <nombre>, y los cinco siguen el mismo patrón de diseño.
El patrón de diseño compartido
La elegancia del Model Context Protocol está en que un buen servidor MCP es muy delgado. Cada uno de nuestros cinco servidores tiene exactamente la misma estructura por capas:
src/<paquete>/
├── client.py — wrapper httpx sobre la API upstream
├── cache.py — TTL en disco con diskcache, key por hash estable
├── schemas.py — modelos Pydantic v2, planos
├── tools/
│ ├── _helpers.py — normalizadores específicos del dominio
│ └── *.py — funciones puras agrupadas por entidad
├── server.py — FastMCP, un @mcp.tool() por función
└── cli.py — argparse, transporte stdio o streamable-http
Cuatro decisiones nos guiaron:
1. Evidencia, no política. Cada herramienta devuelve evidencia estructurada (un RetractionFlag con update_type explícito, un DisambiguationCandidate con score numérico, una lista de WeaknessCluster con sus exemplares). La política la aplica el agente que llama. Si el usuario quiere una configuración estricta (rechaza cualquier flag que no sea «correction»), o permisiva (solo bloquea retracciones formales), o un panel de monitorización, el servidor no cambia. Hornearle una política dentro lo haría frágil.
2. Pydantic v2 para todo lo que sale. Las APIs académicas son irregulares: Crossref envuelve fechas en date-time o date-parts según el campo; ORCID esconde valores tras {value: ...}; DBLP a veces serializa aliases.alias como objeto y a veces como lista. La capa de schemas es donde prometemos lo que devolvemos; ahí cazamos los drifts antes de que lleguen al agente.
3. Caché agresiva y bypass por env-var. La mayor parte de los datos académicos cambia despacio (una retracción se publica una vez; un perfil ORCID se actualiza cada meses; una página DBLP se reindexa semanalmente). Cacheamos en disco con TTL por herramienta y damos al usuario una variable de entorno (<NAME>_MCP_NO_CACHE=1) para saltarlo en debugging. Resultado: la segunda ronda de un sweep contra .bib cuesta segundos.
4. Tests offline contra fakes. Cada repo lleva un conftest.py con un _FakeClient que sirve respuestas JSON pre-cocinadas indexadas por path de URL. Los 41 tests entre los cuatro nuevos servidores se ejecutan en menos de dos segundos sin tocar la red. Los tests offline, sin embargo, no atrapan todos los bugs (ver siguiente sección).
Los bugs que solo aparecen en producción
Esto es lo que aprendimos haciendo los cinco. Cada gotcha solo apareció al hacer un smoke test contra la API real.
Crossref: sort=posted no existe
retractionwatch_mcp.tools.search.recent_retractions quiere devolver las retracciones más recientes ordenadas por fecha de publicación del aviso. La intuición natural es sort=posted&order=desc. Crossref responde con un seco HTTP 400 Bad Request. Los valores aceptados son created (fecha de depósito en Crossref) y updated (fecha de la última modificación de los metadatos). Para el caso de retracciones, updated es lo más cercano al sentir de «fecha del aviso», así que ese es el sort de la versión publicada.
Lección: las APIs grandes documentan los valores aceptados, pero el documento siempre va por detrás del comportamiento. Los tests offline no atrapan rechazos del servidor.
ORCID: /search solo devuelve identificadores
ORCID expone dos endpoints de búsqueda. /search devuelve, literalmente, esto:
{
"result": [{"orcid-identifier": {"path": "0000-0001-9084-8782"}}],
"num-found": 1771
}
Solo el iD, sin nombres ni afiliaciones. Para resolver cada hit a algo útil hay que lanzar un segundo /<orcid>/person por candidato; en una búsqueda de 100 hits, son cien round-trips.
/expanded-search (que ORCID documenta en sus tutoriales pero no menciona en su SDK oficial) inserta nombre y afiliación primaria en cada hit:
{
"expanded-result": [{
"orcid-id": "0000-0002-9322-3515",
"given-names": "Yoshua",
"family-names": "Bengio",
"institution-name": ["CIFAR"]
}]
}
orcid-mcp usa /expanded-search por defecto, con fallback a /search por si ORCID cambia el endpoint. Otra lección: los SDK oficiales no siempre exponen lo mejor de la API.
DBLP: las páginas de autor son XML, no JSON
DBLP soporta JSON para los endpoints de búsqueda (/search/{publ,author,venue}/api?format=json). Por simetría, asumimos que /pid/<pid>.json también funcionaría. Devuelve HTTP 404 Not Found para todos los PIDs que probamos. Las páginas de autor están servidas únicamente como XML (.xml) o BibTeX (.bib); no hay endpoint JSON.
La solución es un parser XML stdlib (xml.etree.ElementTree) de unas cuarenta líneas que mapea cada <r> a la misma Publication flat que el resto del servidor produce. Sin nuevas dependencias.
Hay un segundo bug DBLP que también solo apareció en producción: el @id numérico que viene en cada hit de search/author/api (por ejemplo 308198) no es el PID. El PID persistente está en info.url con la forma https://dblp.org/pid/56/953. Hay que extraer el slug de la URL. Si te quedas con @id, todas las llamadas a /pid/<id>.xml devuelven 404.
Zenodo: los buckets de records publicados están bloqueados
Esta nos pilló al final. Subimos los cuatro papers a Zenodo, capturamos los DOIs, y entonces nos dimos cuenta de que los PDFs que habíamos subido seguían el template antiguo (no el de OpenCódice) y la afiliación de los autores incluía UNED y Lleida (cosas que no queríamos). La solución natural sería editar los records y reemplazar los ficheros.
Zenodo tiene una política explícita: los buckets de records publicados están bloqueados para escritura. Aunque el flujo actions/edit te permite modificar metadatos, no puedes reemplazar ficheros. La única manera de subir un PDF nuevo es mintear una nueva versión vía actions/newversion, que crea un draft con un bucket fresco. La nueva versión recibe un nuevo version-DOI; el concept-DOI se preserva.
Es la decisión correcta de Zenodo (los records publicados son citables y no deben mutar bajo los pies de quien los citó), pero te obliga a pensar en versiones desde el primer minuto. La v1 de cada uno de nuestros papers existe en Zenodo y siempre existirá; la v2 (la que cuenta) tiene su propio DOI bajo el mismo concept-DOI.
El uso de la familia: los cinco servidores razonando juntos
La pregunta que tenía sentido hacerle a un solo servidor era pequeña: dame las reseñas de este paper, está retractado, cuál es el ORCID de esta persona. La pregunta que tiene sentido cuando los cinco están conectados es muy distinta. Un ejemplo que hicimos durante la redacción de los technical reports:
# El agente razona en pasos. Comienza con un DOI:
doi = "10.1016/j.eswa.2023.121640"
# 1. Crossref/openalex resuelve metadata básica.
# 2. retractionwatch-mcp comprueba integridad.
flagged = rw_check_doi(doi)
assert not flagged["is_flagged"]
# 3. orcid-mcp resuelve los autores a IDs canónicos.
authors = [orcid_search_by_name(family_name=..., given_names=...) for a in metadata.authors]
# 4. dblp-mcp tira de la página DBLP del autor principal para
# contextualizar (qué venues le importan, cuántos preprints).
stats = dblp_author_stats(authors[0]["pid"])
# 5. zenodo-mcp busca el dataset acompañante.
dataset = zenodo_search_by_creator(name=metadata.first_author_name)
En cinco llamadas a cinco servidores distintos, el agente tiene: el paper, su estado de integridad, los autores canónicos, el contexto bibliométrico, y los artefactos. Esto es razonar sobre el paper como unidad de investigación. Hasta hace dos semanas, ningún agente conectado a un LLM podía hacer esto sin código a medida.
Y la propia redacción de este post fue una prueba: el ORCID del segundo autor (Jorge) lo resolvimos lanzando orcid-mcp contra su nombre y la pista de afiliación «OpenCódice». La primera coincidencia fue exacta. Dos llamadas, menos de un segundo, ningún hardcoding. El servidor que documenta este post se usó para escribirlo.
Calendarización de los lanzamientos
Una decisión estratégica sobre la que hemos sido bastante explícitos: los cinco no salen el mismo día. Cada lanzamiento merece su propia ventana de visibilidad, y cuatro lanzamientos a la vez fragmentan la atención del público objetivo.
| Fecha | Servidor | Por qué este orden |
|---|---|---|
| 2026-04-25 | openreview-mcp | Hecho. El gancho viral más fuerte (peer review como recurso para LLMs) |
| 2026-05-19 | retractionwatch-mcp | Complemento natural de openreview: «las reseñas te dicen qué pensaron, retractionwatch te dice si sobrevivió» |
| 2026-06-02 | orcid-mcp | Backbone de desambiguación; soporta a los demás |
| 2026-06-16 | dblp-mcp | Audiencia más nicho (CS) pero de muy alta calidad |
| 2026-06-30 | zenodo-mcp | Cierre del bucle: del review a los artefactos reales |
Cada lanzamiento sigue el mismo checklist T-10d → T-0d: depositar el technical report en Zenodo (T-10d), test release en TestPyPI (T-7d), confirmar el DOI vivo (T-3d), poner el repo en público (T-1d), tagear v0.1.0 y publicar el blog post (T-0d, martes 9:00 CEST).
Los technical reports
Cada servidor lleva su propio technical report en formato OC-TR (template propio de OpenCódice, basado en article con TikZ para el branding y los headers). Los cinco están en Zenodo bajo CC-BY 4.0:
- OC-TR-2026-007 —
openreview-mcp— DOI 10.5281/zenodo.19758460 - OC-TR-2026-008 —
retractionwatch-mcp— DOI 10.5281/zenodo.20023896 - OC-TR-2026-009 —
orcid-mcp— DOI 10.5281/zenodo.20023897 - OC-TR-2026-010 —
dblp-mcp— DOI 10.5281/zenodo.20023898 - OC-TR-2026-011 —
zenodo-mcp— DOI 10.5281/zenodo.20023901
Cada uno tiene entre diez y doce páginas, con sección de arquitectura, catálogo de tools, validación end-to-end contra la API real, casos de uso más allá del worked example, limitaciones, y discusión. No son publicaciones formales con peer review (todavía), pero son citables vía DOI y tienen CITATION.cff en cada repo.
Lo que nos llevamos
Tres lecciones para quien vaya a construir un servidor MCP en este espacio:
1. Smoke test contra la API real es no-negociable. Los cuatro bugs que documentamos arriba (Crossref sort, ORCID expanded-search, DBLP XML, Zenodo bucket-locking) eran invisibles a los tests offline. Todos los servidores que vimos en el catálogo público de MCP que parecen frágiles tienen el mismo síntoma: el autor no hizo un smoke test serio antes de publicar la v0.1.
2. Evidencia + Pydantic + transporte stdio = la composición correcta. El MCP es buen sustrato cuando la herramienta es ergonómica para un LLM. Devolver dicts planos y predecibles, con campos opcionales que defaultean a None, dejar la política al agente, no tratar de ser «inteligente» en el wrapper. Los wrappers que intentan ser inteligentes (taxonomías baked-in, classifiers en la frontera) envejecen mal.
3. Las APIs académicas son irregulares por diseño. ORCID, Crossref, DBLP, Zenodo son organizaciones distintas, fundadas en momentos distintos, con decisiones de diseño distintas. La capa de schemas es donde tu wrapper paga ese impuesto: sin Pydantic, te enteras de cada drift por una excepción del agente en producción. Con Pydantic, te enteras al hacer la primera llamada.
Lo que viene
La familia de cinco está cerrada en su versión 0.1. Dos cosas obvias en el horizonte:
cited-by-mcp: un sexto servidor que, dado un DOI, devuelva todo lo que lo cita. Cierra otro bucle natural (citation-graph reasoning como primitivo de composición).- Modo deposit en
zenodo-mcpv0.2: que un agente pueda publicar records, no solo leerlos. Implica una credencial separada con scope acotado, porque el blast radius cambia.
A más largo plazo, el patrón generaliza: los próximos corpus académicos sub-servidos (PubPeer comments, los feeds editoriales de retracción de cada editorial grande, las plataformas de peer-review-credit de Publons o ORCID, los repositorios disciplinares como bioRxiv y SocArXiv con sus propias APIs) son todos candidatos al mismo tratamiento. El listón de calidad lo dejamos en estos cinco; el coste marginal de los siguientes es bajo.
Si construyes algo encima de cualquiera de los cinco, cuéntanoslo. Lo enlazamos.
OpenCódice Research construye herramientas open-source para flujos de trabajo académicos. Esta es la primera parte de la familia académico-MCP; estamos trabajando hacia fuera.