# docs-node MCP Server

Il server `docs-node` indicizza documenti Markdown in un database SQLite locale e supporta relazioni semantiche ("mappa mentale") tra documenti e scaffali.

Per default il database e' `docs-node/docs.db`. In test, sandbox o ambienti dove la cartella del server non e' scrivibile, impostare `DOCS_NODE_DB_PATH` a un path SQLite scrivibile.

## Modalita remote-server read-only

La Fase 2 della roadmap online/sync introduce una modalita HTTP read-only separata dal flusso MCP locale via `stdio`.

Avvio locale invariato:

```bash
node docs-node/index.js
```

Avvio remoto read-only:

```bash
DOCS_NODE_MODE=remote-server DOCS_NODE_HTTP_PORT=8787 node docs-node/index.js
```

Oppure via CLI:

```bash
node docs-node/index.js --mode remote-server --http-port 8787
```

Variabili/env supportati:

- `DOCS_NODE_MODE`: `stdio` (default) oppure `remote-server`
- `DOCS_NODE_HTTP_PORT`: porta HTTP della modalita remota, default `8787`
- `DOCS_NODE_HTTP_TOKEN`: bearer token richiesto per `/sync/*` e `/mcp` se valorizzato
- `DOCS_NODE_HTTP_WRITE_TOKEN`: bearer token separato per gli endpoint sync write Fase 5; se assente, `POST /sync/*` write resta disabilitato
- `DOCS_NODE_ALLOWED_ORIGINS`: allowlist Origin separata da virgole per `/sync/*` e `/mcp`; se vuota/non impostata non viene applicato alcun controllo Origin
- `DOCS_NODE_DB_PATH`: path del database SQLite, invariato
- `DOCS_NODE_SYNC_ON_STARTUP=1`: solo in modalita `stdio`, avvia in background una sync best-effort degli scaffali gia configurati `read_only`/`read_write`; non ha effetto in `remote-server`

Endpoint HTTP principali:

- `GET /health`: pubblico, restituisce stato base del server, `schema_version`, conteggi essenziali, `write_enabled`, `remote_server_mode` e ultimi errori sync redatti senza esporre segreti
- `GET /sync/shelves`: elenco scaffali pubblicabili via sync read-only
- `GET /sync/shelves/{shelf_uuid}`: dettaglio minimo dello scaffale
- `GET /sync/shelves/{shelf_uuid}/snapshot`: snapshot Manifest V3 dello scaffale
- `POST /sync/shelves/{shelf_uuid}/publish`: publish write protetto da `DOCS_NODE_HTTP_WRITE_TOKEN`
- `POST /sync/shelves/{shelf_uuid}/changes`: push batch write protetto da `DOCS_NODE_HTTP_WRITE_TOKEN`
- `POST /sync/shelves/{shelf_uuid}/change-log/prune`: prune remoto esplicito del changelog, protetto da `DOCS_NODE_HTTP_WRITE_TOKEN`
- `POST /mcp`: endpoint MCP Streamable HTTP nello stesso processo remoto

Sicurezza minima:

- se `DOCS_NODE_HTTP_TOKEN` e' impostato, `/sync/*` e `/mcp` richiedono `Authorization: Bearer <token>`
- gli endpoint `POST /sync/shelves/{uuid}/publish` e `POST /sync/shelves/{uuid}/changes` richiedono sempre `DOCS_NODE_HTTP_WRITE_TOKEN`; il token read non abilita write se il token write non e' configurato
- token assente -> `401`, token errato -> `403`
- se `DOCS_NODE_ALLOWED_ORIGINS` e' impostato, un Origin non presente in allowlist viene rifiutato con `403`
- con allowlist configurata, richieste senza header `Origin` ai protected endpoint vengono rifiutate; per client non browser lasciare l'allowlist non configurata oppure passare tramite reverse proxy che imposti l'header coerente
- il token non viene loggato dal server

Note operative:

- la snapshot remota riusa il Manifest V3 esistente (`format_version: 3`) e non introduce nuovi formati
- dopo modifiche al codice server MCP, riavviare `docs-node`
- per esposizione esterna sono raccomandati HTTPS e reverse proxy davanti al bind locale `127.0.0.1`

## Esposizione HTTPS persistente su `sophia-dev-01`

Lo script [`scripts/configure-docs-node-remote.sh`](../scripts/configure-docs-node-remote.sh)
configura un secondo processo `docs-node` in modalità `remote-server`, senza
modificare l'istanza MCP locale via `stdio`. I due processi usano lo stesso
SQLite canonico `docs-node/docs.db`: quello locale continua quindi a funzionare
come prima, mentre quello HTTP espone soltanto le operazioni di lettura.

### Prerequisiti

La configurazione è specifica dell'host `sophia-dev-01` e richiede:

- Apache attivo con `mod_proxy`, `mod_proxy_http` e TLS;
- il virtual host HTTPS di `dev-01.sophiainformatica.it`;
- Node.js e OpenSSL installati;
- possibilità di eseguire `sudo`.

### Installazione

Dalla root del repository eseguire manualmente:

```bash
sudo ./scripts/configure-docs-node-remote.sh
```

Lo script:

1. salva una copia timestampata di `/etc/apache2/sites-available/dev-01-ssl.conf`;
2. genera un bearer token casuale, salvato esclusivamente in
   `/etc/docs-node/remote.env` con permessi `0600` (`root:root`);
3. installa e abilita il servizio `docs-node-remote.service` con backend su
   `127.0.0.1:8787`;
4. aggiunge il reverse proxy HTTPS `/docs-node/` al virtual host esistente;
5. esegue `apache2ctl configtest`, health check loopback e HTTPS, e controlla
   che richieste non autenticate siano rifiutate con `401`.

L'endpoint MCP risultante è:

```text
https://dev-01.sophiainformatica.it/docs-node/mcp
```

Il health check pubblico è:


```text
https://dev-01.sophiainformatica.it/docs-node/health
```

Il virtual host TLS è associato all’IP pubblico `212.54.224.234`; lo script usa esplicitamente tale IP nei controlli `curl --resolve`, mentre i client esterni continuano a usare il nome DNS.

Il token di lettura non è stampato dallo script. Recuperarlo solo localmente,
quando necessario per configurare un client:

```bash
sudo sed -n 's/^DOCS_NODE_HTTP_TOKEN=//p' /etc/docs-node/remote.env
```

Un client Streamable HTTP deve inviarlo come header `Authorization: Bearer
<token>`. Non configurare `DOCS_NODE_HTTP_WRITE_TOKEN`: la sua assenza mantiene
le API remote di scrittura disabilitate.

### Gestione operativa

