MCP eller CLI er feil spørsmål. Spør hvor tilstanden bor.

Tobi Lütke sier at MCP mot CLI diskuteres på feil lag: begge fungerer så snart tilstanden bevares mellom stegene. Enig. For en synkronisering er den tilstanden integrasjonens egne poster, ikke en REPLs heap, og derfor lagrer Tajos MCP-verktøy ingenting i en sesjon.

mcp vs cli

Bør en AI-agent bruke MCP eller en CLI?

Lütke har rett i at MCP mot CLI er feil lag: begge fungerer så snart tilstand bevares mellom stegene, og bash vinner bare fordi filsystemet er den tilstanden. For en synkronisering er tilstanden likevel ikke en REPLs heap. Det er integrasjonens egne poster: en cursor, et ventende forslag, en lease, en plan. Derfor lagrer Tajos MCP-verktøy ingenting i en sesjon: hvilken som helst agent, fra hvilken som helst klient, fortsetter fra postene. Tajo kjører ingen kundekode i dag og har ingen REPL, og det sier vi rett ut nedenfor.

Les mer
På denne siden

Den 21. september 2026, kl. 00.33 UTC, publiserte Tobi Lütke dette: «MCP vs CLI for LLM-er er feil diskusjon. Folk debatterer på feil lag.» Den 23. oktober pekte han så på et nytt repository kalt dquickts. Det er en fork av QuickJS som kan sette et program på pause midt i en for-løkke, skrive hele heapen til bytes og gjenoppta det i en annen prosess. «Utmerket code mode-miljø», skrev han.

Han har rett om laget. Jeg vil først si meg enig med ham, med hans egne ord, og deretter snevre inn påstanden til den sier noe en synkronisering kan bruke.

Laget han mener

Her er argumentet slik han la det fram, som er den eneste rettferdige måten å sitere et argument man har tenkt å bøye på:

Begge fungerer utrolig godt så lenge de kjøres gjennom et REPL-lignende miljø som kan bevare tilstand. Det morsomme med CLI er at CLI-verktøy vanligvis brukes gjennom BASH, som tilfeldigvis er en REPL med varig tilstand (filsystemet), og derfor fungerer CLI mye bedre enn MCP. Men dette er ikke i nærheten av å være en iboende egenskap ved MCP. Det betyr bare at vi trenger bedre harnesses.

Det er riktig, og det forklarer en observasjon mange har gjort uten å forklare den. En agent som får en CLI, skriver en fil, leser den igjen tre steg senere, grepper i den, piper den. En agent som får en naken MCP-server, får ett resultat per kall og må bære alt annet i kontekstvinduet sitt. CLI-en vant ikke på protokoll. Den vant fordi bash kom med et sted å legge ting.

Veddemålet hans følger av dette. Noen bygger en innebyggbar motor i SQLite-stil som gjør bash, TypeScript eller tool calls om til én plan, sjekket før den kjører og bygget for varig kjøring. dquickts er et steg i den retningen. README-en beskriver en REPL «der funksjoner og variabler overlever omstarter». Den beskriver også et varig host call: «et skript kaller en vertsfunksjon der svaret kommer timer senere. Prosessen lagrer skriptet og avslutter.»

Så langt full enighet. Nå innsnevringen.

Hvilken tilstand, for hvilken jobb

«Bevare tilstand» skjuler et spørsmål: hvem sin tilstand, og i hvilken form? For en kodeagent er tilstanden repositoryet og skallet, og en REPL som overlever omstarter, er et godt svar. For en agent som bygger en synkronisering mellom to SaaS-systemer, er tilstanden den trenger mellom stegene, allerede definert av jobben. Fire poster bærer den, og ingen av dem er en variabel i et skript.

Cursoren. En synkronisering leser en kilde i vinduer og må vite hvor forrige vindu sluttet. I Tajos synkroniseringsmotor er det en lagret cursor per synkronisering og omfang, skrevet etter hver batch uten feilede poster, og neste kjøring fortsetter derfra. En planlagt kjøring lagrer startpunktet sitt før den gjør noe annet, slik at en første kjøring som feiler, lar den neste lese fra samme punkt. Prinsippet bak, send deltaet og aldri tabellen, står i forklaringen av reverse ETL. Jeg lar cursoren ligge der.

