hadith-mcp

Servidor del Protocolo de Contexto de Modelo para texto de hadices buscable y seguro para citas.

Documentación

hadith-mcp

Servidor y pipeline de datos del Model Context Protocol (MCP) para servir texto canónico de hadices (en árabe e inglés) a asistentes de manera segura para citas, similar en espíritu a quran-mcp: obtener desde un corpus real en lugar de citar desde la memoria del modelo.

Este repositorio proporciona un servidor FastMCP sobre data/hadith.db además de un pipeline de datos para construir esa base de datos: SQLite normalizado, embeddings de OpenAI (text-embedding-3-large), referencias cruzadas entre colecciones (similitud coseno + puntuación consciente del narrador) y etiquetas tipo procedencia (por ejemplo, enlaces estilo muttafaq entre Sahih al-Bujari y Sahih Muslim).

Fuentes de datos y créditos

  • El texto de los hadices proviene del conjunto de datos comunitario hadith-json (extraído de Sunnah.com), que se alinea con el ecosistema más amplio de sunnah-com / Quran Foundation, la misma familia de fuentes detrás de quran-mcp.
  • La arquitectura y los patrones están inspirados en quran-mcp (FastMCP, mentalidad de fundamentación, disposición de herramientas).

Si publicas un producto o un artículo, mantén visible la atribución upstream (autores del conjunto de datos, Sunnah.com y las propias colecciones académicas).

Estructura del repositorio

