citador_ar_mcp
Un servidor MCP que responde la pregunta que ningún buscador jurídico argentino abierto responde: ¿este fallo de la Corte sigue siendo buen derecho?
No es un buscador de jurisprudencia. Ya hay once. Este construye el grafo de citas entre fallos de la CSJN y clasifica cómo cada fallo posterior trató a los anteriores: si los aplicó, los distinguió, los criticó o los abandonó.
No es asesoramiento legal. Es una herramienta de investigación. Toda afirmación que hace es auditable contra el pasaje que la generó, y toda respuesta lleva su nivel de confianza. Verificá el texto antes de usarla.
El problema
En Estados Unidos la pregunta la resuelve un citador: Shepard's en Lexis, KeyCite en Westlaw, BCite en Bloomberg. Te dicen qué fallos posteriores citaron al tuyo y, sobre todo, cómo lo citaron. Un semáforo rojo significa que no lo cites.
En Argentina eso no existe en abierto. Los servidores MCP jurídicos que hay buscan y devuelven documentos; ninguno construye la red de citas.
El demo: la cadena de la tenencia para consumo personal
Cuatro fallos que se dan vuelta entre sí a lo largo de treinta años. Ningún buscador actual muestra esta cadena.
| Año | Fallo | Cita | Qué hizo |
|---|---|---|---|
| 1978 | Colavini | Fallos: 300:254 |
Declaró constitucional penar la tenencia para consumo |
| 1986 | Bazterrica | Fallos: 308:1392 |
Se apartó de Colavini: inconstitucional (ver Limitaciones) |
| 1990 | Montalvo | Fallos: 313:1333 |
Abandonó Bazterrica: volvió a la criminalización |
| 2009 | Arriola | Fallos: 332:1963 |
Abandonó Montalvo, siguió Bazterrica |
Un pasaje real de Arriola, extraído por el pipeline, que sostiene dos de esas aristas a la vez:
Así en "Colavini" (Fallos: 300:254) se pronunció a favor de la criminalización; en "Bazterrica" y "Capalbo", se apartó de tal doctrina (Fallos: 308:1392); y en 1990, en "Montalvo" vuelve nuevamente sobre sus pasos a favor de la criminalización de la tenencia para consumo personal (Fallos: 313:1333), y como lo adelantáramos en las consideraciones previas, hoy el Tribunal decide volver a "Bazterrica".
— CSJN, Arriola, considerando 12
Esa cadena es también el criterio de aceptación del proyecto:
tests/test_golden_chain.py. Si no la reconstruye, nada más importa.
Las tools
| Tool | Qué hace |
|---|---|
citador_lookup_ruling |
Resuelve cualquier forma de cita a un fallo: Fallos 332:1963, "Arriola", A. 891. XLIV |
citador_check_status |
La tool central: señal agregada de vigencia con los tratamientos que la sustentan |
citador_citing_rulings |
Qué fallos posteriores lo citaron, filtrable por tratamiento |
citador_cited_rulings |
En qué se apoyó este fallo |
citador_trace_doctrine |
Reconstruye la cadena completa de un tema a lo largo del tiempo |
El vocabulario de tratamientos
| Señal | Significado |
|---|---|
applied |
Lo aplica como fundamento |
followed |
Lo sigue expresamente |
distinguished |
Lo reconoce pero lo aparta por los hechos |
limited |
Restringe su alcance |
criticized |
Lo cuestiona sin abandonarlo |
abandoned |
Se aparta de la doctrina anterior |
mentioned |
Cita sin tomar postura |
abandoned es el semáforo rojo y es el más conservador de todos: ante duda, la
clasificación es mentioned con confianza baja.
Cómo se clasifica
En dos etapas, y la segunda sólo corre sobre lo que la primera no resuelve.
Reglas. Fórmulas fijas del castellano jurídico (corresponde apartarse de,
han sido resueltas acertadamente en, no es compatible con el criterio), con
tres guardas, porque la prosa de un fallo es adversarial por construcción:
- la unidad es la cláusula, no el pasaje. El considerando 12 de Arriola cita tres precedentes en una sola oración con tres tratamientos distintos; un clasificador que lee el párrafo entero pinta los tres de rojo.
- negación y atribución.
no corresponde apartarsees lo contrario decorresponde apartarse, yel recurrente sostiene que corresponde apartarsees el argumento de una parte, no la postura del tribunal. - relato histórico. En
en "Bazterrica" se apartó de tal doctrina, el precedente citado es el lugar del apartamiento, no su objeto. Sin esta guarda el pipeline lee que Arriola abandonó a Bazterrica, que es exactamente lo contrario de lo que pasó.
Medido contra la cadena dorada: 4 de 5, con cero rojos falsos. CI falla si aparece uno.
uv run python scripts/eval_treatment.py
LLM. Para lo que requiere criterio a través de cláusulas. Que Arriola se aparta de Montalvo no está dicho en ninguna parte: se sigue de que vuelve al precedente que Montalvo había desplazado. Dos guardas:
- el modelo tiene que devolver la frase textual en la que se apoya, y esa frase tiene que estar en el pasaje; una inventada se descarta;
- su confianza tiene tope por debajo del umbral que necesita un tratamiento
negativo para encender la señal roja. El modelo puede poner
abandoneden el detalle con su pasaje; no puede, por sí solo, decirle a un abogado que descarte un precedente.
Es opcional y opt-in (CITADOR_LLM=1, extra llm), porque cuesta plata por
pasaje. Sin credencial el pipeline sigue con las reglas solas.
Alcance
Solo Corte Suprema de Justicia de la Nación. Un citador nacional completo es un proyecto de años; el recorte tiene razones, no excusas:
- La Corte tiene cita canónica (
Fallos tomo:página), un identificador estable y parseable que no existe en el resto del sistema. - El corpus es acotado: 349 tomos, verificado contra la fuente.
- Es el tribunal ápice, donde la pregunta de vigencia vale más.
Lo que NO hace
- No es asesoramiento legal. Es una herramienta de investigación.
- No busca personas ni partes. El eje es el precedente.
- No cubre expedientes del PJN. Verificación anti-bot antes de cada búsqueda, fueros restringidos y nombres de partes en litigio: es un problema de datos personales, no una limitación técnica.
- No compite con Westlaw en cobertura. Compite en ser abierto y auditable.
Cobertura del corpus
Medido contra la fuente el 1 de septiembre de 2026:
- 349 tomos en la colección Fallos (tomo 349 = 2026).
- Los fallos modernos traen texto extraíble limpio (calidad 0,77–0,89).
- Una parte de los fallos viejos no. No son escaneos: tienen capa de texto, pero con fuentes sin mapa Unicode usable, de modo que lo que se extrae es una sustitución monoalfabética del texto real. El problema es por documento, no por tomo: en el tomo 300 conviven PDFs limpios y cifrados. Se detectan con un filtro de calidad y se recuperan por OCR; los tres fallos viejos de la cadena pasaron de 0,42–0,61 a 0,87–0,92.
- Todo texto recuperado por OCR se marca
text_status='ocr'y las tools lo informan. El OCR se equivoca, sobre todo con impresiones de los años 70 y 80, así que un pasaje puede traer erratas. Está declarado justamente para que quien verifique la cita sepa contra qué la está verificando.
Qué trae el corpus publicado
El .db del release se crawleó el 2 de septiembre de 2026 sobre los tomos
330-349 (2007-2026). Los números de portada halagan al archivo, así que van
con su letra chica:
| Nodos | 7.953 — de los cuales 5.325 son fallos crawleados y 2.628 son stubs |
| Aristas de cita | 73.611 |
Con postura distinta de mentioned |
1.513 (2,1 %) |
| Atribuidas a un voto | 10.444 (14,2 %), de las cuales 7.157 a la mayoría |
| Con texto propio | 391 (387 extraídos del PDF, 4 por OCR) |
Tres cosas que conviene entender antes de confiar en una respuesta:
- Un stub no es un fallo. Es una posición real en la colección, citada desde adentro del rango pero ubicada afuera, de la que sólo se conoce la cita. No tiene texto ni carátula completa. Contarlos junto a los fallos crawleados infla el corpus a la mitad, y por eso van separados.
- Las aristas las publica la propia Corte, en el campo
linksCitantesde cada sumario. El grafo es ancho porque no depende de encontrar la cita en el texto; la clasificación es fina porque sí. - El 97,9 % de las aristas quedó en
mentioned. Eso no significa "citado sin tomar postura": significa no clasificado. La distinción es la diferencia entre una señal gris que informa y una que tranquiliza sin motivo, y las tools la dicen en cada respuesta con esa palabra.
Todo esto está grabado en corpus_meta dentro del propio archivo, lo calcula el
pipeline y lo devuelve la resource citador://corpus. No es un dato de folleto:
si regenerás el grafo, los números se recalculan solos.
Limitaciones del método
Estas no son bugs pendientes. Son límites de lo que un citador construido sobre el grafo de citas puede hacer, y conviene tenerlos a la vista.
El abandono implícito no se detecta. Cuando la Corte se aparta de un
precedente sin citarlo, no hay cita que clasificar. Pasa en la propia cadena de
demostración: doctrinariamente Bazterrica abandona a Colavini, pero el voto de la
mayoría de Bazterrica cita a Colavini una sola vez y a favor, aceptando su
valoración del problema; el apartamiento llega dos considerandos después y no lo
nombra. Este citador registra esa arista como mentioned, que es lo que el
pasaje sostiene. Marcarla abandoned sería afirmar algo que el texto citado no
dice, y eso es exactamente lo que el proyecto no hace.
El precedente se mueve en las dos direcciones. Un fallo abandonado y después retomado no es doctrina muerta. Bazterrica fue abandonado por Montalvo en 1990 y retomado por Arriola en 2009: la señal es amarilla, no roja ni verde, y la advertencia cuenta la historia.
Una cita no es la doctrina del tribunal por estar en el documento. Los tomos impresos traen el dictamen del Procurador General antes del fallo. En Bazterrica el Procurador pedía mantener Colavini y la Corte resolvió al revés. El dictamen, los votos propios y las disidencias se segmentan aparte y no mueven la señal.
Instalación
uv tool install citador-ar-mcp
O pip install citador-ar-mcp. Para trabajar sobre el repo, uv sync --extra ingest.
El servidor necesita el grafo antes de responder nada. Generalo con la cadena dorada, que no requiere red:
uv run python -m citador_ar_mcp.ingest.build_graph --from-fixture
Eso son cuatro fallos: alcanza para probar el servidor y correr las evals, no para investigar. El corpus completo se baja del release (41 MB):
gh release download v0.1.0 --repo LautaroMoya25/citador-ar-mcp --pattern corpus.db --dir data
También está en la página de releases. Como el nombre no coincide con el default, apuntá el servidor al archivo:
export CITADOR_DB="$PWD/data/corpus.db"
Si falta, el servidor levanta igual y lista sus tools: el error de cada llamada trae las instrucciones para generarlo, en lugar de que el servidor no arranque.
Conectarlo a un cliente MCP
En Claude Code:
claude mcp add citador --env CITADOR_DB=/ruta/al/corpus.db -- uvx citador-ar-mcp
En Claude Desktop, en claude_desktop_config.json:
{
"mcpServers": {
"citador": {
"command": "uvx",
"args": ["citador-ar-mcp"],
"env": { "CITADOR_DB": "/ruta/al/corpus.db" }
}
}
}
Si en cambio lo corrés desde el repo, reemplazá el comando por
uv --directory /ruta/a/citador-ar-mcp run citador-ar-mcp.
CITADOR_DB es opcional sólo si trabajás dentro del repo, donde por defecto usa
data/citador.db. Instalado desde PyPI no hay tal directorio, así que declaralo
apuntando al .db que bajaste del release.
Variables de entorno
| Variable | Para qué |
|---|---|
CITADOR_DB |
Dónde está el grafo. Por defecto data/citador.db |
CITADOR_LOG_LEVEL |
Nivel de log a stderr. Por defecto INFO |
CITADOR_TESSERACT |
Ruta al binario de Tesseract si no está en el PATH |
TESSDATA_PREFIX |
Dónde está spa.traineddata, si no está en el directorio estándar |
CITADOR_LLM |
1 para habilitar la etapa de clasificación por LLM. Apagada por defecto |
ANTHROPIC_API_KEY |
Credencial para la etapa de LLM. El SDK también acepta ANTHROPIC_AUTH_TOKEN o un perfil de ant auth login |
ANTHROPIC_WORKSPACE_ID |
Obligatoria si la organización usa claves identity-linked: la API rechaza el pedido con un 400, no con un 401, así que es fácil leerlo como un problema de credencial cuando no lo es |
La etapa de LLM consume créditos de la API, que se cargan aparte y no vienen
con una suscripción a Claude. Si falta crédito, diagnose() lo dice con todas
las letras en lugar de fallar en silencio:
uv run python -c "from citador_ar_mcp.ingest import llm; print(llm.diagnose() or 'lista')"
Para recuperar fallos viejos hace falta Tesseract con el paquete de español:
winget install UB-Mannheim.TesseractOCR
En Linux, apt install tesseract-ocr tesseract-ocr-spa; en macOS,
brew install tesseract tesseract-lang. El paquete spa.traineddata conviene
bajarlo de tessdata_best, que
rinde bastante mejor sobre impresiones viejas. Si el binario no está en el
PATH, definí CITADOR_TESSERACT; si los datos de idioma están fuera del
directorio estándar, TESSDATA_PREFIX.
Uso
El servidor lee un grafo SQLite que produce el pipeline de ingesta. La ingesta no corre dentro del servidor: es un proceso batch que deja un archivo.
Construir el grafo mínimo, con la cadena dorada y sin red:
uv run python -m citador_ar_mcp.ingest.build_graph --from-fixture
Correr el pipeline completo sobre un fallo (baja el PDF, extrae, pasa OCR si hace falta, encuentra las citas, las atribuye a su voto y clasifica el tratamiento):
uv run --extra ingest python -m citador_ar_mcp.ingest.build_graph --ruling 332:1963
Levantar el servidor:
uv run mcp dev src/citador_ar_mcp/server.py
Prompts
| Prompt | Para qué |
|---|---|
auditar_citas |
Revisar todas las citas de un escrito antes de presentarlo |
verificar_precedente |
Comprobar si conviene apoyarse en un fallo |
rastrear_doctrina |
Reconstruir cómo evolucionó una línea jurisprudencial |
auditar_citas es el caso de uso que justifica un citador: encontrar, antes que
la contraparte, que uno de los precedentes del escrito fue abandonado.
Evals
Diez preguntas en evals/evaluation.xml, cuatro de ellas midiendo clasificación
de tratamiento y no sólo recuperación. Corren contra el grafo del fixture, que es
el mismo que arma CI, así que son estables.
Licencia
MIT. El corpus derivado se publica con atribución a la Corte Suprema de Justicia de la Nación, Secretaría de Jurisprudencia.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file citador_ar_mcp-0.1.1.tar.gz.
File metadata
- Download URL: citador_ar_mcp-0.1.1.tar.gz
- Upload date:
- Size: 248.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1dd961beb262f1676703426d301059d01f9c0ed2f21fb48a09e9ee79e68562b2
|
|
| MD5 |
989a95bd2fc2c38ec07ac77752481a8b
|
|
| BLAKE2b-256 |
c8b23f83351dc21ff97a56a6a58f8b302aa6ae77b51a45f9b32af7eae23f93d8
|
Provenance
The following attestation bundles were made for citador_ar_mcp-0.1.1.tar.gz:
Publisher:
release.yml on LautaroMoya25/citador-ar-mcp
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
citador_ar_mcp-0.1.1.tar.gz -
Subject digest:
1dd961beb262f1676703426d301059d01f9c0ed2f21fb48a09e9ee79e68562b2 - Sigstore transparency entry: 2702709971
- Sigstore integration time:
-
Permalink:
LautaroMoya25/citador-ar-mcp@1602d6a3169ac0dfd75c1e5cf916bd5907bea4ae -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/LautaroMoya25
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@1602d6a3169ac0dfd75c1e5cf916bd5907bea4ae -
Trigger Event:
push
-
Statement type:
File details
Details for the file citador_ar_mcp-0.1.1-py3-none-any.whl.
File metadata
- Download URL: citador_ar_mcp-0.1.1-py3-none-any.whl
- Upload date:
- Size: 83.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9bc27560c96c81474b1bc5be27f5ba88fe2829da8063d471f5eacd92d8b5a0c3
|
|
| MD5 |
ba8dfc631f7dade07ab38765ce65d66d
|
|
| BLAKE2b-256 |
6569765b5f1cb8e5f7ce956994299e08b46e6c23f017afabc509dcee37f5caa1
|
Provenance
The following attestation bundles were made for citador_ar_mcp-0.1.1-py3-none-any.whl:
Publisher:
release.yml on LautaroMoya25/citador-ar-mcp
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
citador_ar_mcp-0.1.1-py3-none-any.whl -
Subject digest:
9bc27560c96c81474b1bc5be27f5ba88fe2829da8063d471f5eacd92d8b5a0c3 - Sigstore transparency entry: 2702710053
- Sigstore integration time:
-
Permalink:
LautaroMoya25/citador-ar-mcp@1602d6a3169ac0dfd75c1e5cf916bd5907bea4ae -
Branch / Tag:
refs/tags/v0.1.1 - Owner: https://github.com/LautaroMoya25
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@1602d6a3169ac0dfd75c1e5cf916bd5907bea4ae -
Trigger Event:
push
-
Statement type: