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, écritVotre 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.
L’installer
Section intitulée « L’installer »-
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 surhttp://localhost:8080, que le serveur utilise aussi quandBADLEN_URLn’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 utilisenthttps://badlen.example.com. -
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
node22.12.0 ou plus récent etpnpm12.5.1, les versions fixées par lepackage.jsonà la racine du dépôt. -
Lancez
pnpm installà la racine du dépôt pour récupérer le SDK MCP. -
Créez une clé API avec exactement ces cinq droits :
accounts:read,dashboard:read,positions:read,accounts:writeettransactions:write. -
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 versserver.mjset 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.mjsclaude_desktop_config.json {"mcpServers": {"badlen": {"command": "node","args": ["/absolute/path/to/packages/mcp/src/server.mjs"],"env": {"BADLEN_URL": "https://badlen.example.com","BADLEN_API_KEY": "sk_your_key"}}}}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.
Les huit outils
Section intitulée « Les huit outils »| 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_balancene 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 envoyezentryDate. 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 avecoccurrence: 2, et peut être rejoué tout aussi sûrement que le premier.
Importer un relevé
Section intitulée « Importer un relevé »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.
La route d’import
Section intitulée « La route d’import »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.
Format d’une ligne
Section intitulée « Format d’une ligne »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.
Les doublons
Section intitulée « Les doublons »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é.
La simulation
Section intitulée « La simulation »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.
Quand un appel échoue
Section intitulée « Quand un appel échoue »| 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.