Files

298 lines
13 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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:**
```bash
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-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
```bash
# 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](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
- [Guida utenti famiglia](docs/GUIDA-UTENTI.md)
- [Onboarding tecnico client AI](docs/ONBOARDING.md)
- [Runbook operativo](/home/daniely/rete/ha/RUNBOOK-mcp.md)
- [README progetto](README.md)