```bash
sudo systemctl status docs-node-remote.service
sudo journalctl -u docs-node-remote.service -f
curl -fsS http://127.0.0.1:8787/health
curl -fsS https://dev-01.sophiainformatica.it/docs-node/health
```

Dopo modifiche al codice di `docs-node`, riavviare sia l'istanza MCP locale sia
il servizio remoto:

```bash
sudo systemctl restart docs-node-remote.service
```

#### Riavvio automatico del servizio remoto

Per evitare un'operazione manuale dopo le modifiche a
`docs-node/index.js`, installare il trigger systemd:

```bash
sudo ./scripts/configure-docs-node-remote-auto-restart.sh
```

Il trigger osserva esclusivamente il file di ingresso del server, non la
cartella `docs-node`: il database SQLite condiviso cambia durante il normale
uso e non deve provocare riavvii. Ad ogni modifica esegue prima
`node --check`; se la sintassi non e valida, il servizio attualmente in
esecuzione non viene fermato. Alla correzione successiva il trigger riprova
automaticamente.

Per rimuoverlo:

```bash
sudo ./scripts/configure-docs-node-remote-auto-restart.sh --rollback
```

Aggiornamenti di dipendenze Node.js, del file environment o della unit
principale richiedono ancora un riavvio esplicito del servizio.

### Rollback

Lo script rimuove il servizio e il file token, ripristina l'ultimo backup del
virtual host creato dallo script, valida Apache e lo ricarica:

```bash
sudo ./scripts/configure-docs-node-remote.sh --rollback
```


## Configurare un client su un'altra macchina

Il server ufficiale condiviso è l'istanza HTTPS di `sophia-dev-01`:

```text
https://dev-01.sophiainformatica.it/docs-node/mcp
```

### Client MCP diretto

Se il client MCP supporta Streamable HTTP, configurare un server remoto con:

- URL: `https://dev-01.sophiainformatica.it/docs-node/mcp`;
- header: `Authorization: Bearer <token-di-lettura>`.

Conservare il token nel secret store o nelle variabili d'ambiente della macchina
client; non salvarlo nel repository, nei file di configurazione versionati o nel
DB SQLite. La sintassi esatta del profilo MCP dipende dal client usato (Codex,
Claude, Cursor, VS Code, ecc.), ma URL e header restano invariati.

Questa modalità non richiede un'installazione locale di `docs-node`. Il server
remoto espone operazioni di lettura e mantiene le API remote di scrittura
disabilitate.

### Modalita consigliata: `docs-node` locale con replica opzionale

Se sulla macchina client e' gia' configurato `docs-node`, mantenere il suo
database corrente e registrare il remoto nello stesso processo e' la modalita
consigliata. Gli scaffali esclusivamente locali restano disponibili come prima;
la consultazione read-through non crea dati locali, mentre una replica viene
creata solo con l'azione esplicita `subscribe_shelf` e resta consultabile anche
offline. Non impostare `DOCS_NODE_DB_PATH` per questo caso.

`docs_management list_shelves` espone `origin` (`local` o `remote_replica`) e
`replica_sources[]` con nome remoto, `sync_mode` e stato. `docs_remote
sync_status` mantiene il dettaglio operativo completo. Le repliche
`read_only` non possono essere modificate localmente.

Il token deve essere presente nell'ambiente del processo MCP (o nel secret
store del client), mai nel repository o nel database:

```bash
export DOCS_REMOTE_DEV01_TOKEN=<token-di-lettura>
node docs-node/index.js
```

#### Codex su Windows: configurazione in cinque passi

Se `docs-node` e' gia' configurato come server MCP locale di Codex, non
modificare `DOCS_NODE_DB_PATH`. Per il server avviato da Codex, la modalita'
consigliata e' dichiarare il token nella sezione `env` dedicata del file
`%USERPROFILE%\\.codex\\config.toml`: in questo modo la variabile viene passata
solo al processo `docs-mcp-server`.

```toml
[mcp_servers.docs-mcp-server.env]
DOCS_REMOTE_DEV01_TOKEN = "<token-di-lettura>"
```

La sezione va aggiunta accanto alla configurazione esistente di
`[mcp_servers.docs-mcp-server]`; non inserire il token nel repository, nel DB
SQLite o nel payload di `configure_remote`. In alternativa si puo' definire la
stessa variabile nell'ambiente del profilo Windows o in un secret store
equivalente, ma deve essere visibile al processo che avvia Codex.

Dopo ogni modifica a `config.toml` o alle variabili ambiente, **riavviare
Codex**: un processo MCP gia' avviato non ricarica l'ambiente a caldo.

#### Setup assistito da un agente

Un agente che usa la skill `mcp-docs-navigator` puo' configurare e verificare
un remote senza conoscere il bearer token. L'utente deve fornire l'endpoint
HTTP(S) e, se serve, un nome descrittivo; l'agente propone un `auth_profile`
stabile, ad esempio `DOCS_REMOTE_SOPHIA_DEV01_TOKEN`, e guida la
configurazione locale del segreto.

Per Codex l'istruzione da fornire e': aggiungere il valore del token nella
sezione `[mcp_servers.docs-mcp-server.env]` mostrata sopra, riavviare Codex e
confermare il riavvio. L'agente deve salvare nel remote soltanto il nome della
variabile tramite `auth_profile`, mai il valore del token, e non deve chiedere
di incollarlo nella conversazione.

Dopo il riavvio l'agente esegue, nell'ordine:

1. `configure_remote` con `name`, `base_url` e `auth_profile`;
2. `list_remotes`, verificando `auth_configured: true` e
   `auth_env_present: true`;
3. `list_remote_shelves` come test end-to-end;
4. `list_remote_tags` e `search_remote` per la prima consultazione senza
   replica.

Se `auth_env_present` e' `false`, il remote puo' restare configurato ma il
test deve fermarsi: correggere l'ambiente del client e riavviare Codex, senza
provare o esporre il token. Questo onboarding non modifica gli scaffali o il
DB locale; `subscribe_shelf` resta una scelta esplicita per offline o sync.

1. Configurare il remoto con il payload seguente.
2. Chiamare `list_remotes`: `sophia-dev-01` deve comparire con
   `auth_configured: true` e `auth_env_present: true`.
3. Chiamare `list_remote_shelves` per vedere gli scaffali pubblicati.
4. Per consultare senza replica, usare `list_remote_tags`, `search_remote`,
   `read_remote_document` e `get_remote_mind_map`.
5. Chiamare `docs_management list_shelves`: finche' non viene richiesta una
   replica, tutti gli scaffali esistenti restano `origin: "local"`.
6. Usare `subscribe_shelf` solo per lo scaffale che si vuole mantenere anche
   offline.

