---
title: "MCP ou CLI, c’est la mauvaise question. Demandez où vit l’état."
description: "Tobi Lütke estime que le débat MCP contre CLI se joue au mauvais niveau : les deux fonctionnent dès que l’état persiste entre les étapes. D’accord. Pour une synchronisation, cet état, ce sont les enregistrements propres à l’intégration, pas le tas d’un REPL, et c’est pourquoi les outils MCP de Tajo ne conservent rien dans une session."
url: https://tajo.io/fr/blog/mcp-or-cli-is-the-wrong-question/
markdown: https://tajo.io/fr/blog/mcp-or-cli-is-the-wrong-question.md
lang: fr-FR
author: "Set Noa"
author_url: https://tajo.io/fr/set-noa/
published: 2026-11-04
updated: 2026-11-05
alternates: {en-US: /blog/mcp-or-cli-is-the-wrong-question.md, bg-BG: /bg/blog/mcp-or-cli-is-the-wrong-question.md, cs-CZ: /cz/blog/mcp-or-cli-is-the-wrong-question.md, de-DE: /de/blog/mcp-or-cli-is-the-wrong-question.md, da-DK: /dk/blog/mcp-or-cli-is-the-wrong-question.md, es-ES: /es/blog/mcp-or-cli-is-the-wrong-question.md, el-GR: /gr/blog/mcp-or-cli-is-the-wrong-question.md, hi-IN: /hi/blog/mcp-or-cli-is-the-wrong-question.md, id-ID: /in/blog/mcp-or-cli-is-the-wrong-question.md, hr-HR: /hr/blog/mcp-or-cli-is-the-wrong-question.md, hu-HU: /hu/blog/mcp-or-cli-is-the-wrong-question.md, it-IT: /it/blog/mcp-or-cli-is-the-wrong-question.md, ja-JP: /jp/blog/mcp-or-cli-is-the-wrong-question.md, ko-KR: /kr/blog/mcp-or-cli-is-the-wrong-question.md, ar-SA: /ar/blog/mcp-or-cli-is-the-wrong-question.md, nl-NL: /nl/blog/mcp-or-cli-is-the-wrong-question.md, no-NO: /no/blog/mcp-or-cli-is-the-wrong-question.md, pl-PL: /pl/blog/mcp-or-cli-is-the-wrong-question.md, pt-BR: /pt/blog/mcp-or-cli-is-the-wrong-question.md, ro-RO: /ro/blog/mcp-or-cli-is-the-wrong-question.md, ru-RU: /ru/blog/mcp-or-cli-is-the-wrong-question.md, sv-SE: /se/blog/mcp-or-cli-is-the-wrong-question.md, zh-CN: /zh-cn/blog/mcp-or-cli-is-the-wrong-question.md, sl-SI: /sl/blog/mcp-or-cli-is-the-wrong-question.md, sr-Latn-RS: /sr/blog/mcp-or-cli-is-the-wrong-question.md, th-TH: /th/blog/mcp-or-cli-is-the-wrong-question.md, tr-TR: /tr/blog/mcp-or-cli-is-the-wrong-question.md, ur-PK: /ur/blog/mcp-or-cli-is-the-wrong-question.md, vi-VN: /vi/blog/mcp-or-cli-is-the-wrong-question.md, sk-SK: /sk/blog/mcp-or-cli-is-the-wrong-question.md}
---

