13 KiB
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:
- Archivio contesto — memoria persistente per progetti e task, isolata per utente
- Knowledge base RAG — ricerca semantica su documenti Paperless e sui contesti salvati
Tutti i dati restano in LAN/NAS; l’accesso 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 - L’admin 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:
- Paperless → chunk → embedding (Ollama DS920) → upsert
kb_* - Gitea (repo configurati in
GITEA_INDEX_REPOS_*) → file.md/.py/.sh→ chunk → upsertgitea_* - Irrigazione + Turni → snapshot/storico → upsert
apps_shared_family(vedidocs/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)
PAPERLESS_API_TOKEN_*in.env— vedi PAPERLESS-TOKEN.md- Ollama su DS920 con
nomic-embed-textper embedding locali - Qdrant su DS920 (opzionale):
rete/compose/extrema-ds920/loogle-mcp-compose.yml - Cambio password per tutti gli utenti dal dashboard
MCP_JWT_SECRET— impostare valore robusto in produzione