Dal client MCP locale configurare il remoto:

```json
{
  "action": "configure_remote",
  "name": "sophia-dev-01",
  "base_url": "https://dev-01.sophiainformatica.it/docs-node",
  "auth_profile": "DOCS_REMOTE_DEV01_TOKEN"
}
```

Elencare gli scaffali pubblicati e scegliere l'UUID da replicare:

```json
{
  "action": "list_remote_shelves",
  "remote": "sophia-dev-01"
}
```

#### Consultazione diretta read-through (senza replica)

Le azioni seguenti scaricano lo snapshot autenticato del solo scaffale remoto,
lo interrogano in memoria e non scrivono nel DB locale: non creano scaffali,
stato di sync o cache persistente. Sono la scelta consigliata per una ricerca
occasionale o per esplorare un corpus prima di decidere una replica.

Scoprire i tag effettivamente usati nello scaffale remoto:

```json
{
  "action": "list_remote_tags",
  "remote": "sophia-dev-01",
  "remote_shelf_uuid": "<uuid-scaffale-pubblicato>"
}
```

Cercare testo e tag con semantica `all` (default) o `any`:

```json
{
  "action": "search_remote",
  "remote": "sophia-dev-01",
  "remote_shelf_uuid": "<uuid-scaffale-pubblicato>",
  "query": "login",
  "tags": ["authentication"],
  "tag_match": "all",
  "limit": 20
}
```

Leggere un risultato e includere le relazioni della mappa mentale:

```json
{
  "action": "read_remote_document",
  "remote": "sophia-dev-01",
  "remote_shelf_uuid": "<uuid-scaffale-pubblicato>",
  "document_uuid": "<uuid-documento-restituito-dalla-ricerca>",
  "include_tags": true,
  "include_links": true,
  "links_direction": "both",
  "links_limit": 20
}
```

Per ispezionare direttamente la mappa dello scaffale (o di un singolo
documento con `document_uuid`), usare `get_remote_mind_map`. Il risultato usa
gli UUID delle relazioni e puo' segnalare link non replicabili per
`relative_path` non valido: l'avviso non impedisce la navigazione read-through.
Ogni richiesta scarica lo snapshot completo dello scaffale; per corpus molto
grandi e' preferibile una replica o un futuro endpoint di ricerca lato server.

Per rendere immediata la distinzione anche nella UI, usare un nome locale
esplicito, ad esempio `DEV-01 · Documentazione condivisa`, quando si crea la
replica:

```json
{
  "action": "subscribe_shelf",
  "remote": "sophia-dev-01",
  "remote_shelf_uuid": "<uuid-scaffale-pubblicato>",
  "local_name": "DEV-01 · Documentazione condivisa",
  "sync_mode": "read_only"
}
```

### Alternativa: database dedicato per le repliche

Per lavorare offline o mantenere una copia consultabile localmente, installare
`docs-node` sulla macchina client e assegnargli un database SQLite **dedicato**:

```bash
export DOCS_NODE_DB_PATH=/percorso/sicuro/docs-node-replica.db
export DOCS_REMOTE_DEV01_TOKEN=<token-di-lettura>
node docs-node/index.js
```

Questa variante isola gli scaffali locali del client dalle repliche, ma non
mostra gli scaffali del database ordinario del client. Non condividere il file
SQLite via filesystem o rete: il server ufficiale e ogni client usano database
distinti. Il token deve essere presente solo nell'ambiente runtime del client;
`auth_profile` contiene il nome della variabile, non il suo valore.

Dal client MCP locale configurare il remoto:

```json
{
  "action": "configure_remote",
  "name": "sophia-dev-01",
  "base_url": "https://dev-01.sophiainformatica.it/docs-node",
  "auth_profile": "DOCS_REMOTE_DEV01_TOKEN"
}
```

Elencare gli scaffali pubblicati e scegliere l'UUID da replicare:

```json
{
  "action": "list_remote_shelves",
  "remote": "sophia-dev-01"
}
```

Infine creare una replica read-only:

```json
{
  "action": "subscribe_shelf",
  "remote": "sophia-dev-01",
  "remote_shelf_uuid": "<uuid-scaffale-pubblicato>",
  "local_name": "Documentazione condivisa",
  "sync_mode": "read_only"
}
```

Verificare lo stato della replica con:

```json
{
  "action": "sync_status",
  "shelf": "Documentazione condivisa"
}
```

#### Disconnessione e rollback

`remove_remote` elimina il solo record di configurazione del remoto. Se non
esistono repliche, basta il seguente payload:

```json
{
  "action": "remove_remote",
  "name": "sophia-dev-01"
}
```

Se esistono repliche, il tool rifiuta la rimozione e riporta gli scaffali
coinvolti. Per scollegarle mantenendo documenti, tag e relazioni nel database
come scaffali locali, confermare esplicitamente:

```json
{
  "action": "remove_remote",
  "name": "sophia-dev-01",
  "force": true
}
```

Dopo avere rimosso il remoto, eliminare `DOCS_REMOTE_DEV01_TOKEN` dalla sezione
`[mcp_servers.docs-mcp-server.env]` di `%USERPROFILE%\\.codex\\config.toml` (o
dal profilo utente / secret store alternativo) e riavviare Codex. Il token non
viene mai rimosso dal tool perche' non e' memorizzato nel database.


## Replica locale e sync (`docs_remote`)

La Fase 3 introduce replica locale read-only di scaffali remoti tramite un tool MCP separato. La Fase 4 aggiunge changelog pull incrementale e tombstone read-only lato replica. La Fase 5 aggiunge publish/push manuali espliciti per scaffali pubblicati:

- `docs_remote configure_remote`
- `docs_remote remove_remote`
- `docs_remote list_remotes`
- `docs_remote list_remote_shelves`
- `docs_remote list_remote_tags`
- `docs_remote search_remote`
- `docs_remote read_remote_document`
- `docs_remote get_remote_mind_map`
- `docs_remote lint_remote_mindmap`
- `docs_remote subscribe_shelf`
- `docs_remote publish_shelf`
- `docs_remote sync_shelf`
- `docs_remote prune_change_log`
- `docs_remote list_conflicts`
- `docs_remote resolve_conflict`
- `docs_remote sync_status`

Configurazione minima:

```json
{
  "action": "configure_remote",
  "name": "docs-prod",
  "base_url": "http://127.0.0.1:8787",
  "auth_profile": "DOCS_NODE_REMOTE_TOKEN"
}
```

`auth_profile` e `write_auth_profile` devono contenere **solo il nome** delle variabili ambiente che portano i bearer token. I token non vengono salvati nel DB SQLite.

Replica read-only minima:

