MCP générique pour HFSQL

, ,
mcp hfsql decouverte automatique

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 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

Liens externes