Développement IA

Comment la CLI cf de Cloudflare utilise le JSON pour qu’un agent IA cherche une commande puis appelle une API (2026)

Environ 15 min

Un agent n’a pas à mémoriser plus de 2 900 commandes. Il cherche avec une phrase, lit les champs d’API que la commande enverra, puis l’exécute. Le résultat est du JSON indenté sur la sortie standard. La progression et les erreurs vont sur la sortie d’erreur. Le modèle ne remplit que les arguments. C’est cf qui appelle l’API Cloudflare.

Avant de lire

Objet Faits publics au 2026-10-13 Usage par l’agent
cf Bêta publique le 28 septembre. Couvre toute l’API. La doc indique plus de 2 900 commandes. Installation npm Le JSON est le défaut. Pas de drapeau --json
cf cli search Recherche locale. Pas de connexion. La phrase entière entre guillemets. Cinq résultats au plus, le meilleur d’abord Prendre command et summary. Ne pas exécuter encore
cf schema Décrit le HTTP qu’une commande générée enverra : méthode, chemin, paramètres, champs du corps Remplir d’abord les champs requis. deploy et assimilés : --help
Résultat Doc du 29 septembre : le JSON est toujours indenté. Une suppression non interactive sans --force peut écrire Aborted. sur stderr et sortir quand même avec 0 Lire d’abord le JSON de stdout, puis le code de sortie

Le billet de lancement disait : joli pour les humains, condensé pour les agents. La doc du 29 septembre dit que le JSON est indenté, que stdout soit un terminal ou non. Un contrôle public de 1.0.0-beta.5 le 8 octobre n’a pas non plus vu de sortie condensée. Le parseur suit la doc. orders.example.test est fictif.

Un agent pilote Cloudflare en trois étapes JSON, sans mémoriser les commandes

Cloudflare a publié cf le 28 septembre. Le billet de lancement dit que les agents représentaient environ un quart de l’usage de Wrangler en mars, et 48 % la semaine avant le lancement. Wrangler comptait environ 280 commandes écrites à la main et ne couvrait pas toute l’API. La nouvelle CLI est générée depuis OpenAPI. La doc du 29 septembre parle de plus de 2 900 commandes. Installation : npm install --global cf. cf auth login demande qu’une personne approuve une page du navigateur. Les exécutions sans surveillance utilisent CLOUDFLARE_API_TOKEN, qui prime sur une session enregistrée. Ne pas committer le jeton. L’exemple est sk_demo_not_real.

  • Chercher : cf cli search "create D1 database". Les guillemets restent. La recherche est locale et ne touche pas le compte.
  • Vérifier : cf schema d1 create liste les champs HTTP qui seront envoyés. Ajouter --dry-run avant un vrai changement. Un essai n’a pas besoin d’identifiants.
  • Exécuter : la sortie standard ne contient que le résultat JSON. La zone choisie, les erreurs et Aborted. vont sur la sortie d’erreur.

Ce n’est pas un agent qui clique dans une page. Où un répertoire de plugin place un serveur MCP dans mcp.json : contrat de répertoire d’Agent Plugins 1.0.0. À quoi ressemble un tools/call MCP : comment un coding agent appelle MCP. Cet article ne couvre que le JSON que cf remet à l’agent.

La doc du 29 septembre montre les deux premiers résultats sous cette forme. Les seuls champs sont command et summary :

[
  {
    "command": "cf d1 create",
    "summary": "Create D1 Database"
  },
  {
    "command": "cf d1 update",
    "summary": "Update D1 Database"
  }
]

Sans guillemets, chaque mot après le premier devient une commande inconnue. Le --help racine et celui des groupes renvoient d’abord vers la recherche. Le --help d’une commande précise est du texte brut, pas ce tableau. Si la commande n’existe pas, la CLI liste des noms proches. Cette liste n’est pas un résultat de recherche.

Lire d’abord la requête qui partira, puis la page unique qui revient

