Il server MCP di Zornade risponde su un indirizzo solo, https://mcp.zornade.com/mcp, espone sei strumenti in sola lettura e accetta due forme di autenticazione. La prima è una chiave API nell'header x-api-key, la seconda un access token OAuth 2.1 nel Bearer. Il trasporto è JSON-RPC 2.0 su HTTP, il protocollo dichiara la revisione 2025-06-18 e il server si presenta come zornade 1.1.0.
Non tiene sessioni. Non restituisce nessun identificativo di sessione e ogni richiesta è indipendente, quindi puoi chiamarlo con curl senza aprire prima un handshake.
Dietro le sei funzioni ci sono 18,7 milioni di indirizzi dell'archivio ANNCSU e 85 milioni di particelle catastali, con rischio, subsidenza, demografia, quotazioni OMI e potenziale solare già calcolati. La chiave gratuita regge 10.000 richieste all'ora, e in un uso interattivo non ci si avvicina mai a quel limite.
Questa pagina è il riferimento per collegarlo. La panoramica sugli strumenti e su cosa chiedergli sta nell'articolo di annuncio, il dietro le quinte tecnico in come lo abbiamo costruito. Qui trovi le configurazioni complete dei client, la forma esatta delle risposte e i codici di errore. Li abbiamo provati uno per uno contro la produzione mentre scrivevamo, incluse le risposte sbagliate.
Cosa serve prima di iniziare
Una chiave API gratuita, che si crea in un minuto su app.zornade.com/api e comincia con zrn_, più un client che parli MCP.
Se il client non è tuo, cioè Cursor, VS Code o Claude, imposti l'header x-api-key e hai finito. Se invece stai costruendo un'applicazione che deve far accedere i propri utenti, usi OAuth 2.1 e non distribuisci nessuna chiave dentro il codice. Le due strade convivono sullo stesso endpoint.
Il colloquio con il server, visto da fuori
Guardiamo una chiamata in chiaro una volta sola, perché chiarisce tutti gli errori che incontrerai dopo. Il primo messaggio di un client MCP è sempre initialize, con la versione di protocollo richiesta e il nome del client.
{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2025-06-18",
"capabilities": {},
"clientInfo": { "name": "il-mio-client", "version": "1.0" }
}
}
Il server risponde con le proprie capacità, che qui si riducono ai soli strumenti, e dichiara listChanged come vero. Poi il client chiede tools/list, riceve i sei schemi e da quel momento chiama tools/call. Se il tuo codice fa tutto questo a mano, la chiamata utile somiglia a questa.
curl -s https://mcp.zornade.com/mcp \
-H 'content-type: application/json' \
-H 'accept: application/json, text/event-stream' \
-H "x-api-key: $ZORNADE_API_KEY" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"zornade_admin_lists","arguments":{"type":"regions"}}}'
Quando chiedi text/event-stream la risposta arriva come eventi SSE, quindi ogni riga utile è preceduta da data. Senza quell'header ottieni JSON normale. Il campo id torna indietro identico, come vuole il protocollo.
Claude Code
Un comando, e la configurazione finisce in ~/.claude.json per il progetto corrente.
claude mcp add --transport http zornade https://mcp.zornade.com/mcp \
--header "x-api-key: zrn_LA_TUA_CHIAVE"
Aggiungendo --scope project la stessa voce viene scritta in .mcp.json nella radice del repository, che è la forma da mettere in git quando il server deve essere uguale per tutto il team. Le due varianti accettano anche -t e -H come forme brevi. Per controllare che il collegamento sia vivo, claude mcp list mostra accanto a ogni server lo stato, per esempio Connected oppure Failed to connect.
Una trappola documentata da Anthropic riguarda proprio i server remoti. In un file JSON, una voce che ha url ma non ha type viene letta come server stdio e scartata. Il campo deve esserci.
{
"mcpServers": {
"zornade": {
"type": "http",
"url": "https://mcp.zornade.com/mcp",
"headers": { "x-api-key": "zrn_LA_TUA_CHIAVE" }
}
}
}
Anche streamable-http è accettato come alias di http, così le configurazioni copiate dalla documentazione di altri progetti entrano senza modifiche.
Claude Desktop e il connettore su claude.ai
L'app desktop legge lo stesso blocco mcpServers dentro il proprio file di configurazione. Nella versione web il server si aggiunge invece dai connettori personalizzati, e lì non incolli nessuna chiave, perché parte il flusso OAuth.
Il flusso lo scopre da solo. Quando una richiesta arriva senza credenziali, il server risponde 401 con un header WWW-Authenticate che punta a https://mcp.zornade.com/.well-known/oauth-protected-resource, e il client legge da lì quale authorization server contattare. Nel nostro caso è il servizio di autenticazione Supabase, con gli scope email e profile, e i token viaggiano solo nell'header. Non serve registrare a mano nessuna applicazione OAuth.
Un dettaglio che sfugge a molti. Se hai impostato un header Authorization fisso e quel token viene rifiutato, Claude Code segnala la connessione come fallita invece di ripiegare su OAuth. Se vuoi il flusso OAuth, togli l'header.
Cursor
Cursor legge due file, .cursor/mcp.json nella radice del progetto e ~/.cursor/mcp.json nella home. Il formato è quello standard, e per un server remoto bastano url e headers.
{
"mcpServers": {
"zornade": {
"url": "https://mcp.zornade.com/mcp",
"headers": { "x-api-key": "${env:ZORNADE_API_KEY}" }
}
}
}
Cursor risolve le variabili nei campi command, args, env, url e headers, con la sintassi basata su env. Tenerla fuori dal file è la scelta giusta quando il repository è condiviso, perché una chiave in chiaro in un file versionato è una chiave bruciata. I server si accendono e si spengono dalla voce Customize nella barra laterale, e i log stanno nel pannello Output sotto la voce MCP Logs.
VS Code e Copilot
Qui la configurazione ha tre posti possibili e conviene sapere quale usare. Il formato portabile è .mcp.json nella radice del progetto, con l'oggetto mcpServers, e viene letto anche dagli altri strumenti Copilot. Il formato con chiave servers invece appartiene a .vscode/mcp.json, che la documentazione di VS Code elenca ormai fra le destinazioni deprecate. Per la configurazione personale c'è ~/.copilot/mcp-config.json.
{
"servers": {
"zornade": {
"type": "http",
"url": "https://mcp.zornade.com/mcp",
"headers": { "x-api-key": "zrn_LA_TUA_CHIAVE" }
}
}
}
Su VS Code vale la regola scritta nella documentazione ufficiale, cioè di non scrivere le chiavi in chiaro nel file ma di usare le variabili di input o un file di ambiente. I server di workspace ereditano la fiducia del workspace, quindi in una cartella aperta in modalità limitata non partono affatto.
MCP Inspector
Per capire cosa risponde davvero il server senza tirare in mezzo un agente, MCP Inspector è lo strumento giusto. Si lancia con npx e apre una interfaccia dove scegli il trasporto, incolli l'URL, aggiungi l'header con la chiave e vedi la lista dei sei strumenti. Da lì puoi compilare i parametri e leggere la risposta grezza, che è esattamente quello che serve quando un agente si comporta in modo strano.
Un avvertimento sulla terza cifra: il server non tiene sessioni, quindi non aspettarti un flusso a più passi con stato conservato fra una chiamata e l'altra. Ogni richiesta porta con sé tutto quello che le serve.
I sei strumenti, con i parametri esatti
Questa è la tabella che useremo come riferimento. I valori fra parentesi sono i default quando il parametro non viene passato.
| Strumento | Cosa fa | Obbligatori | Opzionali |
|---|---|---|---|
| zornade_geocode_search | Geocoding diretto sull'archivio ANNCSU | q | city, limit (10, max 50) |
| zornade_geocode_reverse | Dal punto al civico più vicino, con distanza in metri | lat, lng | radius (100, max 500), limit (5, max 20) |
| zornade_parcel_by_id | Scheda di una particella da FID o identificativo GML | fid | include |
| zornade_parcel_locate | Particelle per punto, lista di punti o riquadro | nessuno | lat, lng, points, bbox, limit (50, max 200) |
| zornade_parcel_search | Ricerca per comune, foglio, particella e sezione | comune | foglio, label, sezione, limit (20, max 100) |
| zornade_admin_lists | Regioni, province e comuni, con filtri | type | region, province |
Tre cose non si leggono dalla tabella e contano.
Il parametro include di zornade_parcel_by_id è una stringa libera, non un elenco chiuso, quindi il modello può comporla come vuole. Accetta queste sezioni separate da virgola: risk, subsidence, terrain, population, buildings, economics, demographics, land_cover, land_use, valuation, valuation_history, coastal_erosion, cultural_heritage, poi, solar, nightlights. Se non chiedi niente, il server risponde con risk, economics, solar e valuation, che è il pacchetto più utile per una valutazione immobiliare. La geometria non viene mai restituita, per tenere leggera la risposta.
Lo strumento zornade_parcel_locate ha tre modalità che si escludono a vicenda. Punto singolo con lat e lng, lista di punti nella forma "lat,lng;lat,lng" fino a dieci coppie, oppure riquadro nella forma "minLng,minLat,maxLng,maxLat" con un lato massimo di 0,05 gradi.
Le coordinate di zornade_geocode_reverse sono validate su un intervallo che copre l'Italia, latitudine fra 35,5 e 47,5 e longitudine fra 6,0 e 19,0. Fuori da quell'area la chiave non viene nemmeno usata.
Cosa torna indietro
La risposta a tools/call arriva incartata. Il risultato è un oggetto con un array content, e il campo text del primo elemento è a sua volta una stringa JSON. Dentro quella stringa trovi data, con i risultati veri, e meta, con le informazioni di licenza.
Questa forma a scatole annidate confonde al primo tentativo. result.content[0].text va letto come JSON, e non mostrato come testo. Il campo meta porta attribution_required, che risulta vero, e zornade_attribution_required, vero anche lui quando la chiave è su piano gratuito. C'è poi il blocco zornade_attribution con la stringa markdown da esibire, cioè il link a zornade.com con testo "Dati elaborati da Zornade".
Se il tuo agente mostra i risultati all'utente, quel link va mostrato. È il modo in cui la licenza gratuita chiede di essere citata, e vediamo il dettaglio due paragrafi più sotto.
Gli errori, con il codice esatto
Abbiamo chiamato il server in tutte le condizioni sbagliate possibili, e le risposte seguono regole diverse fra loro. Questa è la parte che fa risparmiare più tempo.
Nessuna credenziale. Arriva un HTTP 401 con un errore JSON-RPC dal codice -32001 e un messaggio che spiega le due strade di autenticazione e rimanda a app.zornade.com/api. Nella stessa risposta c'è l'header WWW-Authenticate con il puntatore ai metadati OAuth.
Chiave sbagliata o scaduta. Qui la sorpresa. La risposta HTTP è 200, quindi un controllo basato solo sullo status code passa senza accorgersi di nulla. L'errore sta in result.isError, che vale true, e nel testo che dice "ERROR (HTTP 401): Invalid or expired API key.". Se il tuo codice guarda solo response.ok, tratterà una chiave invalida come un successo. Va controllato isError.
Parametro obbligatorio mancante. Altro HTTP 200, ma l'errore è a livello di protocollo, con codice -32602 e il messaggio "Validation failed: Invalid input: expected string, received undefined". Niente result.content, quindi un client che legge content[0] va in errore a sua volta.
Strumento inesistente. Codice -32601, "Method not found", e nel campo data.method il nome che hai sbagliato. Utile quando si scrive il nome dello strumento a mano.
Chiamata con GET. L'endpoint accetta solo POST e risponde 400. Serve a poco, ma se stai configurando un health check su quell'URL, sappi che fallirà sempre.
Riassumendo la regola da portarsi a casa: non fidarti dello status HTTP. Guarda error, poi isError, e solo dopo il contenuto.
Limiti, licenza e attribuzione
Il limite è di 10.000 richieste all'ora per chiave, uguale per tutte le API REST di Zornade. Non ci sono header di rate limit nelle risposte, quindi non aspettarti di leggere quanta quota resta. Il numero di richieste resta visibile dalla dashboard di app.zornade.com.
Il server è pubblicamente listato nel registro ufficiale dei server MCP, e il codice del server che usiamo come riferimento per il trasporto è su github.com/zornade/zornade-mcp con licenza MIT.
Sui dati il discorso è diverso, e conviene essere espliciti. L'uso non commerciale è gratuito, anche per un prodotto tuo, a condizione che il link di attribuzione sia presente e cliccabile. Un testo piatto con scritto Dati Zornade senza collegamento ipertestuale non soddisfa la condizione. Se il link non è cliccabile, metti il collegamento ipertestuale vero verso https://zornade.com, nell'interfaccia o nelle note della risposta. Per usi commerciali senza obbligo di attribuzione esiste la licenza commerciale, e le condizioni stanno su licenza commerciale.
Se qualcosa non funziona
Il server non si connette e il client dice che la chiave non va. Nove volte su dieci è una nuova riga incollata insieme alla chiave. Claude Code segnala questo caso con un avviso esplicito sullo spazio bianco nascosto. Prova la chiave con curl prima di dare la colpa al client.
Il client non vede nessuno strumento. Se hai configurato un URL senza il campo type, entra in gioco la trappola descritta sopra. Aggiungi type con il valore http e riconnetti il server.
Il server risponde ma i dati sembrano vuoti. Guarda la stringa dentro result.content[0].text e interpretala come JSON. Se la stai stampando come testo, vedrai una riga sola piena di parentesi graffe e sembrerà vuota.
L'agente non capisce quale strumento usare. Il problema è quasi sempre il parametro, non lo strumento. zornade_geocode_search cerca indirizzi e non punti di interesse, come scrive la sua stessa descrizione: se chiedi un ristorante, non lo troverà. Per una via, invece, trova il civico.
Una chiamata sembra lenta. La latenza della piattaforma resta sotto il secondo, e su una singola particella la risposta è quasi immediata. Se il tuo agente impila dieci chiamate per rispondere a una domanda sola, il tempo si sente. Conviene chiedergli di usare zornade_parcel_locate in modalità elenco invece di dieci chiamate singole.
Dove andare da qui
Se stai integrando il server in un prodotto tuo e ti serve un comportamento che non c'è, le sei funzioni sono il punto di partenza e non il tetto. Abbiamo costruito piattaforme dati catastali e backend GIS per aziende che avevano bisogno di un accesso ai dati dentro i loro processi, e la parte MCP è spesso la più corta del lavoro. La documentazione delle API REST sta su zornade.com/documentation, mentre per un confronto diretto su cosa ti serve scrivici da servizi sviluppo software.
