MCP sau CLI este întrebarea greșită. Întreabă unde trăiește starea.

Tobi Lütke spune că dezbaterea MCP contra CLI se poartă la nivelul greșit: ambele funcționează odată ce starea persistă între pași. De acord. Pentru o sincronizare, acea stare sunt înregistrările integrării înseși, nu heap-ul unui REPL, și de aceea instrumentele MCP ale Tajo nu păstrează nimic într-o sesiune.

mcp vs cli

Ar trebui un agent AI să folosească MCP sau un CLI?

Lütke are dreptate că MCP contra CLI este nivelul greșit: ambele funcționează odată ce starea persistă între pași, iar bash câștigă doar pentru că sistemul de fișiere este acea stare. Pentru o sincronizare însă, starea nu este heap-ul unui REPL. Sunt înregistrările integrării înseși: un cursor, o propunere în așteptare, un lease, un plan. De aceea instrumentele MCP ale Tajo nu păstrează nimic într-o sesiune: orice agent, din orice client, continuă pornind de la înregistrări. Tajo nu rulează astăzi niciun cod al clienților și nu are REPL, iar mai jos spunem asta deschis.

Află mai mult
Pe această pagină

Pe 21 septembrie 2026, la 00:33 UTC, Tobi Lütke a postat: „MCP contra CLI pentru LLM-uri este discuția greșită. Oamenii dezbat la nivelul greșit.” Apoi, pe 23 octombrie, a arătat spre un depozit nou numit dquickts. Este un fork al QuickJS care poate opri un program în mijlocul unei bucle for, îi poate scrie întregul heap în bytes și îl poate relua într-un alt proces. „Excelent mediu pentru code mode”, a scris el.

Are dreptate în privința nivelului. Vreau mai întâi să fiu de acord cu el, cu propriile lui cuvinte, și apoi să îngustez afirmația până când spune ceva ce poate folosi o sincronizare.

Nivelul la care se referă

Iată argumentul așa cum l-a formulat el, singurul mod corect de a cita un argument pe care ai de gând să-l îndoi:

Ambele funcționează incredibil de bine atâta timp cât rulează printr-un mediu de tip REPL care poate păstra starea. Partea amuzantă la CLI este că instrumentele CLI sunt de obicei accesate prin BASH, care se întâmplă să fie un REPL cu stare persistentă (sistemul de fișiere), de aceea CLI funcționează mult mai bine decât MCP. Dar aceasta nu este nici pe departe o proprietate intrinsecă a MCP. Înseamnă doar că avem nevoie de harness-uri mai bune.

Este corect și explică o observație pe care mulți au făcut-o fără să o explice. Un agent care primește un CLI scrie un fișier, îl citește înapoi trei pași mai târziu, îl caută cu grep, îl trimite printr-un pipe. Un agent care primește un server MCP gol obține un rezultat per apel și trebuie să care tot restul în fereastra sa de context. CLI-ul nu a câștigat prin protocol. A câștigat pentru că bash venea cu un loc unde să pui lucrurile.

Pariul lui decurge de aici. Cineva construiește un motor încorporabil, în stilul SQLite, care transformă bash, TypeScript sau apelurile de instrumente într-un singur plan, verificat înainte de a rula și construit pentru execuție durabilă. dquickts este un pas în această direcție. README-ul său descrie un REPL „ale cărui funcții și variabile supraviețuiesc repornirilor”. Descrie și un apel durabil către gazdă: „un script apelează o funcție a gazdei al cărei răspuns sosește ore mai târziu. Procesul salvează scriptul și se închide.”

Până aici, acord deplin. Acum, îngustarea.

Care stare, pentru care sarcină

„Păstrarea stării” ascunde o întrebare: starea cui și sub ce formă? Pentru un agent de programare, starea este depozitul de cod și shell-ul, iar un REPL care supraviețuiește repornirilor este un răspuns bun. Pentru un agent care construiește o sincronizare între două sisteme SaaS, starea de care are nevoie între pași este deja definită de sarcină. Patru înregistrări o poartă și niciuna nu este o variabilă într-un script.

Cursorul. O sincronizare citește o sursă în ferestre și trebuie să știe unde s-a terminat ultima fereastră. În motorul de sincronizare al Tajo, acesta este un cursor stocat pentru fiecare sincronizare și domeniu, scris după fiecare lot fără înregistrări eșuate, iar rularea următoare continuă de la el. O rulare programată își stochează punctul de pornire înainte de a face orice altceva, astfel încât o primă rulare care eșuează o lasă pe următoarea să citească din același punct. Principiul din spatele acestui lucru, trimite delta și niciodată tabelul, este explicat în articolul despre reverse ETL. Las cursorul acolo.

