Aller au contenu

Avec votre assistant

Badlen fournit un serveur MCP qui propose huit outils construits sur l’API /api/v1. C’est par lui qu’un relevé entier peut entrer d’un coup dans le produit.

Vos relevés ──▶ votre client MCP ──▶ outils MCP ──▶ Badlen /api/v1
lit et extrait import_transactions dédoublonne, écrit

Votre client lit les fichiers sur votre machine, avec le modèle que vous payez ou faites déjà tourner. Le serveur se contente de faire le lien par stdio et n’embarque aucun modèle : Badlen n’appelle donc jamais de fournisseur de modèle, aucune clé de modèle n’est stockée sur le serveur, et seules les opérations déjà extraites d’un relevé parviennent à Badlen.

Vous n’avez rien à démarrer ni à exposer. Le serveur est un script Node que votre client lance quand il en a besoin et avec lequel il communique par l’entrée et la sortie standard. Il n’a donc ni port, ni adresse d’écoute, ni interrupteur dans Badlen : fermer le client suffit à l’arrêter. Toute sa configuration tient en deux variables d’environnement que le client lui transmet, BADLEN_URL et BADLEN_API_KEY, définies à l’endroit où ce client déclare le serveur.

  1. Il vous faut une instance Badlen en marche, et son adresse exacte, protocole compris. C’est l’adresse de votre propre instance, celle qu’attend BADLEN_URL. Une pile installée selon Configuration avec les valeurs par défaut répond sur http://localhost:8080, que le serveur utilise aussi quand BADLEN_URL n’est pas renseignée. Si vous avez changé WEB_PORT, placé la pile derrière un reverse proxy ou choisi votre propre domaine, cette valeur par défaut ne convient plus : indiquez l’adresse que vous tapez dans le navigateur. Les exemples ci-dessous utilisent https://badlen.example.com.

  2. Installez Node et pnpm sur la machine où tourne le client. Le serveur est un script Node que votre client démarre directement, hors de Docker : une machine qui n’a suivi que Installation n’a ni l’un ni l’autre. Il faut node 22.12.0 ou plus récent et pnpm 12.5.1, les versions fixées par le package.json à la racine du dépôt.

  3. Lancez pnpm install à la racine du dépôt pour récupérer le SDK MCP.

  4. Créez une clé API avec exactement ces cinq droits : accounts:read, dashboard:read, positions:read, accounts:write et transactions:write.

  5. Déclarez le serveur dans votre client. Tous les clients MCP demandent les trois mêmes éléments, chacun avec sa syntaxe : la commande node, le chemin absolu vers server.mjs et les deux variables. Le chemin doit être absolu, car le client ne lance pas le serveur depuis le dépôt. Voici deux exemples :

    Fenêtre de terminal
    claude mcp add badlen \
    -e BADLEN_URL=https://badlen.example.com \
    -e BADLEN_API_KEY=sk_your_key \
    -- node /absolute/path/to/packages/mcp/src/server.mjs

    Un client qui pilote un modèle que vous faites tourner vous-même, avec Ollama par exemple, accède à ces outils de la même façon, avec les deux mêmes variables. Obtenir d’un tel client qu’il lise correctement un relevé est un usage avancé, qui sort du cadre de cette page.

Outil Rôle
list_accounts Les comptes, avec id, nom, type et valeur, pour que le client sache où importer
get_net_worth Le patrimoine net et l’allocation par classe d’actifs
get_positions Les lignes par titre, avec les plus-values latentes et réalisées
create_account Créer un compte où importer
set_balance Saisir le solde d’un compte manuel à une date donnée
add_holding Enregistrer un achat de fonds ou d’ETF par ISIN, cours résolu automatiquement
reconcile_transactions Simulation : comparer un envoi au compte sans rien écrire
import_transactions Écrire l’envoi, sans les doublons

Aucun ne supprime quoi que ce soit. Avant de laisser un client réessayer, sachez ce que produit un second appel identique :

  • set_balance ne garde qu’un solde par compte et par jour : un second appel pour le même jour remplace le premier, dont le chiffre est perdu. Par ailleurs, le compte est valorisé à son solde daté le plus récent : c’est pourquoi une date située des années dans le futur est refusée au lieu d’être enregistrée.
  • add_holding écrit une ligne par achat : racheter un titre que le compte détient déjà ajoute une ligne, comme dans tout portefeuille vivant. Un achat est identifié par sa date d’entrée, sa quantité, son coût et ses frais : le même appel reçu deux fois n’écrit la ligne qu’une fois, et renvoie la ligne existante. C’est cette identification qui permet de rejouer l’appel sans risque, donc envoyez entryDate. Sans elle, sur un titre que le compte détient déjà, l’appel est refusé sans rien écrire et cite les lignes existantes, car plus rien ne distingue alors un second achat d’un appel répété après une coupure réseau. Un vrai second achat identique le même jour se déclare avec occurrence: 2, et peut être rejoué tout aussi sûrement que le premier.

Indiquez les fichiers à votre client et demandez-lui explicitement de les importer dans Badlen. Il normalise les opérations, appelle reconcile_transactions pour repérer les doublons et vous poser ses questions, puis appelle import_transactions une fois que vous avez répondu. Renvoyer le même relevé ne présente aucun risque : les doublons sont écartés. C’est aussi le cas d’un achat envoyé deux fois, à condition qu’il ait sa date d’entrée, comme expliqué plus haut.

