Repository navigation
Releases: silamir/boondmanager-mcp-server
Release list
v2.19.1
Correctif : boond_documents_create accepte tout type d'entité parente (besoins et positionnements inclus). Mises à jour de dépendances, dont @modelcontextprotocol/sdk 1.30.1 → 1.31.0 (révision de protocole négociée inchangée : 2025-11-25). Aucun nom d'outil ne change. Catalogue : 238 outils.
Fixed
boond_documents_createaccepte désormais n'importe quel type d'entité parente, besoins (opportunity) et positionnements inclus.parentTypeétait une énumération fermée de 23 valeurs qui ne contenait pasopportunity: joindre un fichier à un besoin / AO échouait côté connecteur (« le connecteur refuse le type opportunité ») alors que rien n'avait été envoyé à BoondManager.parentTypedevient une chaîne validée par motif (^[A-Za-z][A-Za-z0-9]{0,63}$, un mot alphanumérique sans espace ni séparateur) et est transmise telle quelle àPOST /documents: l'API BoondManager reste l'autorité sur les types qu'elle accepte, et un type inconnu est refusé par elle (erreur remontée au modèle) sans qu'une liste figée côté MCP ait à suivre ses évolutions.opportunityest ajouté à la liste des types connus, reprise dans la description de l'outil pour guider le modèle ; les 23 valeurs existantes restent valides et inchangées. Vérifié sur un tenant de production : fichiers joints à un besoin (opportunity) et à un positionnement (positioning), visibles dans BoondManager. Aucun nom d'outil ne change. Catalogue : 238 outils.
Changed
- Dépendances :
@modelcontextprotocol/sdk1.30.1 → 1.31.0 (LATEST_PROTOCOL_VERSIONtoujours2025-11-25),proxy-addr2.0.7 → 2.0.8,source-map-js1.2.1 → 1.2.2 et le groupe de dépendances de développement. - CI : publication de la couverture de tests sur Qlty Cloud (rapport seulement, les seuils bloquants restent dans
ci.yml).
What's Changed
- chore: mise en conformité du dépôt by @silamir-compliance-bot[bot] in #329
- chore(deps-dev): bump the dev-dependencies group with 5 updates by @dependabot[bot] in #331
- chore(deps): bump @modelcontextprotocol/sdk from 1.30.1 to 1.31.0 by @dependabot[bot] in #332
- chore(deps): bump proxy-addr from 2.0.7 to 2.0.8 by @dependabot[bot] in #333
- ci: publish test coverage to Qlty Cloud by @fauguste in #335
- chore(deps-dev): bump source-map-js from 1.2.1 to 1.2.2 by @dependabot[bot] in #334
- fix(documents): accept any parent entity type in boond_documents_create (opportunities, positionings...) by @TVA-DKT in #336
- chore(release): 2.19.1 by @fauguste in #337
Full Changelog: v2.19.0...v2.19.1
v2.19.0
Relais d'upload pour joindre à BoondManager une pièce jointe de conversation (boond_documents_upload_slot, puis uploadSlot dans boond_documents_create), plus trois mises à jour de dépendances transitives (ip-address, fast-uri, brace-expansion). Aucun nom d'outil existant ne change. Catalogue : 238 outils.
Added
- Relais d'upload pour les pièces jointes de conversation :
boond_documents_upload_slot+uploadSlotdansboond_documents_create. Une pièce jointe glissée dans une conversation réside dans l'environnement d'exécution de code du client, ni à une URL publique ni sur le poste du serveur, et le modèle ne peut pas la retranscrire de façon fiable en base64. Le nouvel outil ouvre un slot à usage unique (15 min) sur un stockage de l'opérateur : le bac à sable y dépose le fichier en une requêtePUT(commandecurlfournie), sans que les octets passent par la sortie du modèle ;boond_documents_create({ uploadSlot })vérifie le fichier (taille, magic bytes), transmet à Boond une URL de lecture temporaire viafileUrl, puis le supprime définitivement, que Boond ait réussi ou non. Premier backend : SharePoint via Microsoft Graph (identifiants d'application Entra ID, permissionSites.Selectedlimitée à un site,permanentDeletesans corbeille). Désactivé par défaut (BOOND_MCP_UPLOAD_RELAY). Paramètre nomméuploadSlotet nonuploadId, pour respecter l'invariant « tout*Idest un ID Boond numérique ». Vérifié sur un locataire de production : PDF joint à une conversation attaché intact à un candidat, bibliothèque de transit vide après coup, corbeilles de premier et second niveau vides. Le fichier déposé est retrouvé en listant le dossier du slot, et l'URL de lecture est prise dans le listing ou, à défaut, dans la redirection de/content: SharePoint ne renvoie pas toujours l'annotationdownloadUrl. Catalogue : 238 outils.
What's Changed
- chore(deps-dev): bump brace-expansion from 5.0.9 to 5.0.12 by @dependabot[bot] in #325
- chore(deps): bump fast-uri from 3.1.7 to 3.1.8 by @dependabot[bot] in #326
- chore(deps): bump ip-address from 10.4.0 to 10.7.2 by @dependabot[bot] in #327
- feat(documents): upload relay for conversation attachments (boond_documents_upload_slot, SharePoint backend) by @TVA-DKT in #321
- chore(release): 2.19.0 by @fauguste in #328
Full Changelog: v2.18.0...v2.19.0
v2.18.0
Écritures sur les onglets candidat, projection des relations dans fields, téléversement de fichiers locaux ou inline dans boond_documents_create, et date de règlement sur les factures. Aucun nom d'outil existant ne change ; aucun changement de comportement sans configuration (filePath reste désactivé tant que BOOND_MCP_UPLOAD_DIRS n'est pas défini).
Fixed
boond_invoices_create/boond_invoices_updateacceptentperformedPaymentDate(date de règlement effectif) etpaymentMethod, attributs écrivables d'aprèsinformationBodyPut.json; sans eux, passer une facture à « payée » laissait BoondManager dater le règlement du jour. Non vérifié en réel : que Boond conserve la date envoyée dans le même PUT que le changement d'état. Si elle est encore écrasée, rappelerboond_invoices_updateune seconde fois avec ce seul champ.
Added
- Écriture des onglets candidat :
boond_candidates_technical_data_updateetboond_candidates_administrative_update(#318).PUT /candidates/{id}/technical-data(modesmerge|replace, même fusion que côté ressources) etPUT /candidates/{id}/administrative.boond_candidates_updategagneaddress,postcode,town,availability,mobilityAreas,globalEvaluationetinformationComments. Les deux nouveaux outils signalent les champs que l'API n'a pas renvoyés (BoondManager ignore sans erreur un attribut inconnu). Catalogue : 237 outils. fieldsprojette aussi les relations (#319) : un nom absent des attributs mais présent dansrelationships(dependsOnsur les actions / positionnements…) est rendutype#id(une liste pour une relation multiple), sur la sortie texte comme dansstructuredContent. Lister des actions avec l'entité à laquelle elles sont rattachées ne demande plus un_getpar ligne.boond_documents_create: téléversement d'un fichier local (filePath) ou inline (fileContent+fileName), en plus defileUrl. Jusqu'ici, attacher un document imposait de l'héberger sur une URL https publique et anonyme — impraticable pour un CV reçu par mail ou un justificatif glissé dans la conversation, et inacceptable pour des pièces RH ou contractuelles. Exactement une source par appel (contrôle dans le handler, message de correction explicite).filePath: désactivé par défaut, activé parBOOND_MCP_UPLOAD_DIRS(répertoires autorisés, chemins absolus) ; confinement vérifié sur lerealpathdes deux côtés (..et liens symboliques sortants refusés) ; plafondBOOND_MCP_UPLOAD_MAX_BYTES(20 Mo par défaut) ; refusé en transport HTTP, où le serveur ne tourne pas sur le poste de l'utilisateur.fileContent: base64 (préfixedata:accepté), tous transports, plafonné à 2 Mo — chaque octet coûte ~1,33 caractère de sortie au modèle. Les deux sources sont typées par magic bytes contre une liste blanche (PDF, DOCX/XLSX/PPTX, ODT/ODS, DOC/XLS/PPT, RTF, PNG, JPEG, GIF, WebP) ; l'extension ne sert qu'à départager les membres d'un conteneur ZIP/OLE et doit être cohérente avec le contenu.apiUploadFormaccepte une partie binaire optionnelle (DOCUMENT_UPLOAD_FILE_FIELD = "file"). La RAML ne documente pas le corps dePOST /documents: le nom de la partie multipart a été vérifié sur un tenant de production (PDF attaché à un candidat viafilePath, visible et intact dans l'interface).fileContentne sert qu'aux appelants qui détiennent déjà les octets : un modèle ne retranscrit pas de façon fiable une pièce jointe de conversation en base64.fileUrlinchangé ; aucun changement de comportement sans configuration.
Changed
resolveTransport()déplacé deindex.tsverssrc/config/transport-kind.ts, pour que les outils dont le comportement dépend du transport lisent la même règle que le point d'entrée.
What's Changed
- chore(deps): bump the github-actions group with 3 updates by @dependabot[bot] in #316
- chore(deps): bump @modelcontextprotocol/sdk from 1.30.0 to 1.30.1 in the production-dependencies group by @dependabot[bot] in #315
- chore: mise en conformité du dépôt by @silamir-compliance-bot[bot] in #314
- feat(candidates): outils d'écriture des onglets technical-data et administrative by @Antoine-Engibex in #318
- feat(search): projection des relations (type#id) dans
fieldsby @Antoine-Engibex in #319 - feat(documents): upload a local file (filePath) or inline base64 (fileContent) in boond_documents_create by @TVA-DKT in #320
- fix(invoices): accept performedPaymentDate and paymentMethod on invoice writes by @Ninhache in #323
- chore(release): 2.18.0 by @fauguste in #324
New Contributors
- @silamir-compliance-bot[bot] made their first contribution in #314
- @TVA-DKT made their first contribution in #320
- @Ninhache made their first contribution in #323
Full Changelog: v2.17.0...v2.18.0
v2.17.0
Clôture de l'audit technique et fonctionnel v2.16.0 (#265, 40 tickets) : les 21 tickets encore ouverts au matin du 26 septembre ont été livrés en une PR chacun (#291 → #312), CI verte à chaque merge. Le catalogue passe de 185 à 235 outils, 13 à 24 prompts, 22 à 48 ressources, 38 à 42 domaines ; tools/list de 382 à ~500 Kio (plafond CI relevé à 560 Kio, leviers BOOND_MCP_PROFILE / BOOND_MCP_ICONS=0 inchangés). Aucun nom d'outil existant ne change ; deux schémas deviennent plus stricts (boond_absences_search exige startMonth / endMonth, period devient une énumération sur sept recherches) et boond_documents_get renvoie par défaut le texte extrait (mode: "raw" pour l'ancien comportement).
Ce qui est vérifié et ce qui ne l'est pas. Les lectures ont été rejouées sur le tenant de production le jour même (smoke test 39/39, filtres de #248 mesurés sur meta.totals.rows, sondes de forme des routes d'écriture). Les nouveaux corps d'écriture — validations, drapeaux, todolists, avantages, inactivités, formulaires, regroupements, update des entités finance — sont déduits de la RAML et des modèles du dictionnaire et n'ont pas été exercés (tenant de production) ; chaque description le dit, et #311 liste ce qu'il reste à éprouver sur un tenant de test. Nouvelle dépendance runtime : unpdf (extraction de texte des PDF, 2 Mo, sans binaire natif) ; pino-pretty sort des dépendances de production.
Added
- Alertes du tableau de bord :
boond_alerts_search, ressourceboond://alerts/me, promptattention_du_jour(#255). BoondManager calcule déjà les alertes du tableau de bord (fins de contrat et de période d'essai, CRA manquants, factures en retard, prestations qui se terminent, opportunités sans action) et aucun outil ne les exposait : les prompts recomposaient ces règles à coups de recherches. Nouveau domainealerts(dans les cinq profils) :boond_alerts_search(GET /alerts— la RAML ne documente aucun paramètre, seule la projectionfieldscôté client est offerte), la ressourceboond://alerts/me(même liste en JSON, gated par le domaine, bornée àMAX_RESOURCE_BYTES) et le promptattention_du_jour(+ miroir) qui lit la ressource, classe par urgence et propose l'outil qui traite chaque module. Rendu d'aprèsmodels.alert(module,indicator,state,params, rapport quotidien / hebdomadaire), non éprouvé en maintenance — dit dans la description. Catalogue : 235 outils, 24 prompts, 48 ressources, 42 domaines. - Domaines
inactivities,forms,groupmentset sondes du moniteur API (#256). Trois domaines de l'API sans aucun outil : périodes d'inactivité / intercontrat (la métrique n°1 d'une ESN), formulaires (entretiens annuels, bilans de fin de mission, évaluations) et regroupements de prestations (facturation multi-prestations). Chacun ne documente quepost(pas de liste),defaultetrights:boond_{inactivities,forms,groupments}_default,_get,_create, plusboond_groupments_update(PUT /groupments/{id}). Corps déduits demodels.inactivity/form/groupment— non éprouvés (tenant de production, maintenance en cours), dit dans chaque description et dans CLAUDE.md.formentre dans les entités deboond_tasks_get. Profils :deliverygagne les trois,recruitinggagneforms. Le moniteur API (api-monitor.mjs) sonde désormaisquotations,kilometricExpenses,targets,workflows,warnings,savedSearches,tasks,attachedFlags—targets/search.ramletattachedFlags/search.ramlrépondent déjà 200 sur le site de doc,quotations404 (pas de domaine devis tant que la RAML n'existe pas). Catalogue : 233 outils, 41 domaines. - Écritures sur les drapeaux, todolists et avantages ; lecture des tâches et des drapeaux posés (#254). Le serveur écrit là où la RAML le documente :
boond_flags_create(POST /flags),boond_flags_attach/boond_flags_detach(POST/DELETE /attached-flags— le tagging en masse : trier une liste puis marquer fiche par fiche),boond_flags_attached(GET /{entité}/{id}/attached-flags, 11 entités),boond_tasks_get(GET /{entité}/{id}/tasks, 14 entités, un outil paramétré plutôt que 14 onglets),boond_todolists_create(POST /todolists),boond_advantages_default(GET /advantages/default) etboond_advantages_create(POST /advantages). Non exposés parce que non documentés : la création de tâche (tasks.ramln'a queget, pas de collection/tasks) et la création de fil (models.threadest vide). Corps déduits demodels.flag/todolist/advantageet non éprouvés sur un tenant de test — dit dans chaque description et dans CLAUDE.md. Catalogue : 223 outils. boond_absences_defaultetboond_rights_get— les endpointsdefaultetrights(#257).*/default.ramlexiste sur ~25 domaines et seuls les frais et les CRA l'exploitaient ;boond_absences_default(resourceId, agencyId?)litGET /absences-reports/defaultet rend les types d'absence (RTT, maladie, congés… avec leur codereference) depuisworkUnitTypesAllowedde la ressource incluse — publiés nulle part ailleurs — et les valeurs par défaut de la demande ;AbsenceCreateSchema.workUnitTypeReferencey renvoie au lieu d'un « défaut 1 » deviné.*/rights.ramlexiste sur ~20 domaines et aucun outil ne l'exposait ;boond_rights_get(entity, id)litGET /{entité}/{id}/rightspour 21 entités (énumération réduite par la politique d'accès) et rend les droits tels quels — un 403 évité coûte moins qu'un 403 expliqué ; les descriptions des*_update/*_deletede ces entités le mentionnent. Catalogue : 215 outils.boond_documents_getrend les documents lisibles : texte extrait des PDF / DOCX, images en contenu MCPimage(#263, reprend #181). Tout binaire partait en ressource embarquée base64 : un justificatif scanné n'était pas vu par le modèle, et un CV PDF de 5 Mio coûtait ~6,6 Mo de contexte pour quelques Ko de texte. Nouveau paramètremode(textpar défaut,raw= comportement précédent). Entext: PDF → texte viaunpdf(build serverless de pdf.js, sans binaire natif), DOCX → texte via un lecteur zip maison surnode:zlib(word/document.xml, paragraphes / tabulations / entités décodées), bornés àCHARACTER_LIMITavec taille et nombre de pages d'origine en en-tête ; image PNG / JPEG / GIF / WebP → contenuimagejusqu'àMAX_IMAGE_BYTES(2 Mio), au-delà ressource embarquée avec un mot d'explication ; PDF scanné / chiffré / illisible → fichier tel quel avec avertissement, pas d'erreur. Fixtures construites en code dans les tests (PDF minimal, zip STORED / DEFLATE).traiter_note_de_frais(justificatif) etpreparation_entretien(CV) en dépendent.- Prompt
ingest_communication(#180, rouvert par #264) : un e-mail, un compte rendu d'appel ou une note collés → contact, société et action créés ou rattachés. Sans Sampling (déprécié par la spec 2026-07-28 et implémenté par aucun client Claude) : c'est le modèle qui extrait, le serveur fournit le runbook. Déduplication avant toute création parboond_find(entity: "contact"+email, puis nom ; société par nom ou domaine), plan d'écriture présenté et validé explicitement avant les appels, écriture dans l'ordre des dépendances (boond_companies_create→boond_contacts_create→boond_actions_createavectypeOflu dansboond://dictionary/actions/contacts), rattachement optionnel à une opportunité. Catalogue : 213 outils, 23 prompts. - Huit workflows ESN :
relance_cra,absences_a_valider,marge_projet,preparation_entretien,preparation_rdv_client,relance_devis,purge_rgpd_candidats,preparation_facturation(#259), chacun avec son miroirboond_workflow_*. Les runbooks encodent les filtres exacts :validationStates/documentTypessurboond_validations_searchpuisboond_validations_updateune décision à la fois après accord (relance CRA, absences),period: "running"+boond_orders_invoices(deltaInvoicedExcludingTax= reste à facturer) pour la facturation,statesimpayés +period: "expectedPayment"etboond_actions_searchborné pour le brief client,boond://dictionary/states/opportunitiespuisopportunityStatespour les devis,period: "updated"+ pagination complète +boond_candidates_deleteun par appel (elicitation) pour la purge RGPD, simulation / productivité /boond_reporting_projectspour la marge, agrégatboond://candidate/{id}+boond_documents_get(CV) pour l'entretien. Dates et mois calculés côté serveur (periodBounds), périmètre partagé (managerScope:perimeterManagersexplicite ouperimeterDynamic: ['managers']),resolveEntityconnaît désormaiscandidateetcontact(complétion surcandidate_id/contact_id). Profils :invoicesrejointsales(brief client),timesheets+deliveriesrejoignentfinance(facturation),contractsrejointdelivery(alertes_contrats, #253) — sinon ces runbooks n'apparaissaient dans aucun profil ; table dedocs/access-control.mdrecalculée. Catalogue : 212 outils, 22 prompts. boond_validations_update: approuver ou refuser un CRA, une note de frais ou une demande d'absence (#251).boond_validations_search/_getne faisaient que lire ; la tâche de fin de mois la plus fréquente d'un manager n'avait pas d'outil.boond_validations_update(id, decision: validate | reject, reason?)envoiePUT /validations/{id}avec `state:...
v2.16.0
Le projet passe sous la bannière Silamir : le dépôt a été transféré de fauguste/boondmanager-mcp-server vers silamir/boondmanager-mcp-server. Aucun changement de code serveur — le catalogue (182 outils, 12 prompts, 22 ressources, 6 templates), les schémas annoncés et le comportement d'exécution sont identiques à la 2.15.2. Ce qui change relève de l'identité et de la distribution.
Le nom du package npm est inchangé (boondmanager-mcp-server) : tous les pins npx, les boutons d'installation 1-clic (Cursor, VS Code), le .mcp.json du plugin Claude Code et les configurations existantes continuent de fonctionner sans rien modifier.
Changed
- Identifiants indexés sur le propriétaire du dépôt. GitHub sert des redirections permanentes depuis l'ancien chemin, mais trois canaux sont indexés sur le owner et ne redirigent pas : les URLs
github.com/raw.githubusercontent.comsont repointées, les images de conteneur passent àghcr.io/silamir/boondmanager-mcp-server, et le plugin Claude Code s'installe désormais avec/plugin marketplace add silamir/boondmanager-mcp-server. - Nouvelle entrée au MCP Registry :
io.github.silamir/boondmanager-mcp-server. Le namespaceio.github.<owner>est vérifié par OIDC contre le propriétaire du dépôt, il n'existe donc pas de renommage : cette version crée une nouvelle entrée, et l'ancienne (io.github.fauguste/...) reste figée en 2.15.2 puis sera dépréciée. Un client qui référence l'ancien identifiant doit basculer sur le nouveau ; celui qui installe via npm n'a rien à faire. - Copyright et licence.
LICENSEetNOTICEportent désormais « Copyright 2025-2026 Silamir ». La licence reste Apache-2.0, inchangée, et la paternité d'origine est créditée dansNOTICE. Les champs author/owner depackage.json,manifest.json,marketplace.jsonet duplugin.jsongénéré désignent Silamir. - Icône du serveur.
icon.pngdevient la marque Silamir (blanc sur le bleu#1959FF). Elle remplace le logo BoondManager, qui était la marque d'un tiers employée par un connecteur tiers ; l'icône attribue maintenant correctement l'éditeur du serveur. Elle est visible dans Claude Desktop et sur la fiche du MCP Registry. - Mention de non-affiliation.
NOTICEet le README précisent que BoondManager est une marque de BoondManager SAS et que ce projet est un client indépendant et non officiel de son API publique.
Notes de distribution
- Docker Hub reste publié sous
fauguste/boondmanager-mcp-server: il n'existe pas encore d'organisation Silamir sur ce registre. C'est le seul endroit où les deux espaces de noms divergent légitimement, d'où les badges README pointant vers deux propriétaires différents. - Les fiches Glama, Smithery et LobeHub conservent leur ancien slug jusqu'au prochain ré-indexage de ces services, qu'ils déclenchent eux-mêmes. Les modifier par avance produirait des liens morts ;
docs/distribution.mddocumente les slugs attendus et le moment de les vérifier. - Les images publiées avant le transfert restent sur
ghcr.io/fauguste/...et ne sont pas redirigées — les références correspondantes dans les entrées antérieures de ce changelog sont donc exactes et laissées telles quelles.
What's Changed
- chore(deps): bump node from
ef24c50todbaa92eby @dependabot[bot] in #218 - chore(deps-dev): bump the dev-dependencies group with 4 updates by @dependabot[bot] in #219
- chore(deps): bump zod from 4.6.2 to 4.6.5 in the production-dependencies group by @dependabot[bot] in #220
- chore(deps): bump the github-actions group with 6 updates by @dependabot[bot] in #221
- chore(deps-dev): bump the dev-dependencies group with 3 updates by @dependabot[bot] in #222
- chore(branding): move the project under the Silamir banner by @fauguste in #223
- chore(release): 2.16.0 — première version sous bannière Silamir by @fauguste in #224
Full Changelog: v2.15.2...v2.16.0
v2.15.2
Correction du pipeline de release ; aucun changement de code serveur depuis la 2.15.1 (catalogue, schémas et comportement identiques). Cette version existe parce que la 2.15.1 n'a pas pu être publiée entièrement et qu'elle n'était pas rejouable — deux défauts distincts, tous deux corrigés ici.
Fixed
- La soumission au MCP Registry courait après la propagation npm. Le registre revalide
packages[0].versioncontre npm avant d'accepter la soumission, et la réplique de lecture de npm accuse un retard de quelques minutes sur son proprepublish. La 2.15.1 a perdu la course :NPM package 'boondmanager-mcp-server' exists, but version '2.15.1' was not found (status: 404). La course était latente depuis toujours, masquée par le défaut même que corrigeait la 2.15.1 :packages[0].versionétait une copie à la main figée sur une version ancienne et depuis longtemps propagée, donc le registre validait une version que l'on ne publiait pas. Épingler correctement ce champ est ce qui a rendu le défaut d'ordonnancement atteignable. Le workflow attend désormais que la version publiée soit réellement lisible sur npm (30 × 20 s) avant de soumettre. - Un échec du registre emportait les images de conteneur. GHCR et Docker Hub sont enchaînés après la soumission au registre alors qu'ils n'en dépendent en rien ; sur la 2.15.1 ils ont été sautés. Les deux étapes du registre échouent maintenant en douceur et leur échec est relevé après la construction des images — c'est l'entrée du registre, et elle seule, qui peut être resoumise isolément.
continue-on-errorplutôt qu'if: always()sur les étapes d'image : unalways()aurait aussi publié des images après un échec des tests. npm publishrendait la release non rejouable. Le workflow est un job unique :gh run rerun --failedle relance depuis le début, et npm refuse une seconde publication de la même version (You cannot publish over the previously published versions). Un échec tardif laissait donc la release à moitié faite, sans autre issue qu'un nouveau tag — exactement ce qui s'est produit. L'étape saute désormais une version déjà présente sur npm, ce qui ne coûte rien (npm l'aurait refusée) et fait du re-run le remède évident.
Note sur la 2.15.1 : elle est bien publiée sur npm et en GitHub Release (bundle
.mcpbinclus), mais n'a ni image de conteneur ni entrée au MCP Registry. La 2.15.2 lui est identique côté serveur et la remplace sur tous les canaux.
Full Changelog: v2.15.1...v2.15.2
v2.15.1
Version de correction de distribution : aucun changement de code d'exécution, aucun schéma annoncé ne bouge, le catalogue est inchangé (182 outils, 12 prompts, 22 ressources, 6 templates). Un client déjà connecté ne verra aucune différence. Déclenchée par l'issue #217, dont le défaut serveur — le $schema draft-07 sur les schémas annoncés — était déjà corrigé depuis la 2.12.2 : ce qui restait à corriger, c'était la possibilité d'installer une version plus ancienne que celle annoncée, et l'absence de tout point de documentation où reconnaître le symptôme.
Fixed
server.json.packages[0].versionétait figé à2.14.0alors que tout le reste du dépôt annonçait2.15.0. C'est le champ qu'un client du MCP Registry installe réellement : la version affichée était juste, la version installée avait un patch de retard. Le contrôle de cohérence de version en CI couvraitserver.json.versionmais aucune des entrées souspackages[]— il vérifie désormais les deux (le pin npm et levX.Y.Zde l'URL.mcpb), donc cette copie ne peut plus se fossiliser en silence. Même classe de défaut que le pin npm du plugin Claude Code, et même raison de la garder sous test : rien dans l'interface ne dit qu'une version épinglée n'est pas la version annoncée.
Added
- Test : les schémas annoncés compilent aussi sous un validateur draft-07 (
schema-dialect.test.ts, 182 outils, vrai client). Le pendant 2020-12 existait depuis la 2.12.2 ; celui-ci manquait, alors que c'est l'autre moitié de la justification deinstallSchemaDialectCompat— ne déclarer aucun dialecte n'est correct que tant que le catalogue n'emploie aucun mot-clé spécifique à un dialecte (itemstuple vsprefixItems,dependenciesvsdependentRequired,$recursiveRef). Sans ce test, l'apparition d'un tel mot-clé ferait de « ne rien déclarer » un mensonge silencieux au lieu d'un choix neutre. Ajv 8 (dialecte par défaut : draft-07) est de surcroît le client du SDK MCP lui-même, donc ce pair n'a rien d'hypothétique.
Documentation
- Nouvelle section
DépannagedansREADME.mdpour le symptôme de l'issue #217 : « JSON Schema declares an unsupported dialect (draft-07) » sur les seuls outils*_search. Le défaut serveur est corrigé depuis la 2.12.2 ; la section explique pourquoi la dissymétrie (recherches cassées, fiches et dictionnaires intacts) identifie à elle seule un binaire antérieur à cette version, pourquoi redémarrer le connecteur ne corrige rien (un redémarrage relance le binaire installé, il ne le met pas à jour), comment lire la version réellement chargée, et la marche à suivre par canal d'installation (.mcpb, plugin Claude Code,npx, Docker).
What's Changed
- chore(deps): bump node from
2d984a1toef24c50by @dependabot[bot] in #213 - chore(deps-dev): bump the dev-dependencies group with 4 updates by @dependabot[bot] in #214
- chore(deps): bump zod from 4.5.4 to 4.6.2 by @dependabot[bot] in #215
- chore(deps): bump the github-actions group with 3 updates by @dependabot[bot] in #216
Full Changelog: v2.15.0...v2.15.1
v2.15.0
Changed
- Contrat de description des outils — les 182 descriptions annoncées énoncent désormais quatre choses, dans cet ordre : le but (verbe + ressource + portée, une phrase), la guidance d'usage (
Quand :/Plutôt que :, nommant un outil frère réel), le comportement non déductible du schéma, etReturns :. Aucun schéma, aucun nom d'outil et aucune annotation ne change : un client déjà connecté voit le même catalogue, mieux décrit. Motivation : le catalogue compte ~45 outils qui prennent un simpleidet renvoient « quelques champs d'une entité », et l'état mesuré avant ce changement était 181 outils sur 182 sans aucune indication de l'outil à préférer, une médiane de description de 170 caractères et 30 outils sous 75 —boond_actions_getvalait « Récupère les détails d'une action par son ID. », soit la paraphrase de son propre nom. - Nouveau
src/tools/description-builders.ts:composeDescription()plus les cinq gabarits CRUD ettabDescription(). ~145 outils en héritent, ce qui aligne d'un coup la formulation des outils frères — c'est précisément ce qui permet à un modèle de les distinguer. - Nouveau
src/tools/tab-tools.ts: les 42 outils d'onglet passent par un uniqueregisterTabTools()au lieu de six boucles identiques (et six copies de l'interfaceTabDefinition) danscandidates.ts,resources.ts,contacts.ts,companies.ts,opportunities.tsetprojects.ts. UneTabDefinitionne porte plus que{ name, tab, title, subject, content?, returns }. - Les blocs
Args:qui paraphrasaient le schéma ont été retirés des gabarits. Le client a déjà reçu le JSON Schema avec ses propriétés.describe()-ées ; le redire coûte des octets et n'apprend rien. tools/listpasse de 292 à 359 KiB (+23 %). Compromis assumé : les descriptions sont ce qu'un modèle lit pour choisir un outil, et 48 KiB réparties sur 182 outils ne suffisaient pas à choisir juste. Les leviers de réduction existants restent disponibles (BOOND_MCP_PROFILE,BOOND_MCP_DOMAINS,BOOND_MCP_ICONS=0qui retire 40 KiB).hono4.12.34 → 4.13.7 (dépendance transitive, lockfile uniquement). Aucun code du serveur ne l'importe : elle arrive via l'outillage et n'est pas dans le chemin d'exécution du transport HTTP, qui utilise lehttpde Node et leStreamableHTTPServerTransportdu SDK.
Fixed
- Chiffres de pagination faux dans 11 domaines. Le gabarit de recherche par défaut de
crud-factory.tsannonçaitpageSize (défaut: 20, max: 100)alors que le schéma imposeDEFAULT_PAGE_SIZE/MAX_PAGE_SIZE, soit 30 et 500. Une description qui contredit son propre schéma est pire qu'une description absente : un modèle qui la croit plafonne ses pages à 100 et croit sonpageSize: 500refusé. Touchaitaccounts,agencies,business-units,calendars,flags,poles,products,roles,threads,todolists,webhooks. Les textes interpolent maintenantconstants.ts, un test signale tout littéral divergent, et un second test prouve que ce garde-fou attrape bien la chaîne historique — sans quoi il pourrait passer à vide. - Quatre routes de référence ignorent
maxResults, et le disent maintenant. Vérifié contre l'API réelle le 2026-09-09 :GET /poles?maxResults=2renvoie ses 73 lignes,/agenciesses 11,/calendars249,/webhooks3. Le serveur transmet bien le paramètre — c'est BoondManager qui l'écarte sur ces routes.pageSizeetpagey sont donc inertes, et leur annoncer «pageSize1–500,page1–100 » reproduisait exactement le défaut corrigé plus haut : une description qui promet un comportement que l'endpoint n'a pas. Ces outils sont listés dansTOOLS_IGNORING_PAGINATION(constants.ts) et reçoivent la phrase inverse. Sans conséquence opérationnelle — ce sont de petites tables, ~18 Ko de résultat au plus, loin deCHARACTER_LIMIT./business-units,/products,/threadset/todolistsrenvoyaient 0 ligne sur le tenant sondé : comportement inconnu, donc volontairement absents de la liste plutôt que devinés. fieldset la pagination sont désormais divulgués partout où le schéma les accepte (32 et 38 outils). Les deux portent une sémantique qu'un JSON Schema ne peut pas exprimer :fieldsest appliqué côté serveur MCP et n'est jamais transmis à l'API BoondManager, et le plafond de page refuse la requête au lieu de la ramener au maximum. C'était l'unique défaut réel deboond_poles_searchetboond_accounts_search, les deux outils les plus mal décrits du catalogue.
Added
src/tools/parameter-disclosure.tsetsrc/tools/usage-guidance.ts, appliqués par le ProxyregisterToolderegistration-decorators.ts— même mécanisme et même raison que les corrections de filtres : une règle qu'il faut se rappeler dans 38 fichiers est une règle qui se périme. Ajouter un outil de recherche divulgue maintenantfieldsque son auteur y ait pensé ou non.- Plancher de longueur de description (250 caractères) en plus du plafond, porté de 2000 à 2400. Le plancher est la moitié utile : c'est lui qui empêche un nouveau domaine de livrer un énième stub de 50 caractères. Le plafond a été relevé parce que
boond_resources_search— le vocabulaire de filtres le plus large du catalogue — atteint 2149 avec les deux lignes qui disent quand ne pas l'appeler. - 35 tests (1120 → 1155), dont la vérification bidirectionnelle de
USAGE_GUIDANCE: aucune entrée ne peut désigner un outil inexistant, et aucun outil ne peut se retrouver sans guidance. Les assertions portent sur ce qu'un vrai client reçoit, pas sur les arguments passés àregisterTool— la divulgation centrale est invisible d'un serveur mocké.
What's Changed
- feat(tools): passer la note Glama de B à A — contrat de description des outils by @fauguste in #211
- chore(deps): bump hono from 4.12.34 to 4.13.7 by @dependabot[bot] in #212
Full Changelog: v2.14.1...v2.15.0
v2.14.1
Version de maintenance : aucun changement fonctionnel. Le catalogue est inchangé (182 outils, 12 prompts, 22 ressources, 6 templates) et aucun schéma annoncé ne bouge — un client déjà connecté ne verra aucune différence. Le contenu est une remise à niveau des dépendances, dont une correction de vulnérabilité et un passage de Vitest en majeure.
Security
@humanfs/node0.16.7 → 0.16.8 (alerte Dependabot #44, moderate) : la copie récursive suivait les fichiers symlinkés et recopiait donc des données situées hors de l'arbre source. Dépendance de développement, transitive viaeslint, jamais empaquetée dans le serveur publié et jamais atteinte à l'exécution : l'exposition réelle se limitait au poste qui lancenpm run lint. Le correctif est un bump de lockfile seul —eslintdéclare^0.16.6, donc0.16.8était déjà dans la plage autorisée, sans mise à jour d'ESLint ni changement depackage.json.npm auditrepasse à 0 vulnérabilité.
Changed
- Vitest 4.1.10 → 5.0.0 et
@vitest/coverage-v8de même (majeure). Aucune adaptation nécessaire :vitest.config.tsn'utilise que des options stables (globals,environment,include,coverage.provider/reporter/thresholds), aucune source n'a changé, et les 64 fichiers de tests passent inchangés en tenant les mêmes seuils de couverture. La majeure réoutille la couverture en interne (istanbul-reportscède la place à@vitest/istanbul-lib-*). Note pour les contributeurs :@vitest/coverage-v8déclare un peer exact survitest, donc les deux paquets doivent être bumpés dans le même commit — Dependabot en ouvre une PR par paquet et chacune échoue alors surnpm cienERESOLVE, ce qui se lit à tort comme « Vitest 5 casse la suite ». - Dépendances applicatives (toutes dans les plages déjà déclarées, donc lockfile uniquement) :
zod4.4.3 → 4.5.4, et en transitifqs6.15.2 → 6.16.0,fast-uri3.1.5 → 3.1.7, plus quelques micro-bumps (hasown,side-channel,es-object-atoms).@modelcontextprotocol/sdkreste en 1.30.0 : le niveau de spec MCP négocié est donc toujours2025-11-25, inchangé. - Outillage de développement :
eslint10.8.1 → 10.9.1,typescript-eslint8.67.0 → 8.69.0,@types/node26.2.0 → 26.4.1,lint-staged17.3.0 → 17.4.1. - CI / image Docker :
github/codeql-actionv4.37.7 → v4.37.9,docker/setup-buildx-actionetdocker/setup-qemu-actionrepinnés sur v4,softprops/action-gh-releaserepinné sur v3, et le digest de l'image de basenode:26-alpinerafraîchi (aadf416→2d984a1). Toutes les actions restent épinglées par SHA.
Documentation
docs/distribution.mdsuit désormais le référencement dans awesome-ai-plugins (#194).CLAUDE.md: la section Testing annonçait « Vitest 4 » et « 1083 tests » — corrigé en Vitest 5 et 1120 tests (64 fichiers), le compte réel après les ajouts de la 2.14.0.
What's Changed
- chore(deps-dev): bump the dev-dependencies group with 2 updates by @dependabot[bot] in #195
- chore(deps): bump the github-actions group with 4 updates by @dependabot[bot] in #196
- chore(deps-dev): bump the dev-dependencies group with 4 updates by @dependabot[bot] in #197
- chore(deps): bump node from
aadf416to2d984a1by @dependabot[bot] in #198 - chore(deps): bump the github-actions group with 3 updates by @dependabot[bot] in #199
- docs(distribution): track the awesome-ai-plugins listing (#194) by @fauguste in #200
- chore(deps): bump fast-uri from 3.1.5 to 3.1.7 by @dependabot[bot] in #201
- chore(deps): bump qs from 6.15.2 to 6.16.0 by @dependabot[bot] in #202
- chore(deps-dev): bump the dev-dependencies group with 2 updates by @dependabot[bot] in #203
- chore(deps): bump zod from 4.4.3 to 4.5.4 by @dependabot[bot] in #206
- chore(deps): bump the github-actions group with 2 updates by @dependabot[bot] in #207
- chore(deps-dev): bump vitest and @vitest/coverage-v8 to 5.0.0 by @fauguste in #208
- fix(deps): bump @humanfs/node to 0.16.8 (Dependabot #44) by @fauguste in #209
- chore(release): 2.14.1 by @fauguste in #210
Full Changelog: v2.14.0...v2.14.1
v2.14.0
Photo de justificatif → ligne de frais dans BoondManager (#179). Le ticket demandait « juste un prompt » : la vérification préalable du schéma qu'il imposait a montré que l'outil d'écriture visé ne fonctionnait pas du tout, d'où un lot plus large que prévu. Catalogue : 182 outils (180 → 182), 12 prompts (11 → 12), 22 ressources, 6 templates.
Added
- Prompt
traiter_note_de_frais(+ son miroirboond_workflow_traiter_note_de_frais) : à partir d'une photo ou d'un PDF de justificatif joint à la conversation, le runbook extrait la dépense et crée la ligne de frais correspondante. Arguments :resource_id(défaut :boond_application_current_user),project_id,term,contexte— les deux premiers acceptent un ID ou un libellé. Trois garde-fous font le prompt, chacun verrouillé par un test. (1) Aucun montant inventé : un champ illisible, absent ou ambigu est demandé à l'utilisateur, pas déduit. (2) Récapitulatif puis validation explicite avant l'écriture — étape non optionnelle :boond_expenses_createn'est pas idempotent et une lecture visuelle peut se tromper d'un facteur 10 sur un montant ; le prompt doit rendre l'erreur visible avant l'appel, pas après. (3) La limite sur le justificatif est dite :boond_documents_createtéléverse depuis une URL uniquement (le serveur MCP ne lit jamais de fichier local — posture délibérée, non contournée ici) et une image collée dans la conversation n'a pas d'URL atteignable par BoondManager, donc la ligne est créée sans pièce jointe, avec deux échappatoires nommées : une URL https +parentType: "expensesReport", ou l'attachement manuel dans l'interface.domains: ["expenses", "application"]—resourcesest délibérément absent bien que le runbook puisse résoudre un collaborateur par son nom : un prompt est coupé dès qu'un de ses domaines est filtré, etresourcesn'est pas dans le profilfinance, précisément celui pour lequel ce prompt existe. - Outil
boond_expenses_default(GET /expenses-reports/default?resource=&term=) : retourne les référentiels sans lesquels une ligne de frais ne peut pas être écrite. Il existe parce que les codes de types de frais ne sont publiés nulle part ailleurs : ils sont définis par agence,/application/dictionaryn'a aucune table de types de frais (ni de cléexpensesreportdanssetting.state) et/agencies/{id}ne renvoie qu'un nom. La route donne aussi l'agencyId, le couple devise / taux de change, les barèmes kilométriques et les couples (projet, prestation) imputables pour cette ressource et ce mois — les deux IDs qu'une ligne ne peut pas omettre et que l'API refuse s'ils ne sont pas imputables. L'outil rend ces références en clair au lieu de renvoyer la réponse brute, qui inline l'intégralité dutimesreportde la ressource (chaque ligne de temps du mois) : des données qu'aucun payload de frais ne référence, en quantité suffisante pour consommer le budget de caractères. registerCreateToolaccepte unoverrides.description(même forme queregisterDeleteTool), ce qui permet àboond_expenses_createde dire d'appelerboond_expenses_defaultd'abord.
Fixed
-
boond_expenses_createetboond_expenses_updatene pouvaient rien écrire.ExpenseCreateSchemadécrivait un modèle qui n'existe pas :/expenses-reportsest un conteneur mensuel par ressource (term+resource) dont les lignes vivent dansactualExpenses— il n'existe pas d'endpoint de ligne de frais. Les champsamount,expenseDate,typeOfetcurrencysont du vocabulaire de ligne placé au niveau du rapport,notes'appelle en réalitéinformationComments, etstateétait typé entier alors que c'est une énumération de chaînes. Envoyé tel quel, ce payload répondait 422 à chaque appel. Les deux schémas sont réécrits sur le modèle réel, avec unExpenseLineSchemaimbriqué.La RAML ne documente rien de tout ça (
post: description: Create an expenses, et c'est tout) et/application/dictionarypublie les noms d'attributs sans dire lesquels sont obligatoires : le contrat a été vérifié champ par champ contre l'API réelle. Ce qu'il fallait découvrir, et que les schémas encodent désormais :exchangeRateAgencyest obligatoire à la création (1002sinon) →.default(1). À l'inversedata.typeest ignoré par l'API : la chaîne"expense"historique n'était pas le bug.stateest accepté puis ignoré, sur POST comme sur PUT : l'état est déplacé par le workflow de validation BoondManager, une création part toujours ensavedAndNoValidation. Le champ disparaît donc des deux schémas — l'exposer promettait une transition que l'endpoint n'effectue pas.- Une ligne exige, au-delà de
startDate/activityType/project/delivery/ montant, égalementbatch,isKilometricExpense,reinvoiced,currencyetexchangeRate. L'API les signale par vagues, un 422 par groupe : les découvrir à l'essai coûte cinq allers-retours. Ils sont donc.default()és sur leur valeur largement majoritaire plutôt que laissés au modèle. batchdoit être présent avec unidànullquand la ligne n'a pas de lot :{ id: "0" }et{ id: 0 }sont rejetés (1002), une clé absente est un1017. C'est le seul champ qui doit survivre au filtrage desundefineddebuildJsonApiBody, d'où son écriture inconditionnelle dansbuildExpenseLine— idem pourexpenseType, qui doit être présent et valoirnullsur une ligne kilométrique.actualExpensesen PUT remplace le tableau entier, alors que le PUT est par ailleurs partiel (n'envoyer queinformationCommentsconserve les lignes). C'est ce champ qui, seul à rompre le motif, efface un mois de frais en silence — dit dans la description du schéma et dans le runbook.- L'API ne déduplique pas : deux notes de frais peuvent coexister sur le même couple (ressource, mois). Une création à l'aveugle double le mois, d'où l'étape de recherche préalable dans le runbook.
- HT et TVA sont dérivés, pas stockés — c'était la question ouverte du ticket :
amountIncludingTax(TTC) ettax(un taux en %, pas un montant) sont ce qui persiste ;amountExcludingTaxettaxAmountsont acceptés à l'écriture, recalculés, et jamais renvoyés. Il n'y avait donc aucun champ à ajouter et rien n'est perdu à les omettre.
Contrat complet et pièges documentés dans
CLAUDE.md(section Expenses / notes de frais), à modifier contre l'API et non contre l'intuition. -
Le test
reaps idle stateful sessions on sweepn'accordait que 50 ms entre la réponseinitializeet la première passe de balayage. Cette fenêtre doit couvrir un aller-retour HTTP réel plus l'instanciation d'unMcpServercomplet et de ses ~180 enregistrements d'outils : le test mesurait la machine, pas la logique de balayage. Il échouait déjà par intermittence (2 fois sur 6 exécutions de la suite complète surmain) ; deux outils de plus l'ont rendu quasi systématique, ce qui l'a rendu visible. TTL porté à 1 s, attente à 1,1 s — les deux assertions (une session fraîche n'est pas récoltée, une session expirée l'est) restent intactes.
What's Changed
- feat(prompts): traiter_note_de_frais — photo de justificatif → note de frais (#179) by @fauguste in #193
Full Changelog: v2.13.0...v2.14.0