hadith-mcp

Servidor do Model Context Protocol para texto de hadith pesquisável e seguro para citações.

Documentação

hadith-mcp

Servidor Model Context Protocol (MCP) e pipeline de dados para servir textos canônicos de hadith (em árabe e inglês) a assistentes de forma segura para citações—semelhante em espírito ao quran-mcp: buscar de um corpus real em vez de citar da memória do modelo.

Este repositório fornece um servidor FastMCP sobre data/hadith.db além de um pipeline de dados para construir esse banco de dados: SQLite normalizado, embeddings OpenAI (text-embedding-3-large), referências cruzadas entre coleções (similaridade de cosseno + pontuação ciente do narrador) e tags estilo proveniência (ex.: links estilo muttafaq entre Sahih al-Bukhari e Sahih Muslim).

Fontes de dados e créditos

  • Texto dos hadiths vem do dataset comunitário hadith-json (extraído de Sunnah.com), que se alinha ao ecossistema mais amplo sunnah-com / Quran Foundation—a mesma família de fontes por trás do quran-mcp.
  • Arquitetura e padrões são inspirados no quran-mcp (FastMCP, mentalidade de fundamentação, layout de ferramentas).

Se você publicar um produto ou artigo, mantenha a atribuição upstream visível (autores do dataset, Sunnah.com e as próprias coleções acadêmicas).

Estrutura do repositório

