From 04125f91f80a769df11752df6969fdd79302eb4a Mon Sep 17 00:00:00 2001 From: Colin Maudry Date: Mon, 13 Jul 2026 09:23:33 +0200 Subject: [PATCH] Ajout du tool schema_donnees --- src/mcp/queries.py | 28 ++++++++++++++++++++++++++-- src/mcp/tools.py | 19 ++++++++++++++++++- 2 files changed, 44 insertions(+), 3 deletions(-) diff --git a/src/mcp/queries.py b/src/mcp/queries.py index 14833d3..0372ff4 100644 --- a/src/mcp/queries.py +++ b/src/mcp/queries.py @@ -1,11 +1,11 @@ # src/mcp/queries.py import re -from src.api.filters import FilterError, build_where +from src.api.filters import OPERATORS, FilterError, build_where from src.db import aggregate_marches, count_marches, query_marches from src.db import schema as duckdb_schema from src.mcp.serialization import to_json_records -from src.utils.data import DF_ACHETEURS, DF_TITULAIRES +from src.utils.data import DATA_SCHEMA, DF_ACHETEURS, DF_TITULAIRES from src.utils.search import search_org PAGE_SIZE = 50 @@ -41,6 +41,30 @@ _NAMED_FILTERS = [ ] +def describe_schema() -> dict: + """Schéma des marchés (même source que /schema de l'API REST). + + Limité aux colonnes réellement filtrables via filtres_avances (présentes + dans la table DuckDB). L'agrégation (groupby/sum/...) n'est pas supportée + ici : c'est une fonctionnalité de l'API REST /data. + """ + colonnes = { + name: { + "type": field.get("type"), + "titre": field.get("title"), + "description": field.get("description"), # inclut les énumérations + } + for name, field in DATA_SCHEMA.items() + if name in duckdb_schema # exclut la colonne virtuelle "marche" + } + return { + "colonnes_filtrables": colonnes, + "colonnes_retournees": MARCHES_COLUMNS, + "operateurs": sorted(OPERATORS), + "filtres_nommes": {p: f"{c}__{o}" for p, c, o in _NAMED_FILTERS}, + } + + def build_where_args( named: dict, filtres_avances: dict | None ) -> list[tuple[str, str]]: diff --git a/src/mcp/tools.py b/src/mcp/tools.py index 1f08a3b..d676e5e 100644 --- a/src/mcp/tools.py +++ b/src/mcp/tools.py @@ -45,6 +45,22 @@ def stats_titulaire(titulaire_id: str) -> dict: return queries.compute_org_stats("titulaire", titulaire_id) +@mcp_enabled(name="schema_donnees", expose_docstring=True) +def schema_donnees() -> dict: + """Schéma des données marchés (DECP) pour construire des filtres. + + À consulter avant d'utiliser `filtres_avances` de rechercher_marches. + - colonnes_filtrables : {colonne: {type, titre, description}}, utilisables + comme "colonne__operateur" (la description inclut les valeurs possibles). + - colonnes_retournees : colonnes présentes dans chaque marché renvoyé. + - operateurs : opérateurs de filtre valides (exact, contains, greater, less, + in, isnull, sort…). L'agrégation n'est pas supportée ici (API REST /data). + - filtres_nommes : correspondance paramètre nommé -> "colonne__operateur". + """ + track_mcp_tool("schema_donnees") + return queries.describe_schema() + + @mcp_enabled(name="rechercher_marches", expose_docstring=True) def rechercher_marches( acheteur_id: str | None = None, @@ -66,7 +82,8 @@ def rechercher_marches( date_min / date_max (format YYYY-MM-DD, sur dateNotification), departement (code département de l'acheteur). filtres_avances : dict optionnel {"colonne__operateur": valeur} pour les - besoins pointus (mêmes colonnes/opérateurs que l'API REST colibre). + besoins pointus. Colonnes et opérateurs disponibles via l'outil + schema_donnees(). page : numéro de page (50 résultats par page). Retourne {meta: {page, page_size, total}, marches: [...]}. """