Det ventende forslaget. I Tajos connector-MCP-verktøy er en skriving to kall. brevo_propose validerer inndataene mot den deklarerte handlingen og registrerer et forslag. Ingenting blir sendt. Resultatet inneholder en forslags-ID og, når en person må bestemme, siden der vedkommende godkjenner det. brevo_apply kjører de godkjente skrivingene for den tilkoblingen i rekkefølge og stopper ved den første som fortsatt venter på en person. Posten imellom er tilstanden. Den inneholder inndataene og en digest av dem, slik at en godkjent skriving ikke kan endres mellom beslutningen og apply. Den inneholder ikke leverandørens svarverdier, fordi en append-only godkjenningspost ikke må bli et lager for personopplysninger.

Leasen. En kjøring av en synkronisering holder én eksklusiv lease i hele levetiden sin, med et eiertoken, en tidsgrense på femten minutter som standard, og fornyelse. En foreldet eier kan ikke frigi en nyere lease. Hvis lease-lageret ikke kan nås, nekter kjøringen å starte i stedet for å gjette. Hvilken worker som har kjøringen, er et faktum i leasen, ikke i minnet til noen prosess.

Planen. syncs.dryRun leser neste vindu fra cursoren, avgjør sett inn, oppdater, slett eller uendret per post og skriver nøyaktig én rad: planen, som oppbevares i én time. Den flytter ingen cursor og skriver ingenting til noen av systemene. syncs.apply skriver den planen én gang. Den nekter hvis cursoren har flyttet seg, eller hvis mer enn en tolerert andel av postene har endret seg i mellomtiden. En gjentakelse returnerer det første resultatet i stedet for å skrive på nytt.

Det er tilstanden mellom stegene: fire poster med ID-er, lesbare for enhver agent som har lov til å se dem.

Hvorfor MCP-siden ikke lagrer noe i en sesjon

Når tilstanden først ligger i postene, har en sesjon ingenting igjen å holde på. Derfor holder Tajos MCP-endepunkter ikke på noen. Hver forespørsel får en ny server og en transport uten sesjons-ID. Lesinger er én forespørsel per kall, med begrensede resultater slik at ett svar ikke kan oversvømme en tur. Skrivinger er først forslag, så apply, og forslagets ID er det du bærer med deg.

Protokollen beveget seg samme vei. Revisjon 2026-07-28 av MCP-spesifikasjonen fjernet sesjoner på protokollnivå og headeren Mcp-Session-Id fra Streamable HTTP-transporten. En server som trenger kontinuitet, lager nå sitt eget håndtak og får det tilbake som et vanlig argument. Det er nettopp det en forslags-ID er. Hva er en MCP-server dekker den revisjonen og hvordan et tool call skiller seg fra en varig synkronisering; denne teksten handler bare om hvor tilstanden havner.

Konsekvensen er den som betyr noe for integrasjonsarbeid. Si at en agent i Claude Code foreslår en skriving, og at sesjonen dens avsluttes. Personen godkjenner den en time senere. En agent i en annen klient, eller den samme i morgen, kaller apply med ID-en, og skrivingen lander, én gang. En agent som krasjer, mister kontekstvinduet sitt og ingenting annet. Ingenting ble satt på pause, så ingenting måtte gjenopptas.

Hva vi ikke har, og hva det koster

Den ærlige innvendingen kommer fra REPL-siden og lyder slik: «Greit, postene deres overlever. Men hvert kall leser på nytt det forrige kall allerede hadde. Et skript ville hentet kontaktene én gang, joinet dem, filtrert dem og returnert et tall. Agenten deres gjør fem kall og betaler for hvert.» Den innvendingen har rett om mekanismen, og vi har ikke målt prisen. Evalueringen som ville telle kall per oppgave med og uten medbrakt tilstand, er ikke bygget. Inntil den finnes, står påstanden om at call mode koster flere tokens, ubesvart.

Tre ting til må sies rett ut, fordi en leser som leser denne artikkelen ferdig, ikke skal forestille seg dem:

  • Ingen kode skrevet av kunder kjører i dag. Connector-lesingene er én forespørsel per kall, uten sandkasse i veien. Det finnes en code runner i stacken vår, og den er et internt verktøy. I produksjon er den slått av med mindre to separate flagg er satt, og den svarer bare administratorer. Hvert kall får en ny kontekst, bindinger som bare tillater lesing, og et budsjett på to sekunder for synkron kode. Dens egen header sier med store bokstaver at den er en korrekthetsgrense, ikke en sikkerhetsgrense.
  • Ingen REPL. Ingenting en agent beregner i ett kall, er tilgjengelig for det neste, med mindre det ble skrevet som en post med en ID.
  • Ingen varig agentsesjon. Klienten tar vare på samtalen sin. Tajo tar vare på forslagene, planene, cursorene og leasene. Det finnes ingen tredje ting.

