På denne siden
Den 8. juni 2026 ga Anthropic eierne av connectors i Claudes katalog et dashbord. Det viser aktive brukere, totalt antall verktøykall og plassering i katalogen. Det viser en «helsescore, feilrater og latens med ett blikk, med feil fordelt per verktøy så du kan finne ut hva som feiler».
Et år tidligere hadde Alex Volkov i Weights & Biases beskrevet det samme problemet fra den andre enden. Da agenten hans først kalte verktøy over MCP, «ble all den observabiliteten jeg er vant til når min egen kode kjører fra ende til ende, litt mørk». Medforedragsholderen hans, Benjamin Eckel i Dylibso, viste hvorfor. En ekstern MCP-server «dukker opp for oss i tracen som ett enkelt service-span» fordi den kjører utenfor kallerens kontroll.
Begge blikkene står utenfor serveren. Klienten ser ett ugjennomsiktig span. Katalogen ser totaler. Ingen av dem så hva serveren ble bedt om, hva den avviste og hva den berørte. Det gjorde bare serveren, så bare serveren kan skrive det ned.
Dette er hva Tajos MCP-servere skriver ned, lest ut av koden som kjører dem og beskrevet i dokumentasjonen. Hva en MCP-server er dekker protokollen. Dette dekker raden.
Én rad per kall
Et dashbord er havnefogdens blikk: skip inn, skip ut, hvilke som var forsinket. Forespørselsloggen er skipets egen loggbok, skrevet om bord mens det skjedde.
Hvert tools/call på Tajos arbeidsområdeserver og på hver connector-server ender i én rad. Raden skrives når svaret er bygget, uansett hva svaret var. Et kall som avvises fordi nøkkelen ikke får skrive, eller fordi endepunktet er skrivebeskyttet, får en rad som alle andre. Det gjør også et kall til et verktøy som ikke finnes.
Her er hva raden inneholder, og hva den nekter å inneholde.
| Felt | Beholdes | Beholdes ikke |
|---|---|---|
| Hvem | Id-en til API-nøkkelen som kalte. Et token fra en OAuth-innlogging er også en nøkkel. | Nøkkelens hemmelighet eller prefiks. |
| Hvor | Arbeidsområdeserveren eller en connector-server, med connector og profil. | |
| Verktøy | Navnet, når serveren har et verktøy med det navnet. | Ethvert annet navn klienten sender: det lagres som (unknown). |
| Effekt | read, write, send eller destructive: den tyngste effekten kallet kunne ha. | |
| Utfall | ok, eller en kort feilkode i snake_case som unknown_tool. | Feiltekst: alt som ikke er en kode, lagres som tool_error. |
| Varighet og størrelse | Millisekunder fra ankomst til svar, og bytene kalleren mottok. | |
| Trace-id | Forespørselens id, fra en x-request-id-, x-trace-id- eller W3C traceparent-header, ellers en ny. | En headerverdi som ikke er hex eller en UUID. |
| Poster | Opptil 20 id-er til poster kallet nevnte eller opprettet: en kontakt, en melding, en godkjenning. | Selve postene. |
| Bodies | Argumentene og svaret, renset, maks 4 KB hver. | Hemmeligheter, fritekst og personopplysningene listet nedenfor. |
Tre av disse avvisningene handler om klienten, ikke om dataene. Et verktøynavn, en feilkode og en trace-id kommer alle utenfra. Hver av dem lagres bare i en form serveren kan gå god for. En logg som lagrer hva enn kalleren sendte, kan fylles med hva som helst, også en kundes e-postadresse i kolonnen for verktøynavn.
Benjamin Eckels åpne spørsmål på slutten av foredraget var hvordan en collector vet «at det er et MCP-verktøykall som har en feil». På én server er svaret en kolonne. ok er usann, og koden sier hvilken feil.
Argumentene: en regel vi snudde
Innvendingen er rimelig, og den er vår egen. Logger du argumentene, har du bygget en ekstra kopi av kundenes data, utenfor enhver forespørsel om sletting. Fram til 18. oktober 2026 var det vår egen regel. Loggen beholdt verken argumentene eller svarets body, fordi begge kan inneholde personopplysninger.
Det var feil avveining. En forespørselslogg som ikke kan vise hva som ble sendt og hva som kom tilbake, kan ikke svare på det ene spørsmålet den finnes for: hvorfor feilet kallet til agenten min?
Så bodies beholdes, og grunnen til at de ikke ble det, møtes på en annen måte. Hver body går gjennom én deterministisk gjennomgang før den settes i kø.
- Ingen hemmelighet overlever. Et felt der navnet sier token, password, secret, signature, cookie, session eller rundt tjue lignende ord, beholder bare
[secret]. Det samme gjør en verdi i en kjent form for påloggingsinformasjon, uansett hvor den står. Det dekker bearer-tokens, JWT-er, private nøkler, URL-er med passord og enhver ugjennomsiktig rekke på 40 eller flere nøkkeltegn. - Prosa beholdes ikke i det hele tatt. Et felt med navnet
html,text,body,markdownellercontentbeholder bare[redacted]. Prosa inneholder navn og gateadresser som ingen mønstre finner. - Personopplysninger maskeres for godt. Et felt der navnet markerer personopplysninger, som en e-post, et telefonnummer, et navn, en adresse eller et notat, beholder bare
[redacted]. En e-postadresse, et telefonnummer, et kort eller en IBAN inne i enhver annen streng maskeres. - Bodies holdes små. Strenger kuttes ved 512 tegn, lister ved 20 elementer, objekter ved 50 nøkler og nesting ved 6 nivåer. Hele bodyen kuttes ved 4 KB og merkes som avkortet.
To typer kall beholder mindre. Kontakter som en persons chat har hentet inn fra en annen app, logges som en markør og et antall, aldri som en body. Et kall som leser loggen, beholder ikke noe svar, siden svaret bare ville vært andre loggrader.
Så prisen. Maskeringen er permanent, ikke kryptering med en nøkkel noen har. Loggen kan ikke vise deg e-postadressen som returnerte. Den viser deg kontaktens id, og kontakten er, under sine egne regler, ett kall unna. Loggen ligger utenfor arbeidsområdets slettesveip, og derfor kan ikke maskeringen vente på en slik.
Hvorfor loggen ikke er revisjonssporet
Tajo har allerede en revisjonslogg som det bare kan legges til i. Hver rad bærer en hash av raden før, så en endring hvor som helst bryter kjeden. Hvorfor ikke skrive verktøykall der?
Fordi kjeden har en pris. Hvert tillegg tar en lås per arbeidsområde og leser forrige rad for å forlenge kjeden. Å skrive hver lesing gjennom den ville stille et arbeidsområdes kall i kø bak én lås. Det ville også begrave tilstandsendringene revisjonsloggen finnes for å vise, under tusenvis av lesinger.
Så de to registrene deles etter spørsmål. Forespørselsloggen sier hva en agent ba om og hva som skjedde, i 30 dager. Revisjonsloggen sier hva som endret seg, hvem som tillot det, og om noen har endret registeret siden.
Godkjenninger hører til på revisjonssiden. På Tajos connector-profiler klargjør et propose-verktøy en deklarert handling, og et apply-verktøy kjører den når den er godkjent. Ingenting når leverandøren før forslaget er godkjent, av en person eller av en regel arbeidsområdet har slått på. Raden til propose-kallet nevner godkjenningen det opprettet blant postene sine. Selve godkjenningen er bundet til en SHA-256-digest av nøyaktig input. En godkjenning dekker nøyaktig den input du så: endre ett felt, så stemmer den ikke lenger. Når skrivingen lander, beholder revisjonsraden input-digesten og en digest av leverandørens svar, aldri svarets verdier.
Hva raden koster
En rad er en skriving i databasen, og en skriving har en commit. Fram til 8. oktober 2026 ventet hvert kall på sin egen commit før svaret, omtrent 2,6 ms på vår host. For én klient var det en fjerdedel av et lite verktøykall.
Nå bygges raden når kallet avsluttes, og settes i kø. En flush skriver køen maks 100 ms senere, én transaksjon per arbeidsområde, så mange kall deler én commit. Ingenting ved en rad endres, heller ikke tidsstempelet. Hvis 5 000 rader venter, venter neste kall på flushen i stedet for å droppe en rad.
Registrering får aldri et kall til å feile. En skriving som feiler, prøves på nytt rad for rad, og logges og droppes deretter. Det et hardt kill av prosessen mister, er det som lå i køen: maks de siste 100 ms med kall.
Lese det tilbake
logs.list returnerer arbeidsområdets kall, nyeste først, uten bodies. Den filtrerer på tid, på utfall eller HTTP-status, på kilde (MCP eller REST-API-et, som deler tabellen) og på API-nøkkel. Den viser 50 per side som standard, opptil 100. logs.get returnerer ett kall med nøkkelens nåværende navn, status, varighet, den rensede forespørselen og det rensede svaret og postene det berørte.
Begge leser bare kallerens eget arbeidsområde. En rad-id fra et annet arbeidsområde blir ikke funnet.
En verktøyfeil bærer forespørselens id. Når serveren laget den id-en, eller den kom som hex eller en UUID, er den radens trace-id. En agent som fikk en feil, kan gi en person den ene verdien som navngir kallet.
Hva som er bygget, og hva som ikke er det
Bygget, når dette skrives:
- Én rad per
tools/callpå arbeidsområdeserveren og hver connector-server, også avviste og ukjente kall. - Nøkkel-id, server, connector, profil, verktøy, effekt, utfall, varighet, resultatstørrelse, trace-id og opptil 20 berørte poster.
- Rensede argumenter og svar, maks 4 KB hver.
logs.listoglogs.get, avgrenset til arbeidsområdet, med 30 dagers lagring.- Godkjenninger bundet til en input-digest, og utførte skrivinger registrert med input- og output-digester i den hashkjedede revisjonsloggen.
Ikke bygget, og verdt å vite før du stoler på det:
- Ingen helsescore, ingen graf per verktøy og ikke noe varsel ved økende feilrate. En oversikt per verktøy over kall, feil og tregeste varigheter finnes for Tajos driftsfolk, ikke på noen flate du kan åpne.
logs.listkan ikke filtrere på verktøynavn eller trace-id. For å finne raden bak en forespørsels-id må du snevre inn på tid og nøkkel.- Rader eksporteres ikke til et tracingsystem du selv kjører. Trace-id-en leses bare fra HTTP-headere, ikke fra MCP-feltet
_metasom foredraget brukte til å bære trace-kontekst. - Etter 30 dager slettes en rad. Det finnes ikke noe arkiv eller noen eksport.
- Det som ble maskert, kan ikke gjenopprettes fra loggen.
- Et hardt kill kan miste de siste 100 ms med rader.
Foredraget sluttet med at observabilitet i MCP «ikke er jevnt fordelt». Et dashbord lenger nedstrøms kan ikke rette på det, og det kan heller ikke en trace som stopper ved serverens dør. En synkronisering som feiler, trenger sitt eget register; det gjør også hvert kall som ba om ett.
Serveren så kallet. Ingen andre gjorde det. Skriv det ned der.
Ofte Stilte Spørsmål
Hva bør en MCP-server logge for hvert verktøykall?
Én rad per kall, skrevet av serveren: hvilken API-nøkkel som kalte, hvilken server og connector, verktøyets navn hvis verktøyet finnes, effektklassen, ok eller en kort feilkode, varigheten, størrelsen på resultatet, forespørselens trace-id, id-ene til postene kallet nevnte eller opprettet, og argumentene og svaret med hemmeligheter og personopplysninger renset ut. Tajos MCP-servere skriver den raden for hvert tools/call, også avviste kall, og beholder den i 30 dager.
Bør en MCP-forespørselslogg beholde verktøyets argumenter?
Ja, renset. En logg uten argumentene kan ikke si hvorfor et kall feilet. Tajo beholdt ingen bodies før 18. oktober 2026 og beholder nå begge, etter én deterministisk gjennomgang: hemmeligheter blir [secret], fritekst og personfelt blir [redacted], e-postadresser, telefonnumre, kort og IBAN-er maskeres, og hver body kuttes ved 4 KB. Maskeringen er permanent, så loggen peker på posten med id i stedet for å kopiere den.
Hvorfor holde loggen over MCP-verktøykall atskilt fra revisjonsloggen?
Fordi de svarer på ulike spørsmål til ulik pris. Tajos revisjonslogg er en hashkjede av tilstandsendringer, og hvert tillegg tar en lås per arbeidsområde for å forlenge kjeden. Å skrive hver lesing gjennom den ville stille et arbeidsområdes kall i kø bak den låsen og begrave endringene blant lesingene. Verktøykall går til en egen tabell uten kjede som beholdes i 30 dager; skrivingene et kall klargjør, etterlater fortsatt egne revisjonsrader.