```json
{
  "action": "subscribe_shelf",
  "remote": "docs-prod",
  "remote_shelf_uuid": "11111111-1111-4111-8111-111111111111",
  "local_name": "Framework Replica",
  "sync_mode": "read_only"
}
```

Verifica stato:

```json
{
  "action": "sync_status",
  "shelf": "Framework Replica"
}
```

Persistenza locale Fase 3:

- `remote_sources`: remoti configurati (`name`, `base_url`, `auth_profile`, `write_auth_profile`)
- `shelf_sync_state`: stato replica per scaffale (`sync_mode`, `last_pulled_cursor`, ultimo pull riuscito, stato)
- `change_log`: changelog append-only locale/remoto per pull incrementale read-only
- `sync_conflicts`: conflitti persistiti da push read-write, consultabili con `docs_remote list_conflicts`
- `/mcp` remoto resta read-only; i write remoti passano solo dagli endpoint sync HTTP protetti da token write
- `shelf_sync_state.error_json` conserva l'ultimo errore remoto leggibile senza token, visibile via `docs_remote sync_status`

Guardrail Fase 3:

- le repliche `read_only` restano consultabili offline via `docs_navigation`
- write locali sui principali path della replica (`update/remove shelf`, `scan_*`, `set/bulk tags`, `remove_document`, `manage_document_links`, `correlate apply=true`, `docs_mindmap lint_mindmap apply=true`, `resync_all`) falliscono con errore esplicito
- dopo aggiornamento del codice server o delle migrazioni SQLite, riavviare `docs-node`

Endpoint sync Fase 4:

- `GET /sync/shelves/{shelf_uuid}/changes`: feed read-only dei delta incrementali con cursor monotono (`after`, `next_cursor`, `latest_cursor`)
- `docs_remote subscribe_shelf`: dopo la prima snapshot continua con pull incrementale basato su cursor persistito
- le cancellazioni sincronizzabili diventano tombstone/soft delete: gli elementi tombstoned non compaiono piu in `list/search/read/export`, ma restano nel DB per la sync
- la FTS locale viene aggiornata automaticamente dai delta inbound grazie agli update sulle righe `documents`

Endpoint/tool sync Fase 5:

- `docs_remote publish_shelf` pubblica uno scaffale locale su un remote-server; `dry_run=true` calcola impatto senza scrivere
- `docs_remote sync_shelf` supporta `direction: "pull_push"`; `dry_run=true` mostra inbound/outbound e conflitti previsti senza side effect
- `docs_remote sync_shelf` accetta `retry_attempts` e `retry_backoff_ms` per retry/backoff su errori remoti transient di fetch/push; la deduplica resta ancorata a `change_uuid`
- `docs_remote sync_status` estende il payload legacy con blocchi leggibili `remote`, `cursors`, `metrics`, `last_error` e `remote_probe`; i campi top-level preesistenti restano invariati
- `POST /sync/shelves/{shelf_uuid}/changes` riceve batch push con optimistic concurrency su versione/hash quando disponibile
- in caso di divergenza remota, il server non sovrascrive e persiste un record in `sync_conflicts`; il client persiste i conflitti ricevuti e li espone con `docs_remote list_conflicts`
- in `pull_push`, i delta inbound riferiti a entita con conflitto aperto non vengono applicati e restano pending senza avanzare `last_pulled_cursor`; il payload `inbound.skipped_conflicted_changes[]` rende visibili i change saltati
- `docs_remote list_conflicts` mantiene i campi legacy e aggiunge, per conflitti documentali, `base_payload` quando recuperabile e un `delta` agent-friendly con `delta_summary`, excerpt line-based compatti, `metadata_delta` e `recommended_resolution_hints`
- `docs_remote resolve_conflict` risolve al momento i conflitti `entity_type='document'` con `use_local`, `use_remote`, `manual_merge` e `keep_both`; `manual_merge` puo tentare un semplice 3-way merge Markdown line-based quando `merged_content` non e fornito
- `docs_navigation read_document` aggiunge metadata/warning strutturati quando il documento ha conflitti sync aperti, inclusi `has_actionable_delta` e `delta_summary` senza includere il diff completo nel testo documento
- `docs_remote prune_change_log` e' un'azione remota esplicita: `dry_run=true` e' il default raccomandato; il prune effettivo richiede `backup_path` oppure `confirm_without_backup=true`
- il backup del prune remoto esporta un Manifest V3 JSON dello scaffale target sul filesystem del server remoto; il path e' quindi relativo all'host che esegue `remote-server`
- la verifica dei cursori nel prune protegge solo i `shelf_sync_state` noti nello stesso DB del server remoto; client esterni non tracciati restano fuori perimetro operativo e vanno gestiti dall'operatore
- publish e sync restano azioni esplicite; `DOCS_NODE_SYNC_ON_STARTUP` e' solo un opt-in locale best-effort

## 0. Upgrade Path Schema Sync Prep

La Fase 0 della roadmap online/sync introduce una baseline di migrazione **non distruttiva** per il database locale:

- nuova tabella `schema_migrations`;
- colonne `uuid`, `version`, `metadata_sha256`, `deleted_at` per `shelves`, `tags`, `document_links`, `shelf_links`;
- colonne `uuid`, `version`, `content_sha256`, `metadata_sha256`, `deleted_at` per `documents`;
- colonne `uuid`, `version`, `metadata_sha256`, `deleted_at` per `document_tags` e `scan_sources`;
- colonna additiva `shelves.visibility` con fallback legacy `local_only`;
- backfill automatico di UUID/hash sui database legacy esistenti.

Compatibilita preservata:

- `DOCS_NODE_DB_PATH` resta il punto di configurazione supportato;
- `docs_management` e `docs_navigation` continuano a usare gli ID legacy esistenti;
- URI legacy `docs://document/{id}` e `docs://shelf/{id}-{slug}` restano validi;
- sono disponibili anche URI stabili UUID-based `docs://document/{uuid}` e `docs://shelf/{uuid}`;
- import manifest V1/V2 restano supportati; i nuovi export usano Manifest V3 di default;
- Manifest V3 esporta `document_tags[]` come fonte autorevole delle assegnazioni tag sincronizzabili, mantenendo `documents[].tags` come comodita retrocompatibile;
- `scan_sources` entra nella baseline snapshot/read-only con UUID/version/hash/tombstone, senza attivare ancora changelog, pull incrementale o soft delete operativo.

Note operative:

- la migrazione e' idempotente e non elimina dati legacy;
- prima di aggiornare il server e' comunque raccomandato un backup del file SQLite;
- dopo l'update del codice server MCP, riavviare `docs-node` per applicare le migrazioni allo startup.