Hva en varig motor er til for

Ingenting av dette er et argument mot dquickts. README-en lister opp hva snapshots er til for, og listen er god. La et inaktivt skript gå i dvale og frigjør minnet. Kjør en lang beregning i biter på den workeren som er ledig. Varmstart hver instans fra et snapshot i stedet for å gjenta oppsettet. Parker et skript på et host call der svaret kommer senere. For arbeidsområdet til en kodeagent, eller en notebook som skal overleve en omstart, er det riktig verktøy.

En synkronisering bør likevel ikke være avhengig av en, av tre grunner som README-en selv gir meg. For det første: «En sesjonsfil kan bare lastes inn i den qjs-builden som skrev den.» En heap skrevet av én motorversjon er en død fil etter en oppgradering; en cursor er en streng. For det andre: «Snapshots er klarert inndata, akkurat som bytekode», og «ondsinnede blir ikke oppdaget». En lagret heap er noe du må beskytte, ikke noe du kan gjennomgå; et forslag er en rad som en person leser før vedkommende godkjenner det. For det tredje inneholder et snapshot alt skriptet hadde lest, kundeposter inkludert. Postene ovenfor er bygget motsatt vei: planen lagrer digests, ikke verdier, og forslagsposten lagrer aldri leverandørens svar.

En av reglene vi bygger etter, er at deterministisk slår smart. En heap som gjenopptas midt i en løkke, er smart. En cursor, et forslag, en lease og en plan er kjedelige, kan inspiseres og er de samme på hver motor, i hver klient, etter hvert krasj.

Lütke stilte riktig spørsmål. Hvor bor tilstanden? For en synkronisering skulle svaret aldri ligge i prosessen. Det skulle alltid ligge i postene, fordi det er der personen som godkjenner skrivingen, må kunne lese den.

Ofte Stilte Spørsmål

Er MCP eller en CLI best for AI-agenter?

Ingen av dem i seg selv. Poenget Tobi Lütke kom med 21. september 2026 holder: begge fungerer så snart kallene går gjennom noe som bevarer tilstand mellom stegene. En CLI føles bedre i dag fordi bash lagrer tilstand i filsystemet. Forskjellen ligger ikke i protokollen, men i harnessen.

Hvilken tilstand trenger en agent mellom stegene i en synkronisering?

Integrasjonens egne poster: en cursor som sier hvor forrige kjøring stoppet, et forslag som venter på godkjenning fra en person, en lease som sier hvilken worker som har kjøringen, og en dry-run-plan som apply må stemme med. Ingenting av dette er en variabel i et skript. Det er rader med ID-er.

Hvorfor lagrer Tajos MCP-server ingenting i en sesjon?

Hver forespørsel får en ny server og en transport uten sesjons-ID. Alt det neste kallet trenger, er en post: en forslags-ID, en plan-ID, en cursor. Dermed kan en annen agent, i en annen klient, fortsette der den første stoppet, og en agent som krasjer, mister ingenting annet enn sitt eget kontekstvindu.

Kjører Tajo kode skrevet av agenter?

Ikke for kunder. Lesinger via connector-verktøyene er én forespørsel per kall, uten sandkasse. Den code runneren som finnes, er et internt verktøy bak to flagg, som bare administratorer kan nå, med skrivebeskyttede bindinger og en ny kontekst per kall. Den sier om seg selv at den ikke er en sikkerhetsgrense. Det finnes ingen REPL og ingen varig sesjon.

Hva er en varig JavaScript-motor som dquickts god for?

README-en nevner bruksområdene: la et inaktivt skript gå i dvale, dele en lang beregning opp på tvers av workers, varmstarte fra et snapshot og parkere et skript på et host call der svaret kommer timer senere. Alt sammen reelt. En synkronisering bør likevel ikke være avhengig av en: et snapshot kan bare lastes inn i den motorbuilden som skrev det, er ugjennomsiktig for en som gjennomgår det, og inneholder alle kundedata skriptet har lest.

Be om tidlig tilgang

Skriv inn fornavn og e-postadresse. Vi sender deg en lenke for å bekrefte forespørselen om tilgang til Tajo.

Skaff Brevo

Denne siden som Markdown: mcp-or-cli-is-the-wrong-question.md

Laster inn Markdown …
Åpne i Claude Åpne i ChatGPT