Na ovoj stranici
Tobi Lütke je 21. rujna 2026. u 00:33 UTC objavio ovo: „MCP vs CLI za LLM-ove pogrešna je rasprava. Ljudi raspravljaju na pogrešnoj razini.“ Zatim je 23. listopada uputio na novi repozitorij dquickts. Riječ je o forku projekta QuickJS koji može pauzirati program usred petlje for, zapisati cijelu njegovu hrpu u bajtove i nastaviti ga u drugom procesu. „Izvrsno okruženje za code mode“, napisao je.
U pogledu razine ima pravo. Želim se najprije složiti s njim, njegovim vlastitim riječima, a zatim suziti tvrdnju dok ne kaže nešto što sinkronizacija može iskoristiti.
Razina koju ima na umu
Evo argumenta onako kako ga je iznio, što je jedini pošten način da se citira argument koji se namjerava saviti:
Oboje radi nevjerojatno dobro dok god se pokreće kroz okruženje nalik REPL-u koje može čuvati stanje. Smiješno je kod CLI-ja to što se CLI alatima obično pristupa kroz BASH, koji je slučajno REPL s trajnim stanjem (datotečnim sustavom), pa zato CLI radi puno bolje od MCP-a. Ali to ni izdaleka nije svojstvo svojstveno MCP-u. To samo znači da trebamo bolja izvršna okruženja.
To je točno i objašnjava opažanje koje su mnogi iznijeli, a da ga nisu objasnili. Agent koji dobije CLI zapiše datoteku, tri koraka kasnije je pročita, pretraži je grepom, provuče je kroz cijev. Agent koji dobije goli MCP poslužitelj dobiva jedan rezultat po pozivu i sve ostalo mora nositi u svom kontekstnom prozoru. CLI nije pobijedio protokolom. Pobijedio je jer je bash došao s mjestom gdje se stvari mogu odložiti.
Iz toga slijedi njegova oklada. Netko izgradi ugradivi pogon po uzoru na SQLite koji bash, TypeScript ili pozive alata pretvara u jedan plan, provjeren prije pokretanja i izgrađen za trajno (durable) izvršavanje. dquickts je korak prema tome. Njegov README opisuje REPL „čije funkcije i varijable preživljavaju ponovna pokretanja“. Opisuje i trajni poziv domaćina: „skripta poziva funkciju domaćina čiji odgovor stiže satima kasnije. Proces sprema skriptu i izlazi.“
Zasad potpuno slaganje. Sada sužavanje.
Koje stanje, za koji posao
„Čuvati stanje“ skriva pitanje: čije stanje i u kojem obliku? Za agenta koji programira stanje su repozitorij i ljuska, a REPL koji preživljava ponovna pokretanja dobar je odgovor. Za agenta koji gradi sinkronizaciju između dvaju SaaS sustava stanje koje mu treba između koraka već je određeno samim poslom. Nose ga četiri zapisa i nijedan od njih nije varijabla u skripti.
Kursor. Sinkronizacija čita izvor u prozorima i mora znati gdje je završio posljednji prozor. U sinkronizacijskom pogonu Tajo to je pohranjeni kursor po sinkronizaciji i opsegu, zapisan nakon svake serije bez ijednog neuspjelog zapisa, a sljedeće pokretanje nastavlja od njega. Zakazano pokretanje sprema svoju početnu točku prije nego što učini bilo što drugo, pa prvo pokretanje koje ne uspije ostavlja sljedećem čitanje s iste točke. Načelo iza toga, slati razliku, a nikad tablicu, nalazi se u objašnjenju reverse ETL-a. Kursor ću ostaviti tamo.
Prijedlog na čekanju. Na MCP alatima konektora Tajo upis se sastoji od dva poziva. brevo_propose provjerava ulaz prema deklariranoj radnji i bilježi prijedlog. Ništa se ne šalje. Rezultat nosi id prijedloga i, kad mora odlučiti čovjek, stranicu na kojoj ga odobrava. brevo_apply redom izvršava odobrene upise te veze i zaustavlja se na prvom koji još čeka na čovjeka. Zapis između toga je stanje. Sadrži ulaz i njegov sažetak (digest), pa se odobreni upis ne može izmijeniti između odluke i izvršenja. Ne sadrži vrijednosti iz odgovora pružatelja, jer se zapis odobrenja u koji se samo dodaje ne smije pretvoriti u spremište osobnih podataka.
Zaključavanje (lease). Pokretanje sinkronizacije tijekom cijelog svog trajanja drži jedno isključivo zaključavanje, s tokenom vlasnika, vremenskim ograničenjem od zadanih petnaest minuta i obnavljanjem. Zastarjeli vlasnik ne može otpustiti novije zaključavanje. Ako spremište zaključavanja nije dostupno, pokretanje odbija započeti umjesto da nagađa. Koji radnik (worker) drži pokretanje, činjenica je zapisana u zaključavanju, a ne u memoriji bilo kojeg procesa.
Plan. syncs.dryRun čita sljedeći prozor od kursora, za svaki zapis utvrđuje umetanje, ažuriranje, brisanje ili bez promjene i zapisuje točno jedan redak: plan, koji se čuva sat vremena. Ne pomiče nijedan kursor i ništa ne upisuje ni u jedan od dvaju sustava. syncs.apply upisuje taj plan jednom. Odbija ako se kursor pomaknuo ili ako se u međuvremenu promijenio veći udio zapisa od dopuštenog. Ponovljeni poziv vraća prvi rezultat umjesto da ponovno upisuje.
To je stanje između koraka: četiri zapisa s id-ovima, čitljiva svakom agentu kojem je dopušteno da ih vidi.
Zašto MCP strana ne čuva ništa u sesiji
Kad je stanje u zapisima, sesija više nema što čuvati. Zato MCP krajnje točke Tajo ne čuvaju sesiju. Svaki zahtjev dobiva svjež poslužitelj i transport bez id-a sesije. Čitanja su jedan zahtjev po pozivu, s ograničenim rezultatima kako jedan odgovor ne bi mogao preplaviti potez. Upisi su prijedlog, pa izvršenje, a ono što nosite dalje jest id prijedloga.
Protokol se pomaknuo u istom smjeru. Revizija MCP specifikacije od 2026-07-28 uklonila je sesije na razini protokola i zaglavlje Mcp-Session-Id iz transporta Streamable HTTP. Poslužitelj kojem treba kontinuitet sada sam stvara vlastiti identifikator i dobiva ga natrag kao običan argument. Upravo je to id prijedloga. Što je MCP poslužitelj obrađuje tu reviziju i po čemu se poziv alata razlikuje od trajne sinkronizacije; ovaj tekst govori samo o tome kamo ide stanje.
Posljedica je ona koja je važna za integracijski rad. Recimo da agent u Claude Code predloži upis i njegova sesija završi. Čovjek ga odobri sat vremena kasnije. Agent u drugom klijentu, ili isti sutra, pozove apply s id-om i upis se izvrši, jednom. Srušeni agent gubi svoj kontekstni prozor i ništa drugo. Ništa nije bilo obustavljeno, pa ništa nije trebalo nastavljati.
Što nemamo i koliko to košta
Pošten prigovor dolazi sa strane REPL-a i glasi ovako: „Dobro, vaši zapisi preživljavaju. Ali svaki poziv ponovno čita ono što je prethodni poziv već imao. Skripta bi kontakte dohvatila jednom, spojila ih, filtrirala i vratila broj. Vaš agent napravi pet poziva i plaća svaki.“ Taj je prigovor u pravu što se tiče mehanizma, a cijenu nismo izmjerili. Evaluacija koja bi brojala pozive po zadatku s prenesenim stanjem i bez njega nije izgrađena. Dok ne bude, tvrdnja da način rada s pozivima troši više tokena ostaje bez odgovora.
Još tri stvari treba reći otvoreno, jer čitatelj koji dovrši ovaj članak ne bi smio zamišljati da postoje:
- Danas se ne izvršava nikakav kod koji su napisali korisnici. Čitanja preko konektora jedan su zahtjev po pozivu, bez sandboxa na putu. U našem stogu postoji izvršitelj koda i to je interni alat. U produkciji je isključen osim ako nisu postavljene dvije zasebne zastavice i odgovara samo administratorima. Svaki poziv dobiva svjež kontekst, vezanja koja dopuštaju samo čitanje i proračun od dvije sekunde za sinkroni kod. Njegovo vlastito zaglavlje velikim slovima kaže da je granica ispravnosti, a ne sigurnosti.
- Nema REPL-a. Ništa što agent izračuna u jednom pozivu nije dostupno u sljedećem, osim ako nije spremljeno kao zapis s id-om.
- Nema trajne sesije agenta. Klijent čuva svoj razgovor. Tajo čuva prijedloge, planove, kursore i zaključavanja. Treće stvari nema.
Čemu služi trajni pogon
Ništa od ovoga nije argument protiv dquickts. Njegov README navodi čemu služe snimke (snapshots) i taj je popis dobar. Uspavati neaktivnu skriptu i osloboditi njezinu memoriju. Izvoditi dug izračun u odsječcima na bilo kojem slobodnom radniku. Toplo pokretati svaku instancu iz snimke umjesto ponavljanja pripreme. Parkirati skriptu na pozivu domaćina čiji odgovor stiže kasnije. Za radni prostor agenta koji programira ili za bilježnicu (notebook) koja treba preživjeti ponovno pokretanje, to je pravi alat.
Sinkronizacija ipak ne bi trebala ovisiti o njemu, iz tri razloga koja mi daje sam README. Prvo: „Datoteka sesije učitava se samo u qjs build koji ju je zapisao.“ Hrpa koju je zapisala jedna verzija pogona nakon nadogradnje je mrtva datoteka; kursor je niz znakova. Drugo: „Snimke su pouzdan ulaz, poput bajtnog koda“ i „zlonamjerne se ne otkrivaju“. Spremljenu hrpu morate štititi, pregledati je ne možete; prijedlog je redak koji čovjek pročita prije nego što ga odobri. Treće, snimka sadrži sve što je skripta pročitala, uključujući zapise korisnika. Gore opisani zapisi izgrađeni su obrnuto: plan sprema sažetke, a ne vrijednosti, a zapis prijedloga nikad ne sprema odgovor pružatelja.
Jedno od pravila po kojima gradimo kaže da determinističko pobjeđuje pametno. Hrpa koja nastavlja usred petlje je pametna. Kursor, prijedlog, zaključavanje i plan su dosadni, mogu se pregledati i isti su na svakom pogonu, u svakom klijentu, nakon svakog rušenja.
Lütke je postavio pravo pitanje. Gdje živi stanje? Kod sinkronizacije odgovor nikada nije mogao biti u procesu. Uvijek je morao biti u zapisima, jer ga upravo ondje mora moći pročitati čovjek koji odobrava upis.
Često postavljana pitanja
Je li za AI agente bolji MCP ili CLI?
Sam po sebi nijedan. Opaska Tobija Lütkea od 21. rujna 2026. vrijedi: oboje radi čim pozivi prolaze kroz nešto što čuva stanje između koraka. CLI se danas čini boljim jer bash čuva stanje u datotečnom sustavu. Razlika nije u protokolu, nego u izvršnom okruženju.
Koje stanje agentu treba između koraka sinkronizacije?
Vlastiti zapisi integracije: kursor koji kaže gdje je stalo posljednje pokretanje, prijedlog koji čeka odobrenje čovjeka, zaključavanje (lease) koje kaže koji radnik drži pokretanje i plan probnog pokretanja (dry run) s kojim se izvršenje mora podudarati. Ništa od toga nije varijabla u skripti. To su retci s id-ovima.
Zašto MCP poslužitelj Tajo ne čuva ništa u sesiji?
Svaki zahtjev dobiva svjež poslužitelj i transport bez id-a sesije. Sve što sljedećem pozivu treba jest zapis: id prijedloga, id plana, kursor. Tako drugi agent, u drugom klijentu, može nastaviti tamo gdje je prvi stao, a srušeni agent ne gubi ništa osim vlastitog kontekstnog prozora.
Izvršava li Tajo kod koji su napisali agenti?
Za korisnike ne. Čitanja na alatima konektora jedan su zahtjev po pozivu, bez sandboxa. Izvršitelj koda koji postoji interni je alat iza dviju zastavica, dostupan samo administratorima, s vezanjima samo za čitanje i svježim kontekstom po pozivu. Za sebe kaže da nije sigurnosna granica. Nema REPL-a ni trajne sesije.
Čemu služi trajni JavaScript pogon poput dquickts?
Njegov README navodi namjene: uspavati neaktivnu skriptu, podijeliti dug izračun na radnike, toplo pokrenuti iz snimke i parkirati skriptu na pozivu domaćina čiji odgovor stiže satima kasnije. Sve je to stvarno. Sinkronizacija ipak ne bi trebala ovisiti o njemu: snimka se učitava samo u build pogona koji ju je zapisao, recenzentu je neprozirna i sadrži sve podatke korisnika koje je skripta pročitala.