+39 3662317539/+34 620899163
info@simonecosci.com

Un server MCP sui propri dati: cosa esporre e cosa no

Sviluppo Siti Web a Tenerife

Un server MCP sui propri dati: cosa esporre e cosa no

Un server MCP non è un’API come le altre. Un’API la chiama del codice che hai scritto tu: sa quali
endpoint esistono, in che ordine chiamarli, e cosa fare della risposta. Un server MCP lo chiama un
modello. Legge la lista degli strumenti, decide da solo quale usare e con quali argomenti, e si
porta il risultato nel proprio contesto — da dove può uscire in una risposta, in un log, in una
chat.

Cambia il criterio con cui si disegna la superficie. Non è più «cosa potrebbe servire», ma «cosa
sono disposto a far succedere se il modello sbaglia». Quello che segue viene dal server MCP che ho
scritto su Biglins, il mio gestionale di fatturazione, e dalla revisione 2026-07-28 della
specifica MCP, che è quella
corrente.

Nove strumenti, e quello che non c’è

Il server su Biglins espone nove tool. Quattro leggono: list_companies, list_customers,
list_estimations, list_invoices. Tre scrivono: create_customer, create_estimation,
create_invoice. Due mandano una mail: send_estimation_email, send_invoice_email.

Quello che non c’è conta più di quello che c’è. Non ci sono update_*delete_*. Non c’è un
tool generico che accetta una query. Non c’è niente che restituisca la contabilità come blocco
unico. Un modello che sbaglia su questa superficie crea un preventivo di troppo — lo cancello io
dal gestionale in dieci secondi. Con un delete_invoice esposto, l’errore peggiore è di un altro
ordine di grandezza.

1. Nessuno strumento generico

run_query, execute_sql, call_endpoint: sono comodi da scrivere e sono la scorciatoia che
annulla tutto il resto. Un tool generico ha una superficie pari a quella del database sotto, e non
c’è validazione dell’input che tenga quando l’input è SQL arbitrario.

La spec è esplicita su cosa deve fare il server, e sono tutte cose che con un tool generico non
puoi fare: «Servers MUST: validate all tool inputs, implement proper access controls, rate
limit tool invocations, sanitize tool outputs». Un tool stretto — un verbo, argomenti tipizzati,
uno schema JSON che dichiara cosa accetta — le rende possibili tutte e quattro.

2. Lo scope sta negli argomenti

MCP non ha sessioni a livello di protocollo. La spec lo dice a chiare lettere: un server «cannot
rely on implicit per-connection state to relate one tool call to the next». Non esiste un «utente
corrente» implicito nella connessione: se una cosa deve essere vera per tutta la conversazione, o
sta nel token, o va passata a ogni chiamata.

Su Biglins ogni tool tranne list_companies vuole un company_id esplicito. Il modello lo scopre
con list_companies e poi lo porta avanti lui. È una scelta di disegno, non un vincolo del
protocollo, e ha un effetto collaterale utile: la chiamata è leggibile. Guardando gli argomenti si
vede su quale azienda sta per agire, prima che agisca.

Attenzione a un errore che la spec chiama per nome: un identificativo ricevuto dal client non è
un’autenticazione
. «MCP servers MUST NOT treat possession of a state handle as
authentication». L’autorizzazione si verifica a ogni richiesta, contro il token, non contro l’ID
che arriva negli argomenti.

3. Le annotazioni descrivono, non proteggono

Un tool può portare delle annotazioni che dichiarano come si comporta. Sono quattro, con questi
default nello schema:

Campo Significato Default
readOnlyHint non modifica l’ambiente false
destructiveHint può fare aggiornamenti distruttivi true
idempotentHint richiamarlo con gli stessi argomenti non aggiunge effetti false
openWorldHint interagisce con un «mondo aperto» di entità esterne true

Notare i default: in assenza di annotazioni un tool è considerato scrivente, distruttivo e non
idempotente. Il caso peggiore, che è la scelta giusta.

E qui il punto che fa la differenza tra un server MCP che regge e uno che sembra reggere. Lo schema
avverte: «all properties in ToolAnnotations are hints. They are not guaranteed to provide a
faithful description of tool behavior», e «clients should never make tool use decisions based on
ToolAnnotations received from untrusted servers». Sono metadati per l’interfaccia: servono al
client per mostrare un’icona diversa o chiedere conferma. Il controllo vero sta nel tuo server,
nel codice del tool. Un readOnlyHint: true su un tool che scrive non ferma niente e non ferma
nessuno.

4. Gli errori sono per il modello

MCP distingue due modi di sbagliare. Gli errori di protocollo — tool sconosciuto, richiesta
malformata — tornano come errori JSON-RPC. Gli errori applicativi tornano dentro un risultato
valido, con isError: true, e la spec li descrive come «actionable feedback that language models
can use to self-correct and retry with adjusted parameters».

Vuol dire che il testo dell’errore lo legge il modello, e da quel testo dipende se ritenta bene o
ritenta uguale. «Invalid input» non serve a nessuno. «La data di scadenza deve essere successiva
alla data di emissione (2026-09-15)» si corregge da sola al secondo tentativo. Vale la pena
riscrivere i messaggi di errore pensando a chi li legge.

5. Quello che torna indietro è contesto

L’output di un tool finisce nel contesto del modello, e da lì può finire in posti che non hai
scelto tu. Per questo list_customers non restituisce la scheda completa del cliente: restituisce
quello che serve per identificarlo e per usarlo nel tool successivo. Ogni campo in più è un campo
che viaggia.

La stessa logica vale sulla lista degli strumenti. Il set di tool «MUST NOT vary per-connection»,
ma «MAY vary by the authorization presented on the request — for example, returning only the
tools the caller’s granted scopes permit». Chi non ha i permessi per creare fatture non deve
vedere create_invoice: non si nega al momento della chiamata, si toglie dalla lista.

La regola che tiene insieme le altre

Sulla carta c’è sempre un essere umano in mezzo: la spec dice che «there SHOULD always be a
human in the loop with the ability to deny tool invocations». Ma è un should, e riguarda il
client, non te. Il protocollo non impone nessun modello di interazione, e tu non sai quale client
si collegherà domani al tuo server.

Quindi: esponi solo strumenti di cui accetti l’esecuzione senza conferma. Se la risposta a «e se
partisse da solo?» non è «pazienza», quello strumento non va esposto — va lasciato nell’interfaccia
dove a premere il bottone c’è una persona.