
# Laisser votre IA modifier la configuration

Réglez `MCP_CONFIG_WRITE=1` et quatre outils supplémentaires apparaissent, capables de modifier le fichier de configuration lui-même. Ils sont désactivés par défaut, et c'est délibérément une **variable d'environnement plutôt qu'une clé de configuration** : une configuration qui pourrait autoriser sa propre modification serait une configuration qui s'autorise elle-même.

La variable n'est honorée que si **les deux** conditions tiennent : elle est définie, **et** le répertoire du projet a été nommé explicitement, par `--project` ou par un `SOVRIUM_PROJECT_DIR` hérité. Une session retombée sur le répertoire de travail n'obtient que les lectures, et le dit sur la sortie d'erreur. Une surface d'écriture se confine à un seul dossier ; encore faut-il que ce dossier ait été choisi exprès, et non celui dans lequel un client s'est trouvé démarrer.

```json
{
  "mcpServers": {
    "sovrium": {
      "command": "sovrium",
      "args": ["mcp", "--project", "/Users/moi/apps/crm"],
      "env": { "MCP_CONFIG_WRITE": "1" }
    }
  }
}
```

**Sur une application déployée, la variable ne fait rien.** Les outils d'écriture n'existent que sur le tube et ne sont jamais enregistrés sur un point de terminaison HTTP. La définir là n'est pas une erreur — l'instance démarre et sert normalement —, mais le démarrage avertit qu'elle ne vous a rien apporté et nomme ce verbe à la place, parce qu'un opérateur qui croit avoir activé l'écriture de configuration par le réseau ne l'a pas fait.

| Outil                | Arguments                                                          | Renvoie                                                                 |
| -------------------- | ------------------------------------------------------------------ | ----------------------------------------------------------------------- |
| `_config_list_files` | aucun                                                              | `{ files: [{ path, sha256, bytes }] }`, chaque chemin relatif au projet |
| `_config_read_file`  | `path`                                                             | `{ path, content, sha256 }` — les octets, tels quels                    |
| `_config_write_file` | `path`, `content`, `expectedSha`, `acknowledgeDataLoss` facultatif | `{ path, sha256, snapshot, reloadHint }`                                |
| `_config_undo`       | aucun                                                              | `{ snapshot, files }` — quel instantané, et ce qu'il a changé           |

Les deux outils de lecture portent `readOnlyHint`. **Les deux outils d'écriture sont marqués destructifs** : un client qui vous demande confirmation avant un outil destructif vous la demandera donc — écraser un fichier est une modification destructrice de ce fichier, quoi que l'outil refuse ensuite de faire à une table, et l'annulation en écrase plusieurs d'un coup.

**Les octets du fichier entier, jamais un correctif.** Il n'y a ici aucun rédacteur de configuration et aucun sérialiseur : `content` remplace le fichier, donc vos commentaires, l'ordre de vos clés, vos ancres et vos lignes vides survivent intacts à une modification. Le prix, c'est que l'appelant doit lire avant d'écrire, et `expectedSha` est ce qui rend ce prix réel plutôt qu'indicatif.

## Ce pour quoi une écriture est refusée

Chaque écriture passe les mêmes contrôles, dans le même ordre, et chaque refus nomme ce qu'il a refusé. Les contrôles structurels, les moins chers, viennent d'abord ; ceux qui ouvrent une base de données viennent en dernier.