[Agents d’IA](https://tajo.io/fr/blog/?category=ai-agents)

# MCP ou CLI, c’est la mauvaise question. Demandez où vit l’état.

Tobi Lütke estime que le débat MCP contre CLI se joue au mauvais niveau : les deux fonctionnent dès que l’état persiste entre les étapes. D’accord. Pour une synchronisation, cet état, ce sont les enregistrements propres à l’intégration, pas le tas d’un REPL, et c’est pourquoi les outils MCP de Tajo ne conservent rien dans une session.

mcp vs cli

Un agent IA doit-il utiliser MCP ou une CLI ?

Lütke a raison : MCP contre CLI, c’est le mauvais niveau. Les deux fonctionnent dès que l’état persiste entre les étapes, et bash ne gagne que parce que le système de fichiers est cet état. Pour une synchronisation, pourtant, l’état n’est pas le tas d’un REPL. Ce sont les enregistrements propres à l’intégration : un curseur, une proposition en attente, un bail, un plan. C’est pourquoi les outils MCP de Tajo ne conservent rien dans une session : n’importe quel agent, depuis n’importe quel client, reprend à partir des enregistrements. Tajo n’exécute aujourd’hui aucun code client et n’a pas de REPL, et nous le disons clairement plus bas.

[En savoir plus](https://tajo.io/fr/blog/mcp-or-cli-is-the-wrong-question/#le-niveau-dont-il-parle)

Le 21 septembre 2026, à 00 h 33 UTC, Tobi Lütke a publié ceci : « MCP contre CLI pour les LLM, c’est la mauvaise discussion. Les gens débattent au mauvais niveau. » Puis, [le 23 octobre](https://x.com/tobi/status/2113596638899974567), il a signalé un nouveau dépôt appelé [dquickts](https://github.com/stencil-hq/dquickts). C’est un fork de QuickJS capable de mettre un programme en pause au milieu d’une boucle `for`, d’écrire tout son tas en octets et de le reprendre dans un autre processus. « Excellent environnement pour le code mode », a-t-il écrit.

Il a raison sur le niveau. Je veux d’abord lui donner raison, avec ses propres mots, puis resserrer l’affirmation jusqu’à ce qu’elle dise quelque chose d’utile pour une synchronisation.

## Le niveau dont il parle

Voici l’argument tel qu’il l’a formulé, seule façon honnête de citer un argument que l’on compte infléchir :

> Les deux fonctionnent incroyablement bien tant qu’ils passent par un environnement de type REPL capable de faire persister l’état. Ce qui est drôle avec la CLI, c’est que les outils CLI sont généralement utilisés via BASH, qui se trouve être un REPL avec un état persistant (le système de fichiers), et c’est pourquoi la CLI fonctionne beaucoup mieux que MCP. Mais ce n’est même pas de loin une propriété intrinsèque de MCP. Cela signifie juste qu’il nous faut de meilleurs harnais.

C’est exact, et cela explique une observation que beaucoup ont faite sans l’expliquer. Un agent doté d’une CLI écrit un fichier, le relit trois étapes plus tard, le passe à grep, le redirige dans un pipe. Un agent doté d’un simple serveur MCP obtient un résultat par appel et doit porter tout le reste dans sa fenêtre de contexte. La CLI n’a pas gagné grâce au protocole. Elle a gagné parce que bash venait avec un endroit où ranger les choses.

Son pari en découle. Quelqu’un construit un moteur embarquable, dans l’esprit de SQLite, qui transforme bash, TypeScript ou des appels d’outils en un seul plan, vérifié avant son exécution et conçu pour l’exécution durable. dquickts est un pas dans cette direction. [Son README](https://github.com/stencil-hq/dquickts) décrit un REPL « dont les fonctions et les variables survivent aux redémarrages ». Il décrit aussi un appel hôte durable : « un script appelle une fonction hôte dont la réponse arrive des heures plus tard. Le processus sauvegarde le script et se termine. »

Jusqu’ici, accord total. Maintenant, le resserrement.

## Quel état, pour quelle tâche

« Faire persister l’état » cache une question : l’état de qui, et sous quelle forme ? Pour un agent de développement, l’état, c’est le dépôt et le shell, et un REPL qui survit aux redémarrages est une bonne réponse. Pour un agent qui construit une synchronisation entre deux systèmes SaaS, l’état dont il a besoin entre les étapes est déjà défini par la tâche. Quatre enregistrements le portent, et aucun n’est une variable dans un script.

**Le curseur.** Une synchronisation lit une source par fenêtres et doit savoir où la dernière fenêtre s’est terminée. Dans le moteur de synchronisation de Tajo, c’est un [curseur stocké](https://tajo.io/fr/resources/help/troubleshooting/recover-a-stuck-or-failed-sync/#cursor) par synchronisation et par périmètre, écrit après chaque lot sans aucun enregistrement en échec, et l’exécution suivante repart de là. Une exécution planifiée enregistre son point de départ avant de faire quoi que ce soit d’autre, si bien qu’une première exécution qui échoue laisse la suivante lire depuis le même point. Le principe derrière tout cela, envoyer le delta et jamais la table, est expliqué dans [l’article sur le reverse ETL](https://tajo.io/fr/blog/what-is-reverse-etl/). J’en resterai là pour le curseur.

**La proposition en attente.** Sur les outils MCP de connecteur de Tajo, une écriture se fait en deux appels. `brevo_propose` valide l’entrée par rapport à l’action déclarée et enregistre une proposition. Rien n’est envoyé. Le résultat contient un identifiant de proposition et, quand une personne doit décider, la page où elle l’approuve. `brevo_apply` exécute dans l’ordre les écritures approuvées de cette connexion et s’arrête à la première qui attend encore une personne. L’enregistrement entre les deux, c’est l’état. Il contient l’entrée et un condensé de celle-ci, de sorte qu’une écriture approuvée ne peut pas être modifiée entre la décision et l’application. Il ne contient pas les valeurs de réponse du fournisseur, car un registre d’approbation en ajout seul ne doit pas devenir un stockage de données personnelles.

**Le bail.** Une exécution de synchronisation détient un [bail exclusif](https://tajo.io/fr/resources/help/troubleshooting/recover-a-stuck-or-failed-sync/#lease) pendant toute sa durée de vie, avec un jeton de propriétaire, une limite de temps de quinze minutes par défaut et un renouvellement. Un propriétaire périmé ne peut pas libérer un bail plus récent. Si le stockage des baux est injoignable, l’exécution refuse de démarrer plutôt que de deviner. Le worker qui détient l’exécution est un fait inscrit dans le bail, pas dans la mémoire d’un processus.

**Le plan.** `syncs.dryRun` lit la fenêtre suivante à partir du curseur, détermine pour chaque enregistrement s’il faut l’insérer, le mettre à jour, le supprimer ou le laisser inchangé, et écrit exactement une ligne : le plan, conservé une heure. Il ne déplace aucun curseur et n’écrit rien dans aucun des deux systèmes. `syncs.apply` écrit ce plan une seule fois. Il refuse si le curseur a bougé, ou si plus qu’une part tolérée des enregistrements a changé entre-temps. Une répétition renvoie le premier résultat au lieu d’écrire à nouveau.

Voilà l’état entre les étapes : quatre enregistrements avec des identifiants, lisibles par tout agent autorisé à les voir.

## Pourquoi le côté MCP ne conserve rien dans une session

Une fois l’état dans les enregistrements, il ne reste plus rien à garder pour une session. Les points de terminaison MCP de Tajo n’en gardent donc pas. Chaque requête reçoit un serveur neuf et un transport sans identifiant de session. Les lectures se font en une requête par appel, avec des résultats plafonnés pour qu’une réponse ne puisse pas inonder un tour. Les écritures, c’est d’abord propose, puis apply, et c’est l’identifiant de la proposition que vous transmettez.

Le protocole a évolué dans le même sens. La [révision 2026-07-28 de la spécification MCP](https://modelcontextprotocol.io/specification/2026-07-28/changelog) a supprimé les sessions au niveau du protocole et l’en-tête `Mcp-Session-Id` du transport Streamable HTTP. Un serveur qui a besoin de continuité génère désormais son propre identifiant et le récupère comme un argument ordinaire. C’est exactement ce qu’est un identifiant de proposition. [Qu’est-ce qu’un serveur MCP](https://tajo.io/fr/blog/what-is-an-mcp-server/) couvre cette révision et ce qui distingue un appel d’outil d’une synchronisation durable ; cet article porte seulement sur l’endroit où va l’état.

La conséquence est celle qui compte pour le travail d’intégration. Imaginons qu’un agent dans Claude Code propose une écriture et que sa session se termine. La personne l’approuve une heure plus tard. Un agent dans un autre client, ou le même demain, appelle apply avec l’identifiant, et l’écriture aboutit, une seule fois. Un agent qui plante perd sa fenêtre de contexte et rien d’autre. Rien n’a été suspendu, donc rien n’a eu à être repris.

## Ce que nous n’avons pas, et ce que cela coûte

L’objection honnête vient du côté REPL et se formule ainsi : « D’accord, vos enregistrements survivent. Mais chaque appel relit ce que l’appel précédent avait déjà. Un script récupérerait les contacts une fois, les joindrait, les filtrerait et renverrait un nombre. Votre agent fait cinq appels et paie pour chacun. » Cette objection a raison sur le mécanisme, et nous n’avons pas mesuré le prix. L’évaluation qui compterait les appels par tâche avec et sans état conservé n’est pas construite. Tant qu’elle ne l’est pas, l’affirmation selon laquelle le mode appel coûte plus de tokens reste sans réponse.

Trois autres points doivent être dits clairement, car un lecteur qui termine cet article ne doit pas les imaginer :

- **Aucun code écrit par un client ne s’exécute aujourd’hui.** Les lectures de connecteur se font en une requête par appel, sans bac à sable sur le chemin. Un exécuteur de code existe dans notre stack, et c’est un outil interne. En production, il est désactivé à moins que deux flags distincts soient activés, et il ne répond qu’aux administrateurs. Chaque appel reçoit un contexte neuf, des liaisons qui n’autorisent que la lecture et un budget de deux secondes pour le code synchrone. Son propre en-tête dit en majuscules qu’il est une frontière de correction, pas de sécurité.
- **Pas de REPL.** Rien de ce qu’un agent calcule dans un appel n’est disponible pour le suivant, à moins d’avoir été écrit comme enregistrement avec un identifiant.
- **Pas de session d’agent persistante.** Le client garde sa conversation. Tajo garde les propositions, les plans, les curseurs et les baux. Il n’y a pas de troisième chose.

## À quoi sert un moteur durable

Rien de tout cela n’est un argument contre dquickts. Son README liste les usages des snapshots, et la liste est bonne. Mettre en veille un script inactif et libérer sa mémoire. Exécuter un long calcul par tranches sur le worker disponible. Démarrer chaque instance à chaud depuis un snapshot au lieu de répéter l’initialisation. Suspendre un script sur un appel hôte dont la réponse arrive plus tard. Pour l’espace de travail d’un agent de développement, ou un notebook qui doit survivre à un redémarrage, c’est le bon outil.

Une synchronisation ne devrait pourtant pas en dépendre, pour trois raisons que le README lui-même me fournit. Premièrement : « Un fichier de session ne se charge que dans la build qjs qui l’a écrit. » Un tas écrit par une version du moteur devient un fichier mort après une mise à jour ; un curseur est une chaîne de caractères. Deuxièmement : « Les snapshots sont des entrées de confiance, comme le bytecode », et « les snapshots malveillants ne sont pas détectés ». Un tas stocké est quelque chose que vous devez protéger, pas quelque chose que vous pouvez relire ; une proposition est une ligne qu’une personne lit avant de l’approuver. Troisièmement, un snapshot contient tout ce que le script avait lu, enregistrements clients compris. Les enregistrements décrits plus haut sont construits à l’inverse : le plan stocke des condensés, pas des valeurs, et l’enregistrement de proposition ne stocke jamais la réponse du fournisseur.

L’une des règles selon lesquelles nous construisons dit que le déterministe l’emporte sur l’astucieux. Un tas qui reprend au milieu d’une boucle, c’est astucieux. Un curseur, une proposition, un bail et un plan sont ennuyeux, inspectables, et identiques sur chaque moteur, dans chaque client, après chaque plantage.

Lütke a posé la bonne question. Où vit l’état ? Pour une synchronisation, la réponse n’allait jamais être dans le processus. Elle allait toujours être dans les enregistrements, parce que c’est là que la personne qui approuve l’écriture doit pouvoir la lire.

## Questions fréquemment posées

**MCP ou CLI : lequel est le meilleur pour les agents IA ?**

Ni l’un ni l’autre, en soi. Le point de Tobi Lütke du 21 septembre 2026 tient : les deux fonctionnent dès que les appels passent par quelque chose qui conserve l’état entre les étapes. Une CLI semble meilleure aujourd’hui parce que bash conserve l’état dans le système de fichiers. La différence ne vient pas du protocole, mais du harnais.

**De quel état un agent a-t-il besoin entre les étapes d’une synchronisation ?**

Des enregistrements propres à l’intégration : un curseur qui indique où la dernière exécution s’est arrêtée, une proposition en attente de l’approbation d’une personne, un bail qui indique quel worker détient l’exécution, et un plan de simulation auquel l’application doit correspondre. Rien de tout cela n’est une variable dans un script. Ce sont des lignes avec des identifiants.

**Pourquoi le serveur MCP de Tajo ne conserve-t-il rien dans une session ?**

Chaque requête reçoit un serveur neuf et un transport sans identifiant de session. Tout ce dont l’appel suivant a besoin est un enregistrement : un identifiant de proposition, un identifiant de plan, un curseur. Ainsi, un deuxième agent, dans un autre client, peut reprendre là où le premier s’est arrêté, et un agent qui plante ne perd que sa propre fenêtre de contexte.

**Tajo exécute-t-il du code écrit par des agents ?**

Pas pour les clients. Les lectures via les outils de connecteur se font en une requête par appel, sans bac à sable. L’exécuteur de code qui existe est un outil interne placé derrière deux flags, accessible aux seuls administrateurs, avec des liaisons en lecture seule et un contexte neuf à chaque appel. Il dit de lui-même qu’il n’est pas une frontière de sécurité. Il n’y a ni REPL ni session persistante.

**À quoi sert un moteur JavaScript durable comme dquickts ?**

Son README en cite les usages : mettre en veille un script inactif, découper un long calcul entre plusieurs workers, démarrer à chaud depuis un snapshot et suspendre un script sur un appel hôte dont la réponse arrive des heures plus tard. Tout cela est réel. Une synchronisation ne devrait pourtant pas en dépendre : un snapshot ne se charge que dans la build du moteur qui l’a écrit, reste opaque pour une personne qui le relit et contient toutes les données clients que le script a lues.

## Articles connexes

### [La réponse est juste. Le chemin ne l’est pas.](https://tajo.io/fr/blog/the-answer-is-right-the-path-is-wrong/)

8 oct. 2026 · 4 min de lecture

3 sections

### [Ce qu’il faut journaliser pour chaque appel d’outil MCP](https://tajo.io/fr/blog/what-to-log-for-every-mcp-tool-call/)

6 nov. 2026 · 9 min de lecture

6 sections

### [Qu’est-ce qu’un serveur MCP ? Comment fonctionnent MCP et MCP UI en 2026](https://tajo.io/fr/blog/what-is-an-mcp-server/)

3 oct. 2026 · 16 min de lecture

Étape 1 → Étape 6
