На тази страница
На 21 септември 2026 г., в 00:33 UTC, Тоби Лютке (Tobi Lütke) публикува следното: „MCP срещу CLI за LLM е грешната дискусия. Хората спорят на грешното ниво.“ После, на 23 октомври, той посочи ново хранилище, наречено dquickts. Това е fork на QuickJS, който може да спре програма по средата на цикъл for, да запише цялата ѝ heap памет в байтове и да я продължи в друг процес. „Отлична среда за code mode“, написа той.
Прав е за нивото. Искам първо да се съглася с него, с неговите собствени думи, а после да стесня твърдението, докато то не каже нещо, което една синхронизация може да използва.
Нивото, което има предвид
Ето аргумента, както той го формулира, защото само така е честно да цитирате аргумент, който възнамерявате да огънете:
И двете работят невероятно добре, стига да се изпълняват през среда от типа на REPL, която може да запазва състояние. Забавното при CLI е, че до CLI инструментите обикновено се достига през BASH, който се случва да е REPL с постоянно състояние (файловата система), затова CLI работи много по-добре от MCP. Но това дори не е близо до вътрешно свойство на MCP. Просто означава, че ни трябват по-добри harness среди.
Това е вярно и обяснява наблюдение, което много хора са направили, без да го обяснят. Агент, получил CLI, записва файл, прочита го три стъпки по-късно, търси в него с grep, подава го през pipe. Агент, получил гол MCP сървър, получава по един резултат на извикване и трябва да носи всичко останало в контекстния си прозорец. CLI не спечели заради протокола. Спечели, защото bash идваше с място, където да се слагат нещата.
Залогът му произтича от това. Някой изгражда вграждаем енджин в стила на SQLite, който превръща bash, TypeScript или извиквания на инструменти в един план, проверен преди да се изпълни и създаден за устойчиво изпълнение. dquickts е стъпка в тази посока. README файлът му описва REPL, „чиито функции и променливи оцеляват при рестарт“. Описва и устойчиво извикване към хоста: „скрипт извиква функция на хоста, чийто отговор пристига часове по-късно. Процесът запазва скрипта и излиза.“
Дотук пълно съгласие. Сега стесняването.
Кое състояние, за коя задача
„Запазване на състоянието“ крие един въпрос: чие състояние и в каква форма? За агент, който пише код, състоянието е хранилището и обвивката (shell), и REPL, който оцелява при рестарт, е добър отговор. За агент, който изгражда синхронизация между две SaaS системи, състоянието, от което се нуждае между стъпките, вече е определено от задачата. Четири записа го носят и нито един от тях не е променлива в скрипт.
Курсорът. Синхронизацията чете източник на прозорци и трябва да знае къде е свършил последният прозорец. В енджина за синхронизация на Tajo това е курсор, съхранен за всяка синхронизация и обхват, записван след всяка партида без нито един неуспешен запис, и следващото изпълнение продължава от него. Планираното изпълнение съхранява началната си точка, преди да направи каквото и да било друго, така че ако първото изпълнение се провали, следващото чете от същата точка. Принципът зад това, изпращайте делтата и никога таблицата, е обяснен в статията за reverse ETL. Ще оставя курсора там.
Чакащото предложение. В MCP инструментите за конектори на Tajo едно записване е две извиквания. brevo_propose валидира входа спрямо декларираното действие и записва предложение. Нищо не се изпраща. Резултатът съдържа идентификатор на предложението и, когато човек трябва да реши, страницата, на която го одобрява. brevo_apply изпълнява по ред одобрените записвания на тази връзка и спира на първото, което все още чака човек. Записът между двете е състоянието. Той съдържа входа и негов digest, така че одобрено записване не може да бъде редактирано между решението и прилагането. Не съдържа стойностите от отговора на доставчика, защото един append-only запис за одобрение не бива да се превърне в хранилище на лични данни.
Lease. Изпълнението на една синхронизация държи един изключителен lease през целия си живот, с токен на собственика, времеви лимит по подразбиране от петнадесет минути и подновяване. Остарял собственик не може да освободи по-нов lease. Ако хранилището за lease е недостъпно, изпълнението отказва да започне, вместо да гадае. Кой worker държи изпълнението е факт в lease, а не в паметта на някой процес.
Планът. syncs.dryRun прочита следващия прозорец от курсора, определя за всеки запис дали е вмъкване, обновяване, изтриване или без промяна, и записва точно един ред: плана, пазен един час. Не мести курсор и не записва нищо в никоя от двете системи. syncs.apply записва този план веднъж. Отказва, ако курсорът се е преместил или ако междувременно се е променил повече от допустимия дял от записите. Повторното извикване връща първия резултат, вместо да записва отново.
Това е състоянието между стъпките: четири записа с идентификатори, които може да прочете всеки агент, който има право да ги вижда.
Защо MCP страната не пази нищо в сесия
Щом състоянието е в записите, на една сесия не ѝ остава нищо за пазене. Затова MCP крайните точки на Tajo не поддържат сесия. Всяка заявка получава нов сървър и транспорт без идентификатор на сесия. Четенията са по една заявка на извикване, с ограничени резултати, така че един отговор да не може да наводни един ход. Записванията са първо предложение, после прилагане, а това, което носите, е идентификаторът на предложението.
Протоколът тръгна в същата посока. Ревизията 2026-07-28 на спецификацията на MCP премахна сесиите на ниво протокол и заглавката Mcp-Session-Id от транспорта Streamable HTTP. Сървър, който се нуждае от непрекъснатост, сега издава собствен идентификатор и го получава обратно като обикновен аргумент. Точно това е идентификаторът на предложение. Какво е MCP сървър разглежда тази ревизия и как едно извикване на инструмент се различава от устойчива синхронизация; този текст е само за това къде отива състоянието.
Последицата е тази, която има значение за работата по интеграции. Да кажем, че агент в Claude Code предлага записване и сесията му приключва. Човекът го одобрява час по-късно. Агент в друг клиент, или същият на следващия ден, извиква apply с идентификатора и записването се осъществява веднъж. Агент, който се е сринал, губи контекстния си прозорец и нищо друго. Нищо не е било спряно, затова нищо не е трябвало да бъде възобновявано.
Какво нямаме и колко струва това
Честното възражение идва от страната на REPL и звучи така: „Добре, вашите записи оцеляват. Но всяко извикване прочита отново това, което предишното вече е имало. Скрипт би извлякъл контактите веднъж, би ги обединил, филтрирал и би върнал едно число. Вашият агент прави пет извиквания и плаща за всяко.“ Възражението е право за механизма, а ние не сме измерили цената. Оценката, която би преброила извикванията на задача със и без пренесено състояние, не е изградена. Докато не бъде, твърдението, че режимът с извиквания струва повече токени, остава без отговор.
Още три неща трябва да бъдат казани ясно, защото читател, който завърши тази статия, не бива да си ги въобразява:
- Днес не се изпълнява код, написан от клиенти. Четенията през конекторите са по една заявка на извикване, без sandbox по пътя. В нашия стек съществува изпълнител на код и той е вътрешен инструмент. В продукционна среда е изключен, освен ако не са зададени два отделни флага, и отговаря само на администратори. Всяко извикване получава нов контекст, връзки, които позволяват само четене, и бюджет от две секунди за синхронен код. Собствената му заглавна част казва с главни букви, че е граница за коректност, а не за сигурност.
- Няма REPL. Нищо, което агентът изчисли в едно извикване, не е налично в следващото, освен ако не е записано като запис с идентификатор.
- Няма постоянна сесия на агента. Клиентът пази своя разговор. Tajo пази предложенията, плановете, курсорите и lease записите. Трето нещо няма.
За какво е устойчивият енджин
Нищо от това не е аргумент срещу dquickts. README файлът му изброява за какво служат snapshot файловете и списъкът е добър. Приспивате неактивен скрипт и освобождавате паметта му. Изпълнявате дълго изчисление на части на който worker е свободен. Стартирате на топло всяка инстанция от snapshot, вместо да повтаряте настройката. Паркирате скрипт върху извикване към хоста, чийто отговор пристига по-късно. За работното пространство на агент, който пише код, или за notebook, който трябва да оцелее при рестарт, това е правилният инструмент.
Все пак синхронизацията не бива да зависи от него, по три причини, които ми дава самият README. Първо: „Файл на сесия се зарежда само в билда на qjs, който го е записал.“ Heap, записан от една версия на енджина, е мъртъв файл след обновяване; курсорът е низ. Второ: „Snapshot файловете са доверен вход, като байткода“, и „злонамерените не се откриват“. Съхранената heap памет е нещо, което трябва да пазите, а не нещо, което можете да прегледате; предложението е ред, който човек чете, преди да го одобри. Трето, snapshot съдържа всичко, което скриптът е прочел, включително клиентски записи. Записите по-горе са изградени обратното: планът съхранява digest стойности, а не самите стойности, и записът на предложението никога не съхранява отговора на доставчика.
Едно от правилата, по които изграждаме, е, че детерминираното побеждава хитрото. Heap, който продължава по средата на цикъл, е хитър. Курсорът, предложението, lease и планът са скучни, проверими и еднакви на всеки енджин, във всеки клиент, след всеки срив.
Лютке зададе правилния въпрос. Къде живее състоянието? При синхронизация отговорът никога не е щял да бъде в процеса. Винаги е щял да бъде в записите, защото там трябва да може да го прочете човекът, който одобрява записването.
Често Задавани Въпроси
MCP или CLI е по-добро за AI агентите?
Нито едното, само по себе си. Тезата на Тоби Лютке от 21 септември 2026 г. остава в сила: и двете работят, щом извикванията минават през нещо, което пази състоянието между стъпките. CLI изглежда по-добро днес, защото bash пази състоянието във файловата система. Разликата не е в протокола, а в средата за изпълнение (harness).
Какво състояние е нужно на агента между стъпките на една синхронизация?
Собствените записи на интеграцията: курсор, който казва къде е спряло последното изпълнение, предложение, което чака одобрение от човек, lease, който казва кой worker държи изпълнението, и dry-run план, на който прилагането трябва да съответства. Нищо от това не е променлива в скрипт. Това са редове с идентификатори.
Защо MCP сървърът на Tajo не пази нищо в сесия?
Всяка заявка получава нов сървър и транспорт без идентификатор на сесия. Каквото е нужно на следващото извикване, е запис: идентификатор на предложение, идентификатор на план, курсор. Така втори агент, в друг клиент, може да продължи оттам, докъдето е стигнал първият, а агент, който се е сринал, губи само собствения си контекстен прозорец.
Изпълнява ли Tajo код, написан от агенти?
Не за клиенти. Четенията през инструментите на конекторите са по една заявка на извикване, без sandbox. Съществуващият изпълнител на код е вътрешен инструмент зад два флага, достъпен само за администратори, с връзки (bindings) само за четене и нов контекст при всяко извикване. За себе си той казва, че не е граница за сигурност. Няма REPL и няма постоянна сесия.
За какво е полезен устойчив JavaScript енджин като dquickts?
README файлът му изброява приложенията: приспиване на неактивен скрипт, разделяне на дълго изчисление между няколко workers, топъл старт от snapshot и паркиране на скрипт върху извикване към хоста, чийто отговор пристига часове по-късно. Всичко това е реално. Все пак синхронизацията не бива да зависи от такъв енджин: snapshot се зарежда само в билда на енджина, който го е записал, непрозрачен е за проверяващия и съдържа всички клиентски данни, които скриптът е прочел.