In questa pagina
L’8 giugno 2026 Anthropic ha dato ai proprietari dei connettori nella directory di Claude una dashboard. Mostra gli utenti attivi, il totale delle chiamate agli strumenti e la posizione nella directory. Mostra “punteggio di salute, tassi di errore e latenza a colpo d’occhio, con una ripartizione degli errori per strumento per individuare cosa sta fallendo”.
Un anno prima, Alex Volkov di Weights & Biases aveva descritto lo stesso problema dall’altra parte. Una volta che il suo agente chiamava strumenti tramite MCP, “tutta quell’osservabilità a cui sono abituato quando il mio codice gira da cima a fondo si è un po’ spenta”. Il suo co-relatore, Benjamin Eckel di Dylibso, ha mostrato perché. Un server MCP remoto “ci appare nella trace come un unico span di servizio”, perché gira fuori dal controllo di chi chiama.
Entrambe le prospettive stanno fuori dal server. Il client vede uno span opaco. La directory vede dei totali. Nessuna delle due ha visto cosa è stato chiesto al server, cosa ha rifiutato e cosa ha toccato. Solo il server l’ha visto, quindi solo il server può annotarlo.
Questo è ciò che annotano i server MCP di Tajo, letto dal codice che li fa girare e descritto nella documentazione. Cos’è un server MCP spiega il protocollo. Qui si parla della riga.
Una riga per chiamata
Una dashboard è la vista del capitano di porto: navi che entrano, navi che escono, quali erano in ritardo. Il log delle richieste è il giornale di bordo della nave stessa, scritto a bordo, sul momento.
Ogni tools/call sul server del workspace di Tajo e su ogni server di connettore finisce in una riga. La riga viene scritta dopo che la risposta è stata costruita, qualunque fosse la risposta. Una chiamata rifiutata perché la chiave non ha il permesso di scrivere, o perché l’endpoint è in sola lettura, riceve una riga come tutte le altre. Lo stesso vale per una chiamata a uno strumento che non esiste.
Ecco cosa contiene la riga, e cosa si rifiuta di contenere.
| Campo | Conservato | Non conservato |
|---|---|---|
| Chi | L’ID della chiave API che ha chiamato. Anche un token ottenuto da un accesso OAuth è una chiave. | Il segreto della chiave o il suo prefisso. |
| Dove | Il server del workspace o un server di connettore, con il suo connettore e il suo profilo. | |
| Strumento | Il nome, quando il server ha uno strumento con quel nome. | Qualsiasi altro nome inviato dal client: viene memorizzato come (unknown). |
| Effetto | read, write, send o destructive: l’effetto più pesante che la chiamata potrebbe avere. | |
| Esito | ok, o un breve codice di errore in snake_case come unknown_tool. | Il testo degli errori: tutto ciò che non è un codice viene memorizzato come tool_error. |
| Durata e dimensione | I millisecondi dall’arrivo alla risposta, e i byte ricevuti da chi ha chiamato. | |
| ID di trace | L’ID della richiesta, da un header x-request-id, x-trace-id o W3C traceparent, altrimenti uno nuovo. | Un valore di header che non è esadecimale né un UUID. |
| Record | Fino a 20 ID dei record che la chiamata ha nominato o prodotto: un contatto, un messaggio, un’approvazione. | I record stessi. |
| Body | Gli argomenti e la risposta, ripuliti, al massimo 4 KB ciascuno. | Segreti, testo libero e i dati personali elencati più sotto. |
Tre di questi rifiuti riguardano il client, non i dati. Un nome di strumento, un codice di errore e un ID di trace arrivano tutti dall’esterno. Ognuno viene conservato solo in una forma di cui il server può rispondere. Un log che memorizza qualsiasi cosa abbia inviato chi chiama può essere riempito di tutto, compreso l’indirizzo email di un cliente nella colonna del nome dello strumento.
La domanda aperta di Benjamin Eckel alla fine dell’intervento era come fa un collector a sapere “che è una chiamata a uno strumento MCP con un errore”. Su un singolo server la risposta è una colonna. ok è false, e il codice dice quale errore.
Gli argomenti: una regola che abbiamo ribaltato
L’obiezione è giusta, ed è la nostra. Registra gli argomenti e avrai costruito una seconda copia dei dati dei tuoi clienti, fuori dalla portata di ogni richiesta di cancellazione. Fino al 18 ottobre 2026 era la nostra stessa regola. Il log non conservava né gli argomenti né il body del risultato, perché entrambi possono contenere dati personali.
Era il compromesso sbagliato. Un log delle richieste che non può mostrare cosa è stato inviato e cosa è tornato non può rispondere all’unica domanda per cui esiste: perché la chiamata del mio agente è fallita?
Quindi i body vengono conservati, e il motivo per cui non lo erano viene affrontato in un altro modo. Ogni body passa per un passaggio deterministico prima di essere messo in coda.
- Nessun segreto sopravvive. Un campo il cui nome indica token, password, segreto, firma, cookie, sessione o una ventina di parole simili conserva solo
[secret]. Lo stesso vale per un valore con una forma nota di credenziale, ovunque compaia. Questo copre bearer token, JWT, chiavi private, URL con una password e qualsiasi sequenza opaca di 40 o più caratteri di chiave. - La prosa non viene conservata affatto. Un campo chiamato
html,text,body,markdownocontentconserva solo[redacted]. La prosa contiene nomi e indirizzi postali che nessun pattern trova. - I dati personali vengono mascherati per sempre. Un campo il cui nome indica un dato personale, come un’email, un telefono, un nome, un indirizzo o una nota, conserva solo
[redacted]. Un indirizzo email, un numero di telefono, una carta o un IBAN all’interno di qualsiasi altra stringa viene mascherato. - I body restano piccoli. Le stringhe vengono tagliate a 512 caratteri, le liste a 20 elementi, gli oggetti a 50 chiavi e l’annidamento a 6 livelli. L’intero body viene tagliato a 4 KB e segnato come troncato.
Due tipi di chiamata conservano meno. I contatti che la chat di una persona ha portato da un’altra app vengono registrati come un marcatore e un conteggio, mai come body. Una chiamata che legge il log non conserva alcuna risposta, perché la risposta sarebbe solo altre righe del log.
Ora il costo. Il mascheramento è permanente, non una cifratura con una chiave in mano a qualcuno. Il log non può mostrarti l’indirizzo email che è rimbalzato. Ti mostra l’ID del contatto, e il contatto, con le sue regole, è a una chiamata di distanza. Il log sta fuori dalla pulizia di cancellazione del workspace, ed è per questo che il mascheramento non può aspettarla.
Perché il log non è l’audit trail
Tajo ha già un log di audit in sola aggiunta. Ogni riga porta un hash della riga precedente, quindi una modifica in qualsiasi punto spezza la catena. Perché non scriverci le chiamate agli strumenti?
Perché la catena ha un prezzo. Ogni aggiunta prende un lock per workspace e legge la riga precedente per estendere la catena. Scrivere lì ogni lettura metterebbe in fila le chiamate di un workspace dietro un unico lock. Seppellirebbe inoltre sotto migliaia di letture i cambi di stato che il log di audit esiste per mostrare.
Quindi i due registri si dividono le domande. Il log delle richieste dice cosa ha chiesto un agente e cosa è successo, per 30 giorni. Il log di audit dice cosa è cambiato, chi l’ha permesso e se qualcuno ha modificato il registro da allora.
Le approvazioni stanno dalla parte dell’audit. Sui profili di connettore di Tajo, uno strumento propose prepara un’azione dichiarata e uno strumento apply la esegue una volta approvata. Niente raggiunge il provider finché la proposta non è approvata, da una persona o da una regola che il workspace ha attivato. La riga della chiamata propose indica, tra i suoi record, l’approvazione che ha creato. L’approvazione stessa è legata a un digest SHA-256 dell’input esatto. Un’approvazione copre l’input esatto che hai visto: cambia un campo e non corrisponde più. Quando la scrittura va a segno, la sua riga di audit conserva il digest dell’input e un digest della risposta del provider, mai i valori della risposta.
Quanto costa la riga
Una riga è una scrittura nel database, e una scrittura ha un commit. Fino all’8 ottobre 2026 ogni chiamata aspettava il proprio commit prima della risposta, circa 2,6 ms sul nostro host. Per un client, era un quarto di una piccola chiamata a uno strumento.
Ora la riga viene costruita quando la chiamata termina e messa in coda. Un flush scrive la coda al massimo 100 ms dopo, una transazione per workspace, così molte chiamate condividono un solo commit. Niente di una riga cambia, nemmeno il suo timestamp. Se 5.000 righe sono in attesa, la chiamata successiva aspetta il flush invece di scartare una riga.
La registrazione non fa mai fallire una chiamata. Una scrittura che fallisce viene ritentata riga per riga, poi registrata e scartata. Quello che un’interruzione forzata del processo perde è ciò che si trovava in coda: al massimo gli ultimi 100 ms di chiamate.
Rileggere il log
logs.list restituisce le chiamate del workspace, dalla più recente, senza body. Filtra per tempo, per esito o stato HTTP, per origine (MCP o l’API REST, che condivide la tabella) e per chiave API. Pagina 50 alla volta per impostazione predefinita, fino a 100. logs.get restituisce una chiamata con il nome attuale della chiave, il suo stato, la sua durata, la richiesta e la risposta ripulite e i record che ha toccato.
Entrambi leggono solo il workspace di chi chiama. L’ID di una riga di un altro workspace non viene trovato.
Un errore di uno strumento porta l’ID della richiesta. Quando il server ha creato quell’ID, o è arrivato come esadecimale o UUID, è l’ID di trace della riga. Un agente che ha ricevuto un errore può passare a una persona l’unico valore che identifica la chiamata.
Cosa è costruito, e cosa no
Costruito, al momento in cui scriviamo:
- Una riga per ogni
tools/callsul server del workspace e su ogni server di connettore, chiamate rifiutate e sconosciute comprese. - ID della chiave, server, connettore, profilo, strumento, effetto, esito, durata, dimensione del risultato, ID di trace e fino a 20 record toccati.
- Argomenti e risposte ripuliti, al massimo 4 KB ciascuno.
logs.listelogs.get, limitati al workspace, con 30 giorni di conservazione.- Approvazioni legate a un digest dell’input, e scritture applicate registrate con i digest di input e output nel log di audit con catena di hash.
Non costruito, ed è bene saperlo prima di farci affidamento:
- Nessun punteggio di salute, nessun grafico per strumento e nessun avviso su un tasso di errore in crescita. Un riepilogo per strumento di chiamate, errori e durate più lente esiste per gli operatori di Tajo, non su una superficie che tu possa aprire.
logs.listnon può filtrare per nome dello strumento né per ID di trace. Per trovare la riga dietro un ID di richiesta, restringi per tempo e chiave.- Le righe non vengono esportate verso un sistema di tracing che gestisci tu. L’ID di trace viene letto solo dagli header HTTP, non dal campo MCP
_metache l’intervento usava per trasportare il contesto di trace. - Dopo 30 giorni una riga viene eliminata. Non ci sono archivio né esportazione.
- Ciò che è stato mascherato non si può recuperare dal log.
- Un’interruzione forzata può perdere gli ultimi 100 ms di righe.
L’intervento si chiudeva sull’osservabilità in MCP “non distribuita in modo uniforme”. Una dashboard a valle non può rimediare, e nemmeno una trace che si ferma alla porta del server. Una sincronizzazione che fallisce ha bisogno di una propria registrazione; così come ogni chiamata che ne ha chiesta una.
Il server ha visto la chiamata. Nessun altro. Annotala lì.
Domande frequenti
Cosa dovrebbe registrare un server MCP per ogni chiamata a uno strumento?
Una riga per chiamata, scritta dal server: quale chiave API ha chiamato, quale server e quale connettore, il nome dello strumento se lo strumento esiste, la sua classe di effetto, ok o un breve codice di errore, la durata, la dimensione del risultato, l’ID di trace della richiesta, gli ID dei record che la chiamata ha nominato o prodotto, e gli argomenti e la risposta ripuliti da segreti e dati personali. I server MCP di Tajo scrivono questa riga per ogni tools/call, chiamate rifiutate comprese, e la conservano 30 giorni.
Un log delle richieste MCP dovrebbe conservare gli argomenti degli strumenti?
Sì, ripuliti. Un log senza gli argomenti non può dire perché una chiamata è fallita. Tajo non conservava alcun body fino al 18 ottobre 2026 e ora li conserva entrambi, dopo un passaggio deterministico: i segreti diventano [secret], il testo libero e i campi personali diventano [redacted], email, telefoni, carte e IBAN vengono mascherati, e ogni body viene tagliato a 4 KB. Il mascheramento è permanente, quindi il log rimanda al record tramite il suo ID invece di copiarlo.
Perché tenere il log delle chiamate agli strumenti MCP separato dal log di audit?
Perché rispondono a domande diverse con costi diversi. Il log di audit di Tajo è una catena di hash dei cambi di stato, e ogni aggiunta prende un lock per workspace per estendere la catena. Scrivere lì ogni lettura metterebbe in coda le chiamate di un workspace dietro quel lock e seppellirebbe i cambiamenti tra le letture. Le chiamate agli strumenti vanno in una tabella separata, senza catena, conservata 30 giorni; le scritture che una chiamata prepara lasciano comunque le proprie righe di audit.