CaminhoPropósito
scripts/build_db.pyCarrega hadith-json db/by_book JSON → esquema SQLite, embed opcional, referência cruzada, proveniência
scripts/embed_hadith.pyEmbedding somente retomável para linhas com embedding IS NULL (lento, amigável a checkpoints)
scripts/merge_embedding_checkpoints.pyReproduz checkpoints de embedding JSONL em hadith.db após falhas ou restaurações
scripts/compute_crossref.pyRecalcula cross_references + provenance apenas (não reimporta JSON; seguro após o embed)
scripts/fetch_ext_apps.pyFornece / atualiza @modelcontextprotocol/ext-apps como script clássico (define window.__hadithMcpSdk) usado pelo leitor interativo
scripts/generate_search_sitemap.pyRegenera search/sitemap.xml (índice) + search/sitemaps/*.xml (~50k URLs ?id=) a partir de data/hadith.db para SEO após mudanças no banco
src/hadith_mcp/pipeline/Lógica de carregadores, esquema, embed, referência cruzada, proveniência
src/hadith_mcp/server.pyApp FastMCP: ferramentas MCP + uma pequena superfície REST (/api/collections, /api/hadith/{id}, /api/hadith/{slug}/{n}, /api/search) reutilizando o mesmo armazenamento e índice de embeddings
src/hadith_mcp/assets/hadith_app.htmlTemplate de UI do App MCP autocontido (CSS inline + lógica do app, apenas fontes do sistema) servido em ui://hadith.html para a ferramenta show_hadith
src/hadith_mcp/assets/ext-apps.bundle.jsSDK de apps externos fornecido (zero imports externos) embutido em hadith_app.html no momento da renderização do recurso
search/Frontend de busca estático (HTML/CSS/JS) implantado de forma independente (ex.: search.hadith-mcp.org)
site/Página de destino estática para o domínio principal
config.ymlCaminho padrão opcional do banco (substituído por HADITH_MCP_DB_PATH)

Árvores de referência grandes hadith-json-main/ e quran-mcp-master/ estão listadas em .gitignore. Clone ou descompacte hadith-json localmente (por exemplo, como hadith-json-main/) ou passe --data-dir para build_db.py.

Início 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 o banco de dados (sem chamar a OpenAI)

Aponte --data-dir para seu diretório 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 (execução longa; use uma máquina separada se preferir)

Padrões seguros: tamanho de lote 1, commit a cada 10 linhas, pausa entre chamadas, checkpoint JSONL opcional para segurança.

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

Reproduza checkpoints no banco quando necessário:

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

3) Referências cruzadas e proveniência (CPU local)

Não execute novamente build_db.py sem --fresh após o embedding, a menos que pretenda reimportar JSON (esse caminho pode sobrescrever linhas e limpar embedding). Em vez disso:

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

Para uma construção completa em máquina única (importação + embed + cruzamento + proveniência), execute build_db.py uma vez sem --skip-embed / --skip-cross / --skip-provenance, e passe flags de ritmo de embedding conforme necessário (python scripts/build_db.py --help).

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

A partir da raiz do repositório com data/hadith.db presente (ou defina HADITH_MCP_DB_PATH):

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

O --config config.yml opcional define database.path relativo ao arquivo de configuração. HADITH_MCP_DB_PATH substitui ambos.

HTTP / SSE / HTTP transmissível (veja a documentação do FastMCP para variáveis de ambiente de host/porta):

hadith-mcp --transport http

Ferramentas (resumo): fetch_grounding_rules retorna o texto completo uma vez por sessão MCP (depois uma repetição curta, a menos que force_full=True); passe o nonce retornado apenas quando precisar desambiguar erros. fetch_hadith aceita hadith_id global ou collection + hadith_number (int, ou intervalo de string como 1-5) com include_cross_references opcional. O argumento collection é resolvido por um correspondente de slug tolerante — slugs canônicos (bukhari), variantes comuns (sahih-bukhari, Sahih al-Bukhari, sahih_bukhari) e nomes humanos (Sunan Abu Dawud, Musnad Ahmad, 40 Hadith Nawawi) mapeiam todos para a mesma linha. search_hadith usa como padrão a busca semântica (carrega todos os embeddings na inicialização, embute a consulta com o modelo de embedding de consulta configurado); use mode=keyword para busca por substring SQL, ou mode=both. A busca semântica precisa de OPENAI_API_KEY e de um banco cujas linhas incluam embeddings. Se a OpenAI retornar erros de cota/cobrança/limite de taxa (ou o tamanho do vetor da consulta não corresponder ao banco), o servidor recorre à busca por palavras-chave em vez de falhar. fetch_cross_references retorna correspondências algorítmicas de similaridade entre coleções para um hadith específico. show_hadith abre um Leitor de Hadith interativo do App MCP em hosts compatíveis (ChatGPT Developer Apps, Claude com suporte a apps, etc.); prefira chamá-lo com o hadith_id canônico retornado por fetch_hadith / search_hadith / fetch_cross_references — collection + hadith_number e query de texto livre são fallbacks suportados, e a ferramenta sempre retorna um fallback em texto simples para que hosts sem App ainda obtenham uma resposta legível com as mesmas URLs de citação. As instruções MCP de nível superior orientam os assistentes para o fluxo de duas etapas (consultar primeiro, depois show_hadith(hadith_id=…)) para evitar adivinhar slugs ou números da memória. Limites de taxa por cliente opcionais e um cache de consulta LRU reduzem custo e abuso (veja config.yml / .env.example).

URLs de citação. fetch_hadith, search_hadith, fetch_cross_references e show_hadith anexam um campo url a cada linha de hadith ou referência cruzada apontando para o frontend de busca (https://search.hadith-mcp.org/?id=<db_id> por padrão, substituível via HADITH_SEARCH_APP_URL). As instruções MCP do servidor dizem aos assistentes para sempre exibir esse link junto às citações e nunca fabricar links para sites externos de hadith (sunnah.com, etc.).

Leitor interativo (show_hadith). A ferramenta vincula-se a um recurso ui://hadith.html servido como text/html;profile=mcp-app. O template HTML fica em src/hadith_mcp/assets/hadith_app.html e é totalmente autocontido — CSS inline, lógica de app inline, apenas fontes do sistema, zero buscas CDN, zero iframes de origem cruzada. O SDK @modelcontextprotocol/ext-apps é fornecido sob src/hadith_mcp/assets/ext-apps.bundle.js (reescrito como script clássico que anexa a window.__hadithMcpSdk) e inserido no template na inicialização, então o widget inteiro é entregue como um único documento HTML. Atualize a versão fixada do SDK com python3 scripts/fetch_ext_apps.py --version <x.y.z>. O meta do recurso define ui.csp.resourceDomains = [] (sem origens externas em tempo de execução) e intencionalmente omite ui.domain porque ChatGPT e Claude exigem formatos incompatíveis para esse campo (ChatGPT quer qualquer URL https://…; Claude exige um subdomínio *.claudemcpcontent.com derivado de sha256 e retorna erro "App domain configuration is invalid" para qualquer outra coisa) — ambos os hosts funcionam corretamente quando o campo é omitido. Uma vez montado, o app embutido chama fetch_hadith e search_hadith pela ponte MCP (sem HTTPS extra) para permitir que usuários abram referências cruzadas e alternem entre visualizações de detalhe e busca sem idas e voltas ao LLM. Uma chamada show_hadith de hadith único renderiza apenas o chrome do cartão (sem barra de busca); chamadas com query ou sem argumentos renderizam a UI de barra de busca + resultados.

5) Frontend de busca público e API REST

O repositório inclui um pequeno app de busca estático em search/ e uma superfície REST HTTP no mesmo processo FastMCP, destinado a ser implantado como dois subdomínios (ex.: search.hadith-mcp.org e api.hadith-mcp.org) com nginx / Caddy fazendo proxy de /api/* para a porta do FastMCP.

  • Frontend (search/): HTML/CSS/JS simples, sem etapa de build. Inicializa a partir de ?id=<db_id> ou ?q=<query> no carregamento, para que as URLs emitidas pelas ferramentas MCP resolvam diretamente. A base da API usa como padrão https://api.hadith-mcp.org; substitua no navegador via window.HADITH_API_BASE (definido antes de script.js carregar) para implantações locais ou de staging. search/sitemap.xml é um índice de sitemap; listas de URLs por coleção ficam sob search/sitemaps/ — regenere com python3 scripts/generate_search_sitemap.py após reconstruir o banco de dados.
  • Endpoints REST (mesmo processo, montados via @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 padrão com o mesmo comportamento de fallback por palavras-chave da ferramenta MCP. Compartilha HADITH_MCP_RATE_LIMIT_SEARCH_RPM e o cache de consultas com clientes MCP, então um orçamento cobre ambas as superfícies.
    • GET /api/stats (barra final opcional) → contagens agregadas de busca/consulta, visitantes únicos, uptime. As UIs de destino e busca tentam GET /api/stats de mesma origem primeiro, depois o host da API pública; recopie site/ e search/ ao atualizar essas páginas, ou o navegador executará HTML/JS antigos.

Configuração

  • Segredos: .env é ignorado pelo git; veja .env.example para OPENAI_API_KEY e ajustes de MCP (HADITH_MCP_QUERY_EMBEDDING_MODEL, HADITH_MCP_RATE_LIMIT_SEARCH_RPM, HADITH_MCP_SEARCH_CACHE_MAX).
  • MCP hospedado: Coloque OPENAI_API_KEY no servidor apenas se aceitar pagar por embeddings de consulta; ajuste HADITH_MCP_RATE_LIMIT_SEARCH_RPM (ex.: 30–120) e o tamanho do cache. O modelo de consulta deve corresponder à dimensão dos vetores armazenados em hadith.db (a construção deste repositório usa text-embedding-3-large / 3072). Um modelo OpenAI mais barato geralmente significa reconstruir o banco de dados com esse modelo para que as dimensões se alinhem.
  • Artefatos: Outros arquivos data/*.db e globs de checkpoint de embedding são ignorados pelo git; este repositório rastreia data/hadith.db (Git LFS) além de data/SHA256SUMS para verificação (cd data && sha256sum -c SHA256SUMS).
  • Embeddings: Linhas com narrador e texto em inglês vazios ainda geram embedding usando o texto em árabe quando presente. Entradas longas são truncadas com tiktoken (cl100k_base) para permanecer abaixo do limite da API de 8192 tokens, com uma escada de redução adicional se uma linha ainda atingir erros de comprimento.
  • Contar linhas sem o CLI sqlite3:
    python -c "import sqlite3; c=sqlite3.connect('data/hadith.db'); print(c.execute('SELECT COUNT(*) FROM hadiths WHERE embedding IS NULL').fetchone()[0])"

Licença

  • Software neste repositório (Python, scripts e documentação que adicionamos) é licenciado sob GNU General Public License v3.0 only (SPDX: GPL-3.0-only).
  • Texto de hadith e outro material upstream permanece sob seus termos originais (hadith-json, Sunnah.com). Nossa GPL se aplica ao nosso código, não a um relicenciamento desse conteúdo; mantenha a atribuição e siga as regras upstream ao redistribuir dados ou trechos.

Lançamentos e integridade dos dados

  • Checksum: data/SHA256SUMS lista hadith.db. Após clonar ou baixar o banco de dados, execute cd data && sha256sum -c SHA256SUMS.
  • Assinatura (opcional): Uma assinatura GPG ou Sigstore destacada sobre o arquivo de checksum ou o banco de dados prova quem publicou os bytes e que eles não foram alterados posteriormente. A assinatura não certifica a precisão acadêmica de cada narração ou referência cruzada automatizada.
  • Reprodutibilidade: Para auditorias ou reconstruções, registre a revisão do hadith-json, a revisão git deste repositório, o ID do modelo de embedding e as versões de script que você usou.

Contribuindo

Issues e PRs são bem-vindos. Por favor, mantenha os diffs focados e siga o estilo existente (ruff / pytest quando presente).