odoo-mcp
<div align="center">
# đ odoo-mcp
**Pilotez n'importe quelle base Odoo en langage naturel â sans jamais perdre le contrĂŽle.**
Un serveur [MCP](https://modelcontextprotocol.io) qui connecte votre assistant IA
(Claude Code, Antigravity, Gemini CLI, Claude Desktop, CursorâŠ) Ă Odoo via XML-RPC.
Lecture instantanée, écriture gardée, import/export Excel, journal d'audit et
rapports prĂ©sentables au client â le tout depuis une simple conversation.
<br/>





</div>
---
> **En une phrase.** On parle Ă sa base Odoo comme Ă un collĂšgue â *« combien de commandes
> en cours ce mois-ci ? »*, *« importe ce catalogue de 1 500 articles »*, *« prépare une
> maquette pour ce prospect »* â et l'outil traduit, exĂ©cute, et **rend des comptes** :
> rien ne s'écrit sans garde-fou, et tout ce qui s'écrit est tracé.
## đ Sommaire
- [âš Pourquoi odoo-mcp](#-pourquoi-odoo-mcp)
- [đïž Architecture](#-architecture)
- [đ Le modĂšle de sĂ©curitĂ©](#-le-modĂšle-de-sĂ©curitĂ©)
- [đ Installation](#-installation)
- [đŹ Utilisation](#-utilisation)
- [đ§° Les 37 outils](#-les-37-outils)
- [đ Le rapport d'intervention](#-le-rapport-dintervention)
- [đŹ Le guide de prĂ©sentation](#-le-guide-de-prĂ©sentation)
- [đ„ Importer un fichier](#-importer-un-fichier)
- [đ PrĂ©parer une dĂ©monstration](#-prĂ©parer-une-dĂ©monstration)
- [đ GĂ©nĂ©rer un tableau de bord](#-gĂ©nĂ©rer-un-tableau-de-bord)
- [⥠Ăconomie de contexte](#-Ă©conomie-de-contexte)
- [đ§ Notes de terrain](#-notes-de-terrain)
- [đ Licence](#-licence)
## âš Pourquoi odoo-mcp
Ce n'est pas un connecteur « boßte noire » de plus. Chaque décision de conception vise le
mĂȘme objectif : donner de la puissance Ă l'assistant **sans lui donner les pleins pouvoirs**.
| | |
|---|---|
| đ **Aucun identifiant stockĂ©** | L'assistant demande l'URL, le login et la clĂ© API dans la conversation (`odoo_connect`). Ils ne vivent qu'en mĂ©moire, le temps de la session. |
| đ« **Ăcriture bloquĂ©e par dĂ©faut** | Elle s'active par un outil dĂ©diĂ© (`odoo_enable_write`), que l'assistant n'appelle qu'aprĂšs accord explicite. Sinon, seules les mĂ©thodes de **lecture** sont autorisĂ©es (liste blanche) â y compris via l'appel brut `odoo_execute`. |
| đïž **Modifications de masse prĂ©visualisĂ©es** | `odoo_update_where` montre d'abord combien d'enregistrements sont visĂ©s, avec un Ă©chantillon avant/aprĂšs, et n'Ă©crit qu'aprĂšs confirmation. |
| đ **Import/export Excel natif** | Le serveur tourne en local : il lit et Ă©crit les fichiers directement. Un catalogue de 1 500 lignes s'importe sans passer par la conversation. |
| â»ïž **Ăcritures rejouables** | `odoo_upsert` et l'import par External ID mettent Ă jour au lieu de dupliquer. |
| đ§Ÿ **Tout est tracĂ©** | Chaque Ă©criture est journalisĂ©e avec son Ă©tat avant/aprĂšs ; `odoo_journal_report` produit un rapport d'intervention prĂ©sentable au client. Secrets masquĂ©s, binaires rĂ©sumĂ©s, journal lisible par le seul utilisateur (`0600`). |
| đ **Tableaux de bord rĂ©els** | `odoo_dashboard_create` produit de vrais tableaux de bord Odoo dont les graphiques sont recalculĂ©s en direct â pas des captures d'Ă©cran. |
| đ **Maquettes de dĂ©mo sĂ»res** | Un questionnaire de qualification cadre l'avant-vente, et le mode dĂ©monstration neutralise **toute** adresse e-mail Ă©crite : aucune fausse facture ne peut partir chez une vraie entreprise. |
## đïž Architecture
Un seul processus local fait le pont entre l'assistant et Odoo. Rien n'est hébergé ailleurs,
aucune donnée ne transite par un tiers.
```mermaid
flowchart LR
A["đ€ Assistant IA<br/>Claude · Gemini · CursorâŠ"]
B["đ odoo-mcp<br/>serveur local"]
C[("đïž Base Odoo")]
D["đ Fichiers locaux<br/>Excel · rapports · cache"]
A -->|"protocole MCP"| B
B -->|"XML-RPC"| C
B -->|"lit / écrit"| D
classDef ai fill:#eef2ff,stroke:#6366f1,color:#1e1b4b;
classDef srv fill:#ecfeff,stroke:#06b6d4,color:#083344;
classDef odoo fill:#f3e8ff,stroke:#a855f7,color:#3b0764;
classDef file fill:#fff7ed,stroke:#f97316,color:#431407;
class A ai; class B srv; class C odoo; class D file;
```
## đ Le modĂšle de sĂ©curitĂ©
Le serveur est le **point de passage obligé** de toute opération. C'est là que sont posés
les garde-fous â donc ils tiennent quel que soit l'outil appelĂ© (crĂ©ation, modification,
upsert, import de fichier, ou appel brut `odoo_execute`).
```mermaid
flowchart TD
Q["Appel d'un outil d'Ă©criture"] --> R{"Ăcriture activĂ©e ?"}
R -->|Non| RO["â RefusĂ©<br/>lecture seule (liste blanche)"]
R -->|Oui| M{"Modification de masse ?"}
M -->|Oui| P["đ PrĂ©visualisation<br/>volume + Ă©chantillon avant/aprĂšs"] --> CF{"ConfirmĂ© ?"}
CF -->|Non| STOP["đ« AnnulĂ© â rien n'est Ă©crit"]
CF -->|Oui| EX["âïž Ăcriture"]
M -->|Non| EX
EX --> J[("đ Journal 0600<br/>secrets masquĂ©s · avantâaprĂšs")]
classDef gate fill:#fef2f2,stroke:#ef4444,color:#450a0a;
classDef ok fill:#f0fdf4,stroke:#22c55e,color:#052e16;
classDef log fill:#f8fafc,stroke:#64748b,color:#0f172a;
class R,M,CF gate; class EX,P ok; class J log;
```
## đ Installation
> **PrĂ©requis :** [uv](https://docs.astral.sh/uv/getting-started/installation/) â
> `winget install astral-sh.uv` (Windows) · `brew install uv` (macOS) ·
> `curl -LsSf https://astral.sh/uv/install.sh | sh` (Linux).
### En un clic
[](cursor://anysphere.cursor-deeplink/mcp/install?name=odoo&config=eyJjb21tYW5kIjogInV2eCIsICJhcmdzIjogWyItLXJlZnJlc2giLCAiLS1mcm9tIiwgImdpdCtodHRwczovL2dpdGh1Yi5jb20vSmFtaVRoZVMvb2Rvby1tY3AiLCAib2Rvby1tY3AiXX0=)
[](https://vscode.dev/redirect/mcp/install?name=odoo&config=%7B%22name%22%3A%20%22odoo%22%2C%20%22command%22%3A%20%22uvx%22%2C%20%22args%22%3A%20%5B%22--refresh%22%2C%20%22--from%22%2C%20%22git%2Bhttps%3A//github.com/JamiTheS/odoo-mcp%22%2C%20%22odoo-mcp%22%5D%7D)
Clique le bouton de ton IDE â il ouvre l'Ă©diteur et propose d'ajouter le serveur `odoo`.
Accepte, c'est installé. *(Le bouton Cursor n'ouvre l'IDE que si Cursor est installé.)*
### Par prompt â Claude Code · Gemini · Antigravity
Pas de bouton pour ces clients ? **Copie-colle ce prompt dans le chat de ton assistant** :
il installe le serveur lui-mĂȘme.
```text
Installe le serveur MCP « odoo » pour moi, puis confirme.
Il se lance avec la commande `uvx` et les arguments :
--refresh --from git+https://github.com/JamiTheS/odoo-mcp odoo-mcp
ProcĂšde selon l'outil dans lequel tu tournes :
- Claude Code : exécute
claude mcp add --scope user odoo -- uvx --refresh --from git+https://github.com/JamiTheS/odoo-mcp odoo-mcp
- Gemini CLI : ajoute une entrée « odoo » sous « mcpServers » dans ~/.gemini/settings.json
- Antigravity ou autre client : ajoute la mĂȘme entrĂ©e « mcpServers.odoo » dans son fichier
de config MCP.
L'entrée JSON à utiliser (sauf pour la commande Claude Code) :
"odoo": {
"command": "uvx",
"args": ["--refresh", "--from", "git+https://github.com/JamiTheS/odoo-mcp", "odoo-mcp"]
}
Ne mets aucune clé API ni URL Odoo en dur. Quand c'est fait, dis-moi de redémarrer le
client pour que le serveur « odoo » soit chargé.
```
### En manuel
<details>
<summary><b>Claude Code</b> â une seule commande</summary>
```bash
claude mcp add --scope user odoo -- uvx --refresh --from git+https://github.com/JamiTheS/odoo-mcp odoo-mcp
```
</details>
<details>
<summary><b>Antigravity / Gemini CLI</b> â bloc JSON Ă coller</summary>
Antigravity : panneau **MCP Servers** â *Manage MCP config*. Gemini CLI : `~/.gemini/settings.json`.
```json
{
"mcpServers": {
"odoo": {
"command": "uvx",
"args": ["--refresh", "--from", "git+https://github.com/JamiTheS/odoo-mcp", "odoo-mcp"]
}
}
}
```
</details>
<details>
<summary><b>Claude Desktop</b></summary>
ParamĂštres â DĂ©veloppeur â `claude_desktop_config.json` : mĂȘme bloc JSON que ci-dessus.
</details>
C'est tout : `uvx` télécharge, installe et lance le serveur tout seul au premier démarrage.
> [!IMPORTANT]
> **đ Mises Ă jour automatiques.** Le `--refresh` prĂ©sent dans toutes les configs est la
> clé : à **chaque démarrage du client**, `uvx` revérifie la derniÚre version publiée sur
> GitHub et la récupÚre si du nouveau code a été poussé. Autrement dit, dÚs qu'un `git push`
> est fait, les utilisateurs sont Ă jour **au prochain lancement de leur IDE** â sans rien
> réinstaller.
>
> *Contrepartie :* quelques secondes de plus au dĂ©marrage et une connexion rĂ©seau requise Ă
> ce moment-lĂ . Pour figer une version, retirer `--refresh` des `args` : la mise Ă jour
> redevient manuelle.
## đŹ Utilisation
### Au démarrage : deux modes
à l'ouverture d'une session, le serveur se présente (via le champ `instructions` du protocole
MCP) et l'assistant propose de choisir un mode de travail :
- **đ§ Consultant** â intervention sur une base **rĂ©elle**. L'assistant ouvre un journal
d'audit avant d'écrire, garde l'écriture bloquée jusqu'à ton accord, et propose un rapport
d'intervention en fin de travail.
- **đ Avant-vente (Sales)** â construire une **maquette de dĂ©monstration**. L'assistant part
du questionnaire de qualification, active le filet e-mail (`odoo_demo_mode`), puis peuple la
base avec `odoo_demo_generate`.
> [!NOTE]
> La présentation dépend du client MCP : la plupart injectent ces `instructions` dans le
> contexte de l'assistant, qui t'accueille alors dĂšs ton premier message. L'assistant ne
> choisit pas le mode Ă ta place â il te pose la question.
### Connexion
Au premier échange, l'assistant demande trois informations :
1. **URL de la base** â ex. `https://acme.odoo.com`
2. **Login** â l'e-mail de connexion
3. **ClĂ© API** â dans Odoo : avatar â *Mon profil* â *SĂ©curitĂ© du compte* â *Nouvelle clĂ©
API*, en **laissant le champ « Scope » vide** (une clé de scope « MCP » est refusée en
XML-RPC)
Puis on parle à sa base en langage naturel : *« combien de commandes en cours ? »*,
*« montre les champs de res.partner »*, *« corrige le téléphone de ce contact »*.
### Une session type
Un exemple de bout en bout : importer un catalogue puis préparer une démo. Remarquez
l'Ă©tape oĂč l'assistant **s'arrĂȘte pour demander l'autorisation** avant la moindre Ă©criture.
```mermaid
sequenceDiagram
autonumber
actor U as Vous
participant A as Assistant IA
participant M as odoo-mcp
participant O as Odoo
U->>A: « importe ce catalogue et prépare une démo »
A->>M: odoo_connect (URL · login · clé)
M->>O: authentification XML-RPC
A->>M: odoo_import_file (mode check)
M->>O: validation champ par champ
M-->>A: 0 erreur â prĂȘt Ă Ă©crire
A->>U: « je peux activer l'écriture ? »
U->>A: oui, vas-y
A->>M: odoo_enable_write + import (mode run)
M->>O: écriture par lots + journalisation
M-->>A: 40 articles créés
A->>U: rapport d'intervention HTML
```
<details>
<summary><b>Base fixe (optionnel)</b></summary>
Pour se connecter automatiquement à une base donnée, ajouter à la déclaration du serveur
(la clĂ© est alors en clair dans le fichier de config du client â Ă rĂ©server aux bases de test) :
```json
"env": {
"ODOO_URL": "https://acme.odoo.com",
"ODOO_USERNAME": "vous@acme.com",
"ODOO_API_KEY": "votre-clé"
}
```
</details>
## đ§° Les 37 outils
Huit familles, une progression logique : se connecter â lire â explorer â Ă©crire (sous
garde) â Ă©changer des fichiers â prĂ©parer une dĂ©mo â visualiser â rendre des comptes.
```mermaid
flowchart LR
R(["đ odoo-mcp<br/>37 outils"])
R --> A["đ Connexion · 3"]
R --> B["đ Lecture · 7"]
R --> C["đ SchĂ©ma · 4"]
R --> D["âïž Ăcriture · 6"]
R --> E["đ Fichiers · 3"]
R --> F["đ DĂ©monstration · 4"]
R --> G["đ Tableaux de bord · 4"]
R --> H["đ§Ÿ TraçabilitĂ© · 6"]
classDef root fill:#0f2b46,stroke:#0f2b46,color:#fff;
classDef cat fill:#eef2ff,stroke:#6366f1,color:#1e1b4b;
class R root; class A,B,C,D,E,F,G,H cat;
```
<table>
<tr><td valign="top" width="50%">
**đ Connexion**
| Outil | RĂŽle |
|---|---|
| `odoo_connect` | Se connecter (URL + login + clĂ©) â en mĂ©moire |
| `odoo_status` | Ătat connexion, version serveur, mode |
| `odoo_enable_write` | Activer/couper l'écriture |
**đ Lecture**
| Outil | RĂŽle |
|---|---|
| `odoo_models` | Lister les modĂšles |
| `odoo_fields` | Décrire les champs (`writable_only`) |
| `odoo_search` | Recherche + lecture (domaine, tri, page) |
| `odoo_count` | Comptage |
| `odoo_read` | Lecture par identifiants |
| `odoo_name_find` | Retrouver un enreg. par son nom |
| `odoo_aggregate` | Sommes/comptes par groupe, par date |
**đ Exploration du schĂ©ma** *(cache disque)*
| Outil | RĂŽle |
|---|---|
| `odoo_find_models` | Chercher un modÚle par mot-clé |
| `odoo_find_fields` | Chercher un champ par mot-clé |
| `odoo_fields_batch` | Champs de plusieurs modĂšles d'un coup |
| `odoo_schema_status` | Ătat du cache ; `refresh=true` |
**đ Fichiers**
| Outil | RĂŽle |
|---|---|
| `odoo_import_file` | Importer .xlsx/.csv (`inspect`/`check`/`run`) |
| `odoo_export_file` | Exporter vers .xlsx/.csv (`ecraser=True`) |
| `odoo_get_attachment` | Télécharger une piÚce jointe |
</td><td valign="top" width="50%">
**âïž Ăcriture** *(exige `odoo_enable_write`)*
| Outil | RĂŽle |
|---|---|
| `odoo_create` | Création |
| `odoo_write` | Modification par identifiants |
| `odoo_update_where` | Modif. de masse **prévisualisée** |
| `odoo_unlink` | Suppression (confirmation > 50) |
| `odoo_upsert` | Créer-ou-mettre-à -jour par External ID |
| `odoo_execute` | Appel brut `execute_kw` (liste blanche) |
**đ DĂ©monstration & avant-vente**
| Outil | RĂŽle |
|---|---|
| `odoo_demo_questionnaire` | Trame de qualification avant démo |
| `odoo_demo_generate` | Générer un jeu de démo depuis une recette compacte |
| `odoo_demo_mode` | Filet : neutralise toute adresse e-mail |
| `odoo_demo_check` | Audite et corrige les adresses réelles |
**đ Tableaux de bord**
| Outil | RĂŽle |
|---|---|
| `odoo_dashboard_list` | Inventaire des tableaux de bord |
| `odoo_dashboard_inspect` | Décrire en clair un tableau de bord |
| `odoo_dashboard_create` | Créer un tableau de graphiques live |
| `odoo_saved_analysis` | Enregistrer une analyse réutilisable |
**đ§Ÿ TraçabilitĂ© & reporting**
| Outil | RĂŽle |
|---|---|
| `odoo_journal_start` | Ouvrir un journal (titre + objectif) |
| `odoo_journal_chapter` | Ouvrir une étape + sa justification |
| `odoo_journal_note` | Consigner décision / observation / alerte |
| `odoo_journal_report` | Générer le rapport (HTML / Markdown) |
| `odoo_presentation_guide` | Générer le déroulé de démo client |
| `odoo_recent_changes` | Ce qui a bougé (audit natif Odoo) |
</td></tr>
</table>
## đ Le rapport d'intervention
Quand c'est l'assistant qui construit un flux entier, retrouver aprÚs coup ce qui a été fait
â et l'expliquer Ă un client â devient vite impossible Ă partir du seul historique de
conversation. Le serveur étant le point de passage obligé de toute écriture, il journalise
tout automatiquement.
```
odoo_journal_start("Maquette de démonstration", "Traduire le flux affaires dans Odoo")
odoo_journal_chapter("Référentiel articles", "Aucun catalogue n'existait : prérequis
pour bloquer les achats hors contrat")
â les Ă©critures suivantes sont tracĂ©es, avec leur Ă©tat avant/aprĂšs
odoo_journal_note("Le stock reste hors périmÚtre (décision actée en réunion)", "decision")
odoo_journal_report(format="both")
```
Le rapport HTML est autonome (aucune ressource externe), présentable tel quel ou imprimable
en PDF. Il contient la synthÚse chiffrée, les volumes par type d'information, le déroulé
chronologique par Ă©tape, et pour chaque modification le dĂ©tail `avant â aprĂšs`. Les
suppressions y apparaissent avec **le nom de ce qui a disparu** et un marquage
« irréversible ».
> [!TIP]
> **Le rapport parle français, pas Odoo.** Les noms techniques sont traduits en langage
> courant â `res.partner` devient « Contacts (clients, fournisseurs) », `sale.order` devient
> « Devis et commandes clients » â et chaque type d'information est accompagnĂ© de l'endroit oĂč
> le trouver dans l'interface. Un dirigeant qui n'a jamais ouvert Odoo comprend le document.
### Ă quoi ressemble un rapport
> *Extrait reconstitué et anonymisé d'un rapport réel. Le document livré est une page HTML
> autonome et mise en forme (en-tĂȘte, cartes de synthĂšse, code couleur) â voici son contenu.*
<table>
<tr><td>
**RAPPORT D'INTERVENTION ODOO**
### Mise en place d'un jeu de démonstration
Alimenter la base avec un jeu de données de démonstration complet et varié.
`Base âąâąâąâąâąâą` · `Utilisateur âąâąâąâąâąâą` · le 27/07/2026
</td></tr>
</table>
**Ce qui a été fait**
| Enregistrements touchés | Opérations | Import de données | Création |
|:---:|:---:|:---:|:---:|
| **325** | **9** | **323** | **2** |
| Ce qui a Ă©tĂ© touchĂ© | OĂč le trouver | DĂ©tail | Total |
|---|---|---|---:|
| Contacts (clients, fournisseurs) | Contacts | import de données | **40** |
| SalariĂ©s | EmployĂ©s â EmployĂ©s | import de donnĂ©es | **40** |
| Articles | Ventes â Articles â Articles | import de donnĂ©es | **40** |
| Planning des Ă©quipes | Planning â Planning | import de donnĂ©es | **195** |
| Taxes | ComptabilitĂ© â Configuration â Taxes | crĂ©ation | **1** |
**Déroulé détaillé** *(extrait)*
> `10:36` · **Import de donnĂ©es** â 40 · *Contacts (clients, fournisseurs)*
> *Pourquoi : ajout de personnes physiques avec leurs coordonnées professionnelles (poste, email, téléphone).*
>
> `10:37` · **Import de donnĂ©es** â 40 · *SalariĂ©s*
> *Pourquoi : alimenter le module Planning avec les employés, les rÎles métier et un planning complet sur 5 jours.*
>
> `10:48` · **CrĂ©ation** â 1 · *Taxes*
> *Pourquoi : appliquer la TVA réduite sur le catalogue et enrichir les descriptions de vente.*
Chaque ligne est passĂ©e par le connecteur MCP et a Ă©tĂ© journalisĂ©e automatiquement â rien
n'est saisi Ă la main dans le rapport.
## đŹ Le guide de prĂ©sentation
`odoo_presentation_guide` produit le **déroulé à suivre en réunion client**, écran par écran,
déduit de ce qui a réellement été fait : seules les étapes correspondant aux données mises
en place apparaissent, dans l'ordre naturel du mĂ©tier (contacts â catalogue â devis â
livraison â facture â rentabilitĂ©).
Chaque Ă©tape donne le chemin de menu exact, les clics Ă faire sous forme de **cases Ă
cocher**, les enregistrements précis à ouvrir, et une phrase d'accroche à dire au client.
Le fichier HTML s'ouvre pendant la réunion : on coche au fur et à mesure, on n'oublie
aucune étape, et on garde le fil du discours.
```
### Ătape 3 â Du devis Ă la commande client
OĂč aller : Ventes â Commandes â Devis
[ ] Ouvrir un devis de la démonstration
[ ] Montrer les lignes : articles, quantités, prix
[ ] Expliquer le bouton « Confirmer » : le devis devient une commande ferme
> Ă dire : C'est le point de bascule â un clic sur « Confirmer », et le reste
de la chaĂźne se met en route tout seul.
```
Les journaux sont écrits en JSONL dans `~/odoo-mcp-journaux/` (une ligne par opération,
lisible et diffable), et un rapport peut ĂȘtre regĂ©nĂ©rĂ© plus tard Ă partir d'un journal
ancien via `journal_path`.
`odoo_recent_changes` complĂšte le dispositif : il interroge les champs d'audit d'Odoo
(`write_date`, `write_uid`), donc il voit aussi les modifications faites directement dans
l'interface par d'autres personnes.
## đ„ Importer un fichier
Trois modes à enchaßner, qui évitent d'écrire n'importe quoi dans la base :
```mermaid
flowchart LR
F["đ Fichier<br/>.xlsx / .csv"]
I["đ inspect<br/>structure et doublons<br/>hors ligne"]
C["â
check<br/>validation champ par champ<br/>sans écrire"]
R["đ run<br/>import par lots<br/>via load()"]
O[("đïž Odoo")]
F --> I --> C --> R --> O
classDef step fill:#f0f9ff,stroke:#0ea5e9,color:#082f49;
classDef odoo fill:#f3e8ff,stroke:#a855f7,color:#3b0764;
class F,I,C,R step; class O odoo;
```
1. **`inspect`** â structure du fichier : colonnes, taux de remplissage, valeurs distinctes,
doublons d'identifiant. Aucune connexion nécessaire.
2. **`check`** â construit les lignes et vĂ©rifie chaque champ contre le modĂšle, sans rien
écrire.
3. **`run`** â importe par lots via `load()`, l'import natif d'Odoo.
Le `mapping` relie les colonnes du fichier aux champs Odoo. Il est indispensable : les
en-tĂȘtes des fichiers exportĂ©s depuis Odoo sont des libellĂ©s d'interface (`Name*`,
`Sales Price`), jamais des noms de champs.
```json
{
"_columns": {
"Code": "id",
"Nom": "name",
"Pays": "country_id/id",
"Notes": null
},
"_constants": { "is_company": "True" },
"_replace": { "type": { "Goods": "consu" } }
}
```
Mapper une colonne sur `id` (External ID) rend l'import **rejouable** : une seconde exécution
met Ă jour au lieu de dupliquer. Et `load()` rejette un lot entier en cas d'erreur â un Ă©chec
ne laisse jamais de données à moitié écrites.
## đ PrĂ©parer une dĂ©monstration
Deux usages coexistent dans cet outil. Le **consultant** intervient sur des données
rĂ©elles : il lui faut de la traçabilitĂ©, d'oĂč le journal et le rapport. L'**avant-vente**
construit des données fictives pour un prospect : il lui faut de la vraisemblance, vite.
Pour ce second cas, `odoo_demo_questionnaire` fournit une trame de qualification en huit
sections â le mĂ©tier, ce qu'il vend, ses achats, son pilotage, sa facturation, **les
spécificités venant de ses propres clients**, son vocabulaire maison, et le problÚme qu'il
cherche à résoudre. C'est ce dernier point qui fait la différence entre une démonstration
gĂ©nĂ©rique et une dĂ©monstration oĂč le prospect se reconnaĂźt.
La composition de la maquette n'est pas codée dans le serveur : c'est l'assistant qui
l'écrit à partir des réponses. Aucun catalogue figé ne produira des noms d'articles et un
vocabulaire aussi justes qu'un modĂšle de langage â et surtout, cela fonctionne pour un
métier qu'on n'avait pas prévu.
### Peupler sans exploser les tokens
Ăcrire 40 fiches complĂštes coĂ»te cher : Ă chaque ligne, le modĂšle réécrit l'e-mail, la
rĂ©fĂ©rence, la TVA⊠des champs identiques et sans intĂ©rĂȘt mĂ©tier. `odoo_demo_generate` sĂ©pare
les deux : **le modÚle fournit le vocabulaire, le serveur déroule la plomberie.**
```json
{
"model": "product.template",
"gabarit": { "type": "consu" },
"sequence": { "default_code": "ART-{i:03}" },
"id_prefixe": "art",
"lignes": [
{ "name": "Article vitrine A", "list_price": 2.90 },
{ "name": "Coffret découverte", "list_price": 15.90 }
]
}
```
Le modÚle n'écrit que `lignes` (le vocabulaire, spécifique au métier) ; le serveur ajoute les
constantes (`gabarit`), les champs séquentiels (`{i}`, `{i:03}`, `{autre_champ}`), un e-mail
dérivé du nom (`email_depuis`) et un External ID rejouable (`id_prefixe`), puis importe par
lots via `load()`. **~20 lignes de vocabulaire au lieu de ~200 lignes de fiches**, sans perdre
en pertinence â la base reste taillĂ©e pour *ce* mĂ©tier.
> [!NOTE]
> Ce n'est **pas** de la génération de contenu figée : le serveur ne multiplie que les champs
> répétitifs. Deux modes, comme l'import : `check` (valide la recette sans écrire) puis `run`
> (importe ; exige `odoo_enable_write`). Le filet e-mail et le journal s'appliquent
> automatiquement, puisque tout passe par `load()`.
### Le filet e-mail, non négociable
> [!WARNING]
> Une base de démonstration n'est presque jamais neutralisée : Odoo y envoie de vrais
> courriels dÚs qu'on confirme une commande ou une facture. **Une adresse réelle dans un jeu
> fictif, et une vraie entreprise reçoit une fausse facture.**
```
odoo_demo_mode(actif=true)
```
Une fois activĂ©, **toute** adresse Ă©crite est réécrite vers `example.com` â domaine rĂ©servĂ©
par la RFC 2606, qui ne peut appartenir à personne. La garantie est posée au seul endroit
par lequel passent toutes les écritures, elle tient donc quel que soit l'outil utilisé :
crĂ©ation, modification, upsert, import de fichier, ou mĂȘme appel brut `odoo_execute`. Elle
couvre aussi les lignes imbriquées (commandes one2many/many2many) et les duplications
(`copy`), et **chaque** adresse d'un champ multi-destinataires est vérifiée, pas seulement
la derniÚre. Le domaine de remplacement n'est pas paramétrable : seuls les domaines réservés
(`example.com` & co.) sont acceptés. La partie gauche de l'adresse est conservée, donc
`jean.dupont@example.com` reste lisible à l'écran pendant la démonstration.
`odoo_demo_check` balaie une base reprise de quelqu'un d'autre et signale â ou corrige â les
adresses qui pourraient encore recevoir du courrier : contacts, employés, pistes
(`crm.lead`) et utilisateurs.
## đ GĂ©nĂ©rer un tableau de bord
Les graphiques produits sont **recalculĂ©s par Odoo Ă chaque ouverture** â ce ne sont ni des
images ni des valeurs figées. Chacun porte sa source, son regroupement, sa mesure et son
filtre :
```json
[
{"titre": "Chiffre d'affaires par mois", "model": "sale.order",
"groupby": ["date_order:month"], "mesure": "amount_untaxed",
"type": "line", "domaine": [["state","=","sale"]], "pleine_largeur": true},
{"titre": "Commandes par vendeur", "model": "sale.order",
"groupby": ["user_id"], "mesure": "__count", "type": "bar"}
]
```
Trois types : `bar` (comparer), `line` (suivre dans le temps), `pie` (répartition). Sur un
champ date, la granularitĂ© s'Ă©crit `champ:month` â aussi `day`, `week`, `quarter`, `year`.
`__count` compte au lieu de sommer.
Le modÚle, les champs de regroupement et le type de la mesure sont **vérifiés avant
écriture**, et la vérification fonctionne en lecture seule : on peut valider une maquette
avant de demander l'autorisation d'écrire. C'est important, car un tableau de bord qui
référence un champ inexistant s'ouvre vide, sans message d'erreur.
> [!NOTE]
> **[SPREADSHEETS.md](SPREADSHEETS.md) explique en détail** comment fonctionnent les
> tableurs Odoo : le format o-spreadsheet, les quatre façons de connecter des données et leur
> robustesse respective, le choix de la bonne source (`sale.report` plutĂŽt que `sale.order`âŠ),
> et ce que ce connecteur ne génÚre volontairement pas.
## ⥠Ăconomie de contexte
Le serveur rĂ©gule lui-mĂȘme ce qu'il renvoie, sans perdre l'accĂšs Ă la donnĂ©e.
**Il rogne, il ne refuse jamais.** Une réponse trop volumineuse est réduite progressivement ;
les lignes rendues restent complÚtes, les métadonnées sont préservées, et la réponse indique
toujours combien de lignes sur combien et l'`offset` pour la suite. Une troncature silencieuse
mĂšnerait Ă des conclusions fausses â c'est pire qu'une rĂ©ponse longue.
**Il dépose l'intégralité sur disque.** Quand un résultat dépasse le plafond, le jeu complet
est écrit dans `~/odoo-mcp-resultats/` et le chemin est renvoyé. L'assistant le relit avec
son propre outil de lecture, sans repasser par Odoo : quelques dizaines de tokens au lieu de
dizaines de milliers. Ces fichiers sont purgés automatiquement (au-delà de 7 jours ou de
50 fichiers).
**Il se resserre Ă mesure que la session avance.**
| Palier | Déclenché à | Plafond | Champs par défaut | Limite par défaut |
|---|---|---|---|---|
| confort | départ | 60 k car | 12 | 50 |
| économie | 200 k rendus | 30 k | 8 | 30 |
| strict | 500 k | 15 k | 6 | 20 |
**Les défauts sont sobres.** `odoo_search` sans `fields` choisissait autrefois *tous* les
champs â 169 000 tokens pour dix contacts. Il retient dĂ©sormais une douzaine de champs utiles
et écarte les types lourds. `odoo_fields` renvoie une forme compacte
(`"partner_id":"many2one>res.partner*"`) au lieu de 20 000 tokens de détail.
**Le schéma est mémorisé sur disque.** La premiÚre exploration d'une base (liste des modÚles,
`fields_get` modÚle par modÚle) est ce qui coûte le plus cher à chaque session. ModÚles et
champs sont donc conservés dans `~/odoo-mcp-cache/`, par base, et remplis à l'usage : un
modÚle déjà consulté n'est jamais redemandé à Odoo. Le cache est vérifié une fois par session
(version serveur + modules installés) et invalidé tout seul s'il ne correspond plus ;
`odoo_schema_status(refresh=true)` force le réexamen aprÚs une (dés)installation de modules.
Pour chercher sans tout lister : `odoo_find_models` et `odoo_find_fields` filtrent cÎté
serveur, et `odoo_fields_batch` décrit plusieurs modÚles en un seul aller-retour.
L'état de consommation est visible à tout moment dans `odoo_status`. Si le coût permanent des
dĂ©finitions d'outils gĂȘne, le levier restant est de dĂ©sactiver dans votre client MCP ceux
dont vous ne vous servez pas.
## đ§ Notes de terrain
- Inspecter les champs (`odoo_fields`) **avant** d'écrire : les noms changent entre versions
d'Odoo (en 19, `is_company` existe mais pas `company_type` ; `groups_id` est devenu
`group_ids` ; le contenu d'une piÚce jointe est passé de `datas` à `raw`).
- Préférer `odoo_upsert` à `odoo_create` pour toute donnée de maquette ou d'import.
- Un échec d'écriture sur un champ non fourni (ex. `credit_limit` en créant un contact) est un
problÚme de **droits Odoo**, pas de données.
- Les relations one2many (ex. `seller_ids`) **s'accumulent** à chaque écriture au lieu de se
remplacer â purger avant de rejouer un chargement.
- Archiver (`{"active": false}`) est presque toujours préférable à supprimer : Odoo n'a pas de
corbeille.
## đ Licence
PubliĂ© sous licence **MIT** â voir [LICENSE](LICENSE). Auteur : Damien Dechamps.
TDQS
Scored across 32 tools
Most tools have clearly distinct purposes: search, read, count, aggregate, name_find, create, write, unlink, upsert, etc. are all separate operations. The main ambiguity is odoo_execute, which is a generic method caller that could overlap with many others, and odoo_write vs odoo_update_where, though the descriptions clarify the difference (by IDs vs by domain).
All tools share the odoo_ prefix, which creates a strong brand, but the second part mixes nouns (models, fields, status), verbs (search, read, create), and verb-noun compounds (enable_write, update_where, name_find, get_attachment). Some names like odoo_name_find are oddly ordered, but overall the pattern is predictable and readable.
With 32 tools, the server is on the heavier side, but the scope is broad: it covers ORM operations, imports/exports, attachments, journaling, demo features, and dashboards. Each tool appears to serve a distinct function, so the count feels justifiable for an Odoo integration, even if it exceeds the typical well-scoped range.
The tool surface is remarkably complete: it includes connection management, model/field introspection, search/read/count/aggregate, create/write/unlink/upsert, bulk updates, file import/export, attachment retrieval, a journaling system, demo preparation, and dashboard creation/inspection. There are no obvious dead ends for common Odoo workflows, and odoo_execute covers any missing long-tail operations.