## 1. Schema Database

Il database SQLite (`docs.db`) contiene le seguenti tabelle:

### `shelves`
* `id`: INTEGER PRIMARY KEY AUTOINCREMENT
* `uuid`: TEXT UNIQUE (aggiunto per sync futuro, con backfill automatico)
* `version`: INTEGER NOT NULL DEFAULT 1
* `name`: TEXT UNIQUE NOT NULL
* `description`: TEXT
* `visibility`: TEXT NOT NULL DEFAULT `local_only`
* `metadata_sha256`: TEXT
* `deleted_at`: TEXT NULL
* `commonRoot`: TEXT
* `created_at` / `updated_at`: TIMESTAMP

### `documents`
* `id`: INTEGER PRIMARY KEY AUTOINCREMENT
* `uuid`: TEXT UNIQUE
* `version`: INTEGER NOT NULL DEFAULT 1
* `shelf_id`: INTEGER REFERENCES shelves(id) ON DELETE CASCADE
* `title`: TEXT NOT NULL
* `file_path`: TEXT UNIQUE NOT NULL
* `content`: TEXT
* `content_sha256`: TEXT
* `metadata_sha256`: TEXT
* `deleted_at`: TEXT NULL
* `headings`: TEXT (JSON array di heading estratti)
* `tags`: TEXT (JSON array di tag)
* `created_at` / `updated_at`: TIMESTAMP

### `tags`
* `id`: INTEGER PRIMARY KEY AUTOINCREMENT
* `uuid`: TEXT UNIQUE
* `version`: INTEGER NOT NULL DEFAULT 1
* `name`: TEXT UNIQUE NOT NULL
* `description`: TEXT NOT NULL
* `metadata_sha256`: TEXT
* `deleted_at`: TEXT NULL
* `created_at` / `updated_at`: TIMESTAMP

### `document_tags`
Assegnazioni autorevoli documento-tag sincronizzabili.
* `document_id`: INTEGER REFERENCES documents(id) ON DELETE CASCADE
* `tag_id`: INTEGER REFERENCES tags(id) ON DELETE CASCADE
* `uuid`: TEXT UNIQUE
* `version`: INTEGER NOT NULL DEFAULT 1
* `metadata_sha256`: TEXT
* `deleted_at`: TEXT NULL
* *PRIMARY KEY(document_id, tag_id)*

### `scan_sources`
Sorgenti di scansione persistite per resync e snapshot read-only.
* `id`: INTEGER PRIMARY KEY AUTOINCREMENT
* `shelf_id`: INTEGER REFERENCES shelves(id) ON DELETE CASCADE
* `source_type`: TEXT NOT NULL (`file` o `folder`)
* `source_path`: TEXT NOT NULL
* `recursive`: INTEGER NOT NULL DEFAULT 0
* `uuid`: TEXT UNIQUE
* `version`: INTEGER NOT NULL DEFAULT 1
* `metadata_sha256`: TEXT
* `deleted_at`: TEXT NULL
* `created_at` / `updated_at`: TIMESTAMP

### `shelf_links` (Nuovo)
Rappresenta relazioni parent/child tra scaffali (ad es. per estendere scaffali standard con varianti cliente).
* `parent_shelf_id`: INTEGER REFERENCES shelves(id) ON DELETE CASCADE
* `child_shelf_id`: INTEGER REFERENCES shelves(id) ON DELETE CASCADE
* `relation`: TEXT NOT NULL DEFAULT 'parent'
* `uuid`: TEXT UNIQUE
* `version`: INTEGER NOT NULL DEFAULT 1
* `metadata_sha256`: TEXT
* `deleted_at`: TEXT NULL
* `created_at` / `updated_at`: TIMESTAMP
* *PRIMARY KEY(parent_shelf_id, child_shelf_id, relation)* (con check di ciclo transitivo)

### `document_links` (Nuovo)
Memorizza relazioni direzionali molti-a-molti tra documenti.
* `id`: INTEGER PRIMARY KEY AUTOINCREMENT
* `uuid`: TEXT UNIQUE
* `version`: INTEGER NOT NULL DEFAULT 1
* `source_document_id`: INTEGER REFERENCES documents(id) ON DELETE CASCADE
* `target_document_id`: INTEGER REFERENCES documents(id) ON DELETE CASCADE
* `relation`: TEXT NOT NULL (es. `extends_standard`, `references_standard`, `references_custom`, `mentions`)
* `score`: REAL (punteggio calcolato)
* `confidence`: REAL (livello di confidenza da 0 a 1)
* `origin`: TEXT NOT NULL (`manual` o `heuristic`)
* `heuristic_version`: TEXT
* `label`: TEXT
* `rationale`: TEXT
* `metadata_json`: TEXT
* `metadata_sha256`: TEXT
* `deleted_at`: TEXT NULL
* `created_at` / `updated_at`: TIMESTAMP
* *UNIQUE(source_document_id, target_document_id, relation, origin)*

---

## 2. Contratti MCP Principali

### `docs_management`

#### `manage_shelf_links`
Gestisce le relazioni gerarchiche tra scaffali. Evita cicli in modo transitivo.
```json
{
  "action": "manage_shelf_links",
  "operation": "add_parent" | "remove_parent" | "set_parents" | "list",
  "shelf": "Nome Scaffale",
  "parent_shelf": "Nome Parent",        // Per add/remove
  "parent_shelves": ["Parent1", "Parent2"] // Per set_parents
}
```

#### `manage_document_links`
Gestisce i collegamenti direzionali manuali tra documenti.
```json
{
  "action": "manage_document_links",
  "operation": "add" | "remove" | "list" | "replace_for_document" | "promote_link" | "prune_heuristic_links",
  "source_document_id": 101,
  "target_document_id": 22,
  "relation": "references_standard",
  "confidence": 1.0,
  "label": "Descrizione opzionale",
  "rationale": "Razionale opzionale"
}
```

`promote_link` richiede `link_id` e promuove solo un link euristico attivo con
`certainty="inferred"`; mantiene `origin="heuristic"`, conserva evidence e
provenance e imposta `certainty="curated"`. I link curated sono esclusi da
`correlate` con replace e da `prune_heuristic_links`.

#### `docs_mindmap lint_mindmap`

Il tool separato `docs_mindmap` controlla la manutenzione del grafo senza
persistire audit. Richiede `shelf` (nome o UUID), accetta `checks[]` con
`dead_links`, `stale_source_hash`, `ambiguous_edges`, `orphan_nodes` e usa
`apply:false` di default. Un documento collegato a un concept attivo non e' un
`orphan_nodes`: il controllo considera sia `document_links` sia `concept_links`.
Il report espone issue strutturate, provenance/evidence
quando disponibili e un riepilogo di quelle correggibili.

