bdm-mcp
README.md
# mcp-bdm
Connettore casalingo per la **Banca Dati di Merito** pubblica del Ministero
(`bdp.giustizia.it`), pensato per essere pilotato da un assistente AI (Claude).
> **Skill inclusa.** Questo repo contiene la skill Claude **manolesta**
> (in `skill/manolesta/`), che pilota il connettore `bdm` per cercare e
> recuperare provvedimenti dalla BDM.
Backend **REST + GraphQL** (`/api/bdm/frontoffice/...`), autenticazione con **JWT
di sessione in cookie httpOnly**, depositato dopo il login **CNS** (chiavetta +
PIN) sul portale B2C del Ministero. La chiavetta serve solo a *ottenere* la
sessione: le ricerche girano poi server-side rigiocando la sessione via httpx.
Forma **ibrida**: un solo motore, due facce.
- **CLI** (`bdm …`) — tirare giù un provvedimento come `.md` pulito in una
cartella, senza bruciare il contesto del modello.
- **MCP sottile** (`bdm-mcp`) — per l'uso inline in Claude Desktop:
`bdm_check_session`, `bdm_estremi` (ricerca per estremi: per numero di
provvedimento **o per numero di ruolo/R.G.**), `bdm_search`
(full-text), `bdm_get_provvedimento` (testo integrale, con salvataggio
opzionale in cartella) e `bdm_get_workflow` / `bdm_set_workflow` (onboarding).
## ⚠️ Non è plug-and-play — è un riferimento «ecco come si fa»
Questo repo è pubblicato come **riferimento** per chi vuole capire *come* si
interroga la BDM da codice, non come tool pronto all'uso. Per farlo girare serve:
- **Windows** (il TLS verso la PA usa il trust store di sistema via `truststore`;
su Linux/macOS va adattato);
- **Python 3.10+**;
- un **login CNS proprio** (chiavetta + PIN): l'autenticazione è personale e non è
automatizzabile né condivisibile;
- **adattare i path** e le variabili d'ambiente al proprio ambiente.
Aspettati di dover mettere le mani nel codice. È materiale di studio, non un
prodotto.
## Stato — funzionante (nucleo verificato end-to-end in rete)
Nucleo CLI verificato **end-to-end in rete** (`check`, `search`, `get`, `estremi`).
**Contratto verificato dal vivo:**
- **Auth = replay del cookie** (`jwt_bdm_frontoffice` + cookie di sessione), via
httpx. Nessun header Authorization: i data-endpoint vogliono una richiesta "da
browser" — UA Chrome reale + `Sec-Fetch-Site: same-origin`, `Origin` solo sui
POST. TLS via **trust store di Windows** (`truststore`): certifi non ha la CA
della PA. Con la sessione scaduta i protetti danno `401` → `BdmAuthError`.
- **Ricerca = GraphQL** (`/api/bdm/frontoffice/graphql`, operazione
`searchProvvedimento`) su `provvedimento(from,size,area,q,sort_field,sort_order)`.
`area="CIVILE"`. **`q` NON è testo libero**: è una query-string `campo:"testo"`
unita con ` AND ` (come `filterExpressionToString` della SPA); il full-text sul
testo del provvedimento è **`anonymized_testo:"..."`**. I `*/filter` REST NON
sono la ricerca: sono il CRUD dei filtri salvati.
- **Testo integrale = REST** `GET provvedimento/{id}/document/testo` → testo piano,
**pseudonimizzato** all'origine (le parti diventano `Parte_1`, `C.F._1`…).
- **Tassonomie**: `GET materia?area=CIVILE`, `ufficio`, `giudice`, ecc.
- **Durata sessione ~120 min**: captured_at→exp = 2 ore. Il replay regge per tutta
la finestra; a scadenza serve il re-login CNS (`Rinnova-BDM.bat`).
- **Selezione dei cookie = denylist.** Si mandano i cookie del dominio dei dati,
scartando per *forma* quelli del portale B2C (nomi malformati col `:`, che
romperebbero l'header) e i domini estranei. In precedenza era un *allowlist*
ricavato da una singola cattura: bastava che il sito aggiungesse o rinominasse un
cookie perché il replay fallisse con un 401 **subito dopo un login riuscito**, in
loop e senza nulla, in nessun output, che nominasse i cookie. Ora `bdm check`
stampa i **nomi** dei cookie inviati (mai i valori), e `BDM_COOKIE_NAMES` permette
di forzare la selezione a mano.
**Residui (follow-up, non bloccanti):**
- Rifinire i nomi-campo delle faccette in `q` (`UFFICIO`/`MATERIA` maiuscoli sono
ipotesi da confermare; il full-text `anonymized_testo` è verificato).
## Uso
```
# login (apre il browser; l'accesso CNS è manuale, le credenziali non passano dallo script)
python -m mcp_bdm login # oppure: Rinnova-BDM.bat
# verifica sessione (chiamata dati reale)
python -m mcp_bdm check
# ricerca full-text
python -m mcp_bdm search "usucapione" --size 10
# ricerca per estremi (numero + anno + eventuale ufficio)
python -m mcp_bdm estremi --numero 1234 --anno 2024 --ufficio "TRIBUNALE DI VERONA"
# recupero testo integrale per id, salvato in una cartella
python -m mcp_bdm get <id> --dir "C:\percorso\alla\cartella"
```
(con `pip install -e .` i comandi diventano `bdm …` / `bdm-mcp`.)
## Config
La sessione vive in `config.json`, creato da `login`, sotto `%LOCALAPPDATA%\manolesta`
(override con `BDM_HOME`) — **fuori dall'albero del codice**: il codice puo' stare in
una cartella condivisa, il JWT no. Il file viene ristretto al solo utente corrente
via `icacls`; su Windows `chmod(0o600)` **non** basta, perche' tocca solo
l'attributo di sola lettura e non le ACL.
Contiene il **cookie jar** catturato (col JWT httpOnly), `exp` e l'utente (solo per
visualizzazione). Ai data-endpoint il client manda i cookie del dominio dei dati,
scartando quelli del portale B2C (vedi *Selezione dei cookie* sopra).
Le preferenze di flusso dell'utente (dove salvare, come nominare) vivono a parte, in
`manolesta.workflow.json`: non sono segreti e non stanno nel file di sessione.
Override via `BDM_API_BASE`, `BDM_HOME`, `BDM_COOKIE_NAMES`; profilo di login via
`BDM_LOGIN_PROFILE`, debug con `BDM_LOGIN_DEBUG=1`. Parti da `config.example.json`.
## Dipendenze runtime
`httpx`, `truststore` (TLS via store di Windows). Per il login: `playwright`.
Per l'MCP: `mcp`.
## Note di sicurezza e di uso
Il JWT è un segreto: vive fuori dall'albero del codice, con ACL ristretta al solo
utente corrente, e non viene mai stampato per intero. La BDM è pubblica e
gratuita, i testi sono **pseudonimizzati** all'origine. Uso previsto =
**consultazione mirata con la propria sessione**, come farebbe un umano: **niente
scraping massivo**. Copre la sola giurisprudenza **civile di merito** (Tribunali e
Corti d'Appello, dal 2016; esclude Cassazione/amministrativo/costituzionale e
famiglia/minori/stato delle persone).
## Licenza
[MIT](LICENSE).
This server cannot be deployed
Maintenance
ActivityStale
ResponsivenessNo issues