Propunerea în așteptare. În instrumentele MCP ale conectorilor Tajo, o scriere înseamnă două apeluri. brevo_propose validează intrarea față de acțiunea declarată și înregistrează o propunere. Nu se trimite nimic. Rezultatul conține un id de propunere și, când o persoană trebuie să decidă, pagina pe care o aprobă. brevo_apply rulează în ordine scrierile aprobate ale acelei conexiuni și se oprește la prima care încă așteaptă o persoană. Înregistrarea dintre cele două este starea. Ea conține intrarea și un digest al acesteia, astfel încât o scriere aprobată nu poate fi modificată între decizie și aplicare. Nu conține valorile din răspunsul furnizorului, pentru că o înregistrare de aprobare de tip append-only nu trebuie să devină un depozit de date cu caracter personal.

Lease-ul. O rulare a unei sincronizări deține un singur lease exclusiv pe toată durata ei, cu un token de proprietar, o limită de timp implicită de cincisprezece minute și reînnoire. Un proprietar expirat nu poate elibera un lease mai nou. Dacă depozitul de lease-uri nu poate fi accesat, rularea refuză să pornească în loc să ghicească. Ce worker deține rularea este un fapt din lease, nu din memoria vreunui proces.

Planul. syncs.dryRun citește fereastra următoare de la cursor, stabilește pentru fiecare înregistrare dacă este inserare, actualizare, ștergere sau neschimbată și scrie exact un rând: planul, păstrat timp de o oră. Nu mută niciun cursor și nu scrie nimic în niciunul dintre sisteme. syncs.apply scrie acel plan o singură dată. Refuză dacă cursorul s-a mutat sau dacă între timp s-a schimbat mai mult decât o proporție tolerată din înregistrări. O repetare returnează primul rezultat în loc să scrie din nou.

Aceasta este starea dintre pași: patru înregistrări cu id-uri, care pot fi citite de orice agent care are voie să le vadă.

De ce partea MCP nu păstrează nimic într-o sesiune

Odată ce starea se află în înregistrări, unei sesiuni nu-i mai rămâne nimic de păstrat. Așa că endpointurile MCP ale Tajo nu păstrează nicio sesiune. Fiecare cerere primește un server nou și un transport fără id de sesiune. Citirile sunt o cerere per apel, cu rezultate limitate, astfel încât un singur răspuns să nu poată inunda o tură. Scrierile sunt mai întâi propunere, apoi aplicare, iar ce porți cu tine este id-ul propunerii.

Protocolul a mers în aceeași direcție. Revizia 2026-07-28 a specificației MCP a eliminat sesiunile la nivel de protocol și headerul Mcp-Session-Id din transportul Streamable HTTP. Un server care are nevoie de continuitate își emite acum propriul identificator și îl primește înapoi ca argument obișnuit. Exact asta este un id de propunere. Articolul Ce este un server MCP tratează această revizie și diferența dintre un apel de instrument și o sincronizare durabilă; textul de față se ocupă doar de locul în care ajunge starea.

Consecința este cea care contează pentru munca de integrare. Să zicem că un agent din Claude Code propune o scriere, iar sesiunea lui se încheie. Persoana o aprobă o oră mai târziu. Un agent dintr-un alt client, sau același agent a doua zi, apelează apply cu id-ul, iar scrierea ajunge la destinație, o singură dată. Un agent care s-a prăbușit își pierde fereastra de context și nimic altceva. Nimic nu a fost suspendat, așa că nimic nu a trebuit reluat.

Ce nu avem și cât costă

Obiecția onestă vine din tabăra REPL și sună cam așa: „Bine, înregistrările voastre supraviețuiesc. Dar fiecare apel recitește ce avea deja apelul anterior. Un script ar aduce contactele o singură dată, le-ar uni, le-ar filtra și ar returna un număr. Agentul vostru face cinci apeluri și plătește pentru fiecare.” Obiecția are dreptate în privința mecanismului, iar noi nu am măsurat prețul. Evaluarea care ar număra apelurile per sarcină, cu și fără stare transportată, nu este construită. Până când va fi, afirmația că modul bazat pe apeluri costă mai mulți tokeni rămâne fără răspuns.

Încă trei lucruri trebuie spuse deschis, pentru că un cititor care termină acest articol nu ar trebui să și le imagineze:

  • Astăzi nu rulează niciun cod scris de clienți. Citirile prin conectori sunt o cerere per apel, fără sandbox pe traseu. În infrastructura noastră există un executor de cod și este un instrument intern. În producție este oprit, dacă nu sunt setate două flaguri separate, și răspunde doar administratorilor. Fiecare apel primește un context nou, legături care permit doar citiri și un buget de două secunde pentru codul sincron. Propriul antet spune, cu majuscule, că este o barieră de corectitudine, nu una de securitate.
  • Niciun REPL. Nimic din ce calculează un agent într-un apel nu este disponibil în următorul, decât dacă a fost scris ca înregistrare cu id.
  • Nicio sesiune persistentă de agent. Clientul își păstrează conversația. Tajo păstrează propunerile, planurile, cursoarele și lease-urile. Nu există un al treilea lucru.