Con `apply:true` tombstona esclusivamente `dead_links` attivi con
`origin="heuristic"` e `certainty="inferred"`, riusando UUID/version/hash e il
normale change-log. Link manual, curated, ambigui e documenti orfani restano nel
solo report. Il tool e leggibile sul remote-server, ma il relativo `apply:true`
e rifiutato come ogni write MCP remoto.

Un client locale puo analizzare uno snapshot remoto senza creare una replica con:

```json
{
  "action": "lint_remote_mindmap",
  "remote": "docs-prod",
  "remote_shelf_uuid": "11111111-1111-4111-8111-111111111111",
  "checks": ["dead_links", "stale_source_hash"]
}
```

`docs_remote lint_remote_mindmap` e sempre read-through: non aggiorna cursor,
replica, change-log, push o conflitti. Poiche uno snapshot di shelf puo non
contenere endpoint esterni, questi non sono classificati automaticamente come
dead link.

#### `correlate`
Esegue l'algoritmo di correlazione euristica per trovare documenti correlati.
```json
{
  "action": "correlate",
  "mode": "document" | "shelf",
  "document_id": 101,               // Richiesto se mode="document"
  "shelf": "Nome Scaffale",         // Richiesto se mode="shelf"
  "scope": "shelf_with_parents" | "shelf" | "cross_shelf",
  "min_confidence": 0.6,
  "min_signal_count": 2,
  "max_links_per_document": 8,
  "apply": false                    // Impostare a true per scrivere fisicamente nel DB
}
```

L'euristica e' pensata soprattutto per bootstrap/triage rapido della mappa mentale. Dopo
l'inizializzazione, i collegamenti piu' affidabili vanno inseriti sul momento con
`manage_document_links`, usando il contesto funzionale effettivo dell'analisi in corso.
Per ripulire link euristici deboli senza toccare quelli manuali:

```json
{
  "action": "manage_document_links",
  "operation": "prune_heuristic_links",
  "shelf": "Cliente X",
  "max_confidence": 0.55,
  "apply": false
}
```

Usare prima `apply:false` per vedere i candidati; `apply:true` elimina solo link con
`origin="heuristic"` nello scope selezionato.

---

## 3. Algoritmo di Correlazione Euristica

Il motore di correlazione confronta i documenti calcolando un punteggio pesato:

1. **Title Similarity** (peso: `0.25`): Jaccard similarity sui token dei titoli.
2. **Heading Similarity** (peso: `0.20`): Jaccard similarity sugli heading Markdown.
3. **Tag Overlap** (peso: `0.20`): Jaccard similarity sui tag assegnati ai documenti.
4. **Keyword Overlap** (peso: `0.15`): Overlap delle top 30 parole chiave (escluse stopword comuni).
5. **Path Proximity** (peso: `0.10`): Vicinanza nel filesystem (`1.0` se nella stessa cartella, `0.5` se sotto lo stesso parent comune, `0` altrimenti).
6. **Explicit Mention** (peso: `0.10`): `1.0` se il testo del documento sorgente cita esplicitamente il titolo o il nome file del target.

`min_signal_count` permette di scartare candidati sostenuti da un solo segnale debole
(tipicamente solo tag generici o sola vicinanza di path). Il report di `correlate`
include `signals` nei `sample_links` e salva gli stessi segnali in `metadata_json` per
audit successivo. I candidati con confidence almeno `0.75` sono `inferred` e, con
`apply:true`, sono gli unici persistiti; quelli sotto soglia sono `ambiguous` e restano
nel solo report dry-run. I nuovi link automatici includono `extractor`, source hash,
sorgente ed evidence strutturata; link legacy privi di metadata restano leggibili con
certainty derivata dall'origin. Snapshot, replica e read-through remoto conservano il
raw `metadata_json` ed espongono certainty/provenance/evidence strutturate.

### Regole di Routing Direzionale
* I link da documenti in scaffali "child" a documenti in scaffali "parent" sono permessi ed incoraggiati (es. varianti cliente che puntano allo standard).
* I link da scaffali "parent" a scaffali "child" sono esclusi automaticamente per evitare di inquinare la documentazione del framework generico con casi specifici del cliente.

---

## 4. Manifest Import/Export (V3 default, V1/V2 backward compatible)

L'export degli scaffali genera di default un manifest con `format_version: 3`. E' comunque possibile forzare `format_version: 2` nel tool `export_shelf` per compatibilita con workflow legacy.

Manifest V3 aggiunge agli snapshot locali:

- `shelf.uuid`, `version`, `metadata_sha256`, `deleted_at`, `visibility` (`local_only` come default locale documentato);
- `documents[].uuid`, `version`, `content_sha256`, `metadata_sha256`, `deleted_at`;
- `document_tags[]` come payload autorevole delle assegnazioni tag con `uuid`, `version`, `metadata_sha256`, `deleted_at`;
- `scan_sources[]` con `uuid`, `version`, `metadata_sha256`, `deleted_at`;
- `tags_dictionary[].uuid`, `version`, `metadata_sha256`, `deleted_at`;
- `shelf_links[]` e `document_links[]` con `uuid`, `version`, `metadata_sha256`, `deleted_at` dove disponibili;
- endpoint legacy/fallback ancora presenti come `relative_path` e `original_file_path`.

Compatibilita:

- `import_shelf` continua ad accettare manifest V1/V2 esistenti;
- i pacchetti V3 reimportati usano i UUID per evitare duplicazioni quando possibile;
- i manifest V3 prodotti dalle commit precedenti restano accettati anche se privi di `document_tags[]` o dei nuovi campi su `scan_sources[]`;
- i `document_links` V3 vengono risolti prima via UUID e, se assente/non risolto, con fallback su `relative_path`;
- remote-server `/mcp` resta read-only; publish/push/conflitti sono disponibili solo tramite endpoint sync HTTP protetti e tool `docs_remote` espliciti.

---

## 5. Esecuzione Test

Per verificare la correttezza del server e delle nuove funzionalità della mappa mentale:
```bash
npm run smoke
```
Oppure:
```bash
node tests/smoke/docs-node.smoke.mjs
```

Smoke mirato per la modalita remota read-only:

```bash
node tests/smoke/docs-node-remote-server.smoke.mjs
node tests/smoke/docs-node-sync-hardening.smoke.mjs
node tests/smoke/docs-node-conflict-delta.smoke.mjs
node tests/smoke/docs-node-correlate-tombstone.smoke.mjs
```

#### Concept graph minimale