cf schema ne couvre que les commandes d’API générées. cf deploy se lit avec --help. Les clés documentées sont operationId, httpMethod, path, pathParams, queryParams, hasRequestBody et requestBodyFields. Les paramètres utilisent les noms d’API, comme account_id. Les champs du corps utilisent le nom d’option et une description. Si la liste est incomplète, passer le corps entier avec --body. Ci-dessous, les mêmes clés en exemple : le nom d’hôte est fictif, ce n’est pas une capture de production :

{
  "operationId": "dns-records-create",
  "httpMethod": "POST",
  "path": "/zones/{zone_id}/dns_records",
  "pathParams": [
    { "name": "zone_id", "type": "string", "required": true }
  ],
  "queryParams": [],
  "hasRequestBody": true,
  "requestBodyFields": [
    { "name": "type", "type": "string", "required": true, "description": "Record type" },
    { "name": "name", "type": "string", "required": true, "description": "Record name" },
    { "name": "content", "type": "string", "required": true, "description": "Record content" }
  ]
}

Les champs requis passent la porte avant l’exécution. Pourquoi cette porte ne peut pas rester seulement dans le prompt : pourquoi l’IA a besoin de JSON Schema. La sortie standard d’une commande de liste est un tableau JSON d’une page. Les drapeaux de pagination diffèrent selon la commande. Une modification sans données laisse stdout vide. Les octets bruts d’un objet R2 ou d’une image de modèle partent tels quels sur stdout, pas comme un objet. Une page DNS fictive :

[
  {
    "id": "rec_demo_1042",
    "type": "TXT",
    "name": "orders.example.test",
    "content": "demo-pass"
  }
]

Les noms de champs dépendent de la commande. Le parseur vérifie d’abord que c’est un tableau, puis prend id ou name dans le schema de cette commande. Ne pas supposer qu’une enveloppe success entoure chaque page.

Ne pas fondre la sortie standard, la sortie d’erreur et le code de sortie en un seul succès

Un agent écrase facilement trois signaux en un seul :

Ce qui se voit Ce que cela veut dire
JSON indenté sur stdout C’est le résultat. La doc dit : indenté, que stdout soit un terminal ou non. La phrase de lancement sur une sortie condensée pour les agents n’est pas le contrat actuel.
Zone choisie, erreurs, Aborted. Sur stderr. Sans --force, une commande destructive non interactive peut n’imprimer que Aborted. et sortir quand même avec 0.
cf workers delete --force Sur certaines commandes, --force est aussi un paramètre d’API. Il peut supprimer un Worker encore référencé par d’autres. Lire le schema avant d’autoriser.
Dépôt qui n’a que wrangler.jsonc Tant qu’il n’y a pas de cloudflare.config.ts, ne pas laisser l’agent lancer cf dev, cf build ou cf deploy. D’abord cf migrate --dry-run.

Un projet qui a déjà une config Wrangler reste sur Wrangler. Les autres tâches Cloudflare passent par cf. Cette phrase va dans AGENTS.md ou CLAUDE.md. --local ne couvre que certains KV, D1 et R2. cf d1 query n’a pas d’équivalent local : il renvoie une erreur au lieu d’appeler silencieusement l’API réelle.

Six étapes avant de laisser un agent modifier une ressource réelle

Ne pas commencer en collant le jeton dans le prompt :

  1. Une personne termine cf auth login. CLOUDFLARE_API_TOKEN seulement sans surveillance. Ne pas mettre le jeton dans le JSON de commande.
  2. Chercher entre guillemets. La phrase entière est un seul argument. Lire d’abord les cinq valeurs au plus de command.
  3. Lancer cf schema. Regarder required. Si la liste est incomplète, passer à --body et à la doc d’API.
  4. D’abord --dry-run. Il imprime le JSON de la requête, n’envoie rien et n’a pas besoin d’identifiants. Vérifier que le chemin contient une valeur de démo comme zone_demo_1042.
  5. Ne parser que stdout. Une sortie vide veut dire que cette modification ne renvoie pas de données. Pour une commande destructive, chercher Aborted. sur stderr.
  6. Migrer d’abord un projet Wrangler. Sans cloudflare.config.ts, ne pas déployer. Un TODO(@cloudflare) resté dans un fichier généré fait échouer le build.

