Files
loogle-scripts/services/loogle-mcp/docs/ARCHITETTURA.md
T

13 KiB
Raw Blame History

Loogle MCP Hub — Architettura e parametri

Documento di riferimento per la piattaforma MCP domestica LOOGLE.IT.
Ultimo aggiornamento: 2026-08-22


Scopo

Loogle MCP Hub espone agli assistenti AI (Claude, ChatGPT, Gemini, Cursor) due capacità principali:

  1. Archivio contesto — memoria persistente per progetti e task, isolata per utente
  2. Knowledge base RAG — ricerca semantica su documenti Paperless e sui contesti salvati

Tutti i dati restano in LAN/NAS; laccesso esterno avviene solo via HTTPS + OAuth.


Architettura logica

┌─────────────────────────────────────────────────────────────┐
│  Client AI (ChatGPT / Claude / Gemini / Cursor)             │
└───────────────────────────┬─────────────────────────────────┘
                            │ HTTPS + OAuth Bearer
                            ▼
┌─────────────────────────────────────────────────────────────┐
│  NPM VIP 192.168.128.85  →  mcp.loogle.it:443               │
└───────────────────────────┬─────────────────────────────────┘
                            │
                            ▼
┌─────────────────────────────────────────────────────────────┐
│  Pi-1 :8700  loogle-mcp (FastAPI)                          │
│    ├── /mcp          JSON-RPC MCP                             │
│    ├── /oauth/*      OAuth 2.1 PKCE                         │
│    └── /dashboard    UI famiglia                            │
│  Pi-1 :8700  loogle-mcp-indexer (worker Paperless→vector)   │
└───────┬─────────────────────────────┬───────────────────────┘
        │                             │
        ▼                             ▼
┌───────────────┐           ┌─────────────────────┐
│ SQLite        │           │ Vector store        │
│ /data/*.db    │           │ Qdrant DS920 :6333  │
│ utenti OAuth  │           │ o fallback SQLite   │
└───────────────┘           └─────────────────────┘
        │
        ▼
┌─────────────────────────────────────────────────────────────┐
│  NFS /mnt/ha-apps/mcp/context/{utente}/projects/...       │
└─────────────────────────────────────────────────────────────┘
        │
        ▼
┌─────────────────────────────────────────────────────────────┐
│  Paperless docs.loogle.it (sorgente documenti + OCR)        │
└─────────────────────────────────────────────────────────────┘

Componenti e percorsi

Componente Host Porta Percorso / container
MCP Gateway Pi-1 8700 loogle-mcp
Indexer Pi-1 loogle-mcp-indexer
Qdrant (primario) DS920 6333 opzionale, compose extrema
Vector fallback Pi-1 /data/vector_fallback.db
Context NFS NAS /mnt/ha-apps/mcp/context/
DB SQLite Pi-1 /home/daniely/docker/loogle-mcp/data/loogle_mcp.db
Compose locale Pi-1 /home/daniely/docker/loogle-mcp/
Compose failover Pi-1/Pi-2 /home/daniely/rete/compose/failover/loogle-mcp-compose.yml
Runbook /home/daniely/rete/ha/RUNBOOK-mcp.md

URL e endpoint

Servizio URL
MCP (client AI) https://mcp.loogle.it/mcp
Dashboard web https://mcp.loogle.it/dashboard
Health https://mcp.loogle.it/health
OAuth authorize https://mcp.loogle.it/oauth/authorize
OAuth token https://mcp.loogle.it/oauth/token
Metadata OAuth https://mcp.loogle.it/.well-known/oauth-authorization-server
Metadata risorsa MCP https://mcp.loogle.it/.well-known/oauth-protected-resource

DNS

Record Tipo Valore Dove configurato
mcp.loogle.it A 192.168.128.85 (VIP NPM) Pi-hole /etc/pihole/pihole.toml → hosts
Risoluzione LAN Pi-hole → Unbound → split-horizon Come docs.loogle.it, pass.loogle.it

Script setup: /home/daniely/rete/scripts/setup-mcp-dns-npm.sh

Verifica:

dig +short mcp.loogle.it @127.0.0.1   # → 192.168.128.85
curl -sf https://mcp.loogle.it/health

NPM (reverse proxy)

Parametro Valore
Proxy host ID 30
Dominio mcp.loogle.it
Upstream normale 192.168.128.80:8700
Certificato TLS npm-65 (Let's Encrypt)
Failover route rete/ha/npm-routes.conf riga 30|mcp.loogle.it|8700|...
Websocket ON
Force SSL ON

Failover: npm-failover-route.sh aggiorna upstream su Pi-2 / DS920 in base allo scenario HA.


Variabili d'ambiente (.env)

File: /home/daniely/docker/loogle-mcp/.env

Variabile Descrizione Esempio
MCP_BASE_URL URL pubblico (issuer JWT) https://mcp.loogle.it
MCP_JWT_SECRET Segreto firma token OAuth (stringa lunga casuale)
MCP_OAUTH_CLIENT_ID Client OAuth pre-registrato loogle-mcp-public
PAPERLESS_URL API documenti https://docs.loogle.it
PAPERLESS_API_TOKEN Token API admin (fallback) vedi PAPERLESS-TOKEN.md
PAPERLESS_API_TOKEN_{USER} Token API per utente DANIELE, LUCIA, DAVIDE, LUCA
QDRANT_URL Vector DB remoto http://192.168.128.100:6333
OLLAMA_URL Embedding locale http://192.168.128.100:11434
OLLAMA_EMBED_MODEL Modello embedding nomic-embed-text
OPENAI_API_KEY Fallback embedding cloud (opzionale)
INDEXER_INTERVAL_MINUTES Intervallo sync Paperless 30
THERMAL_TEMP_HARD_C Pausa embedding se CPU DS920 ≥ °C 70
THERMAL_TEMP_SOFT_C Rallenta embedding sopra °C 58
THERMAL_CPU_TARGET_PCT Cap load1 (nproc × %) 40
OLLAMA_NUM_THREAD Thread CPU per richiesta embed 1
OLLAMA_EMBED_DELAY_S Pausa tra embed (duty-cycle lento) 10
DS920_THERMAL_URL Probe temp/load DS920 http://192.168.128.100:9191/thermal

Utenti e isolamento

Persona Username Ruolo Password iniziale
Daniele daniele admin daniele (cambiarla)
Lucia lucia user lucia
Davide davide user davide
Luca luca user luca
  • Ogni utente vede solo i propri progetti in /mnt/ha-apps/mcp/context/{username}/
  • I token OAuth JWT contengono sub = username; ogni tool filtra per utente
  • Ladmin può reindex_document, vedere audit completo, revocare refresh token

Scope OAuth: context:read, context:write, knowledge:read, knowledge:write, gitea:read, gitea:write, home:read, irrigation:read, turni:read, admin (solo daniele)


Tool MCP esposti

Tool Scope minimo Funzione
ping Test connettività
whoami Utente e scope attivi
list_projects context:read Elenco progetti
create_project context:write Nuovo progetto
get_project_context context:read Legge memoria progetto
save_context context:write Salva/appende memoria
archive_project context:write Archivia progetto
search_context context:read Ricerca semantica contesti
search_knowledge knowledge:read RAG Paperless + contesti + Gitea + app
search_gitea_knowledge knowledge:read RAG solo Gitea
search_apps_knowledge knowledge:read RAG Irrigazione + Turni
list_apps_indexed knowledge:read Stato indicizzazione app
reindex_apps admin Re-sync RAG app
get_document knowledge:read Testo documento Paperless
list_recent_documents knowledge:read Ultimi doc indicizzati
reindex_document admin Re-indicizza documento
get_home_dashboard home:read Dashboard Loogle Casa
get_home_weather home:read Meteo casa
get_network_overview home:read Panoramica rete LAN
get_network_failover_status home:read Stato failover cluster
get_ha_entity home:read Entità Home Assistant
list_ha_entities home:read Elenco entità HA
search_ha_entities home:read Cerca entità HA
get_irrigation_status irrigation:read Stato irrigazione
get_irrigation_zones irrigation:read Zone irrigazione
get_irrigation_history irrigation:read Storico irrigazioni
get_turni_status Stato servizio Turni
get_my_shifts turni:read Turni utente corrente
list_turni_doctors turni:read Medici Turni-Live

Vedi anche tool Gitea in docs/GITEA-TOKEN.md e app homelab in docs/APPS-INTEGRATION.md.


Storage contesto (per progetto)

/mnt/ha-apps/mcp/context/{username}/projects/{project-id}/
  meta.json       # titolo, tag, date, stato, gitea_repo (opz.)
  context.md      # memoria persistente
  sessions/       # snapshot conversazioni
  artifacts/      # file generati dagli agenti

Vector store / RAG

Collection Qdrant Contenuto
kb_shared_family Documenti famiglia (tag non personali)
kb_personal_{user} Documenti personali
ctx_{user} Embedding dei contesti agente
gitea_shared_family File testo da repo Gitea non privati
gitea_personal_{user} File testo da repo Gitea privati dell'owner
apps_shared_family Export Irrigazione + Turni (P7)

Nota Pi 5: Qdrant locale su ARM (page size 16K) non è supportato. Default: Qdrant su DS920 o fallback SQLite in /data/vector_fallback.db.

Pipeline indexer: ogni 30 min:

  1. Paperless → chunk → embedding (Ollama DS920) → upsert kb_*
  2. Gitea (repo configurati in GITEA_INDEX_REPOS_*) → file .md/.py/.sh → chunk → upsert gitea_*
  3. Irrigazione + Turni → snapshot/storico → upsert apps_shared_family (vedi docs/APPS-INTEGRATION.md)

Sicurezza

  • TLS terminato su NPM (Let's Encrypt)
  • OAuth 2.1 Authorization Code + PKCE
  • JWT access token 1h + refresh token 30 giorni (revocabili)
  • Audit log: {timestamp, user, tool, resource_id} in SQLite
  • CrowdSec attivo su NPM
  • Nessun accesso cross-user ai dati

Operazioni comuni

# Stato servizi
cd /home/daniely/docker/loogle-mcp && docker compose ps

# Log
docker logs -f loogle-mcp
docker logs -f loogle-mcp-indexer

# Redeploy
./scripts/deploy.sh

# DNS + NPM (se reinstall)
sudo /home/daniely/rete/scripts/setup-mcp-dns-npm.sh

# Failover route
/home/daniely/rete/scripts/npm-failover-route.sh normal

# Backup dati MCP
/home/daniely/rete/infra-monitor/mcp-restic-backup.sh

Failover (tier-b)

Scenario Comportamento
Pi-1 down, Pi-2 up Stack MCP su Pi-2, NPM upstream → .81:8700
Extrema DS920 NPM3 + upstream DS920
Monitoraggio Probe mcp_ok in failover-status.sh

Da completare (post-install)

  1. PAPERLESS_API_TOKEN_* in .env — vedi PAPERLESS-TOKEN.md
  2. Ollama su DS920 con nomic-embed-text per embedding locali
  3. Qdrant su DS920 (opzionale): rete/compose/extrema-ds920/loogle-mcp-compose.yml
  4. Cambio password per tutti gli utenti dal dashboard
  5. MCP_JWT_SECRET — impostare valore robusto in produzione

Riferimenti