`docs_mindmap` espone inoltre `extract_concepts`, `list_concepts` e
`manage_concept_links`. L'estrazione usa heading Markdown e tag dello shelf:
per default e' un dry-run; con `apply:true` crea o riusa un `concept_node` per
`normalized_label` e un edge `document -> concept` `mentions`. Il dizionario
dei concetti e volutamente globale al database: documenti e scaffali diversi
possono riferire lo stesso concetto normalizzato; non esiste ownership per
scaffale. L'identita di convergenza e `normalized_label`, mentre UUID e
metadata restano attributi del nodo globale. Le relazioni
`concept -> concept` non fanno parte di M8: `concept_links` supporta solo
`document -> concept`; tassonomie e relazioni concettuali dirette restano fuori
scope fino a un contratto dedicato. La convergenza non e' distruttiva:
documenti diversi possono riferire lo stesso concetto.
Le estrazioni successive riusano il nodo attivo senza sovrascriverne i metadata;
le evidence specifiche della nuova sorgente restano sul relativo link. In import,
una label normalizzata gia presente con UUID diverso conserva UUID e metadata locali;
l'UUID entrante viene rimappato al nodo esistente e i link importati vengono
preservati. Questa policy evita che l'import di uno shelf modifichi
silenziosamente il concetto globale gia osservato da altri shelf. Un import con
lo stesso UUID e considerato un aggiornamento della stessa identita e ne applica
versione e metadata. Un nodo non viene tombstonato automaticamente quando perde
l'ultimo link.
Provenance, evidence, source hash, UUID, versione e metadata hash restano
leggibili anche con metadata legacy non validi. I link manuali sono gestiti con
`manage_concept_links` (`list`, `add`, `remove`) e restano bloccati su repliche
read-only e remote-server.

Manifest V3, import/export, snapshot e replica iniziale includono
`concept_nodes[]` e `concept_links[]`. In M8 il graph concettuale e'
`snapshot_only`: sync incrementale, push e conflitti dei concetti non sono
ancora supportati e una replica puo' quindi risultare stale dopo modifiche
remote successive allo snapshot iniziale. I payload graph espongono anche
`concept_graph_sync` machine-readable con `mode`, `source` e
`potentially_stale`: una lettura remota legge uno snapshot appena richiesto e
quindi restituisce `source: "fresh_remote_snapshot"` e
`potentially_stale: false`; le letture locali di uno shelf sincronizzato con
pull restituiscono `potentially_stale: true` e una warning, perche i delta
documentali non aggiornano i concetti snapshot-only. Il source distingue
`local_replica` per `read_only` e `local_sync` per `read_write`.

#### Pathfinding e neighborhood graph

`docs_mindmap.map_path` e `map_neighborhood` leggono il grafo senza scrivere:
traversano bidirezionalmente `document_links` e `concept_links`, conservando
comunque direzione e relazione originarie nell'output. I nodi sono riferiti da
`{ type: "document"|"concept", uuid }`; di default gli edge ambiguous sono
esclusi, mentre curated/manual/inferred sono ordinati per distanza, certainty e
confidence. `max_depth` default 4 (massimo 8), `max_nodes` default 50
(massimo 200) e `output_format` puo' aggiungere Mermaid a JSON.

Per consultazione remota senza side effect, `docs_remote.map_remote_path` e
`map_remote_neighborhood` analizzano lo snapshot V3 corrente. Non creano
repliche, non avanzano cursor e non partecipano a push/conflitti; il concept
graph resta dichiarato `snapshot_only`; la lettura remota corrente espone
`concept_graph_sync.potentially_stale: false` perche recupera lo snapshot nel
corso della stessa richiesta.

#### Codebase inventory metadata-only

`docs_codebase` e' disponibile esclusivamente nell'istanza MCP locale. Con
`attach_codebase` associa una root canonica a uno shelf senza avviare scansioni;
`scan_codebase` esegue poi l'inventory incrementale della source per nome o
UUID e `list_code_files` restituisce solo path relativo, dimensione, mtime,
SHA-256, UUID/versione/hash e stato tombstone. Non vengono mai salvati o
restituiti contenuti, snippet, simboli o sorgenti.

Le esclusioni robuste di default comprendono `.git`, `node_modules`, cache e
directory build/output comuni, oltre a database SQLite, sidecar WAL/SHM, log e
file di stato `.docs-node-*`; `exclude[]` puo' soltanto aggiungere pattern ed
e' persistito nella source. I symlink sono seguiti solo quando risolvono entro
la root canonica, con protezione da loop; quelli esterni o irrisolvibili sono
ignorati e conteggiati. Le esclusioni predefinite restano attive anche per una
source gia' attaccata prima di un aggiornamento del server. Un nuovo attach non
esegue implicitamente lo scan.

L'inventory e' `local_only`: non e' disponibile nel remote-server, non entra
in Manifest V3, replica, read-through, delta, push, change-log o conflitti.
Le repliche read-only non accettano attach o scan. Dopo modifiche al codice
server, riavviare `docs-node`.

#### Link documento–codice manuali e proposti

Nello stesso tool locale, `manage_document_code_links` gestisce i link tra un
documento e un file gia' inventariato nello stesso shelf (`add`, `list`,
`remove`). Le relazioni iniziali sono `implemented_by`, `tested_by`,
`configures` e `references`. Un add e' sempre manuale: registra rationale,
provenance e hash dei due endpoint, ma non salva contenuto o snippet.

`propose_document_code_links` e' invece un dry-run di review: usa riferimenti
a path/file, action MCP, endpoint, package/script e test per proporre candidati
con confidence ed evidence. Un singolo match action o endpoint resta un segnale
debole di review; alla soglia predefinita `0.75` diventano candidati i riferimenti
espliciti o la coppia action+endpoint coerente. `apply:true` e'
rifiutato e non vengono modificati link, versioni, hash o change-log.
I link documento–codice restano `local_only`: non sono inclusi in Manifest V3,
export/import, replica, read-through, delta, push o conflitti; cross-shelf,
stale-doc detection e code symbols non fanno parte di questa milestone.

#### Symbol extraction leggera

`extract_code_symbols` esegue esplicitamente l'indice simboli locale per file
JS, TS, MJS e CJS della source: import/export, function, class, costanti
uppercase, action MCP ed endpoint HTTP. Il parser e' line-aware e best-effort,
senza Tree-sitter, LLM o dipendenze native. `list_code_symbols` espone solo
metadata, linee, hash e provenance, filtrabili per file, tipo e prefisso.

L'estrazione riusa l'hash del file invariato; per default legge al massimo
524288 byte e persiste fino a 500 simboli per file. File grandi, linguaggi non
supportati e sintassi non riconosciuta non interrompono l'operazione. I simboli
sono tombstonati con il file inventariato e riallineati alla successiva
estrazione. Restano `local_only`, senza link documento-simbolo o integrazione
con Manifest, replica e sync.