Après la bêta publique, Wrangler reste maintenu 18 mois. Les deux peuvent coexister. Dans le même projet, ne pas laisser l’agent recouvrir l’ancienne config avec cf deploy.

Déplier trois JSON avant d’autoriser une écriture

L’échec vient souvent d’une recherche sans guillemets, d’un schema qui ne correspond pas au résultat, ou d’un code 0 lu comme une suppression déjà faite.

  • formater le tableau de recherche et le schema, puis validation de schema pour voir si chaque required: true de requestBodyFields est présent.
  • Avec extraction JSONPath, extraire $[0].command, $.httpMethod et $.path.
  • Comparer avec diff JSON la requête --dry-run et la page revenue. Compléter les champs manquants avant de changer le prompt.
  • Si stderr contient Aborted., la suppression n’a pas eu lieu.
  • Ne pas coller zone, jeton ou identifiant de compte dans un formateur public. JSONSort tourne en local dans le navigateur.

Quand les trois pièces concordent, l’agent appelle une API au lieu de deviner une commande. L’indentation elle-même n’est pas une erreur.

Erreurs fréquentes

  • Attendre du JSON condensé à cause du billet de lancement. La doc actuelle dit : toujours indenté. Le contrôle de beta.5 le 8 octobre n’a pas vu de sortie plus courte parce qu’un agent était détecté.
  • Oublier les guillemets dans la recherche. Le deuxième mot devient une commande inconnue. Le résultat de recherche et la liste des noms proches ne sont pas le même JSON.
  • Lire le code 0 comme une suppression réussie. Sans --force, une session non interactive peut seulement avoir été interrompue. Lire aussi stderr.
  • Lancer cf deploy dans un dépôt qui n’a que wrangler.jsonc. Cela peut créer une nouvelle config et laisser l’ancienne de côté. Faire d’abord un dry-run de la migration.
  • Mettre le jeton d’API dans le prompt ou dans un JSON qui sera commité. Sans surveillance, seulement la variable d’environnement. Ne pas utiliser un vrai jeton dans l’exemple.
  • Décrire cf comme un agent qui clique lui-même dans le navigateur. Une personne approuve la connexion. Ensuite, les appels d’API sont le JSON de la CLI, pas des clics de page.

FAQ

Un agent doit-il encore ajouter --json à chaque commande ?

Non. Le résultat de cf est du JSON sur stdout par défaut. --json et les tableaux appartiennent à Wrangler. D’après la doc du 29 septembre, ce JSON est indenté. Inutile d’attendre un format condensé.

cf cli search utilise-t-il mon compte ?

Non. La recherche est locale et n’a pas besoin d’identifiants. Elle renvoie au plus cinq objets, chacun avec command et summary. C’est la commande suivante qui change une ressource.

L’absence de JSON est-elle un échec ?

Pas toujours. Une modification qui ne renvoie pas de données laisse stdout vide. Un échec est un code différent de zéro, avec le message sur stderr. Une suppression interrompue affiche Aborted. sur stderr et peut quand même sortir avec 0.

Faut-il basculer un projet Wrangler vers cf aujourd’hui ?

Le déploiement n’a pas à changer le même jour. Tant que wrangler.jsonc ou wrangler.toml est là, développement, build et déploiement restent sur Wrangler. Le passage à cloudflare.config.ts se fait avec cf migrate, d’abord avec --dry-run. Après la bêta publique, Wrangler reste maintenu 18 mois.

Que faire si requestBodyFields est incomplet ?

La doc dit que requestBodyFields est incomplet ou vide pour certaines commandes. Dans ce cas, envoyer le corps entier avec --body et le comparer à la doc d’API. Une liste vide ne veut pas dire que l’API n’a pas de corps.

Conclusion

cf est le chemin par lequel un agent IA appelle l’API Cloudflare en JSON. La recherche donne le nom de commande, schema les champs qui partiront, stdout une page de résultat. Parser le JSON indenté, et séparer stderr du code de sortie.

Vérifier d’abord que cf d1 create vient du tableau de recherche, que zone_id vient du schema, et que orders.example.test n’apparaît que dans le résultat de démo. Quand les trois endroits concordent, la ressource réelle peut changer.

À lire ensuite

Historique : Première publication