Introduction
Dans un récent échange sur LinkedIn, une question simple m’a été posée : « Comment mettre en place un serveur MCP générique capable d’interroger dynamiquement n’importe quelle analyse HFSQL ? » Cet article propose une réponse concrète, réutilisable, et alignée avec les bonnes pratiques de modélisation de données.
L’objectif est de fournir au serveur MCP une fonction unique : getDataBaseModels(). Celle-ci expose la structure complète d’une analyse HFSQL (fichiers, rubriques, index, et relations) sous forme d’un modèle JSON. Ce modèle, auto-généré et auto-décrit, permet à un agent intelligent de déterminer quelles entités interroger et comment les relier selon les besoins exprimés par l’utilisateur.
Découverte du contenu d’une analyse HFSQL
1. Les fonctions Windev
Windev fournit toutes les fonctions nécessaires pour « découvrir » le contenu d’une analyse :
- HListeFichier : pour obtenir la listes des tables d’une analyse
- HListeRubrique : extrait les colonnes (ou champs) d’une table
- HListeClé : extraction des clés d’indexage
- HListeLiaison : listes les liaisons entre les champs avec les cardinalités
2 les structures nécessaires
Pour rendre l’analyse exploitable par le serveur MCP, il est indispensable de structurer les informations extraites sous une forme cohérente, claire et standardisée. Cela garantit une lisibilité optimale pour toute consommation automatisée, notamment par une IA.
Les quatre structures suivantes servent de socle au modèle retourné :
STR_Columns: décrit chaque colonne d’une table, avec son nom, sa description fonctionnelle et son type de données.STR_Indexes: formalise les index de la table (y compris la nature de l’unicité), en précisant le champ clé concerné.STR_Relationship: encode les liaisons entre deux tables (clef étrangère), avec la cardinalité dans chaque sens.STR_Table: agrège toutes les informations ci-dessus pour une table donnée : colonnes, index, et liaisons.
Cette structuration permet de passer d’un modèle relationnel natif HFSQL à un modèle de données sémantique exploitable directement par un moteur d’IA. Elle prépare également la sérialisation JSON de ces données pour exposition via API.
STR_Relationship est une Structure
name est une chaîne
table1 est une chaîne
column1 est une chaîne
cardinality1 est une chaîne
table2 est une chaîne
column2 est une chaîne
cardinality2 est une chaîne
FIN
STR_Indexes est une Structure
name est une chaîne
description est une chaîne
type est une chaîne
unicity est une chaîne
keycomponent est une chaîne
FIN
STR_Columns est une Structure
name est une chaîne
description est une chaîne
datatype est une chaîne
FIN
STR_Table est une Structure
table est une chaîne
description est une chaîne
columns est un tableau de STR_Columns
indexes est un tableau dynamique de STR_Indexes
relationships est un tableau de STR_Relationship
FIN
3. La fonction getDataBaseModels
L’API du serveur MCP expose getDataBaseModels() qui retourne un JSON décrivant analyses, fichiers, rubriques (nom + description), clés (détaillées) et liaisons. Ce context model est ensuite consommé par l’IA pour décider où et comment interroger HFSQL.
PROCÉDURE getDataBaseModels()
jsModel est un Buffer
// Déclaration des variables
sListeFichier est une chaîne
sListeRubrique est une chaîne
sListeClé est une chaîne
sListeLiaison est une chaîne
model est un tableau dynamique de STR_Table
// Chargement de la liste des fichiers de l'analyse
sListeFichier = HListeFichier(hLstDétail + hLstTout)
// Parcours de la liste des fichiers de l'analyse
POUR TOUTE CHAÎNE sNomFichier DE sListeFichier SÉPARÉE PAR RC
uneTable est un STR_Table
uneTable.table = ExtraitChaîne(sNomFichier,1,TAB)
uneTable.description = ExtraitChaîne(sNomFichier,2,TAB)
// Chargement des rubriques du fichier
sListeRubrique = HListeRubrique(uneTable.table, hLstDétailPlus)
// Chargement des clés du fichier
sListeClé = HListeClé(uneTable.table, hLstDétailPlus)
// Chargement des liaisons du fichier
sListeLiaison = HListeLiaison(uneTable.table,hLstDétail)
// traitements des rubriques
POUR TOUTE CHAÎNE sRubrique DE sListeRubrique SÉPARÉE PAR RC
uneRubrique est un STR_Columns
uneRubrique.name = ExtraitChaîne(sRubrique,1,TAB)
uneRubrique.description = ExtraitChaîne(sRubrique,6,TAB)
uneRubrique.sdatatype = ExtraitChaîne(sRubrique,2,TAB)
TableauAjoute(uneTable.columns, uneRubrique)
FIN
// traitements des indexes
POUR TOUTE CHAÎNE sClé DE sListeClé SÉPARÉE PAR RC
unIndex est une STR_Indexes
unIndex.name = ExtraitChaîne(sClé,1,TAB)
unIndex.description = ExtraitChaîne(sClé,6,TAB)
unIndex.type = ExtraitChaîne(sClé,2,TAB)
unIndex.keycomponent = ExtraitChaîne(sClé,4,TAB)
SELON ExtraitChaîne(sClé,5,TAB)
CAS "0" : unIndex.unicity = "primary"
CAS "1" : unIndex.unicity = "unique"
CAS "2" : unIndex.unicity = "multiple"
AUTRE CAS : unIndex.unicity = "unknown"
FIN
TableauAjoute(uneTable.indexes,unIndex)
FIN
// traitements des liaisons
POUR TOUTE CHAÎNE sLiaison DE sListeLiaison SÉPARÉE PAR RC
uneLiaison est un STR_Relationship
uneLiaison.name = ExtraitChaîne(sLiaison,1,TAB)
uneLiaison.table1 = ExtraitChaîne(sLiaison,2,TAB)
uneLiaison.column1 = ExtraitChaîne(sLiaison,3,TAB)
uneLiaison.cardinality1 = ExtraitChaîne(sLiaison,4,TAB)
uneLiaison.table2 = ExtraitChaîne(sLiaison,5,TAB)
uneLiaison.column2 = ExtraitChaîne(sLiaison,6,TAB)
uneLiaison.cardinality2 = ExtraitChaîne(sLiaison,7,TAB)
TableauAjoute(uneTable.relationships,uneLiaison)
FIN
// on ajoute la table au modèle BDD
model.Ajoute(uneTable)
FIN
Sérialise(model,jsModel,psdJSON)
RENVOYER jsModel
4 Le json retourné au MCP
Le tableau JSON ci-dessous illustre le résultat retourné par la fonction getDataBaseModels() pour une analyse contenant trois fichiers : Client, Ticket et Solution.
Chaque objet JSON correspond à une table. On y retrouve :
- La liste des colonnes avec noms, types et descriptions.
- Les index définis sur la table, y compris leur unicité (primaire, unique, multiple).
- Les relations inter-tables, exprimées avec les noms de champs de liaison et les cardinalités.
Ce modèle devient un véritable dictionnaire de données dynamique utilisable par le serveur MCP. Il autorise :
- Une navigation transverse entre les entités.
- Une interprétation automatisée des jointures possibles.
- Un mapping clair entre un prompt utilisateur (ex : « affiche les tickets récents d’un client ») et les fichiers HFSQL à interroger.
Ce JSON peut être facilement injecté dans une couche middleware (Node.js, Python, ou autre) ou directement consommé par des agents IA dans le cadre de requêtes NLP to SQL/NoSQL.
[
{
"table":"Client",
"description":"",
"columns":
[
{
"name":"IDClient",
"description":"Identifiant de Client",
"sdatatype":"N"
},
{
"name":"Societe",
"description":"Societe",
"sdatatype":"T"
},
{
"name":"Ville",
"description":"Ville",
"sdatatype":"T"
},
{
"name":"email_principal",
"description":"email_principal",
"sdatatype":"T"
}
],
"indexes":
[
{
"name":"IDClient",
"description":"Identifiant de Client",
"type":"N",
"unicity":"multiple",
"keycomponent":"IDClient"
}
],
"relationships":
[
{
"name":"Contrainte_Ticket_Client",
"table1":"Client",
"column1":"IDClient",
"cardinality1":"0,N",
"table2":"Ticket",
"column2":"IDClient",
"cardinality2":"1,1"
}
]
},
{
"table":"Ticket",
"description":"",
"columns":
[
{
"name":"IDTicket",
"description":"Identifiant de Ticket",
"sdatatype":"N"
},
{
"name":"date_creation",
"description":"date_creation",
"sdatatype":"T"
},
{
"name":"date_modification",
"description":"date_modification",
"sdatatype":"T"
},
{
"name":"libelle_ticket",
"description":"libelle_ticket",
"sdatatype":"T"
},
{
"name":"description_ticket",
"description":"description_ticket",
"sdatatype":"T"
},
{
"name":"IDClient",
"description":"Identifiant de Client",
"sdatatype":"N"
}
],
"indexes":
[
{
"name":"IDTicket",
"description":"Identifiant de Ticket",
"type":"N",
"unicity":"multiple",
"keycomponent":"IDTicket"
},
{
"name":"date_creation",
"description":"date_creation",
"type":"T",
"unicity":"primary",
"keycomponent":"date_creation"
},
{
"name":"date_modification",
"description":"date_modification",
"type":"T",
"unicity":"primary",
"keycomponent":"date_modification"
},
{
"name":"IDClient",
"description":"Identifiant de Client",
"type":"N",
"unicity":"primary",
"keycomponent":"IDClient"
}
],
"relationships":
[
{
"name":"Contrainte_Ticket_Client",
"table1":"Ticket",
"column1":"IDClient",
"cardinality1":"1,1",
"table2":"Client",
"column2":"IDClient",
"cardinality2":"0,N"
},
{
"name":"Contrainte_Ticket_Solution",
"table1":"Ticket",
"column1":"IDTicket",
"cardinality1":"0,1",
"table2":"Solution",
"column2":"IDTicket",
"cardinality2":"1,1"
}
]
},
{
"table":"Solution",
"description":"",
"columns":
[
{
"name":"IDSolution",
"description":"Identifiant de Solution",
"sdatatype":"N"
},
{
"name":"IDTicket",
"description":"Identifiant de Ticket",
"sdatatype":"N"
},
{
"name":"description_solution",
"description":"description_solution",
"sdatatype":"T"
}
],
"indexes":
[
{
"name":"IDSolution",
"description":"Identifiant de Solution",
"type":"N",
"unicity":"multiple",
"keycomponent":"IDSolution"
},
{
"name":"IDTicket",
"description":"Identifiant de Ticket",
"type":"N",
"unicity":"unique",
"keycomponent":"IDTicket"
}
],
"relationships":
[
{
"name":"Contrainte_Ticket_Solution",
"table1":"Solution",
"column1":"IDTicket",
"cardinality1":"1,1",
"table2":"Ticket",
"column2":"IDTicket",
"cardinality2":"0,1"
}
]
}
]
Conclusion
La combinaison des fonctions d’introspection HFSQL et d’une modélisation dynamique standardisée permet de construire un serveur MCP générique, indépendant de toute analyse spécifique. Grâce à la fonction getDataBaseModels(), l’IA dispose d’une source de vérité fiable, contextualisée, et interrogeable automatiquement.
Ce type d’architecture facilite :
- la réduction des erreurs de jointures ou de navigation dans la base.
- la scalabilité vers de multiples analyses HFSQL,
- l’automatisation de la génération des requêtes métier
