Spaces:
Running
Running
| """Gradio MCP Server for Indicateurs Territoriaux de Transition Écologique. | |
| This application exposes 4 MCP tools for querying French territorial | |
| ecological indicators via the Cube.js API. | |
| Tools: | |
| - list_indicators: List all indicators with optional filters | |
| - get_indicator_details: Get detailed info about a specific indicator | |
| - query_indicator_data: Query data values for a territory | |
| - search_indicators: Search indicators by keywords | |
| Usage: | |
| Run locally: | |
| python app.py | |
| Deploy on HuggingFace Spaces: | |
| Push to a Space with Gradio SDK configured. | |
| Connect as MCP Server: | |
| URL: http://your-server:7860/gradio_api/mcp/ | |
| """ | |
| import os | |
| import logging | |
| import gradio as gr | |
| from dotenv import load_dotenv | |
| # ============================================================================= | |
| # Logging Configuration - Filter out non-critical ASGI errors | |
| # ============================================================================= | |
| class ASGIErrorFilter(logging.Filter): | |
| """Filter to suppress known non-critical ASGI/MCP errors. | |
| These errors occur when external clients send malformed requests | |
| or when health checks hit MCP endpoints. They don't affect functionality. | |
| """ | |
| SUPPRESSED_MESSAGES = [ | |
| "Exception in ASGI application", | |
| "'NoneType' object is not callable", | |
| "Exception Group Traceback", | |
| ] | |
| def filter(self, record: logging.LogRecord) -> bool: | |
| """Return False to suppress the log record.""" | |
| message = record.getMessage() | |
| for suppressed in self.SUPPRESSED_MESSAGES: | |
| if suppressed in message: | |
| return False | |
| return True | |
| # Apply filter to uvicorn error logger | |
| uvicorn_error_logger = logging.getLogger("uvicorn.error") | |
| uvicorn_error_logger.addFilter(ASGIErrorFilter()) | |
| # Also filter the root logger for starlette errors | |
| logging.getLogger("starlette").addFilter(ASGIErrorFilter()) | |
| # Load environment variables | |
| load_dotenv() | |
| # Import tools | |
| from src.tools import ( | |
| list_indicators, | |
| get_indicator_details, | |
| query_indicator_data, | |
| search_indicators, | |
| ) | |
| from src.models import GEOGRAPHIC_LEVELS | |
| # Check if token is configured | |
| if not os.getenv("INDICATEURS_TE_TOKEN"): | |
| print("WARNING: INDICATEURS_TE_TOKEN not set. API calls will fail.") | |
| print("Set the token in .env file or as environment variable.") | |
| # Create individual interfaces for each tool | |
| list_interface = gr.Interface( | |
| fn=list_indicators, | |
| inputs=[ | |
| gr.Textbox( | |
| label="Thématique FNV", | |
| placeholder="Ex: mieux se déplacer, mieux se loger...", | |
| info="Filtre par thématique France Nation Verte (recherche partielle)", | |
| ), | |
| gr.Dropdown( | |
| choices=[""] + GEOGRAPHIC_LEVELS, | |
| label="Maille géographique", | |
| info="Filtre par niveau géographique disponible", | |
| ), | |
| ], | |
| outputs=gr.JSON(label="Indicateurs"), | |
| title="Lister les indicateurs", | |
| description="Liste tous les indicateurs disponibles avec filtres optionnels.", | |
| api_name="list_indicators", | |
| ) | |
| details_interface = gr.Interface( | |
| fn=get_indicator_details, | |
| inputs=[ | |
| gr.Textbox( | |
| label="ID de l'indicateur", | |
| placeholder="Ex: 611", | |
| info="Identifiant numérique de l'indicateur", | |
| ), | |
| ], | |
| outputs=gr.JSON(label="Détails"), | |
| title="Détails d'un indicateur", | |
| description="Retourne les métadonnées complètes et les sources d'un indicateur.", | |
| api_name="get_indicator_details", | |
| ) | |
| query_interface = gr.Interface( | |
| fn=query_indicator_data, | |
| inputs=[ | |
| gr.Textbox( | |
| label="ID de l'indicateur", | |
| placeholder="Ex: 611", | |
| info="Identifiant numérique de l'indicateur", | |
| ), | |
| gr.Dropdown( | |
| choices=GEOGRAPHIC_LEVELS, | |
| label="Niveau géographique", | |
| value="region", | |
| info="Maille territoriale à interroger", | |
| ), | |
| gr.Textbox( | |
| label="Code INSEE", | |
| placeholder="Ex: 93 (PACA), 13 (Bouches-du-Rhône)...", | |
| info="Code du territoire (optionnel)", | |
| ), | |
| gr.Textbox( | |
| label="Année", | |
| placeholder="Ex: 2020", | |
| info="Année des données (optionnel)", | |
| ), | |
| ], | |
| outputs=gr.JSON(label="Données"), | |
| title="Interroger les données", | |
| description="Récupère les valeurs d'un indicateur pour un territoire donné.", | |
| api_name="query_indicator_data", | |
| ) | |
| search_interface = gr.Interface( | |
| fn=search_indicators, | |
| inputs=[ | |
| gr.Textbox( | |
| label="Recherche", | |
| placeholder="Ex: consommation espace, surface bio, émissions CO2...", | |
| info="Mots-clés à rechercher dans le nom et la description", | |
| ), | |
| ], | |
| outputs=gr.JSON(label="Résultats"), | |
| title="Rechercher des indicateurs", | |
| description="Recherche des indicateurs par mots-clés.", | |
| api_name="search_indicators", | |
| ) | |
| # Combine all interfaces into a tabbed interface | |
| demo = gr.TabbedInterface( | |
| interface_list=[ | |
| list_interface, | |
| search_interface, | |
| details_interface, | |
| query_interface, | |
| ], | |
| tab_names=[ | |
| "Lister", | |
| "Rechercher", | |
| "Détails", | |
| "Données", | |
| ], | |
| title="MCP Server - Indicateurs Territoriaux de Transition Écologique", | |
| ) | |
| # Add a description block | |
| with demo: | |
| gr.Markdown( | |
| """ | |
| --- | |
| ### Connexion MCP | |
| Pour utiliser ce serveur comme outil MCP dans Claude Desktop, Cursor ou autre client MCP : | |
| ```json | |
| { | |
| "mcpServers": { | |
| "indicateurs-te": { | |
| "url": "https://YOUR-SPACE.hf.space/gradio_api/mcp/" | |
| } | |
| } | |
| } | |
| ``` | |
| ### Structure des données | |
| Les cubes de données suivent le format `{thematique}_{maille}` : | |
| - `conso_enaf_com` → Consommation ENAF, maille commune | |
| - `surface_bio_dpt` → Surface bio, maille département | |
| Les measures contiennent l'ID de l'indicateur : `{cube}.id_{indicator_id}` | |
| ### API Cube.js | |
| Ce serveur interroge l'API du Hub d'Indicateurs Territoriaux du Ministère de la Transition Écologique. | |
| - Documentation : [ecologie.data.gouv.fr/indicators](https://ecologie.data.gouv.fr/indicators) | |
| - API : `https://api.indicateurs.ecologie.gouv.fr` | |
| """ | |
| ) | |
| if __name__ == "__main__": | |
| demo.launch( | |
| mcp_server=True, | |
| server_name="0.0.0.0", | |
| server_port=7860, | |
| ) | |