##### Scope futuro AST e multi-linguaggio

L'adapter AST opzionale (M15) e' esplicitamente `OUT OF SCOPE / DEFERRED` fino
a una nuova decisione basata su proof of value. L'installazione corrente non
include Tree-sitter, parser WASM, binding nativi o feature flag AST; non va
dedotta alcuna capability AST dalla presenza dell'inventory o di
`code_symbols`.

La roadmap propone, senza autorizzarne l'implementazione, una M16 per adapter
metadata-only ColdFusion/CFML (`.cfm`, `.cfc`, `.cfs`, tag e CFScript), PHP e
C# 14 per progetti .NET 10. La proposta mantiene `extract_code_symbols` come
azione esplicita, `symbols_light` come fallback, nessun contenuto sorgente
persistito e il confine `local_only`. M15/M16 potranno entrare in scope solo
dopo approvazione separata di benchmark, dipendenze opzionali, Node 24,
supporto Windows e comportamento di fallback. Il dettaglio e' nella
[roadmap implementativa](../docs/analisi-tecniche/TODO/analisi-implementativa-mindmap-codebase-docs-node.md#milestone-15---ast-adapter-opzionale).

#### Stale documentation detection

`docs_codebase.detect_stale_docs` e' un report locale read-only: confronta gli
hash snapshot di un link manuale documento-file con SHA-256 calcolato live sul
file entro la root canonica della source. Segnala `high` quando il codice e'
cambiato e il documento conserva l'hash della validazione; file mancanti,
metadata legacy invalidi o path non verificabili diventano warning strutturati,
senza interrompere il report. Il confronto live non aggiorna l'inventory ne'
persiste il codice letto.

Il formato standard e' JSON; `output_format: markdown|both` aggiunge una vista
di review. `docs_navigation.read_document` conserva il contratto legacy e
mostra il riepilogo soltanto con `include_stale_warning:true`. Il report resta
consultabile su repliche read-only, ma non viene esposto dal remote-server e
non partecipa a Manifest, export/import, replica, delta, push o conflitti.

#### Export Obsidian-compatible

`docs_mindmap.export_mindmap_wiki` genera, per un solo shelf locale scrivibile,
un vault Markdown Obsidian-compatible derivato: `index.md`, note in
`documents/`, `concepts/` e `code/`, frontmatter con UUID/versione/hash e
wikilink stabili. SQLite rimane la fonte canonica: il vault e' rigenerabile e
non viene mai reimportato implicitamente. Le note code espongono soltanto path
relativo, hash, simboli estratti e link documentali; non contengono body,
snippet o altro contenuto sorgente.

`save_path` deve essere assoluto e senza traversal. Una destinazione non vuota
e' rifiutata salvo `force:true` e marker di vault generato da docs-node; la
rigenerazione usa staging e pubblica solo un risultato completo. L'azione e'
strettamente `local_only`: e' rifiutata per repliche/read-only e remote-server
e non partecipa a Manifest V3, snapshot, delta, push, cursor o conflitti.

#### Visualizzazione graph: Mermaid e HTML interattivo

Per visualizzare subito sottografi bounded in chat, `map_path` e
`map_neighborhood` supportano `output_format:"mermaid"` o `"both"`: il
diagramma deriva dallo stesso JSON che conserva document link e concept link.
La mappa mentale e il concept graph sono quindi layer dello stesso grafo, non
due superfici separate; usare path o neighborhood invece di espandere uno
shelf grande per intero.

`docs_mindmap.export_mindmap_html` genera invece un singolo file HTML locale,
derivato e navigabile. Richiede `shelf` e `save_path` assoluto a un file
`.html`; supporta `view:"path"`, `"neighborhood"` e `"overview"`. Path e
neighborhood riusano la selezione bounded esistente; overview incorpora il
grafo completo dello shelf e rende inizialmente concept hub semantici fino a
`max_nodes`, con ricerca ed espansione client-side dei cluster. Il file non ha
CDN, fetch o analytics, non include contenuto/sorgenti codice e non modifica
SQLite, change-log, cursor o replica.

Il target esistente richiede `force:true` ed il marker dello stesso shelf;
l'export usa staging e recupero del precedente artefatto. L'azione e'
`local_only`: e' assente dal remote-server e non esiste un export HTML da
snapshot remoto. Il tool non avvia il browser; l'agente consegna il percorso
generato come link locale. Workflow e limiti sono nella
[reference graph visualization](../skills/mcp-docs-navigator/references/graph-visualization.md).

#### Uso da parte degli agenti

Le capability M6--M14 sono pubblicate nella lista MCP attraverso tre tool:
`docs_mindmap` per grafo/lint/concetti/export, `docs_codebase` per workflow
codebase strettamente locali e `docs_remote` per sync e letture snapshot
read-through. Gli agenti che usano il corpus devono applicare la skill
[`mcp-docs-navigator`](../skills/mcp-docs-navigator/SKILL.md), che contiene i
workflow di discovery, curatela e reference dedicate per
[edge/provenance/lint](../skills/mcp-docs-navigator/references/edge-provenance-lint.md),
[concept/pathfinding](../skills/mcp-docs-navigator/references/concept-graph-pathfinding.md),
[codebase/stale-doc](../skills/mcp-docs-navigator/references/codebase-documentation-workflow.md),
[export Obsidian](../skills/mcp-docs-navigator/references/obsidian-export.md)
ed [export HTML graph](../skills/mcp-docs-navigator/references/graph-visualization.md).

Prima di una write, distinguere esplicitamente: dry-run/report, operazione
locale scrivibile, replica read-only e remote read-through. In particolare
`docs_codebase`, `export_mindmap_wiki` ed `export_mindmap_html` sono `local_only`; non vanno proposti
né tentati su remote-server o come parte del sync.

`docs_management feature_status` aggiunge `adoption_advisories` solo per
capability che richiedono una preparazione prima dell'uso: evidence/provenance,
concept graph, inventory codebase, link documento-file, simboli, stale-doc e
consultazione remota. Le evidenze derivano da conteggi aggregati dello storage:
non sono telemetria di invocazione e non registrano agenti, prompt, contenuti,
path, hash o token, né eseguono rete. Il server applica un gate di avvio: ogni
action pubblicata da `docs_mindmap`, `docs_codebase` o `docs_remote` deve
classificare la capability; un advisory `feature_status` è obbligatorio solo
quando `requires_initialization:true`. Lint, pathfinding e export restano
scopribili da `tools/list` e skill, senza falsi avvisi di mancato utilizzo.
