Ezen az oldalon
- szeptember 21-én, 00:33 UTC-kor Tobi Lütke ezt posztolta: „Az MCP kontra CLI az LLM-eknél rossz vita. Az emberek rossz rétegen vitatkoznak.” Aztán október 23-án rámutatott egy új repositoryra, a dquickts-re. Ez a QuickJS egy forkja, amely egy programot meg tud állítani egy
forciklus közepén, a teljes heapjét bájtokba írja, és egy másik folyamatban folytatja. „Kiváló code mode környezet”, írta.
Igaza van a réteget illetően. Először egyet akarok érteni vele, a saját szavaival, aztán addig szűkíteni az állítást, amíg olyasmit mond, amit egy szinkronizálás használni tud.
A réteg, amelyre gondol
Íme az érvelés úgy, ahogy ő megfogalmazta, mert csak így tisztességes idézni egy érvet, amelyet az ember meg akar hajlítani:
Mindkettő hihetetlenül jól működik, amíg egy REPL-szerű környezeten keresztül futnak, amely képes megőrizni az állapotot. A CLI-ben az a vicces, hogy a CLI-eszközöket általában BASH-en keresztül érik el, ami történetesen egy tartós állapotú REPL (a fájlrendszer), ezért a CLI sokkal jobban működik, mint az MCP. De ez a legkevésbé sem az MCP belső tulajdonsága. Csak annyit jelent, hogy jobb harnessekre van szükségünk.
Ez helytálló, és megmagyaráz egy megfigyelést, amelyet sokan tettek már magyarázat nélkül. Egy ügynök, amely CLI-t kap, ír egy fájlt, három lépéssel később visszaolvassa, greppel keres benne, pipe-pal továbbadja. Egy ügynök, amely csupasz MCP-szervert kap, hívásonként egy eredményt kap, és minden mást a kontextusablakában kell cipelnie. A CLI nem a protokollon nyert. Azért nyert, mert a bash hozott magával egy helyet, ahová dolgokat lehet tenni.
Ebből következik a fogadása. Valaki megépít egy beágyazható, SQLite-stílusú motort, amely a basht, a TypeScriptet vagy az eszközhívásokat egyetlen tervvé alakítja, amelyet futás előtt ellenőriz, és amelyet tartós végrehajtásra terveztek. A dquickts egy lépés efelé. A README-je egy olyan REPL-t ír le, „amelynek függvényei és változói túlélik az újraindításokat”. Leír egy tartós gazdahívást is: „egy szkript meghív egy gazdafüggvényt, amelynek válasza órákkal később érkezik. A folyamat elmenti a szkriptet és kilép.”
Eddig teljes az egyetértés. Most jön a szűkítés.
Melyik állapot, melyik feladathoz
Az „állapot megőrzése” elrejt egy kérdést: kinek az állapota, és milyen formában? Egy kódoló ügynök számára az állapot a repository és a shell, és egy újraindításokat túlélő REPL jó válasz. Egy ügynök számára, amely két SaaS-rendszer közötti szinkronizálást épít, a lépések között szükséges állapotot már maga a feladat meghatározza. Négy rekord hordozza, és egyikük sem változó egy szkriptben.
A kurzor. Egy szinkronizálás ablakokban olvas egy forrást, és tudnia kell, hol ért véget az utolsó ablak. A Tajo szinkronizálási motorjában ez egy szinkronizálásonként és hatókörönként tárolt kurzor, amelyet minden olyan köteg után megír, amelyben nem volt hibás rekord, és a következő futás onnan folytatja. Egy ütemezett futás minden más előtt eltárolja a kiindulópontját, így ha egy első futás meghiúsul, a következő ugyanonnan olvas. Az elv mögötte, küldd a deltát és soha a táblát, a reverse ETL magyarázóban olvasható. A kurzort ott is hagyom.
A függő javaslat. A Tajo konnektoros MCP-eszközein egy írás két hívás. A brevo_propose ellenőrzi a bemenetet a deklarált művelettel szemben, és rögzít egy javaslatot. Semmi nem megy ki. Az eredmény tartalmaz egy javaslatazonosítót, és ha egy embernek döntenie kell, azt az oldalt, ahol jóváhagyja. A brevo_apply sorrendben lefuttatja az adott kapcsolat jóváhagyott írásait, és megáll az elsőnél, amely még emberre vár. A kettő közötti rekord az állapot. Tartalmazza a bemenetet és annak digestjét, így egy jóváhagyott írás nem módosítható a döntés és az alkalmazás között. Nem tartalmazza a szolgáltató válaszának értékeit, mert egy csak hozzáfűzhető jóváhagyási rekordból nem válhat személyes adatok tárolója.
A lease. Egy szinkronizálás futása teljes élettartama alatt egyetlen kizárólagos leaset tart, tulajdonosi tokennel, alapértelmezetten tizenöt perces időkorláttal és megújítással. Egy elavult tulajdonos nem engedhet el egy újabb leaset. Ha a lease-tároló nem érhető el, a futás nem indul el, ahelyett hogy találgatna. Hogy melyik worker tartja a futást, az a lease-ben rögzített tény, nem valamelyik folyamat memóriájában.
A terv. A syncs.dryRun beolvassa a következő ablakot a kurzortól, rekordonként eldönti, hogy beszúrás, frissítés, törlés vagy változatlan, és pontosan egy sort ír: a tervet, amelyet egy óráig megőriz. Nem mozdít kurzort, és egyik rendszerbe sem ír semmit. A syncs.apply egyszer írja meg ezt a tervet. Megtagadja, ha a kurzor elmozdult, vagy ha közben a rekordok egy megengedett hányadánál több megváltozott. Egy ismétlés az első eredményt adja vissza ahelyett, hogy újra írna.
Ez az állapot a lépések között: négy azonosítóval rendelkező rekord, amelyet bármely ügynök elolvashat, akinek joga van látni.
Miért nem tárol semmit munkamenetben az MCP-oldal
Ha az állapot a rekordokban van, egy munkamenetnek nem marad mit tárolnia. Így a Tajo MCP-végpontjai nem is tartanak fenn munkamenetet. Minden kérés friss szervert kap, és munkamenet-azonosító nélküli transzportot. Az olvasások hívásonként egy kérést jelentenek, korlátozott méretű eredménnyel, hogy egyetlen válasz ne áraszthasson el egy kört. Az írások: előbb javaslat, aztán alkalmazás, és amit magaddal viszel, az a javaslat azonosítója.
A protokoll ugyanebbe az irányba mozdult. Az MCP-specifikáció 2026-07-28-as revíziója eltávolította a protokollszintű munkameneteket és a Mcp-Session-Id fejlécet a Streamable HTTP transzportból. Egy szerver, amelynek folytonosságra van szüksége, most saját azonosítót bocsát ki, és azt közönséges argumentumként kapja vissza. Pontosan ez egy javaslatazonosító. A Mi az az MCP szerver cikk tárgyalja ezt a revíziót, és azt, miben különbözik egy eszközhívás egy tartós szinkronizálástól; ez az írás csak arról szól, hová kerül az állapot.
A következmény az, ami az integrációs munkában számít. Tegyük fel, hogy egy ügynök a Claude Code-ban javasol egy írást, és a munkamenete véget ér. Az ember egy órával később jóváhagyja. Egy ügynök egy másik kliensben, vagy ugyanaz másnap, meghívja az applyt az azonosítóval, és az írás egyszer megtörténik. Egy összeomlott ügynök elveszíti a kontextusablakát, és semmi mást. Semmit nem függesztettek fel, így semmit sem kellett folytatni.
Ami nincs, és mibe kerül
A tisztességes ellenvetés a REPL-oldalról érkezik, és így hangzik: „Rendben, a rekordjaitok túlélnek. De minden hívás újraolvassa, ami az előző hívásnak már megvolt. Egy szkript egyszer lekérné a kapcsolatokat, összekapcsolná, szűrné őket, és visszaadna egy számot. A ti ügynökötök öt hívást tesz, és mindegyikért fizet.” Az ellenvetés a mechanizmust illetően igaz, az árat pedig nem mértük meg. Az az értékelés, amely feladatonként megszámolná a hívásokat átvitt állapottal és anélkül, nem készült el. Amíg el nem készül, az az állítás, hogy a hívásalapú mód több tokenbe kerül, megválaszolatlan marad.
Még három dolgot nyíltan ki kell mondani, mert aki végigolvassa ezt a cikket, ne képzelje őket oda:
- Ma nem fut ügyfelek által írt kód. A konnektoros olvasások hívásonként egy kérést jelentenek, az útvonalon nincs sandbox. Létezik egy kódfuttató a rendszerünkben, és az belső eszköz. Éles környezetben ki van kapcsolva, hacsak két külön kapcsoló nincs beállítva, és csak adminisztrátoroknak válaszol. Minden hívás friss kontextust kap, csak olvasást engedő kötéseket és két másodperces keretet a szinkron kódra. A saját fejléce nagybetűkkel mondja ki, hogy helyességi határ, nem biztonsági.
- Nincs REPL. Semmi, amit egy ügynök egy hívásban kiszámol, nem érhető el a következőben, hacsak nem azonosítóval rendelkező rekordként írták meg.
- Nincs tartós ügynöki munkamenet. A kliens megőrzi a beszélgetését. A Tajo megőrzi a javaslatokat, terveket, kurzorokat és leaseket. Harmadik dolog nincs.
Mire jó egy tartós motor
Mindez nem érv a dquickts ellen. A README-je felsorolja, mire jók a snapshotok, és a lista jó. Hibernálj egy tétlen szkriptet, és szabadítsd fel a memóriáját. Futtass egy hosszú számítást szeletekben azon a workeren, amelyik éppen szabad. Melegen indíts minden példányt egy snapshotból ahelyett, hogy megismételnéd a beállítást. Parkolj le egy szkriptet egy gazdahíváson, amelynek a válasza később érkezik. Egy kódoló ügynök munkaterületéhez vagy egy újraindítást túlélő notebookhoz ez a megfelelő eszköz.
Egy szinkronizálás mégse függjön tőle, három okból, amelyet maga a README ad meg. Az első: „Egy munkamenetfájl csak abba a qjs-buildbe tölthető be, amely megírta.” Egy motorverzió által írt heap frissítés után halott fájl; egy kurzor egy karakterlánc. A második: „A snapshotok megbízható bemenetek, akárcsak a bájtkód”, és „a rosszindulatúakat nem észleli”. Egy tárolt heap olyasmi, amit védeni kell, nem olyasmi, amit át lehet nézni; egy javaslat egy sor, amelyet egy ember elolvas, mielőtt jóváhagyja. A harmadik: egy snapshot tartalmazza mindazt, amit a szkript beolvasott, az ügyfélrekordokat is beleértve. A fenti rekordok fordítva épülnek fel: a terv digesteket tárol, nem értékeket, a javaslatrekord pedig soha nem tárolja a szolgáltató válaszát.
Az egyik szabály, amely szerint építünk: a determinisztikus legyőzi az okosat. Egy ciklus közepén folytatódó heap okos. Egy kurzor, egy javaslat, egy lease és egy terv unalmas, ellenőrizhető, és ugyanolyan minden motoron, minden kliensben, minden összeomlás után.
Lütke a helyes kérdést tette fel. Hol él az állapot? Egy szinkronizálásnál a válasz soha nem lehetett a folyamatban. Mindig is a rekordokban kellett lennie, mert ott kell tudnia elolvasni annak, aki jóváhagyja az írást.
Gyakran Ismételt Kérdések
Az MCP vagy egy CLI a jobb az AI-ügynököknek?
Önmagában egyik sem. Tobi Lütke 2026. szeptember 21-i megállapítása megállja a helyét: mindkettő működik, ha a hívások olyasmin futnak át, ami megőrzi az állapotot a lépések között. A CLI ma azért tűnik jobbnak, mert a bash a fájlrendszerben tartja az állapotot. Nem a protokoll a különbség, hanem a futtatókörnyezet (harness).
Milyen állapotra van szüksége egy ügynöknek egy szinkronizálás lépései között?
Az integráció saját rekordjaira: egy kurzorra, amely megmondja, hol állt meg az előző futás, egy javaslatra, amely egy ember jóváhagyására vár, egy leasere, amely megmondja, melyik worker tartja a futást, és egy dry-run tervre, amelynek az alkalmazásnak meg kell felelnie. Ezek egyike sem változó egy szkriptben. Ezek azonosítóval rendelkező sorok.
Miért nem tárol semmit munkamenetben a Tajo MCP-szervere?
Minden kérés friss szervert kap, és munkamenet-azonosító nélküli transzportot. Amire a következő hívásnak szüksége van, az egy rekord: egy javaslatazonosító, egy tervazonosító, egy kurzor. Így egy második ügynök, egy másik kliensben, onnan folytathatja, ahol az első abbahagyta, egy összeomlott ügynök pedig a saját kontextusablakán kívül semmit sem veszít.
Futtat a Tajo ügynökök által írt kódot?
Ügyfeleknek nem. A konnektoreszközökön keresztüli olvasások hívásonként egy kérést jelentenek, sandbox nélkül. A létező kódfuttató belső eszköz, két kapcsoló (flag) mögött, csak adminisztrátorok érhetik el, csak olvasásra szolgáló kötésekkel és hívásonként friss kontextussal. Saját magáról azt mondja, hogy nem biztonsági határ. Nincs REPL és nincs tartós munkamenet.
Mire jó egy olyan tartós JavaScript-motor, mint a dquickts?
A README-je megnevezi a felhasználási módokat: egy tétlen szkript hibernálása, egy hosszú számítás szétosztása workerek között, meleg indítás snapshotból, és egy szkript leparkolása egy olyan gazdahíváson, amelynek válasza órákkal később érkezik. Mind valós. Egy szinkronizálás mégse függjön tőle: egy snapshot csak abba a motorbuildbe tölthető be, amely megírta, átláthatatlan egy ellenőrző számára, és tartalmazza mindazt az ügyféladatot, amit a szkript beolvasott.