- **Hors du projet.** Le chemin est résolu depuis le répertoire du projet et doit tomber à l'intérieur.
- **Pas un fichier de configuration.** `.yaml`, `.yml` et `.json` uniquement, et jamais un lien symbolique : un lien symbolique peut pointer n'importe où, y compris hors du dossier.
- **Un emplacement protégé.** `.env` et ses voisins, `.git/`, `.claude/`, le répertoire de données et le marqueur de modèle. Un outil capable d'écrire `.env` est un outil qui écrit des identifiants ; un outil capable d'écrire `.git/` réécrit l'historique.
- **Un `expectedSha` périmé.** L'empreinte contre laquelle vous avez édité ne correspond plus au fichier : quelque chose a donc enregistré par-dessus — très possiblement vous, dans votre propre éditeur. Le refus demande de relire, parce qu'un assistant à qui l'on répond seulement « non » réessaie indéfiniment le même appel.
- **Le résultat ne se décoderait pas.** Le candidat est superposé en mémoire au graphe de `$ref` résolu, et **l'application entière** est décodée avant que quoi que ce soit ne soit écrit. Une configuration invalide n'atteint jamais le disque ; ce sont les constats qui reviennent à sa place, dans le vocabulaire que `_config_validate` parle déjà. C'est ce qui rend sûre la modification d'un seul partiel extrait : il est jugé comme un morceau de l'application à laquelle il appartient, et non comme un document qui se trouve à être bien formé.
- **Une nouvelle référence hors du dossier.** Un candidat qui introduit un `$ref` se résolvant hors du répertoire du projet est refusé, sans quoi le fichier **lu** pourrait sortir du dossier que le fichier écrit n'a pas le droit de quitter.
- **La base de données en marche le refuserait.** Là où un serveur tourne, le changement passe par le même planificateur de migration que rapporte `sovrium migrate --dry-run`, et un refus de sa part est le refus de l'écriture — **avant** que le fichier ne change plutôt qu'après que le serveur a renoncé à l'appliquer.
- **Il détruit des données.** Un candidat qui introduit `allowDestructive: true` exige `acknowledgeDataLoss: true` à ses côtés, et l'outil ne pose jamais ni l'un ni l'autre de lui-même. Supprimer une colonne supprime les lignes qu'elle contient : la décision reste la vôtre, chaque fois.
- **Il renumérote une table.** L'`id` d'un champ est facultatif, et un `id` omis vaut la **position** du champ. Insérer un champ au-dessus de l'un d'eux décale tous les id suivants et repointe les données derrière eux : l'insertion est donc refusée jusqu'à ce que les id de la table soient explicites. La même insertion dans une table qui écrit chaque id en toutes lettres est acceptée, puisque rien ne bouge.

## Revenir en arrière

Une écriture acceptée copie l'état **d'avant l'écriture** dans l'historique décrit dans [Annuler et réinitialiser](/fr/docs/undo-and-reset) avant de changer un seul octet, si bien que `_config_undo` a quelque part où revenir même lorsque aucun serveur ne tourne et que rien n'a jamais atteint l'état « accepté ». La copie est omise lorsque l'entrée la plus récente contient déjà exactement ces octets : un projet sous surveillance se retrouve donc avec une entrée avant la modification et une après, plutôt que trois.

`_config_undo` restaure **l'instantané le plus récent dont les fichiers diffèrent de ce qui est sur le disque**, et répond avec les fichiers qu'il a changés. Lorsque aucun ne diffère, il n'y a nulle part où revenir, et il refuse plutôt que de rapporter un succès qui n'a rien changé.

L'annulation remet le **fichier** en place sans condition. Qu'une instance en marche la suive est une autre question : revenir sur un champ que vous avez ajouté est une **suppression** de colonne, donc le contrôle en pré-vol de la surveillance refuse ce rechargement sans `allowDestructive`, conserve la configuration qu'elle sert déjà, et publie la raison. Votre application reste debout et vos lignes restent où elles sont.

**Une écriture ne met rien en service.** Elle pose des octets sur le disque ; `_config_status` est la façon de savoir si une instance les a pris. Les appels sont traités un à la fois, dans l'ordre où vous les avez envoyés : une écriture suivie d'une annulation se produit donc dans cet ordre.

Les quatre outils de lecture, la façon de diriger un client vers le serveur et la configuration qu'il lit sont décrits dans [Votre configuration en MCP](/fr/docs/mcp-config).