RutaPropósito
scripts/build_db.pyCargar hadith-json db/by_book JSON → esquema SQLite, embedding opcional, referencias cruzadas, procedencia
scripts/embed_hadith.pyEmbedding solo reanudable para filas con embedding IS NULL (lento, compatible con puntos de control)
scripts/merge_embedding_checkpoints.pyReproducir puntos de control de embedding JSONL en hadith.db después de fallos o restauraciones
scripts/compute_crossref.pyRecalcular solo cross_references + provenance (no reimporta JSON; seguro después del embedding)
scripts/fetch_ext_apps.pyProveer / actualizar @modelcontextprotocol/ext-apps como script clásico (establece window.__hadithMcpSdk) utilizado por el lector interactivo
scripts/generate_search_sitemap.pyRegenerar search/sitemap.xml (índice) + search/sitemaps/*.xml (~50k URLs de ?id=) desde data/hadith.db para SEO después de cambios en la base de datos
src/hadith_mcp/pipeline/Cargadores, esquema, embedding, referencias cruzadas, lógica de procedencia
src/hadith_mcp/server.pyAplicación FastMCP: herramientas MCP + una pequeña superficie REST (/api/collections, /api/hadith/{id}, /api/hadith/{slug}/{n}, /api/search) reutilizando el mismo almacén e índice de embeddings
src/hadith_mcp/assets/hadith_app.htmlPlantilla de UI de App MCP autocontenida (CSS en línea + lógica de aplicación, solo fuentes del sistema) servida en ui://hadith.html para la herramienta show_hadith
src/hadith_mcp/assets/ext-apps.bundle.jsSDK de apps externas incluido (cero importaciones externas) integrado en hadith_app.html en el momento de renderizar el recurso
search/Frontend de búsqueda estático (HTML/CSS/JS) desplegado de forma independiente (por ejemplo, search.hadith-mcp.org)
site/Página de aterrizaje estática para el dominio principal
config.ymlRuta de base de datos predeterminada opcional (anulada por HADITH_MCP_DB_PATH)

Los grandes árboles de referencia hadith-json-main/ y quran-mcp-master/ están listados en .gitignore. Clona o descomprime hadith-json localmente (por ejemplo, como hadith-json-main/) o pasa --data-dir a build_db.py.

Inicio rápido

python3 -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"
cp .env.example .env   # set OPENAI_API_KEY for embedding steps

1) Construir la base de datos (sin llamar a OpenAI)

Apunta --data-dir a tu directorio local hadith-json db/by_book.

python scripts/build_db.py --fresh --skip-embed --skip-cross --skip-provenance \
  --data-dir ./hadith-json-main/db/by_book

2) Embeddings (ejecución larga; usa una máquina separada si lo prefieres)

Valores predeterminados seguros: tamaño de lote 1, confirmar cada 10 filas, pausa entre llamadas, punto de control JSONL opcional para seguridad.

python scripts/embed_hadith.py \
  --db-path ./data/hadith.db \
  --checkpoint ./data/embeddings_checkpoint.jsonl \
  --batch-size 1 \
  --commit-every 10 \
  --sleep-between-batches 0.15

Reproduce los puntos de control en la base de datos cuando sea necesario:

python scripts/merge_embedding_checkpoints.py --db-path ./data/hadith.db \
  ./data/embeddings_checkpoint.jsonl --only-missing

3) Referencias cruzadas y procedencia (CPU local)

No vuelvas a ejecutar build_db.py sin --fresh después del embedding a menos que tengas la intención de reimportar JSON (esa ruta puede sobrescribir filas y borrar embedding). En su lugar:

python scripts/compute_crossref.py --db-path ./data/hadith.db

Para una compilación completa en una sola máquina (importar + embedding + referencias cruzadas + procedencia), ejecuta build_db.py una vez sin --skip-embed / --skip-cross / --skip-provenance, y pasa los indicadores de ritmo de embedding según sea necesario (python scripts/build_db.py --help).

4) Servidor MCP (stdio para Cursor / Claude Desktop)

Desde la raíz del repositorio con data/hadith.db presente (o establece HADITH_MCP_DB_PATH):

hadith-mcp --transport stdio
# or: python -m hadith_mcp --transport stdio
# or: fastmcp run hadith_mcp.server:mcp

Opcionalmente, --config config.yml establece database.path relativo al archivo de configuración. HADITH_MCP_DB_PATH anula ambos.

HTTP / SSE / HTTP transmisible (consulta la documentación de FastMCP para las variables de entorno de host/puerto):

hadith-mcp --transport http

Herramientas (resumen): fetch_grounding_rules devuelve el texto completo una vez por sesión MCP (luego una repetición corta a menos que force_full=True); pasa el nonce devuelto solo cuando necesites desambiguar errores. fetch_hadith acepta hadith_id global o collection + hadith_number (int, o rango de cadena como 1-5) con include_cross_references opcional. El argumento collection se resuelve mediante un coincidente de slugs tolerante — slugs canónicos (bukhari), variantes comunes (sahih-bukhari, Sahih al-Bukhari, sahih_bukhari) y nombres humanos (Sunan Abu Dawud, Musnad Ahmad, 40 Hadith Nawawi) se asignan todos a la misma fila. search_hadith predetermina a búsqueda semántica (carga todos los embeddings al inicio, embebe la consulta con el modelo de embedding de consulta configurado); usa mode=keyword para búsqueda de subcadena SQL, o mode=both. La búsqueda semántica necesita OPENAI_API_KEY y una base de datos cuyas filas incluyan embeddings. Si OpenAI devuelve errores de cuota/facturación/límite de velocidad (o el tamaño del vector de consulta no coincide con la base de datos), el servidor cae a búsqueda por palabras clave en lugar de fallar. fetch_cross_references devuelve coincidencias de similitud algorítmica entre colecciones para un hadiz dado. show_hadith abre un Lector de Hadices interactivo de App MCP en hosts compatibles (ChatGPT Developer Apps, Claude con soporte de apps, etc.); prefiere llamarlo con el hadith_id canónico devuelto por fetch_hadith / search_hadith / fetch_cross_references — collection + hadith_number y query de texto libre son respaldos compatibles, y la herramienta siempre devuelve un respaldo de texto plano para que los hosts sin Apps aún obtengan una respuesta legible con las mismas URLs de cita. Las instrucciones MCP de nivel superior empujan a los asistentes hacia el flujo de dos pasos (buscar primero, luego show_hadith(hadith_id=…)) para evitar adivinar slugs o números desde la memoria. Límites de velocidad por cliente opcionales y una caché de consultas LRU reducen costos y abuso (consulta config.yml / .env.example).

URLs de citas. fetch_hadith, search_hadith, fetch_cross_references y show_hadith adjuntan un campo url a cada fila de hadiz o referencia cruzada apuntando al frontend de búsqueda (https://search.hadith-mcp.org/?id=<db_id> por defecto, anulable mediante HADITH_SEARCH_APP_URL). Las instrucciones MCP del servidor indican a los asistentes que siempre muestren este enlace junto con las citas y que nunca fabriquen enlaces a sitios externos de hadices (sunnah.com, etc.).

Lector interactivo (show_hadith). La herramienta se vincula a un recurso ui://hadith.html servido como text/html;profile=mcp-app. La plantilla HTML vive en src/hadith_mcp/assets/hadith_app.html y es completamente autocontenida — CSS en línea, lógica de aplicación en línea, solo fuentes del sistema, cero descargas CDN, cero iframes de origen cruzado. El SDK de @modelcontextprotocol/ext-apps está incluido bajo src/hadith_mcp/assets/ext-apps.bundle.js (reescrito como script clásico que se adjunta a window.__hadithMcpSdk) y se inserta en la plantilla al inicio, de modo que todo el widget se envía como un solo documento HTML. Actualiza la versión fijada del SDK con python3 scripts/fetch_ext_apps.py --version <x.y.z>. El meta del recurso establece ui.csp.resourceDomains = [] (sin orígenes externos en tiempo de ejecución) y omite intencionalmente ui.domain porque ChatGPT y Claude requieren formatos incompatibles para ese campo (ChatGPT quiere cualquier URL de https://…; Claude requiere un subdominio de *.claudemcpcontent.com derivado de sha256 y da error con "App domain configuration is invalid" en cualquier otra cosa) — ambos hosts funcionan correctamente cuando el campo se omite. Una vez montada, la aplicación integrada llama a fetch_hadith y search_hadith a través del puente MCP (sin HTTPS adicional) para permitir a los usuarios abrir referencias cruzadas y alternar entre vistas de detalle y búsqueda sin idas y vueltas del LLM. Una llamada de show_hadith de un solo hadiz renderiza solo el marco de la tarjeta (sin barra de búsqueda); las llamadas con un query o sin argumentos renderizan la interfaz de barra de búsqueda + resultados.

5) Frontend de búsqueda público y API REST

El repositorio incluye una pequeña aplicación de búsqueda estática en search/ y una superficie REST HTTP en el mismo proceso FastMCP, destinada a desplegarse como dos subdominios (por ejemplo, search.hadith-mcp.org y api.hadith-mcp.org) con nginx / Caddy haciendo proxy de /api/* al puerto de FastMCP.

  • Frontend (search/): HTML/CSS/JS plano, sin paso de compilación. Se inicializa desde ?id=<db_id> o ?q=<query> al cargar, de modo que las URLs que emiten las herramientas MCP se resuelven directamente. La base de la API predetermina a https://api.hadith-mcp.org; anula en el navegador mediante window.HADITH_API_BASE (establece antes de que script.js cargue) para despliegues locales o de staging. search/sitemap.xml es un índice de sitemap; las listas de URLs por colección viven bajo search/sitemaps/ — regenera con python3 scripts/generate_search_sitemap.py después de reconstruir la base de datos.
  • Endpoints REST (mismo proceso, montados mediante @mcp.custom_route):
    • GET /api/collections → {collections: [...]}
    • GET /api/hadith/{hadith_id} → {hadith: {...}}
    • GET /api/hadith/{slug}/{id_in_book} → {hadith: {...}}
    • GET /api/search?q=&limit=&collection= → {results, mode, note}; semántico por defecto con el mismo comportamiento de respaldo por palabras clave que la herramienta MCP. Comparte HADITH_MCP_RATE_LIMIT_SEARCH_RPM y la caché de consultas con los clientes MCP, de modo que un presupuesto cubre ambas superficies.
    • GET /api/stats (barra final opcional) → recuentos agregados de búsqueda/consulta, visitantes únicos, tiempo de actividad. Las UIs de aterrizaje y búsqueda intentan mismo origen GET /api/stats primero, luego el host de API público; vuelve a copiar site/ y search/ cuando actualices esas páginas, o el navegador ejecutará HTML/JS antiguos.

Configuración

  • Secretos: .env está en gitignore; consulta .env.example para OPENAI_API_KEY y ajuste de MCP (HADITH_MCP_QUERY_EMBEDDING_MODEL, HADITH_MCP_RATE_LIMIT_SEARCH_RPM, HADITH_MCP_SEARCH_CACHE_MAX).
  • MCP alojado: Pon OPENAI_API_KEY en el servidor solo si aceptas pagar por embeddings de consulta; ajusta HADITH_MCP_RATE_LIMIT_SEARCH_RPM (por ejemplo, 30–120) y el tamaño de caché. El modelo de consulta debe coincidir con la dimensión de los vectores almacenados en hadith.db (la compilación de este repositorio usa text-embedding-3-large / 3072). Un modelo de OpenAI más barato generalmente significa reconstruir la base de datos con ese modelo para que las dimensiones se alineen.
  • Artefactos: Otros archivos data/*.db y globs de puntos de control de embedding están en gitignore; este repositorio rastrea data/hadith.db (Git LFS) además de data/SHA256SUMS para verificación (cd data && sha256sum -c SHA256SUMS).
  • Embeddings: Las filas con narrador y texto en inglés vacíos aún se embeben usando texto árabe cuando está presente. Las entradas largas se recortan con tiktoken (cl100k_base) para mantenerse bajo el límite de API de 8192 tokens, con una escalera de reducción adicional si una fila aún encuentra errores de longitud.
  • Contar filas sin la CLI de sqlite3:
    python -c "import sqlite3; c=sqlite3.connect('data/hadith.db'); print(c.execute('SELECT COUNT(*) FROM hadiths WHERE embedding IS NULL').fetchone()[0])"

Licencia

  • Software en este repositorio (Python, scripts y documentación que añadimos) está licenciado bajo GNU General Public License v3.0 only (SPDX: GPL-3.0-only).
  • El texto de los hadices y otro material de fuentes originales permanecen bajo sus términos originales (hadith-json, Sunnah.com). Nuestra GPL se aplica a nuestro código, no a una relicencia de ese contenido; mantén la atribución y sigue las reglas de las fuentes originales cuando redistribuyas datos o extractos.

Lanzamientos e integridad de datos

  • Suma de verificación: data/SHA256SUMS lista hadith.db. Después de clonar o descargar la base de datos, ejecuta cd data && sha256sum -c SHA256SUMS.
  • Firma (opcional): Una firma GPG o Sigstore separada sobre el archivo de suma de verificación o la base de datos demuestra quién publicó los bytes y que no fueron alterados posteriormente. Firmar no certifica la precisión académica de cada narración o referencia cruzada automatizada.
  • Reproducibilidad: Para auditorías o reconstrucciones, registra la revisión de hadith-json, la revisión de git de este repositorio, el id del modelo de incrustación y las versiones de los scripts que usaste.

Contribuciones

Se aceptan issues y PRs. Por favor, mantén los diffs enfocados y sigue el estilo existente (ruff / pytest cuando estén presentes).