Un server MCP consegna numeri a qualcosa che poi ci ragionerà sopra ad alta voce, con sicurezza, davanti a qualcuno che non può verificare. È un problema di progettazione diverso da quello di un'API per un programma, e in gran parte non riguarda il protocollo.

Queste sono sei decisioni nel server MCP di Skomi e da che cosa ognuna si sta difendendo. Nessuna è ingegnosa; parecchie sono la seconda versione, dopo che quella ovvia si è rivelata una trappola.

1. Il codice è un'intestazione, mai un parametro dello strumento

La progettazione ovvia è un parametro token su ogni strumento. Si documenta da sé, funziona, e mette il segreto del cliente dentro la conversazione, dove raggiunge i registri del client, qualunque trascrizione venga esportata, e qualunque cosa il modello scelga di ripetere mentre spiega quello che ha appena fatto.

Un'intestazione è invisibile al modello. Non può essere citata perché non è nel contesto.

Lo stesso ragionamento dice che il server non dovrebbe tenere nessun codice proprio. Quello di Skomi inoltra qualunque cosa gli mandi chi chiama e non tiene niente, quindi non può fare niente che tu non potessi già fare con curl. Non c'è nessuna modalità in cui abbia un accesso permanente ai dati di qualcuno, che è l'unica versione di «prendiamo la sicurezza sul serio» che si possa verificare.

2. Niente scrive

Ogni strumento è annotato con readOnlyHint, e l'API che ci sta dietro è di sola lettura in partenza, quindi l'annotazione è una descrizione più che una promessa.

Vale la pena dirlo chiaramente perché la tentazione va nell'altro verso: un assistente che potesse cambiare un'impostazione fa una dimostrazione bellissima. Vuol dire anche che un'istruzione letta male ha conseguenze che sopravvivono alla conversazione. Leggere è recuperabile; scrivere no, e uno strumento di analitica non ha niente da scrivere che valga quel rischio.

3. Un'interruzione restituisce un errore, non degli zeri

Quando l'archivio non è disponibile, gli strumenti falliscono. Deliberatamente non restituiscono una pagina di zeri.

Gli zeri sarebbero la risposta più gentile e la più pericolosa, perché un modello non sa distinguere una settimana rotta da una settimana tranquilla. «Il traffico è sceso a zero martedì» è una frase che un assistente produrrà, in buona fede, a partire da dati dall'aria plausibile, e la persona che la legge non ha nessun modo di sapere che era un'interruzione dell'analitica e non un'interruzione del proprio sito.

Un errore è un'esperienza peggiore ed è vera. La regola generale: non restituire mai un valore indistinguibile da una misura vera quando non hai nessuna misura.

4. «Nessun confronto» invece di un numero inventato

Ogni metrica arriva con la sua variazione rispetto al periodo precedente. Quando il periodo precedente era zero, la variazione è nulla: non infinito, non 100%, non un numero grosso che per caso si riesce a mostrare.

La variazione percentuale a partire da zero è indefinita, e qualunque valore concreto tu ci sostituisca è un numero che il modello ripeterà come un fatto. Gli strumenti dicono «nessun confronto». È meno soddisfacente ed è l'unica cosa onesta disponibile.

5. Le righe non fanno 100, e lo strumento lo dice

Una suddivisione conta persone distinte per riga. Una persona su un telefono e su un portatile sta in due righe, quindi le righe sommano più del totale e le percentuali non fanno 100.

Non detto, questo è una trappola con un fallimento molto preciso: a un modello a cui si chieda la «quota di traffico per paese» normalizzerà premurosamente le righe al 100%, una cosa del tutto ragionevole da fare con un elenco di numeri, e sbagliata qui in un modo che nessuno a valle coglierà.

Quindi la descrizione dello strumento lo dice: dividi per il totale delle persone del periodo che viene dallo strumento di riepilogo, mai per la somma delle righe. La correzione deve vivere dove il modello sta guardando, che è la descrizione dello strumento, non la documentazione che ha letto qualcun altro.

6. I filtri sono dimensioni, e c'è un solo interprete

Ogni nome di dimensione funziona anche come filtro, con lo stesso significato in tutti e due i posti:

breakdown  dimension=path  filters={"country":"EE","device":"mobile"}

Un solo interprete vuol dire che i due non possono essere in disaccordo: un filtro che il modello costruisce è esattamente un filtro che la dashboard potrebbe applicare, e una dimensione che ha scoperto chiedendo è una dimensione su cui può filtrare subito. C'è anche uno strumento che non restituisce altro che le dimensioni, i periodi e le regole di filtro accettate, così un modello può chiedere che cosa sia lecito invece di indovinare e riprovare.

L'angolo scomodo: per filtrare su un valore che non è mai stato registrato (visite senza campagna, per dire), si passa __none__. Una stringa vuota è indistinguibile da un parametro assente una volta che la richiesta è stata interpretata, quindi il valore vuoto deve viaggiare come qualcosa. Ogni API finisce per farsene crescere una; la mossa onesta è documentarla invece di fingere che la forma sia uniforme.

Che cosa dà tutto questo nell'insieme

In gran parte è un principio solo applicato più volte: una risposta ambigua è peggio di un rifiuto, perché la cosa che consuma la tua API risolverà l'ambiguità con sicurezza e ad alta voce.

Non è una particolarità di MCP. È solo che il consumatore solito (un programma con una persona che programma dietro) avrebbe lanciato un'eccezione, e questo invece scrive un paragrafo.

Il server di Skomi sta su mcp.skomi.com e non c'è niente da installare; l' API di interrogazione che legge è la stessa che può usare qualunque altra cosa.

I numeri che legge sono gli stessi che Analytics ti mostra, attraverso la stessa API di interrogazione; non c'è nessun secondo canale su cui il modello si possa sbagliare. Il server MCP definisce il termine se è nuovo per te.