mcp / mcp_server.py
Tracy André
updated
256c0b7
"""
MCP Server for Agricultural Weed Pressure Analysis
Integrates tools, resources and prompts from the mcp/ folder
"""
import gradio as gr
import pandas as pd
import numpy as np
import plotly.express as px
from data_loader import AgriculturalDataLoader
from agricultural_mcp.tools import WeedPressureAnalyzer, analyze_herbicide_trends, predict_future_weed_pressure, recommend_sensitive_crop_plots, explore_raw_data, get_available_plots, get_available_crops, get_available_interventions
from agricultural_mcp.resources import AgriculturalResources
import warnings
warnings.filterwarnings('ignore')
# Initialize analyzer and resources
analyzer = WeedPressureAnalyzer()
resources = AgriculturalResources()
def create_mcp_interface():
"""
Create the main MCP interface with 5 tabs:
1. Analyse Tendances - IFT herbicide analysis
2. Prédictions - Weed pressure predictions 2025-2027
3. Recommandations - Sensitive crop recommendations
4. Exploration Données - Raw data exploration
5. MCP Resources - Display resources.py content
"""
with gr.Blocks(title="Serveur MCP - Analyse Pression Adventices", theme=gr.themes.Soft()) as demo:
gr.Markdown("# 🌾 Serveur MCP - Analyse Pression Adventices")
gr.Markdown("""
**Analyse de la pression adventices et recommandations pour cultures sensibles**
Ce serveur MCP (Model Context Protocol) fournit des outils d'analyse pour :
- Calculer l'IFT (Indice de Fréquence de Traitement) des herbicides
- Prédire la pression adventices pour 2025-2027
- Recommander des parcelles pour cultures sensibles (pois, haricot)
- Explorer les données agricoles avec filtres avancés
""")
with gr.Tabs():
# Tab 1: Analyse Tendances
with gr.Tab("📈 Analyse Tendances"):
gr.Markdown("### Analyse des tendances IFT herbicides")
gr.Markdown("""
**Méthode de calcul IFT :**
- IFT = Nombre d'applications herbicides / Surface de la parcelle
- Analyse de l'évolution temporelle par parcelle
- Classification des niveaux de risque : Faible (IFT < 1.0), Modéré (1.0 ≤ IFT < 2.0), Élevé (IFT ≥ 2.0)
""")
with gr.Row():
with gr.Column():
year_start = gr.Slider(
minimum=2014,
maximum=2025,
value=2014,
step=1,
label="Année de début"
)
year_end = gr.Slider(
minimum=2014,
maximum=2025,
value=2025,
step=1,
label="Année de fin"
)
plot_filter = gr.Dropdown(
choices=get_available_plots(),
value="Toutes",
label="Filtrer par parcelle"
)
analyze_btn = gr.Button("📊 Analyser les Tendances", variant="primary")
with gr.Row():
trend_plot = gr.Plot()
trend_summary = gr.Markdown()
analyze_btn.click(
analyze_herbicide_trends,
inputs=[year_start, year_end, plot_filter],
outputs=[trend_plot, trend_summary]
)
# Tab 2: Prédictions
with gr.Tab("🔮 Prédictions"):
gr.Markdown("### Prédictions de pression adventices 2025-2027")
gr.Markdown("""
**Méthode de prédiction :**
- Régression linéaire sur les données IFT historiques
- Extrapolation pour les années 2025-2027
- Classification des niveaux de risque basée sur l'IFT prédit
- Prise en compte de l'historique des cultures récentes
""")
with gr.Row():
predict_btn = gr.Button("🔮 Générer les Prédictions", variant="primary")
with gr.Row():
pred_plot = gr.Plot()
pred_summary = gr.Markdown()
predict_btn.click(
predict_future_weed_pressure,
outputs=[pred_plot, pred_summary]
)
# Tab 3: Recommandations
with gr.Tab("🌱 Recommandations"):
gr.Markdown("### Recommandations pour cultures sensibles")
gr.Markdown("""
**Critères de recommandation :**
- Parcelles avec IFT prédit < 1.0 (faible pression adventices)
- Score de recommandation : 100 - (IFT_prédit × 30)
- Cultures sensibles : pois, haricot
- Prise en compte de l'historique cultural récent
""")
with gr.Row():
recommend_btn = gr.Button("🌱 Générer les Recommandations", variant="primary")
with gr.Row():
rec_plot = gr.Plot()
rec_summary = gr.Markdown()
recommend_btn.click(
recommend_sensitive_crop_plots,
outputs=[rec_plot, rec_summary]
)
# Tab 4: Exploration Données
with gr.Tab("🔍 Exploration Données"):
gr.Markdown("### Exploration des données brutes")
gr.Markdown("""
**Filtres disponibles :**
- Années : 2014-2025
- Parcelles : 106 parcelles disponibles
- Cultures : 42 types de cultures
- Types d'intervention : Herbicides, Fertilisation, Semis, etc.
""")
with gr.Row():
with gr.Column():
data_year_start = gr.Slider(
minimum=2014,
maximum=2025,
value=2014,
step=1,
label="Année de début"
)
data_year_end = gr.Slider(
minimum=2014,
maximum=2025,
value=2025,
step=1,
label="Année de fin"
)
data_plot_filter = gr.Dropdown(
choices=get_available_plots(),
value="Toutes",
label="Filtrer par parcelle"
)
data_crop_filter = gr.Dropdown(
choices=get_available_crops(),
value="Toutes",
label="Filtrer par culture"
)
data_intervention_filter = gr.Dropdown(
choices=get_available_interventions(),
value="Toutes",
label="Filtrer par type d'intervention"
)
explore_btn = gr.Button("🔍 Explorer les Données", variant="primary")
with gr.Row():
data_plot = gr.Plot()
data_summary = gr.Markdown()
explore_btn.click(
explore_raw_data,
inputs=[data_year_start, data_year_end, data_plot_filter, data_crop_filter, data_intervention_filter],
outputs=[data_plot, data_summary]
)
# Tab 5: MCP Resources
with gr.Tab("🔧 MCP Resources"):
gr.Markdown("### Resources MCP disponibles")
gr.Markdown("""
**Resources MCP exposées :**
- `dataset://years` - Liste des millésimes disponibles
- `exploitation://{siret}/{millesime}` - Informations d'exploitation
- `parcelle://{millesime}/{numparcell}` - Informations de parcelle
- `intervention://{millesime}/{numparcell}/{rang}` - Informations d'intervention
- `intrant://{codeamm}` - Informations d'intrant
- `materiel://{millesime}/{numparcell}/{rang}` - Informations de matériel
- `maindoeuvre://{millesime}/{numparcell}/{rang}` - Informations de main d'œuvre
""")
with gr.Row():
with gr.Column():
gr.Markdown("#### Test des Resources MCP")
# Test Years
years_btn = gr.Button("📅 Liste des Années", variant="primary")
# Test Exploitation
with gr.Row():
siret_input = gr.Textbox(
label="SIRET Exploitation",
value="18560001000016",
placeholder="18560001000016"
)
siret_millesime_input = gr.Textbox(
label="Millesime",
value="2025",
placeholder="2025"
)
siret_btn = gr.Button("🏢 Test Exploitation", variant="secondary")
# Test Parcelle
with gr.Row():
parcelle_millesime_input = gr.Textbox(
label="Millesime",
value="2025",
placeholder="2025"
)
parcelle_input = gr.Textbox(
label="Numéro Parcelle",
value="12",
placeholder="12"
)
parcelle_btn = gr.Button("🏞️ Test Parcelle", variant="secondary")
# Test Intervention
with gr.Row():
intervention_millesime_input = gr.Textbox(
label="Millesime",
value="2025",
placeholder="2025"
)
intervention_parcelle_input = gr.Textbox(
label="Num Parcelle",
value="12",
placeholder="12"
)
intervention_rang_input = gr.Textbox(
label="Rang",
value="1",
placeholder="1"
)
intervention_btn = gr.Button("⚙️ Test Intervention", variant="secondary")
# Test Intrant
with gr.Row():
intrant_input = gr.Textbox(
label="Code AMM Intrant",
value="9100296",
placeholder="9100296"
)
intrant_millesime_input = gr.Textbox(
label="Millesime (optionnel)",
value="",
placeholder="2025"
)
intrant_btn = gr.Button("🧪 Test Intrant", variant="secondary")
# Test Matériel
with gr.Row():
materiel_millesime_input = gr.Textbox(
label="Millesime",
value="2025",
placeholder="2025"
)
materiel_parcelle_input = gr.Textbox(
label="Num Parcelle",
value="12",
placeholder="12"
)
materiel_rang_input = gr.Textbox(
label="Rang",
value="1",
placeholder="1"
)
materiel_btn = gr.Button("🔧 Test Matériel", variant="secondary")
with gr.Column():
gr.Markdown("#### Résultat")
resource_output = gr.JSON(label="Résultat de la resource")
# Connexions des boutons
years_btn.click(
lambda: resources.list_years(),
outputs=[resource_output]
)
siret_btn.click(
lambda siret, millesime: resources.get_exploitation(siret, millesime),
inputs=[siret_input, siret_millesime_input],
outputs=[resource_output]
)
parcelle_btn.click(
lambda millesime, parcelle: resources.get_parcelle(millesime, parcelle),
inputs=[parcelle_millesime_input, parcelle_input],
outputs=[resource_output]
)
intervention_btn.click(
lambda millesime, parcelle, rang: resources.get_intervention(millesime, parcelle, rang),
inputs=[intervention_millesime_input, intervention_parcelle_input, intervention_rang_input],
outputs=[resource_output]
)
intrant_btn.click(
lambda codeamm, millesime: resources.get_intrant(codeamm, millesime if millesime else None),
inputs=[intrant_input, intrant_millesime_input],
outputs=[resource_output]
)
materiel_btn.click(
lambda millesime, parcelle, rang: resources.get_materiel(millesime, parcelle, rang),
inputs=[materiel_millesime_input, materiel_parcelle_input, materiel_rang_input],
outputs=[resource_output]
)
return demo