archivist-mcp
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@archivist-mcpwhat files changed in the 'Research' dataset since yesterday?"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Minion Vault
Un magazzino di documenti che Claude può leggere e scrivere, versionato con git a ogni scrittura, self-hosted a casa tua.
Nessun dato esce dal tuo server se non verso la conversazione che l'ha chiesto. Ogni modifica è un commit. Niente si cancella per sbaglio, e quello che si cancella si recupera.
Perché esiste
Chi lavora con Claude su qualcosa di serio si scontra presto con lo stesso muro: le conversazioni non ricordano. Ogni chat riparte da zero, e il materiale che dovrebbe accumularsi — decisioni, dati, note di lavoro — resta sparso fra allegati ricaricati ogni volta e chat vecchie che non ritrovi più.
La risposta ovvia è "mettiamo i file in una cartella condivisa". Ma la cartella condivisa risolve metà del problema e crea l'altra metà:
Cartella sincronizzata | Minion Vault | |
Claude legge i file | vanno ricaricati a mano | li legge quando gli servono |
Claude scrive i file | no | sì, con commit |
Due chat che scrivono insieme | l'ultima vince, in silenzio | la seconda viene rifiutata e avvisata |
"Com'era questo file martedì?" | dipende dal cestino del servizio |
|
"Cosa è cambiato" | niente | storia git completa |
Un progetto non deve vedere l'altro | niente | dataset con chiave |
Il salto vero non è l'accesso: è git sotto. Quando ogni scrittura è un commit, smetti di aver paura di far scrivere a un modello. Se sbaglia, torni indietro. Se due chat si pestano i piedi, te lo dice invece di far vincere l'ultima arrivata. Se un file sparisce, era solo spostato.
I dataset
La radice del vault contiene dataset: directory di primo livello, ognuna col proprio repository git indipendente. Il nome è preso in prestito da ZFS, e per lo stesso motivo: un dataset è un'unità che si sposta, si replica e si ripristina da sola, senza toccare le altre.
vault/
├── keys.txt ← registro delle chiavi
├── Example Project/ ← dataset, col suo .git
│ ├── 01 Notes/
│ └── Trash/
└── Scratch/ ← un altro dataset, col suo .gitOgni path comincia col nome del dataset. Non esiste nessuna operazione di livello radice — ed è da questa singola regola che discende tutta la protezione del sistema, senza liste di eccezioni da mantenere.
Le chiavi
Un dataset che ha una riga in keys.txt è locked: ogni chiamata deve portare
la chiave. Senza riga è open.
La chiave non serve a tenere fuori gli estranei — di quello si occupa OAuth, e solo un account può entrare. Serve a separare i progetti fra loro. Il caso concreto: una chat aperta al volo, fuori da ogni progetto, che si mette a leggere i dati di un progetto serio perché sa che esistono. La chiave sta nelle istruzioni del progetto, quindi ce l'hanno in contesto solo le chat lanciate lì dentro.
Da qui discende una regola che sostituisce da sola tre meccanismi che sarebbero serviti altrimenti: la presenza di una chiave è la dichiarazione che quei dati contano. Un dataset con chiave non si droppa dai tool, punto. Uno senza sì, perché è roba nata per essere buttata.
Related MCP server: obsidian-vault-mcp
Com'è fatto
Ogni pezzo è stato scelto per una ragione precisa, e vale la pena dirle: sono le stesse che ti servono se vuoi adattarlo.
MCP — Model Context Protocol
Il protocollo con cui Claude parla con strumenti esterni. Un server MCP espone dei tool: funzioni con un nome, dei parametri tipizzati e una descrizione. Claude legge le descrizioni e decide da solo quando chiamarli.
Questo ha una conseguenza che governa tutto il design: la descrizione di ogni tool viene caricata nel contesto di ogni conversazione, sempre, anche quando non ne usi nessuno. Per questo i docstring qui dentro sono densi e i tool non si moltiplicano per gusto — ognuno è un costo fisso su ogni chat che fai.
FastMCP
L'implementazione Python del protocollo. Gestisce il trasporto HTTP, la serializzazione degli schemi e — la parte che vale davvero — l'intera danza OAuth 2.1 con Dynamic Client Registration e PKCE, che è quello che Claude pretende da un connettore remoto. Scriverla a mano sarebbe stato il grosso del lavoro.
OAuth 2.1 con login GitHub
Il servizio non ha utenti propri: delega il login a GitHub e poi rifiuta chiunque non sia l'unico username configurato. Chiunque su GitHub può tentare il login; il no lo dice il server, non GitHub.
Perché GitHub e non una password: una password su un servizio esposto è un segreto che vive in chiaro da qualche parte e non ha revoca. Un'identità OAuth ha scadenza, revoca e nessun segreto lato client.
Tailscale Funnel
Il servizio ascolta su 127.0.0.1 e non sa come gli arriva il traffico. Il
Funnel gira nello stesso container e pubblica quella porta su un URL HTTPS
pubblico con certificato valido, senza aprire una singola porta sul router e
senza esporre l'IP di casa.
Il disaccoppiamento è voluto: se domani metti un reverse proxy al posto del Funnel, non cambi una riga di codice.
Git, server-side
Ogni scrittura fa un commit. Non è un backup: è memoria dell'intenzione.
history dice cosa è successo, diff cosa è cambiato, read_at com'era,
dataset_restore lo riporta indietro.
E c'è un dettaglio che fa la differenza nell'uso reale: se qualcuno scrive nel vault da fuori dei tool — via SMB, a mano, con un editor — il server se ne accorge e committa quelle modifiche a parte, con un messaggio onesto, prima di eseguire la propria. I commit dei tool restano puri e la storia non mente nemmeno per sbaglio.
Docker su Unraid
Il container parte come root solo per sistemare i permessi, poi lascia i
privilegi e gira come nobody:users con umask 000, così i file restano
accessibili anche dalle share SMB.
Preflight bloccante
All'avvio girano dieci controlli. Se uno solo fallisce, il servizio non parte — e un controllo che va in crash conta come fallito, non come passato.
Sembra eccessivo finché non ti capita: un mount sbagliato che rende il vault vuoto, un Funnel che pubblica la porta sbagliata, una chiave del nodo con scadenza attiva che ti spegne tutto fra sei mesi. Meglio un servizio che si rifiuta di partire dicendoti perché, di uno che parte e funziona male.
Architettura
Claude (server Anthropic)
│ HTTPS + OAuth 2.1 (DCR + PKCE)
▼
Tailscale Funnel ──► https://<host>.<tailnet>.ts.net
│ (nello stesso container)
▼
127.0.0.1:3000 server.py ── 21 tool MCP
│ ├─ filtro identità GitHub
│ └─ filtro IP egress Anthropic
▼
vault.py ── VaultRoot (dataset, chiavi) ──► Dataset (file + git)
│
▼
/vault ── un repository git per datasetInstallazione
Un tailnet Tailscale con MagicDNS e HTTPS Certificates attivi.
Unraid 7 con il plugin Tailscale installato: fornisce l'hook Docker che dà al container un'identità Tailscale propria. Non disinstallarlo mai, anche se Tailscale sull'host è disattivato.
Sull'host: Allow Tailscale Funnel = No. Il Funnel è del container, non dell'host.
Un pool SSD per il vault. I dischi meccanici pagano lo spin-up a ogni tocco, e questo servizio tocca spesso.
Un account GitHub.
github.com → Settings → Developer settings → OAuth Apps → New OAuth App
Campo | Valore |
Application name | un nome qualsiasi, es. |
Homepage URL |
|
Authorization callback URL |
|
Generate a new client secret, poi salva Client ID e Client Secret nel gestore di password: il secret si vede una volta sola, ma non scade.
⚠ Un'applicazione nuova per ogni servizio. Non riciclare quella di un altro container: la callback è una sola e i due si contendono.
zfs create <pool-ssd>/Vault
mkdir "/mnt/<pool-ssd>/Vault/Example Project"
chown -R 99:100 "/mnt/<pool-ssd>/Vault"
chmod -R 777 "/mnt/<pool-ssd>/Vault"Ci copi dentro i tuoi file con rsync o scp. Il repository git lo crea il
server al primo avvio: non serve fare git init a mano, e non serve che i file
appartengano a qualcuno in particolare — l'entrypoint dichiara safe.directory
per evitare il dubious ownership di git.
⚠ Snapshot ZFS sul dataset Vault, non sui singoli progetti. Gli snapshot
sono la rete per la catastrofe e proteggono anche i .git; il rollback
quotidiano è mestiere di git, non loro.
⚠ Nel container si monta il path diretto del pool (/mnt/<pool>/Vault), mai
/mnt/user/...: niente FUSE in mezzo.
printf 'Example Project\tk7m2xq4p\n' > "/mnt/<pool-ssd>/Vault/keys.txt"
chown 99:100 "/mnt/<pool-ssd>/Vault/keys.txt"
chmod 640 "/mnt/<pool-ssd>/Vault/keys.txt"Nome del dataset, TAB, chiave. Una riga per dataset; righe vuote e righe che
iniziano con # sono commenti.
Otto caratteri alfanumerici bastano: davanti c'è già OAuth, e la minaccia è una
chat che tira a indovinare, non un attacco a forza bruta. Evita 0/O e 1/l,
che le ricopierai a mano.
640 con owner 99:100: il servizio legge, il resto del mondo no. Non
root-only — il servizio non gira come root e non riuscirebbe ad aprirlo.
Il file viene riletto a caldo: aggiungi o togli una riga dall'editor di Unraid e ha effetto subito, senza riavviare niente.
Sta dentro il vault ma è irraggiungibile dai tool, perché il suo nome non è
quello di un dataset — lo ferma lo stesso controllo che ferma .. e .git.
mkdir -p /mnt/user/appdata/vault-mcp/src
# copia qui i file del pacchetto, poi:
docker build --no-cache -t vault-mcp /mnt/user/appdata/vault-mcp/src⚠ --no-cache non è pedanteria. La cache di Docker ha già mentito almeno una
volta, dichiarando CACHED uno strato il cui file era cambiato. Ci si perde
un'ora a collaudare l'immagine vecchia convinti di aver corretto qualcosa.
Prima di installare, collauda il motore senza rete e senza Docker:
python3 test_v16.py # 62 controlli, devono passare tuttiMetà di quei controlli verifica cose che non devono succedere — traversal, chiavi sbagliate, drop di dataset protetti — e sono quelli che contano di più.
Importa vault-mcp.template.xml in Unraid, oppure crea il container a mano.
Ogni campo ha la sua descrizione nell'interfaccia; qui il riassunto.
Path
Nome | Host → Container |
Vault |
|
App Data |
|
Tailscale State |
|
Variabili
Variabile | Valore |
|
|
|
|
|
|
|
|
| dal punto 2 |
| dal punto 2 |
| il tuo username GitHub |
|
|
|
|
|
|
Tailscale: Enabled true, Hostname <host>, Serve funnel, Serve Port
uguale a PORT, State Dir /var/lib/tailscale.
Poi Apply, mai Restart. Restart riavvia il container esistente con la configurazione vecchia; solo Apply lo ricrea leggendo il template aggiornato.
Nei log del container devi vedere, in ordine: l'init dei repo git per dataset, la sistemazione dei permessi, il drop dei privilegi, il preflight 10/10, e infine l'avvio del server.
Se il preflight blocca, il messaggio dice quale controllo e perché. Non è un avviso: il servizio non è partito.
Poi, in Claude: Impostazioni → Connettori → Aggiungi connettore personalizzato,
URL https://<host>.<tailnet>.ts.net/mcp. Si apre il login GitHub, autorizzi, e i
tool compaiono.
Prova subito, in quest'ordine:
vault_status() → deve elencare i dataset
dataset_status("Example Project", "") → deve essere RIFIUTATO
dataset_status("Example Project", "k7m2xq4p") → deve rispondere
dataset_create("Scratch") → nasce aperto
list_files("Scratch") → funziona senza chiave
dataset_drop("Example Project", "<manifest>") → deve essere RIFIUTATOInfine incolla la chiave nelle istruzioni del progetto a cui il dataset appartiene. Da quel momento solo le chat lanciate dentro quel progetto ce l'hanno in contesto.
Ci sono tre livelli di cache: il server, il connettore e la sessione di chat.
Dopo qualunque cambiamento alla superficie dei tool — nomi, parametri, docstring — serve disconnettere e riconnettere il connettore in Claude, e collaudare in una chat nuova. Se salti questo passo vedrai i tool vecchi e penserai che il deploy non abbia funzionato.
Le modifiche interne al comportamento (limiti, formati, logica) non cambiano la superficie: basta ricreare il container.
Manutenzione e guasti
Cosa | Dove vive | Se la perdi |
| OAuth App su GitHub | se ne crea una nuova in 5 minuti, poi si aggiorna il template |
| solo nel template | i token salvati diventano illeggibili: riconnetti il connettore e via. Ma non cambiarla mai senza motivo, perché l'effetto è lo stesso |
Le chiavi in | il vault | vanno riscritte, e ricopiate nelle istruzioni dei progetti |
Il vault | il dataset ZFS + snapshot + git | l'unica perdita vera |
⚠ Il template che Unraid salva in
/boot/config/plugins/dockerMan/templates-user/ contiene i segreti in chiaro
anche per i campi mascherati. Quel backup è materiale sensibile: la copia
condivisibile è il template sanitizzato del pacchetto.
La cache di Docker mente. Sempre
--no-cachedopo aver toccato i sorgenti.Restart ≠ Apply. Restart riusa la configurazione vecchia.
mkstempcrea a 600 ignorando l'umask. Il codice fachmod 666esplicito dopo ogni scrittura atomica, altrimenti i file nuovi non sarebbero scrivibili da SMB.gite il dubious ownership. L'entrypoint dichiarasafe.directoryprima di toccare qualsiasi repository.Il permesso Funnel è legato all'identità del nodo. Se ricrei il container e perdi
ts-state, il nodo si ripresenta come nuovo e il Funnel va riautorizzato. Nella policy del tailnet conviene concedere il Funnel aautogroup:memberinvece che a nodi specifici.La scadenza della chiave del nodo è un guasto programmato. Disattivala nella console Tailscale, alla voce Machines. Il preflight la controlla proprio perché è silenziosa: funziona tutto per sei mesi, poi smette.
Gli aggiornamenti automatici di Tailscale possono rompere il Funnel. È successo con la 1.102.1, in cui una regressione faceva fallire le connessioni Funnel in ingresso; risolta nella 1.102.2 del 4 agosto 2026. Se ti capita, la diagnosi giusta è confrontare la versione con il changelog prima di cercare il guasto in casa propria.
Il preflight dice quale controllo è fallito. I più frequenti:
Controllo | Cosa guardare |
| il mount del vault è sbagliato, o punta a una cartella vuota |
| i repository non ci sono ancora: rilancia, li crea il boot |
|
|
| una variabile è ancora |
|
|
| il Funnel non è attivo, o pubblica una porta diversa da |
| la chiave del nodo ha una scadenza attiva |
| l'hostname di |
Per collaudare saltando i controlli di rete:
PREFLIGHT_SKIP="funnel,chiave_nodo,dns_pubblico". Mai in esercizio.
Quasi sempre è BASE_URL che non combacia esattamente con la callback
registrata su GitHub — schema compreso, barra finale compresa. È l'errore numero
uno al primo avvio.
Se il servizio risponde ma i tool non compaiono, è cache: disconnetti e riconnetti il connettore, poi apri una chat nuova.
In ordine di gravità crescente:
history("Example Project/file.md", 20) cosa è successo
read_at("Example Project/file.md", "<hash>") com'era
write_file(...) rimettilo
diff("HEAD~5", "Example Project") cosa è cambiato nel dataset
dataset_restore("Example Project", "<hash>", "<manifest>", key)dataset_restore riscrive tutti i file del dataset, ma lo fa con un commit in
avanti: la storia non si perde e si torna indietro anche da lì.
Sotto a tutto c'è lo snapshot ZFS, che è la rete per quando è git stesso ad essere andato.
Guida d'uso
1. Ogni tool restituisce un verdetto, non un dump. Il ritorno è un oggetto
piccolo con dentro i fatti: sha, conteggi, byte, hash del commit. Il contenuto
viaggia solo quando l'hai chiesto. Per sapere si usano search, manifest,
list_files; per leggere, read_file.
2. Lo sha256 è l'unità di verità. Ogni lettura lo dà, ogni scrittura lo pretende:
read_file("X") → sha256: a3f9…
↓
write_file("X", nuovo, expected_sha256="a3f9…")Se nel frattempo il file è cambiato, la scrittura è rifiutata senza toccare
niente. Si chiama compare-and-swap. Per creare un file nuovo:
expected_sha256="new".
3. Non si cancella. Non esiste un tool delete. Lo smaltimento è move_path
verso Trash/, e move_path non sovrascrive mai.
4. Ogni scrittura è atomica, verificata e committata. Lock, eventuale commit
delle modifiche esterne, scrittura su temporaneo, os.replace (atomico),
rilettura e confronto sha, commit. Se un tool fallisce, il vault è esattamente
come prima.
5. commit_esterno_preventivo non è un errore. Significa che il repo era
sporco e le modifiche arrivate da fuori sono state committate a parte prima della
tua.
Vuoi | Usa | Sha? |
aggiungere righe a un registro |
| no |
cambiare una frase o un numero |
| sì |
rifare il file, o crearlo |
| sì ( |
spostare, rinominare, cestinare |
| no |
sapere se una cosa c'è e dove |
| — |
sapere quali file ci sono |
| — |
confrontare due alberi |
| — |
leggere testo |
| — |
leggere PDF o binari |
| — |
leggerne tanti in un colpo |
| — |
leggere com'era prima |
| — |
append non chiede lo sha perché non tocca mai i byte esistenti: non c'è
conflitto possibile, quindi non c'è niente da proteggere. È l'operazione giusta
per log e registri.
edit_file fa viaggiare solo i due frammenti invece del file intero: su un file
da 80 KB è la differenza fra una chiamata leggera e una pesante.
Livello vault — nessuna chiave
Tool | Cosa fa |
| il vault risponde, elenco dataset con open/locked. Nient'altro |
| la guida rapida, servita dall'immagine |
| crea un dataset aperto e vuoto |
| cancella un dataset open; i locked no |
Livello dataset — tutti accettano key
Tool | Cosa fa |
| file, cestino, git, commit, dimensione del repo |
| elenco ricorsivo con size e sha di ognuno |
| testo UTF-8 + sha |
| qualunque file in base64, max 2 MB |
| il file com'era a una revisione |
| blocco in coda, max 64 KB, senza sha |
| file intero, CAS |
| sostituzione chirurgica, CAS |
| binario da base64, CAS |
| sposta o rinomina dentro un dataset |
| grep server-side, |
| impronta dell'albero in un numero |
| tar.gz in base64, tutto in una chiamata |
| ultime N voci di storia git |
| differenze fra due revisioni |
| ⚠ riporta l'intero dataset a una revisione |
| svuota il cestino prima di una data |
I parametri esatti di ogni tool stanno nella sua descrizione, che Claude ha già in
contesto: qui non si duplicano. Il tool reference_guide() restituisce le regole
e le ricette in forma compatta, per quando una chat si perde.
Limite | Valore |
lettura e scrittura testo | 2 MB |
binari | 2 MB |
blocco di | 64 KB |
file elencabili | 3.000 |
righe di | 200 |
| 60 KB (tronca, non fallisce) |
| 30 MB non compressi |
| 5 MB di tgz |
I limiti sui binari sono tarati sul consumo reale: un file più grande di 2 MB non
è comunque utilizzabile dentro una conversazione. Meglio un rifiuto parlante che
un fallimento muto più a valle. Sopra quella soglia i file viaggiano via SMB o
scp, e il vault fa da archivista.
modificare un numero: read_file → sha → edit_file(path, vecchio, nuovo, sha)
aggiungere a un log: append(path, riga)
creare un documento: write_file(path, contenuto, "new")
archiviare: move_path("X/doc.md", "X/Trash/doc.md")
trovare qualcosa: search("termine", "X") → read_file solo sul file giusto
recuperare: history → read_at(path, hash) → write_file
verificare due momenti: manifest prima, manifest dopo — uguali = niente si è mosso
audit completo: manifest → list_files → archive → verifica sha → manifestMessaggio | Cura |
| la chiave sta nelle istruzioni del progetto |
|
|
| rileggi, riconcilia, riprova |
| hai usato |
| il contrario |
| rileggi e copia il frammento esatto |
| allunga il contesto finché è unico |
| c'è un |
|
|
| scendi di un livello |
|
|
Un errore non lascia mai scritture parziali.
Cosa non fa, di proposito
Nessun tool "esegui comando". Nessuna cancellazione di file. Nessun git gc --prune a richiesta. Nessun dump: ogni tool restituisce un verdetto, perché ogni
byte che torna indietro finisce nel contesto della conversazione, e il contesto è
la risorsa scarsa.
E una cosa che è bene dire chiaramente: le chiavi dei dataset non sono autenticazione. Il servizio riconosce un solo account, e tutte le sue conversazioni hanno la stessa identità — il server non può distinguerle. Le chiavi funzionano perché una chat che non ha la chiave in contesto non può inventarla: sono un confine fra progetti, non una difesa contro un attaccante. Quella è OAuth.
Contenuto del pacchetto
File | |
| il motore: |
| i 21 tool MCP, con i contratti nei docstring |
| i 10 controlli bloccanti |
| la guida compatta servita da |
| init, permessi, drop privilegi, preflight, avvio |
| immagine |
| template Unraid, ogni campo descritto |
| 62 controlli sul motore, senza rete |
Licenza
MIT.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- Flicense-quality-maintenanceAn MCP server that wraps gitingest to enable Claude Code to analyze GitHub repositories, providing access to code structures, statistics, and full content. It facilitates the creation of structured study notes and supports both public and private repositories through the Model Context Protocol.1
- Alicense-qualityCmaintenanceAn MCP server that enables Claude Desktop to read and write an Obsidian vault hosted on a VPS, using SSH or HTTP transport with OAuth authentication.1156MIT
- Alicense-qualityBmaintenanceRemote MCP server that exposes a personal git repository of markdown notes to Claude, enabling reading, writing, searching, and running scripts with automatic git commits and GitHub OAuth authentication.MIT
- Alicense-qualityAmaintenanceAuthenticated remote MCP server that exposes a private GitHub-hosted Obsidian vault to Claude, enabling list, read, write, and search operations on notes.7MIT
Related MCP Connectors
A MCP server built for developers enabling Git based project management with project and personal…
An MCP server that gives your AI access to the source code and docs of all public github repos
Hosted MCP server connecting claude.ai, ChatGPT and other AI apps to your own computer
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/alcor6502/archivist-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server