Un serveur MCP de qualité des données expose des scores de qualité et des résultats de validation comme outils accessibles par l’IA. Cela permet d’interroger l’état de qualité en conversation — « quel est le score de qualité pour sales.orders ? » — plutôt que de naviguer dans un tableau de bord.
Le schéma s’applique aux équipes utilisant Great Expectations, Elementary, Soda ou des frameworks de qualité internes. Le serveur MCP encapsule l’API de la plateforme qualité, exposant les contrôles, scores et problèmes comme outils.
Le serveur
from mcp.server.fastmcp import FastMCP, Contextfrom pydantic import BaseModel, Fieldfrom enum import Enumimport json
mcp = FastMCP("DataQualityMCP")
class CheckType(str, Enum): COMPLETENESS = "completeness" UNIQUENESS = "uniqueness" FRESHNESS = "freshness" VALIDITY = "validity" CONSISTENCY = "consistency"
class QualityCheckResult(BaseModel): """Result of a single quality check.""" check_name: str check_type: CheckType table_name: str passed: bool score: float = Field(ge=0, le=1, description="Score between 0 and 1") rows_checked: int issues_found: int details: str | None = NoneL’enum CheckType contraint les dimensions de qualité que l’IA peut vérifier. Le modèle QualityCheckResult garantit une sortie cohérente sur tous les contrôles — l’IA reçoit toujours les mêmes champs, rendant la comparaison et l’agrégation fiables. Voir MCP Tool Design Patterns pour la justification de la sortie structurée.
Outils essentiels
Exécuter un contrôle de qualité
L’outil ciblé — exécuter un type de contrôle spécifique sur une table spécifique :
@mcp.tool()async def run_quality_check( table_name: str, check_type: CheckType, ctx: Context) -> QualityCheckResult: """Run a specific data quality check on a table.
Args: table_name: Table to validate check_type: Type of quality check to run ctx: Context for progress reporting
Returns: Detailed check results """ await ctx.info(f"Running {check_type.value} check on {table_name}") await ctx.report_progress(progress=0.5, total=1.0, message="Scanning table...")
# In real implementation, call your quality framework score = QUALITY_SCORES.get(table_name, {}).get(check_type.value, 0.5) passed = score >= 0.9
await ctx.report_progress(progress=1.0, total=1.0, message="Check complete")
return QualityCheckResult( check_name=f"{check_type.value}_{table_name.replace('.', '_')}", check_type=check_type, table_name=table_name, passed=passed, score=score, rows_checked=100000, issues_found=int((1 - score) * 100000), details=f"{'Passed' if passed else 'Failed'}: {score:.1%} of rows meet {check_type.value} criteria" )Le reporting de progression via l’objet Context est important ici. Les contrôles de qualité sur des tables volumineuses peuvent prendre des minutes. Sans reporting de progression, l’utilisateur ne sait pas si l’outil fonctionne ou est bloqué.
Vue d’ensemble du score de qualité
L’outil de consultation rapide — état de santé global d’une table :
@mcp.tool()def get_quality_score(table_name: str) -> str: """Get the overall data quality score for a table.
Args: table_name: Table to get scores for
Returns: Quality scores across all dimensions """ if table_name not in QUALITY_SCORES: return json.dumps({ "error": f"No quality scores found for '{table_name}'", "available_tables": list(QUALITY_SCORES.keys()) }, indent=2)
scores = QUALITY_SCORES[table_name] return json.dumps({ "table": table_name, "overall_score": scores["overall"], "dimensions": { "completeness": scores["completeness"], "uniqueness": scores["uniqueness"], "freshness": scores["freshness"], "validity": scores.get("validity", "not measured") }, "status": "healthy" if scores["overall"] >= 0.9 else "needs attention" }, indent=2)Retourner available_tables dans le cas d’erreur est un schéma à souligner. Quand l’IA demande une table qui n’existe pas dans le système qualité, elle obtient la liste de ce qui existe — et peut soit suggérer des alternatives, soit demander à l’utilisateur de clarifier. Bien mieux qu’une simple erreur « introuvable ».
Résumé des problèmes de qualité
L’outil de triage — « quoi nécessite mon attention ? »
@mcp.tool()def list_quality_issues(min_severity: str = "warning") -> str: """List tables with data quality issues.
Args: min_severity: Minimum severity to include ('warning' or 'critical')
Returns: Tables that need attention """ issues = [] threshold = 0.8 if min_severity == "critical" else 0.9
for table_name, scores in QUALITY_SCORES.items(): if scores["overall"] < threshold: dimensions = {k: v for k, v in scores.items() if k != "overall"} worst_dim = min(dimensions, key=dimensions.get)
issues.append({ "table": table_name, "overall_score": scores["overall"], "severity": "critical" if scores["overall"] < 0.8 else "warning", "primary_issue": worst_dim, "issue_score": dimensions[worst_dim] })
return json.dumps(sorted(issues, key=lambda x: x["overall_score"]), indent=2)Identifier le primary_issue — la dimension avec le score le plus bas — donne à l’IA quelque chose d’actionnable à rapporter. « La table customers a un problème de fraîcheur (0,75) » est plus utile que « la table customers a obtenu 0,87 ».
Validation complète
L’outil exhaustif — tout exécuter :
@mcp.tool()async def run_full_validation(table_name: str, ctx: Context) -> str: """Run all quality checks on a table.
Args: table_name: Table to validate ctx: Context for progress reporting
Returns: Complete validation report """ await ctx.info(f"Starting full validation of {table_name}")
results = [] checks = list(CheckType)
for i, check_type in enumerate(checks): await ctx.report_progress( progress=(i + 1) / len(checks), total=1.0, message=f"Running {check_type.value} check..." ) result = await run_quality_check(table_name, check_type, ctx) results.append({ "check": result.check_type.value, "passed": result.passed, "score": result.score })
overall_passed = all(r["passed"] for r in results)
return json.dumps({ "table": table_name, "validation_passed": overall_passed, "checks": results, "summary": f"{'All checks passed' if overall_passed else 'Some checks failed'}" }, indent=2)
if __name__ == "__main__": mcp.run(transport="stdio")Ce que vous pouvez demander
Avec ce serveur connecté :
- « Quel est le score de qualité des données pour la table orders ? »
- « Effectuez un contrôle de complétude sur sales.customers »
- « Quelles tables présentent des problèmes de qualité à investiguer ? »
- « Exécutez une validation complète sur la table revenue_daily »
- « Quel est le score de fraîcheur de nos données clients ? »
Considérations pour la production
Exécution des contrôles vs scores mis en cache. L’exemple confond l’exécution des contrôles avec la récupération de scores stockés. En production, séparez ces deux aspects — get_quality_score lit depuis la base de données de votre plateforme qualité (rapide, peu coûteux), tandis que run_quality_check déclenche un scan réel (lent, potentiellement coûteux). Rendez la distinction claire dans les descriptions des outils afin que l’IA utilise par défaut les scores mis en cache et n’exécute de nouveaux contrôles que sur demande explicite.
Configuration des seuils. Les seuils 0,9/0,8 pour warning/critical sont codés en dur dans l’exemple. En production, rendez-les configurables par table ou par type de contrôle. Certaines tables peuvent tolérer des scores de fraîcheur plus bas ; d’autres nécessitent une complétude à 100 %.
Tendances historiques. Un outil get_quality_trend montrant l’historique des scores sur les N derniers jours révèle des patterns de dégradation. « La fraîcheur se détériore depuis une semaine » est le type d’insight qui n’apparaît qu’avec un contexte historique.
Implémentations de référence. L’intégration MCP Elementary démontre des patterns de production pour l’observabilité des données, incluant l’historique des résultats de tests, la détection d’anomalies et la gestion des alertes. Étudiez-la avant de construire votre propre serveur de qualité.