Prima o poi qualcuno trascina la foto di un passaporto nella chat con un assistente e gli chiede di "compilare il modulo e basta". Un modello di visione generico ci proverà. Magari azzeccherà perfino il nome. Quello che non farà è dirti se le cifre di controllo della zona a lettura ottica (MRZ) sono corrette, restituire le date sempre nello stesso formato o dire "non sono riuscito a leggerlo" invece di tirare a indovinare.
Quel vuoto è il lavoro giusto per uno strumento. Il Model Context Protocol permette a un agente di chiamarne uno, e questo articolo mostra come dare a Claude Desktop, Claude Code, Cursor e ad altri client uno strumento di riconoscimento dei documenti, cosa riceve l'agente, come tenere sotto controllo i costi e a cosa pensare prima ancora di mettere un agente davanti a dei documenti d'identità.
Questo è il blog di doc.cheap, l'API dietro il server MCP usato qui. Il server ha licenza MIT, e le domande sulla configurazione valgono per qualsiasi strumento di questo tipo.
Cosa riceve l'agente
Il server è @doc-cheap/mcp su npm (MIT, Node 20 o successivo) ed espone tre strumenti:
| Strumento | Cosa fa | Consuma crediti? |
|---|---|---|
scan_document |
Riconosce un passaporto, una carta d'identità o una patente di guida da una foto o da una scansione e restituisce il risultato strutturato | Sì, solo quando un documento viene riconosciuto |
check_balance |
Legge i crediti rimanenti e i contatori del mese in corso | No (sola lettura) |
search_docs |
Cerca nella documentazione dell'API inclusa nel server, offline | No (sola lettura) |
Ogni strumento ha un titolo, una descrizione (le due che toccano lo stato dell'account riportano il prezzo) e gli hint di comportamento MCP che un client legge prima di decidere se chiederti conferma: scan_document è marcato come non di sola lettura, gli altri due come di sola lettura. Il server invia anche istruzioni che il modello legge prima di qualsiasi chiamata, in cui dice cosa riconosce e quanto costa una chiamata. Oltre agli strumenti ci sono quattro prompt (scan_document_to_json, check_document_expiry, batch_scan, explain_error), e ogni pagina della documentazione è esposta come risorsa di sola lettura, per esempio doccheap://docs/reference/fields.
scan_document risponde con il risultato completo in JSON strutturato più un riepilogo di una riga, per esempio:
Scan 01a0af18-cd8d-7a61-9f2d-4c7b8e105da3: recognized · passport (GRC) · PARADEIGMA ELENI SOFIA · billed · 684 ms
(Un titolare di facsimile inventato, preso dalla documentazione.) Il JSON dietro contiene il titolare, il numero del documento e le date in ISO 8601, ogni campo con una fascia di confidenza, le righe della MRZ con un verdetto passed / failed / absent e un flag billed. Il punto è proprio questa struttura: l'agente non deve interpretare pixel, legge campi.
Installazione: il server locale
Tutti i client qui sotto avviano il server con npx. Se non imposti una chiave, usa la chiave sandbox pubblica, che dà 10 documenti riconosciuti gratis per indirizzo IP in totale e al massimo 10 richieste all'ora. Basta per provarlo. Registrandoti ottieni 20 crediti gratuiti; dopo, un documento riconosciuto costa $0.01.
Claude Desktop, Cursor e Windsurf leggono lo stesso blocco, rispettivamente in claude_desktop_config.json, ~/.cursor/mcp.json e ~/.codeium/windsurf/mcp_config.json:
{
"mcpServers": {
"doc-cheap": {
"command": "npx",
"args": ["-y", "@doc-cheap/mcp"],
"env": { "DOC_CHEAP_API_KEY": "sk_live_your_key" }
}
}
}
Togli la riga env per usare la chiave sandbox.
Claude Code:
claude mcp add-json doc-cheap '{"command":"npx","args":["-y","@doc-cheap/mcp"],"env":{"DOC_CHEAP_API_KEY":"sk_live_your_key"}}'
VS Code:
code --add-mcp '{"name":"doc-cheap","command":"npx","args":["-y","@doc-cheap/mcp"]}'
Gemini CLI e Kiro usano lo stesso blocco mcpServers nei rispettivi file di impostazioni; la guida MCP (in inglese) riporta ogni percorso. Riavvia il client dopo la modifica: il server rileva un ambiente cambiato solo a un nuovo avvio.
Installazione: il server in hosting
Se il tuo client si collega a un URL invece di avviare un comando, gli stessi tre strumenti sono disponibili in hosting su https://mcp.doc.cheap/mcp tramite Streamable HTTP, senza login. La chiave va in un header, X-Doc-Cheap-Api-Key oppure Authorization: Bearer (se li invii entrambi vince l'header con il nome dedicato); senza chiave si usa quella sandbox.
Claude Code:
claude mcp add --transport http doc-cheap https://mcp.doc.cheap/mcp --header "Authorization: Bearer sk_live_your_key"
Cursor:
{
"mcpServers": {
"doc-cheap": {
"url": "https://mcp.doc.cheap/mcp",
"headers": { "Authorization": "Bearer sk_live_your_key" }
}
}
}
In Claude Desktop e su claude.ai, aggiungilo da Settings → Connectors come connettore personalizzato con quell'URL.
Il server in hosting non vede i file sul tuo computer, quindi lì scan_document riceve l'immagine come image_base64 o come image_url pubblico.
File locali e URL sono recintati di proposito
L'argomento di uno strumento lo sceglie un modello, e un modello si può convincere a fare cose. Per questo il server locale non legge percorsi arbitrari:
image_pathè disattivato finché non impostiDOC_CHEAP_IMAGE_ROOTsu una directory. I percorsi vengono prima risolti seguendo i link simbolici, e..o un link che punta fuori dalla directory vengono rifiutati. Un file mancante e un file fuori dai limiti ricevono lo stesso messaggio, così lo strumento non può servire a sondare se un file esiste.image_urldeve esserehttps:, deve risolversi solo in indirizzi pubblici (loopback, reti private, link-local e intervalli simili vengono rifiutati), segue al massimo tre redirect ricontrollando ogni passaggio ed è limitato a 25 MB.
Se in passato hai già collegato a un agente uno strumento che legge file, confrontalo con questo elenco. "Il modello passerà solo percorsi sensati" non è un confine di sicurezza.
Tenere sotto controllo i costi
Due proprietà rendono prevedibile l'uso da parte di un agente:
- Si pagano solo i documenti riconosciuti. Una chiamata viene addebitata quando il tipo di documento è stato determinato e i dati sono stati effettivamente estratti: una MRZ le cui cifre di controllo risultano corrette, almeno cinque campi stampati o un codice a barre decodificato correttamente. Nessun documento trovato, un'immagine illeggibile, un tipo non supportato, un errore interno o un timeout non costano nulla. Il flag
billeddel risultato dice ogni volta cosa è successo. Con la chiave sandbox non viene addebitato nulla, e allora il flag indica se la stessa scansione sarebbe stata addebitata con una chiave live. - I retry possono essere gratuiti.
scan_documentaccetta unaidempotency_key; con una chiave live, una ripetizione con la stessa chiave restituisce il primo risultato salvato invece di addebitare di nuovo. Un retry senza chiave è una seconda scansione. Una ripetizione restituisce il risultato salvato, quindi traretain_hours: 0e retry ripetibili bisogna scegliere.
In pratica:
- Fai chiamare
check_balanceall'agente prima di un batch. Le istruzioni del server stesso dicono al modello di farlo, e il promptbatch_scanlo fa per primo. Con la chiave sandbox il saldo ènull, e lo strumento dice che non c'è un saldo invece di mostrare degli zeri. - Approva automaticamente solo gli strumenti di sola lettura. La configurazione di Kiro, per esempio, supporta
"autoApprove": ["check_balance", "search_docs"]. Lasciascan_documentdietro una richiesta di conferma, perché è quello che spende. - Lascia che l'agente consulti la documentazione.
search_docsfunziona offline sulla documentazione inclusa, quindi "cosa significaunsupported_document" non costa nulla e non dipende dalla memoria del modello.
Privacy: le domande da porsi prima di farlo
I documenti d'identità sono tra i dati più sensibili che esistano, e un agente aggiunge soggetti al flusso. Ecco cosa è vero lato API e cosa dipende dalla tua configurazione.
Lato API (come da documentazione):
- L'immagine caricata resta in memoria per la durata della richiesta e non viene mai scritta su uno storage persistente.
- Il risultato del riconoscimento viene conservato perché si possa rileggerlo in seguito, per un periodo impostato sull'account (24 ore, 7 giorni, 30 giorni o un anno). Il valore predefinito per un nuovo account è un anno. Per singola chiamata,
retain_hours: 0non scrive alcuna riga, e anchescan_documentaccettaretain_hours. Se all'agente la risposta serve una volta sola, impostalo. - L'elaborazione avviene nell'Unione europea. I dati non vengono usati per addestrare modelli.
return_portrait: falseescludeimages.main_photo, il ritaglio della foto del titolare. Il ritaglio dell'intera pagina viene comunque restituito, e così pure la seconda copia sbiadita del volto che alcuni documenti stampano sulla pagina.
Dal tuo lato:
- Il risultato entra nel contesto del modello. Qualsiasi cosa restituisca
scan_document(nomi, numeri, date) ora fa parte della conversazione, ed è elaborata dal fornitore di LLM che fa girare il tuo client, alle condizioni di quel fornitore. È così per qualsiasi strumento MCP, non è una particolarità di questo. - Conta come viaggia l'immagine. Con
image_pathsul server locale, il server legge il file e lo invia direttamente all'API. Conimage_base64, i byte dell'immagine stanno negli argomenti della chiamata allo strumento generati dal modello. Se vuoi che i pixel restino fuori dal contesto del modello, usa una directory locale recintata. - Questo è riconoscimento, non verifica. Il gruppo
authenticitydel risultato riportanot_checked. Una MRZ corretta significa che la zona è stata letta ed è coerente al suo interno, non che il documento sia autentico. Non c'è alcun passaggio di liveness o di confronto del volto. Se il tuo caso d'uso è il KYC, questo è un input, non la decisione. - Usa documenti sintetici mentre sviluppi. Facsimili e MRZ generate bastano per collegare tutto.
Una breve sessione
Con il server installato, basta un prompt come "Leggi la scansione di questo passaporto e dimmi se scade nei prossimi sei mesi" con un facsimile sintetico allegato. Un agente che si comporta bene chiama scan_document, legge document.expiry_date e document.days_remaining dal risultato e risponde in base a quei campi, non alla sua impressione dell'immagine. Se la scansione torna unreadable, dovrebbe dirlo e chiedere una foto migliore, e a te non è stato addebitato nulla.
Quest'ultimo comportamento è il vero motivo per usare uno strumento qui: l'agente riceve un "non sono riuscito a leggerlo" esplicito invece della tentazione di riempire un vuoto.
Link
- Pagina MCP con gli snippet per ogni client: https://doc.cheap/mcp
- Guida completa: https://doc.cheap/docs/guides/use-the-mcp-server
- Codice sorgente (MIT): https://gitlab.com/doccheap/ocr-mcp
- npm: https://www.npmjs.com/package/@doc-cheap/mcp
Se ci costruisci qualcosa, o trovi un client in cui la configurazione qui sopra non funziona, scrivi a admin@doc.cheap.
Ogni affermazione su doc.cheap e sul suo server MCP è stata verificata sul loro codice.