Spaces:
Running
Running
File size: 6,663 Bytes
bad6218 67fee40 bad6218 67fee40 bad6218 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 |
"""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,
)
|