Un relevé, un envoi. Le dédoublonnage s’arrête aux limites d’un envoi : deux opérations identiques le même jour (deux paiements par carte du même montant chez le même commerçant) se distinguent par leur position dans l’envoi. Si vous découpez un relevé en plusieurs appels, l’une des deux opérations identiques est perdue, et la réponse annonce un doublon écarté : on croirait voir la protection fonctionner normalement.

Les deux outils d’import appellent une seule route, POST /api/v1/import, et sa simulation, POST /api/v1/import/reconcile, avec le droit transactions:write de la clé. Un script à vous utiliserait la même route et la même clé ; cette section explique comment elles traitent un envoi, quel qu’en soit l’expéditeur.

La route attend un identifiant de compte et un tableau de lignes normalisées. C’est à l’appelant de convertir un fichier de courtier dans ce format.

Champ Obligatoire Format
date oui YYYY-MM-DD, un jour du calendrier, de 1900 à demain
amount oui Chaîne décimale signée ; négatif pour l’argent qui sort
label oui Texte non vide
currency non Trois lettres ; EUR à défaut
fee non Chaîne décimale positive ou nulle
tax non Chaîne décimale positive ou nulle

Les montants circulent sous forme de chaîne décimale, jamais de nombre JSON : le temps que l’API le lise, un nombre est devenu un flottant double, et une quantité perd ses dernières décimales en route. Une requête qui contient un nombre est refusée, comme un chiffre qui dépasse la capacité d’une colonne monétaire.

Un envoi compte 20 000 lignes au plus, davantage qu’aucun relevé, et il est accepté en entier ou pas du tout. Il suffit d’une ligne rejetée par le schéma pour que tout l’envoi soit refusé ; le message d’erreur donne la position de la ligne dans l’envoi et explique ce qui ne va pas dans sa valeur. Rien n’a été écrit : vous pouvez corriger les lignes et renvoyer le même envoi.

Le schéma accepte aussi type et counterparty, mais l’import ne les enregistre pas. Il n’accepte pas non plus de catégorie, et n’en a pas besoin : c’est l’application qui classe les opérations, quelle que soit leur provenance. L’import classe donc chaque ligne au moment de l’écrire, d’abord selon vos propres règles, puis selon les mots-clés fournis. La réponse indique dans categorized combien de lignes ont été classées ; une ligne qu’aucune règle ne reconnaît reste sans catégorie, et Badlen n’en devine pas.

Pour les opérations importées avant l’arrivée de ce classement, utilisez Recatégoriser dans Réglages → Catégories : il traite les opérations sans catégorie et ne touche jamais à une opération déjà classée.

Chaque ligne reçoit une empreinte calculée à partir de sa date, de son montant et de son libellé mis en minuscules. Une ligne dont l’empreinte existe déjà sur le compte est écartée : c’est ce qui permet de renvoyer le même relevé sans risque. Deux lignes identiques d’un même envoi sont numérotées différemment, et les deux sont donc écrites. Ce numéro dépend de l’envoi, jamais de ce qui est déjà stocké : voilà pourquoi un relevé doit partir en un seul appel.

Une opération que vous avez supprimée après un import reste connue. Badlen conserve une trace de la suppression pour cette empreinte, et la même ligne n’est pas réécrite lors d’un nouvel import du même relevé.

POST /api/v1/import/reconcile accepte exactement la même requête et n’écrit rien. Elle renvoie la répartition de l’envoi (lignes nouvelles, lignes déjà présentes, lignes supprimées auparavant), sa plage de dates, les totaux des entrées et des sorties, le nombre d’opérations déjà présentes sur le compte et la date de la plus récente, ainsi que les lignes qui correspondent à une date et à un montant existants sous un autre libellé. Elle renvoie aussi les questions qu’il vaut la peine de poser avant de valider.

Les deux appels comptent avec la même fonction, et l’import répond avec les mêmes chiffres : ce qu’annonce la simulation est exactement ce que fera l’écriture.

Message Signification
401 sur tous les outils La clé est absente, inconnue, révoquée ou expirée
403 sur un seul outil La clé a été créée sans le droit dont cet outil a besoin
400 sur un montant Les montants s’écrivent en chaînes décimales ; un nombre JSON est refusé
400 sur une date Une date fait dix caractères, YYYY-MM-DD, désigne un jour qui existe au calendrier, et pas plus tard que demain
400 nommant une ligne Une ligne de l’envoi a été refusée. Le message donne sa position dans l’envoi et ce qui ne
va pas dans sa valeur, et rien n’a été écrit : corrigez ces lignes et renvoyez le même envoi
Cannot reach Badlen at … Rien n’a répondu à cette adresse. Le message cite l’adresse essayée et précise si elle vient de BADLEN_URL ou de la valeur par défaut : vérifiez que l’instance tourne et qu’elle
répond bien sur ce port. C’est la panne que la plupart des utilisateurs rencontrent en premier

Toutes les routes qu’utilisent ces outils sont décrites dans la référence de l’API REST, générée à partir du contrôleur plutôt qu’écrite à la main.