Tracy André commited on
Commit
256c0b7
·
1 Parent(s): c2f1b4d
Files changed (4) hide show
  1. PROMPT.md +1 -1
  2. agricultural_mcp/resources.py +216 -75
  3. mcp_server.py +62 -18
  4. test_fixed_resources.py +47 -0
PROMPT.md CHANGED
@@ -37,7 +37,7 @@ hf_token = os.environ.get("HF_TOKEN")
37
  dataset_id = "HackathonCRA/2024"
38
 
39
 
40
- (dataset accessible via HF avec cet id et ce token, synchronisé depuis OneDrive_1_9-17-2025).
41
 
42
  Fournir au LLM des tools et resources pour :
43
 
 
37
  dataset_id = "HackathonCRA/2024"
38
 
39
 
40
+ (dataset accessible via HF avec cet id et ce token, ces données sont dispo pour toi dans OneDrive_1_9-17-2025. Ce dossier sert uniquement a t'aider mais tu dois utiliser data_loader.py pour charger ces données qui sont sur HF en format csv).
41
 
42
  Fournir au LLM des tools et resources pour :
43
 
agricultural_mcp/resources.py CHANGED
@@ -1,96 +1,237 @@
 
 
1
  import gradio as gr
2
  from data_loader import AgriculturalDataLoader
3
 
 
 
 
 
 
 
 
4
 
5
  class AgriculturalResources:
6
  def __init__(self):
7
  self.data_loader = AgriculturalDataLoader()
8
- self.data_cache = None
9
 
10
- def load_data(self):
11
  if self.data_cache is None:
12
- self.data_cache = self.data_loader.load_all_files()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
  return self.data_cache
14
 
15
- # ===========================
16
- # Ressource : Exploitation
17
- # ===========================
18
- @gr.mcp.resource("exploitation://{siret}")
19
- def get_exploitation(self, siret: str) -> dict:
20
- """Retourne les infos d'une exploitation à partir du SIRET"""
 
 
 
 
 
 
 
 
 
 
 
 
 
21
  df = self.load_data()
22
- exp = df[df["siret"] == siret].iloc[0]
23
- return {
24
- "siret": exp["siret"],
25
- "raison_sociale": exp["raisonsoci"],
26
- "pacage": exp["pacage"],
27
- "millesime": exp["millesime"],
28
- }
29
-
30
- # ===========================
31
- # Ressource : Parcelle
32
- # ===========================
33
- @gr.mcp.resource("parcelle://{numparcell}")
34
- def get_parcelle(self, numparcell: str) -> dict:
35
- """Retourne les infos d'une parcelle"""
36
  df = self.load_data()
37
- parc = df[df["numparcell"] == numparcell].iloc[0]
38
- return {
39
- "numparcell": parc["numparcell"],
40
- "nomparc": parc["nomparc"],
41
- "surfparc": parc["surfparc"],
42
- "numilot": parc["numilot"],
43
- "refca": parc["refca"],
44
- }
45
-
46
- # ===========================
47
- # Ressource : Intervention
48
- # ===========================
49
- @gr.mcp.resource("intervention://{rang}")
50
- def get_intervention(self, rang: str) -> dict:
51
- """Retourne les infos d'une intervention"""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
52
  df = self.load_data()
53
- inter = df[df["rang"] == rang].iloc[0]
54
- return {
55
- "rang": inter["rang"],
56
- "date": inter.get("dateinterv", None),
57
- "mainoeuvre": inter["mainoeuvre"],
58
- "materiel": inter["materiel"],
59
- }
60
-
61
- # ===========================
62
- # Ressource : Intrants
63
- # ===========================
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
64
  @gr.mcp.resource("intrant://{codeamm}")
65
- def get_intrant(self, codeamm: str) -> dict:
66
- """Retourne les infos d’un produit utilisé"""
67
  df = self.load_data()
68
- intr = df[df["codeamm"] == codeamm].iloc[0]
69
- return {
70
- "codeamm": intr["codeamm"],
71
- "codegnis": intr["codegnis"],
72
- "quantite": intr["kqte"],
73
- "teneurN": intr["teneurn"],
74
- "teneurP": intr["teneurp"],
75
- "teneurK": intr["teneurk"],
76
- }
77
-
78
- # ===========================
79
- # Ressource : Matériel
80
- # ===========================
81
- @gr.mcp.resource("materiel://{id}")
82
- def get_materiel(self, id: str) -> dict:
83
- """Retourne un matériel (ligne correspondante)"""
 
 
 
 
 
84
  df = self.load_data()
85
- mat = df[df.index == int(id)].iloc[0] # ici on prend par index de ligne
86
- return {
87
- "materiel": mat["materiel"],
88
- "intervention": mat["rang"],
89
- "parcelle": mat["numparcell"],
90
- }
 
 
 
 
91
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
92
 
93
- # ===========================
94
  # Instance pour utilisation
95
- # ===========================
96
  resources = AgriculturalResources()
 
1
+ import typing as t
2
+ import pandas as pd
3
  import gradio as gr
4
  from data_loader import AgriculturalDataLoader
5
 
6
+ # -------------------------------------------------------------------
7
+ # Hypothèse: AgriculturalDataLoader.load_all_files() concatène 10 CSV
8
+ # et renvoie un DataFrame avec au moins ces colonnes (si présentes):
9
+ # ["millesime","raisonsoci","siret","pacage","refca","numilot","numparcell",
10
+ # "nomparc","surfparc","rang","kqte","teneurn","teneurp","teneurk",
11
+ # "keq","volumebo","codeamm","codegnis","materiel","mainoeuvre", ...]
12
+ # -------------------------------------------------------------------
13
 
14
  class AgriculturalResources:
15
  def __init__(self):
16
  self.data_loader = AgriculturalDataLoader()
17
+ self.data_cache: t.Optional[pd.DataFrame] = None
18
 
19
+ def load_data(self) -> pd.DataFrame:
20
  if self.data_cache is None:
21
+ df = self.data_loader.load_all_files()
22
+
23
+ # Normalisation minimale & robustesse
24
+ df = df.copy()
25
+ # Harmonise noms connus (au cas où)
26
+ rename_map = {
27
+ "raisonsoci": "raisonsociale",
28
+ "numparcelle": "numparcell",
29
+ "NomParc": "nomparc",
30
+ "SurfParc": "surfparc",
31
+ }
32
+ for k, v in rename_map.items():
33
+ if k in df.columns and v not in df.columns:
34
+ df[v] = df[k]
35
+
36
+ # Types & trim
37
+ for col in ["millesime", "siret", "pacage", "refca", "numilot", "numparcell", "rang",
38
+ "codeamm", "codegnis"]:
39
+ if col in df.columns:
40
+ df[col] = df[col].astype(str).str.strip()
41
+
42
+ for col in ["nomparc", "raisonsociale", "materiel", "mainoeuvre"]:
43
+ if col in df.columns:
44
+ df[col] = df[col].astype(str).str.strip()
45
+
46
+ for col in ["surfparc", "kqte", "teneurn", "teneurp", "teneurk", "keq", "volumebo"]:
47
+ if col in df.columns:
48
+ # coerce = NaN si non convertible
49
+ df[col] = pd.to_numeric(df[col], errors="coerce")
50
+
51
+ # IDs composites utiles
52
+ if {"millesime", "numparcell"}.issubset(df.columns):
53
+ df["parcelle_id"] = df["millesime"] + ":" + df["numparcell"]
54
+ else:
55
+ df["parcelle_id"] = None
56
+
57
+ if {"millesime", "numparcell", "rang"}.issubset(df.columns):
58
+ df["intervention_id"] = df["millesime"] + ":" + df["numparcell"] + ":" + df["rang"]
59
+ else:
60
+ df["intervention_id"] = None
61
+
62
+ self.data_cache = df
63
+
64
  return self.data_cache
65
 
66
+ # -------------------------
67
+ # Utilitaires internes
68
+ # -------------------------
69
+ def _safe_first(self, df: pd.DataFrame) -> t.Optional[pd.Series]:
70
+ if df is None or df.empty:
71
+ return None
72
+ return df.iloc[0]
73
+
74
+ def _notnull(self, d: dict) -> dict:
75
+ # Retire les champs None/NaN pour des payloads plus propres
76
+ return {k: v for k, v in d.items() if pd.notna(v)}
77
+
78
+ # -------------------------
79
+ # LISTINGS / DISCOVERY
80
+ # -------------------------
81
+
82
+ @gr.mcp.resource("dataset://years")
83
+ def list_years(self) -> t.List[str]:
84
+ """Liste des millésimes disponibles dans l'ensemble des fichiers."""
85
  df = self.load_data()
86
+ if "millesime" not in df.columns:
87
+ return []
88
+ years = sorted(df["millesime"].dropna().astype(str).unique())
89
+ return years
90
+
91
+ @gr.mcp.resource("exploitation://{siret}/parcelles")
92
+ def list_parcelles_by_exploitation(self, siret: str, millesime: t.Optional[str] = None) -> t.List[dict]:
93
+ """Liste les parcelles d'une exploitation (optionnellement filtrées par millésime)."""
 
 
 
 
 
 
94
  df = self.load_data()
95
+ q = df[df["siret"] == siret] if "siret" in df.columns else df.iloc[0:0]
96
+ if millesime:
97
+ q = q[q["millesime"] == str(millesime)]
98
+ cols = [c for c in ["parcelle_id","millesime","numparcell","nomparc","surfparc","refca","numilot"] if c in q.columns]
99
+ out = q[cols].drop_duplicates().to_dict(orient="records")
100
+ return out
101
+
102
+ @gr.mcp.resource("parcelles://search")
103
+ def search_parcelles(self, query: str = "", millesime: t.Optional[str] = None, limit: int = 50) -> t.List[dict]:
104
+ """Recherche de parcelles par nom/numéro, filtrable par millésime."""
105
+ df = self.load_data()
106
+ q = df
107
+ if millesime:
108
+ q = q[q["millesime"] == str(millesime)]
109
+ if query:
110
+ mask = False
111
+ if "numparcell" in q.columns:
112
+ mask = q["numparcell"].str.contains(query, case=False, na=False)
113
+ if "nomparc" in q.columns:
114
+ mask = mask | q["nomparc"].str.contains(query, case=False, na=False)
115
+ q = q[mask]
116
+ cols = [c for c in ["parcelle_id","millesime","numparcell","nomparc","surfparc","refca","numilot","siret"] if c in q.columns]
117
+ return q[cols].drop_duplicates().head(limit).to_dict(orient="records")
118
+
119
+ # -------------------------
120
+ # RESSOURCES CANONIQUES
121
+ # -------------------------
122
+
123
+ @gr.mcp.resource("exploitation://{siret}/{millesime}")
124
+ def get_exploitation(self, siret: str, millesime: str) -> dict:
125
+ """Infos d'une exploitation (pour un millésime donné)."""
126
+ df = self.load_data()
127
+ q = df[(df["siret"] == siret) & (df["millesime"] == str(millesime))] if {"siret","millesime"}.issubset(df.columns) else df.iloc[0:0]
128
+ row = self._safe_first(q.sort_values(by=[c for c in ["millesime"] if c in q.columns], ascending=False))
129
+ if row is None:
130
+ return {}
131
+ return self._notnull({
132
+ "millesime": row.get("millesime"),
133
+ "siret": row.get("siret"),
134
+ "raison_sociale": row.get("raisonsociale"),
135
+ "pacage": row.get("pacage"),
136
+ })
137
+
138
+ @gr.mcp.resource("parcelle://{millesime}/{numparcell}")
139
+ def get_parcelle(self, millesime: str, numparcell: str) -> dict:
140
+ """Infos d'une parcelle (identifiée par millésime + numparcell)."""
141
  df = self.load_data()
142
+ q = df[(df["millesime"] == str(millesime)) & (df["numparcell"] == str(numparcell))]
143
+ row = self._safe_first(q)
144
+ if row is None:
145
+ return {}
146
+ return self._notnull({
147
+ "parcelle_id": row.get("parcelle_id"),
148
+ "millesime": row.get("millesime"),
149
+ "numparcell": row.get("numparcell"),
150
+ "nomparc": row.get("nomparc"),
151
+ "surfparc": row.get("surfparc"),
152
+ "siret": row.get("siret"),
153
+ "refca": row.get("refca"),
154
+ "numilot": row.get("numilot"),
155
+ })
156
+
157
+ @gr.mcp.resource("intervention://{millesime}/{numparcell}/{rang}")
158
+ def get_intervention(self, millesime: str, numparcell: str, rang: str) -> dict:
159
+ """Infos d'une intervention (clé composite millésime + numparcell + rang)."""
160
+ df = self.load_data()
161
+ q = df[(df["millesime"] == str(millesime)) & (df["numparcell"] == str(numparcell)) & (df["rang"] == str(rang))]
162
+ row = self._safe_first(q)
163
+ if row is None:
164
+ return {}
165
+ return self._notnull({
166
+ "intervention_id": row.get("intervention_id"),
167
+ "millesime": row.get("millesime"),
168
+ "numparcell": row.get("numparcell"),
169
+ "rang": row.get("rang"),
170
+ "mainoeuvre": row.get("mainoeuvre"),
171
+ "materiel": row.get("materiel"),
172
+ "codeamm": row.get("codeamm"),
173
+ "codegnis": row.get("codegnis"),
174
+ "kqte": row.get("kqte"),
175
+ "teneurn": row.get("teneurn"),
176
+ "teneurp": row.get("teneurp"),
177
+ "teneurk": row.get("teneurk"),
178
+ "keq": row.get("keq"),
179
+ "volumebo": row.get("volumebo"),
180
+ })
181
+
182
  @gr.mcp.resource("intrant://{codeamm}")
183
+ def get_intrant(self, codeamm: str, millesime: t.Optional[str] = None) -> dict:
184
+ """Infos d’un intrant (filtrable par millésime)."""
185
  df = self.load_data()
186
+ q = df[df["codeamm"] == str(codeamm)] if "codeamm" in df.columns else df.iloc[0:0]
187
+ if millesime:
188
+ q = q[q["millesime"] == str(millesime)]
189
+ row = self._safe_first(q)
190
+ if row is None:
191
+ return {}
192
+ return self._notnull({
193
+ "codeamm": row.get("codeamm"),
194
+ "codegnis": row.get("codegnis"),
195
+ "millesime": row.get("millesime"),
196
+ "kqte": row.get("kqte"),
197
+ "teneurn": row.get("teneurn"),
198
+ "teneurp": row.get("teneurp"),
199
+ "teneurk": row.get("teneurk"),
200
+ "keq": row.get("keq"),
201
+ "volumebo": row.get("volumebo"),
202
+ })
203
+
204
+ @gr.mcp.resource("materiel://{millesime}/{numparcell}/{rang}")
205
+ def get_materiel(self, millesime: str, numparcell: str, rang: str) -> dict:
206
+ """Matériel utilisé pour une intervention donnée."""
207
  df = self.load_data()
208
+ q = df[(df["millesime"] == str(millesime)) & (df["numparcell"] == str(numparcell)) & (df["rang"] == str(rang))]
209
+ row = self._safe_first(q)
210
+ if row is None:
211
+ return {}
212
+ return self._notnull({
213
+ "millesime": row.get("millesime"),
214
+ "numparcell": row.get("numparcell"),
215
+ "rang": row.get("rang"),
216
+ "materiel": row.get("materiel"),
217
+ })
218
 
219
+ @gr.mcp.resource("maindoeuvre://{millesime}/{numparcell}/{rang}")
220
+ def get_main_oeuvre(self, millesime: str, numparcell: str, rang: str) -> dict:
221
+ """Main d’œuvre associée à une intervention donnée."""
222
+ df = self.load_data()
223
+ q = df[(df["millesime"] == str(millesime)) & (df["numparcell"] == str(numparcell)) & (df["rang"] == str(rang))]
224
+ row = self._safe_first(q)
225
+ if row is None:
226
+ return {}
227
+ return self._notnull({
228
+ "millesime": row.get("millesime"),
229
+ "numparcell": row.get("numparcell"),
230
+ "rang": row.get("rang"),
231
+ "mainoeuvre": row.get("mainoeuvre"),
232
+ })
233
 
234
+ # -------------------------------------------------------------------
235
  # Instance pour utilisation
236
+ # -------------------------------------------------------------------
237
  resources = AgriculturalResources()
mcp_server.py CHANGED
@@ -188,17 +188,22 @@ def create_mcp_interface():
188
  gr.Markdown("### Resources MCP disponibles")
189
  gr.Markdown("""
190
  **Resources MCP exposées :**
191
- - `exploitation://{siret}` - Informations d'exploitation
192
- - `parcelle://{numparcell}` - Informations de parcelle
193
- - `intervention://{rang}` - Informations d'intervention
 
194
  - `intrant://{codeamm}` - Informations d'intrant
195
- - `materiel://{id}` - Informations de matériel
 
196
  """)
197
 
198
  with gr.Row():
199
  with gr.Column():
200
  gr.Markdown("#### Test des Resources MCP")
201
 
 
 
 
202
  # Test Exploitation
203
  with gr.Row():
204
  siret_input = gr.Textbox(
@@ -206,10 +211,20 @@ def create_mcp_interface():
206
  value="18560001000016",
207
  placeholder="18560001000016"
208
  )
 
 
 
 
 
209
  siret_btn = gr.Button("🏢 Test Exploitation", variant="secondary")
210
 
211
  # Test Parcelle
212
  with gr.Row():
 
 
 
 
 
213
  parcelle_input = gr.Textbox(
214
  label="Numéro Parcelle",
215
  value="12",
@@ -219,8 +234,18 @@ def create_mcp_interface():
219
 
220
  # Test Intervention
221
  with gr.Row():
222
- intervention_input = gr.Textbox(
223
- label="Rang Intervention",
 
 
 
 
 
 
 
 
 
 
224
  value="1",
225
  placeholder="1"
226
  )
@@ -233,12 +258,27 @@ def create_mcp_interface():
233
  value="9100296",
234
  placeholder="9100296"
235
  )
 
 
 
 
 
236
  intrant_btn = gr.Button("🧪 Test Intrant", variant="secondary")
237
 
238
  # Test Matériel
239
  with gr.Row():
240
- materiel_input = gr.Textbox(
241
- label="ID Matériel",
 
 
 
 
 
 
 
 
 
 
242
  value="1",
243
  placeholder="1"
244
  )
@@ -249,29 +289,33 @@ def create_mcp_interface():
249
  resource_output = gr.JSON(label="Résultat de la resource")
250
 
251
  # Connexions des boutons
 
 
 
 
252
  siret_btn.click(
253
- lambda siret: resources.get_exploitation(siret),
254
- inputs=[siret_input],
255
  outputs=[resource_output]
256
  )
257
  parcelle_btn.click(
258
- lambda parcelle: resources.get_parcelle(parcelle),
259
- inputs=[parcelle_input],
260
  outputs=[resource_output]
261
  )
262
  intervention_btn.click(
263
- lambda intervention: resources.get_intervention(intervention),
264
- inputs=[intervention_input],
265
  outputs=[resource_output]
266
  )
267
  intrant_btn.click(
268
- lambda intrant: resources.get_intrant(intrant),
269
- inputs=[intrant_input],
270
  outputs=[resource_output]
271
  )
272
  materiel_btn.click(
273
- lambda materiel: resources.get_materiel(materiel),
274
- inputs=[materiel_input],
275
  outputs=[resource_output]
276
  )
277
 
 
188
  gr.Markdown("### Resources MCP disponibles")
189
  gr.Markdown("""
190
  **Resources MCP exposées :**
191
+ - `dataset://years` - Liste des millésimes disponibles
192
+ - `exploitation://{siret}/{millesime}` - Informations d'exploitation
193
+ - `parcelle://{millesime}/{numparcell}` - Informations de parcelle
194
+ - `intervention://{millesime}/{numparcell}/{rang}` - Informations d'intervention
195
  - `intrant://{codeamm}` - Informations d'intrant
196
+ - `materiel://{millesime}/{numparcell}/{rang}` - Informations de matériel
197
+ - `maindoeuvre://{millesime}/{numparcell}/{rang}` - Informations de main d'œuvre
198
  """)
199
 
200
  with gr.Row():
201
  with gr.Column():
202
  gr.Markdown("#### Test des Resources MCP")
203
 
204
+ # Test Years
205
+ years_btn = gr.Button("📅 Liste des Années", variant="primary")
206
+
207
  # Test Exploitation
208
  with gr.Row():
209
  siret_input = gr.Textbox(
 
211
  value="18560001000016",
212
  placeholder="18560001000016"
213
  )
214
+ siret_millesime_input = gr.Textbox(
215
+ label="Millesime",
216
+ value="2025",
217
+ placeholder="2025"
218
+ )
219
  siret_btn = gr.Button("🏢 Test Exploitation", variant="secondary")
220
 
221
  # Test Parcelle
222
  with gr.Row():
223
+ parcelle_millesime_input = gr.Textbox(
224
+ label="Millesime",
225
+ value="2025",
226
+ placeholder="2025"
227
+ )
228
  parcelle_input = gr.Textbox(
229
  label="Numéro Parcelle",
230
  value="12",
 
234
 
235
  # Test Intervention
236
  with gr.Row():
237
+ intervention_millesime_input = gr.Textbox(
238
+ label="Millesime",
239
+ value="2025",
240
+ placeholder="2025"
241
+ )
242
+ intervention_parcelle_input = gr.Textbox(
243
+ label="Num Parcelle",
244
+ value="12",
245
+ placeholder="12"
246
+ )
247
+ intervention_rang_input = gr.Textbox(
248
+ label="Rang",
249
  value="1",
250
  placeholder="1"
251
  )
 
258
  value="9100296",
259
  placeholder="9100296"
260
  )
261
+ intrant_millesime_input = gr.Textbox(
262
+ label="Millesime (optionnel)",
263
+ value="",
264
+ placeholder="2025"
265
+ )
266
  intrant_btn = gr.Button("🧪 Test Intrant", variant="secondary")
267
 
268
  # Test Matériel
269
  with gr.Row():
270
+ materiel_millesime_input = gr.Textbox(
271
+ label="Millesime",
272
+ value="2025",
273
+ placeholder="2025"
274
+ )
275
+ materiel_parcelle_input = gr.Textbox(
276
+ label="Num Parcelle",
277
+ value="12",
278
+ placeholder="12"
279
+ )
280
+ materiel_rang_input = gr.Textbox(
281
+ label="Rang",
282
  value="1",
283
  placeholder="1"
284
  )
 
289
  resource_output = gr.JSON(label="Résultat de la resource")
290
 
291
  # Connexions des boutons
292
+ years_btn.click(
293
+ lambda: resources.list_years(),
294
+ outputs=[resource_output]
295
+ )
296
  siret_btn.click(
297
+ lambda siret, millesime: resources.get_exploitation(siret, millesime),
298
+ inputs=[siret_input, siret_millesime_input],
299
  outputs=[resource_output]
300
  )
301
  parcelle_btn.click(
302
+ lambda millesime, parcelle: resources.get_parcelle(millesime, parcelle),
303
+ inputs=[parcelle_millesime_input, parcelle_input],
304
  outputs=[resource_output]
305
  )
306
  intervention_btn.click(
307
+ lambda millesime, parcelle, rang: resources.get_intervention(millesime, parcelle, rang),
308
+ inputs=[intervention_millesime_input, intervention_parcelle_input, intervention_rang_input],
309
  outputs=[resource_output]
310
  )
311
  intrant_btn.click(
312
+ lambda codeamm, millesime: resources.get_intrant(codeamm, millesime if millesime else None),
313
+ inputs=[intrant_input, intrant_millesime_input],
314
  outputs=[resource_output]
315
  )
316
  materiel_btn.click(
317
+ lambda millesime, parcelle, rang: resources.get_materiel(millesime, parcelle, rang),
318
+ inputs=[materiel_millesime_input, materiel_parcelle_input, materiel_rang_input],
319
  outputs=[resource_output]
320
  )
321
 
test_fixed_resources.py ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Test de l'application avec resources corrigées
3
+ """
4
+
5
+ import os
6
+ from mcp_server import create_mcp_interface
7
+
8
+ # Hugging Face configuration
9
+ hf_token = os.environ.get("HF_TOKEN")
10
+ if hf_token:
11
+ os.environ["HF_TOKEN"] = hf_token
12
+ os.environ["DATASET_ID"] = "HackathonCRA/2024"
13
+
14
+ def test_fixed_resources():
15
+ """Test de l'application avec resources corrigées"""
16
+ print("🧪 Test de l'application avec resources corrigées...")
17
+
18
+ try:
19
+ demo = create_mcp_interface()
20
+ print("✅ Interface créée avec succès")
21
+
22
+ # Test des resources
23
+ from agricultural_mcp.resources import AgriculturalResources
24
+ resources = AgriculturalResources()
25
+
26
+ print("✅ Resources MCP initialisées")
27
+
28
+ # Test d'une resource simple
29
+ years = resources.list_years()
30
+ print(f"✅ Test list_years: {len(years)} années trouvées")
31
+
32
+ print("\n🎯 Application avec resources corrigées fonctionnelle !")
33
+ print("📋 5 onglets disponibles")
34
+ print("🔧 Onglet MCP Resources avec test des resources corrigées")
35
+ print("📁 Structure modulaire : tools.py, resources.py, prompts.py")
36
+ print("🚀 Prêt pour déploiement avec mcp_server=True")
37
+
38
+ return True
39
+
40
+ except Exception as e:
41
+ print(f"❌ Erreur: {e}")
42
+ import traceback
43
+ traceback.print_exc()
44
+ return False
45
+
46
+ if __name__ == "__main__":
47
+ test_fixed_resources()