La ce folosește un motor durabil

Nimic din toate acestea nu este un argument împotriva dquickts. README-ul său enumeră la ce folosesc snapshoturile, iar lista este bună. Hibernezi un script inactiv și îi eliberezi memoria. Rulezi un calcul lung în felii pe orice worker este liber. Pornești la cald fiecare instanță dintr-un snapshot în loc să repeți configurarea. Parchezi un script pe un apel către gazdă al cărui răspuns sosește mai târziu. Pentru spațiul de lucru al unui agent de programare sau pentru un notebook care ar trebui să supraviețuiască unei reporniri, acesta este instrumentul potrivit.

Totuși, o sincronizare nu ar trebui să depindă de un astfel de motor, din trei motive pe care mi le dă chiar README-ul. Primul: „Un fișier de sesiune se încarcă doar în build-ul qjs care l-a scris.” Un heap scris de o versiune a motorului devine un fișier mort după o actualizare; un cursor este un șir de caractere. Al doilea: „Snapshoturile sunt intrări de încredere, ca bytecode-ul”, iar „cele malițioase nu sunt detectate”. Un heap stocat este ceva ce trebuie să protejezi, nu ceva ce poți evalua; o propunere este un rând pe care o persoană îl citește înainte să-l aprobe. Al treilea, un snapshot conține tot ce citise scriptul, inclusiv înregistrări ale clienților. Înregistrările de mai sus sunt construite invers: planul stochează digesturi, nu valori, iar înregistrarea propunerii nu stochează niciodată răspunsul furnizorului.

Una dintre regulile după care construim este că determinismul bate ingeniozitatea. Un heap care se reia în mijlocul unei bucle este ingenios. Un cursor, o propunere, un lease și un plan sunt banale, pot fi inspectate și sunt la fel pe orice motor, în orice client, după orice prăbușire.

Lütke a pus întrebarea potrivită. Unde trăiește starea? Pentru o sincronizare, răspunsul nu avea cum să fie în proces. Avea să fie mereu în înregistrări, pentru că acolo trebuie să o poată citi persoana care aprobă scrierea.

Întrebări Frecvente

Este MCP sau un CLI mai bun pentru agenții AI?

Niciunul, luat singur. Observația lui Tobi Lütke din 21 septembrie 2026 rămâne valabilă: ambele funcționează odată ce apelurile trec prin ceva care păstrează starea între pași. Un CLI pare mai bun astăzi pentru că bash păstrează starea în sistemul de fișiere. Diferența nu o face protocolul, ci mediul de execuție (harness-ul).

De ce stare are nevoie un agent între pașii unei sincronizări?

De înregistrările integrării înseși: un cursor care spune unde s-a oprit ultima rulare, o propunere care așteaptă aprobarea unei persoane, un lease care spune ce worker deține rularea și un plan de dry-run cu care aplicarea trebuie să se potrivească. Nimic din toate acestea nu este o variabilă într-un script. Sunt rânduri cu id-uri.

De ce serverul MCP al Tajo nu păstrează nimic într-o sesiune?

Fiecare cerere primește un server nou și un transport fără id de sesiune. Tot ce îi trebuie apelului următor este o înregistrare: un id de propunere, un id de plan, un cursor. Astfel, un al doilea agent, într-un alt client, poate continua de unde s-a oprit primul, iar unul care s-a prăbușit nu pierde nimic în afară de propria fereastră de context.

Rulează Tajo cod scris de agenți?

Nu pentru clienți. Citirile prin instrumentele conectorilor sunt o cerere per apel, fără sandbox. Executorul de cod care există este un instrument intern, aflat în spatele a două flaguri, accesibil doar administratorilor, cu legături (bindings) doar pentru citire și un context nou la fiecare apel. Despre sine spune că nu este o barieră de securitate. Nu există REPL și nici sesiune persistentă.

La ce folosește un motor JavaScript durabil precum dquickts?

README-ul său numește utilizările: hibernarea unui script inactiv, împărțirea unui calcul lung între mai mulți workeri, pornirea la cald dintr-un snapshot și parcarea unui script pe un apel către gazdă al cărui răspuns sosește ore mai târziu. Toate sunt reale. Totuși, o sincronizare nu ar trebui să depindă de un astfel de motor: un snapshot se încarcă doar în build-ul de motor care l-a scris, este opac pentru un evaluator și conține orice date ale clienților pe care le-a citit scriptul.

Solicită acces anticipat

Introduceți prenumele și adresa de e-mail. Vă vom trimite un link pentru a confirma cererea de acces la Tajo.

Obține Brevo

Această pagină în Markdown: mcp-or-cli-is-the-wrong-question.md

Se încarcă Markdown…
Deschide în Claude Deschide în ChatGPT