Tracy André commited on
Commit
26a5e78
·
1 Parent(s): 7691575
Files changed (3) hide show
  1. mcp_server.py +80 -14
  2. test_final_mcp.py +47 -0
  3. test_mcp_decorators.py +44 -0
mcp_server.py CHANGED
@@ -89,11 +89,57 @@ class WeedPressureAnalyzer:
89
  # Initialize analyzer
90
  analyzer = WeedPressureAnalyzer()
91
 
92
- # MCP Resources (to be activated when @gr.mcp.resource is available)
93
- # These functions provide structured data access for LLMs and can be exposed as MCP resources
94
- # To activate: uncomment the @gr.mcp.resource decorators below
95
 
96
- # @gr.mcp.resource("agricultural://plots")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
97
  def get_available_plots_resource() -> str:
98
  """Get list of all available agricultural plots from the dataset"""
99
  try:
@@ -102,7 +148,7 @@ def get_available_plots_resource() -> str:
102
  except Exception as e:
103
  return f"Error loading plots: {str(e)}"
104
 
105
- # @gr.mcp.resource("agricultural://crops")
106
  def get_available_crops_resource() -> str:
107
  """Get list of all crop types in the dataset"""
108
  try:
@@ -111,7 +157,7 @@ def get_available_crops_resource() -> str:
111
  except Exception as e:
112
  return f"Error loading crops: {str(e)}"
113
 
114
- # @gr.mcp.resource("agricultural://years")
115
  def get_available_years_resource() -> str:
116
  """Get range of years available in the dataset"""
117
  try:
@@ -121,7 +167,7 @@ def get_available_years_resource() -> str:
121
  except Exception as e:
122
  return f"Error loading years: {str(e)}"
123
 
124
- # @gr.mcp.resource("agricultural://dataset-info")
125
  def get_dataset_info() -> str:
126
  """Get comprehensive information about the agricultural dataset"""
127
  try:
@@ -144,7 +190,7 @@ Agricultural Dataset Information:
144
  except Exception as e:
145
  return f"Error loading dataset info: {str(e)}"
146
 
147
- # @gr.mcp.resource("agricultural://plot/{plot_name}")
148
  def get_plot_info(plot_name: str) -> str:
149
  """Get detailed information about a specific agricultural plot"""
150
  try:
@@ -171,7 +217,7 @@ Plot Information: {plot_name}
171
  except Exception as e:
172
  return f"Error loading plot info: {str(e)}"
173
 
174
- # @gr.mcp.resource("agricultural://crop/{crop_type}")
175
  def get_crop_info(crop_type: str) -> str:
176
  """Get information about a specific crop type and its cultivation patterns"""
177
  try:
@@ -199,7 +245,7 @@ Crop Information: {crop_type}
199
  except Exception as e:
200
  return f"Error loading crop info: {str(e)}"
201
 
202
- # @gr.mcp.resource("agricultural://year/{year}")
203
  def get_year_summary(year: int) -> str:
204
  """Get summary of agricultural activities for a specific year"""
205
  try:
@@ -228,7 +274,7 @@ Year Summary: {year}
228
  except Exception as e:
229
  return f"Error loading year summary: {str(e)}"
230
 
231
- # @gr.mcp.resource("agricultural://herbicide-usage")
232
  def get_herbicide_usage_summary() -> str:
233
  """Get comprehensive summary of herbicide usage patterns"""
234
  try:
@@ -268,7 +314,7 @@ Herbicide Usage Summary:
268
  except Exception as e:
269
  return f"Error loading herbicide usage summary: {str(e)}"
270
 
271
- # @gr.mcp.resource("agricultural://predictions/2025-2027")
272
  def get_predictions_summary() -> str:
273
  """Get summary of weed pressure predictions for 2025-2027"""
274
  try:
@@ -298,7 +344,7 @@ Weed Pressure Predictions 2025-2027:
298
  except Exception as e:
299
  return f"Error loading predictions summary: {str(e)}"
300
 
301
- # @gr.mcp.resource("agricultural://recommendations/sensitive-crops")
302
  def get_recommendations_summary() -> str:
303
  """Get summary of plot recommendations for sensitive crops (pois, haricot)"""
304
  try:
@@ -331,7 +377,7 @@ Sensitive Crop Recommendations (Pois, Haricot):
331
  except Exception as e:
332
  return f"Error loading recommendations summary: {str(e)}"
333
 
334
- # @gr.mcp.resource("agricultural://plot/{plot_name}/predictions")
335
  def get_plot_predictions(plot_name: str) -> str:
336
  """Get weed pressure predictions for a specific plot"""
337
  try:
@@ -357,6 +403,23 @@ Predictions for {plot_name}:
357
  except Exception as e:
358
  return f"Error loading plot predictions: {str(e)}"
359
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
360
  def analyze_herbicide_trends(year_start, year_end, plot_filter):
361
  """
362
  Analyze herbicide usage trends over time by calculating IFT (Treatment Frequency Index).
@@ -851,6 +914,7 @@ def create_mcp_interface():
851
  static_btn5 = gr.Button("🧪 Usage herbicides", variant="secondary")
852
  static_btn6 = gr.Button("🔮 Prédictions 2025-2027", variant="secondary")
853
  static_btn7 = gr.Button("🌱 Recommandations", variant="secondary")
 
854
 
855
  with gr.Column():
856
  gr.Markdown("#### Resources paramétrées")
@@ -878,6 +942,7 @@ def create_mcp_interface():
878
  static_btn5.click(lambda: get_herbicide_usage_summary(), outputs=[resource_output])
879
  static_btn6.click(lambda: get_predictions_summary(), outputs=[resource_output])
880
  static_btn7.click(lambda: get_recommendations_summary(), outputs=[resource_output])
 
881
 
882
  plot_btn.click(lambda plot: get_plot_info(plot), inputs=[plot_input], outputs=[resource_output])
883
  crop_btn.click(lambda crop: get_crop_info(crop), inputs=[crop_input], outputs=[resource_output])
@@ -887,3 +952,4 @@ def create_mcp_interface():
887
  return demo
888
 
889
 
 
 
89
  # Initialize analyzer
90
  analyzer = WeedPressureAnalyzer()
91
 
92
+ # MCP Resources Implementation
93
+ # Custom MCP resource decorator implementation for Gradio compatibility
94
+ # This simulates @gr.mcp.resource functionality until it's officially available
95
 
96
+ class MCPResourceRegistry:
97
+ """Registry for MCP resources with URI patterns"""
98
+ def __init__(self):
99
+ self.resources = {}
100
+
101
+ def resource(self, uri_pattern: str):
102
+ """Decorator to register MCP resources"""
103
+ def decorator(func):
104
+ self.resources[uri_pattern] = {
105
+ 'function': func,
106
+ 'uri_pattern': uri_pattern,
107
+ 'description': func.__doc__ or "No description available"
108
+ }
109
+ return func
110
+ return decorator
111
+
112
+ def get_resource(self, uri: str):
113
+ """Get resource by URI pattern match"""
114
+ for pattern, resource_info in self.resources.items():
115
+ if self._match_pattern(pattern, uri):
116
+ return resource_info
117
+ return None
118
+
119
+ def _match_pattern(self, pattern: str, uri: str):
120
+ """Simple pattern matching for URI templates"""
121
+ if pattern == uri:
122
+ return True
123
+ # Handle {param} patterns - simple string replacement approach
124
+ if '{' in pattern and '}' in pattern:
125
+ # Extract the base pattern and check if URI starts with it
126
+ base_pattern = pattern.split('{')[0]
127
+ return uri.startswith(base_pattern)
128
+ return False
129
+
130
+ def list_resources(self):
131
+ """List all registered resources"""
132
+ return self.resources
133
+
134
+ # Global MCP resource registry
135
+ mcp_registry = MCPResourceRegistry()
136
+
137
+ # MCP resource decorator
138
+ def mcp_resource(uri_pattern: str):
139
+ """Decorator to register MCP resources"""
140
+ return mcp_registry.resource(uri_pattern)
141
+
142
+ @mcp_resource("agricultural://plots")
143
  def get_available_plots_resource() -> str:
144
  """Get list of all available agricultural plots from the dataset"""
145
  try:
 
148
  except Exception as e:
149
  return f"Error loading plots: {str(e)}"
150
 
151
+ @mcp_resource("agricultural://crops")
152
  def get_available_crops_resource() -> str:
153
  """Get list of all crop types in the dataset"""
154
  try:
 
157
  except Exception as e:
158
  return f"Error loading crops: {str(e)}"
159
 
160
+ @mcp_resource("agricultural://years")
161
  def get_available_years_resource() -> str:
162
  """Get range of years available in the dataset"""
163
  try:
 
167
  except Exception as e:
168
  return f"Error loading years: {str(e)}"
169
 
170
+ @mcp_resource("agricultural://dataset-info")
171
  def get_dataset_info() -> str:
172
  """Get comprehensive information about the agricultural dataset"""
173
  try:
 
190
  except Exception as e:
191
  return f"Error loading dataset info: {str(e)}"
192
 
193
+ @mcp_resource("agricultural://plot/{plot_name}")
194
  def get_plot_info(plot_name: str) -> str:
195
  """Get detailed information about a specific agricultural plot"""
196
  try:
 
217
  except Exception as e:
218
  return f"Error loading plot info: {str(e)}"
219
 
220
+ @mcp_resource("agricultural://crop/{crop_type}")
221
  def get_crop_info(crop_type: str) -> str:
222
  """Get information about a specific crop type and its cultivation patterns"""
223
  try:
 
245
  except Exception as e:
246
  return f"Error loading crop info: {str(e)}"
247
 
248
+ @mcp_resource("agricultural://year/{year}")
249
  def get_year_summary(year: int) -> str:
250
  """Get summary of agricultural activities for a specific year"""
251
  try:
 
274
  except Exception as e:
275
  return f"Error loading year summary: {str(e)}"
276
 
277
+ @mcp_resource("agricultural://herbicide-usage")
278
  def get_herbicide_usage_summary() -> str:
279
  """Get comprehensive summary of herbicide usage patterns"""
280
  try:
 
314
  except Exception as e:
315
  return f"Error loading herbicide usage summary: {str(e)}"
316
 
317
+ @mcp_resource("agricultural://predictions/2025-2027")
318
  def get_predictions_summary() -> str:
319
  """Get summary of weed pressure predictions for 2025-2027"""
320
  try:
 
344
  except Exception as e:
345
  return f"Error loading predictions summary: {str(e)}"
346
 
347
+ @mcp_resource("agricultural://recommendations/sensitive-crops")
348
  def get_recommendations_summary() -> str:
349
  """Get summary of plot recommendations for sensitive crops (pois, haricot)"""
350
  try:
 
377
  except Exception as e:
378
  return f"Error loading recommendations summary: {str(e)}"
379
 
380
+ @mcp_resource("agricultural://plot/{plot_name}/predictions")
381
  def get_plot_predictions(plot_name: str) -> str:
382
  """Get weed pressure predictions for a specific plot"""
383
  try:
 
403
  except Exception as e:
404
  return f"Error loading plot predictions: {str(e)}"
405
 
406
+ @mcp_resource("agricultural://resources")
407
+ def list_mcp_resources() -> str:
408
+ """List all available MCP resources with their URIs and descriptions"""
409
+ try:
410
+ resources = mcp_registry.list_resources()
411
+ if not resources:
412
+ return "No MCP resources available"
413
+
414
+ info = "## Available MCP Resources\n\n"
415
+ for uri_pattern, resource_info in resources.items():
416
+ info += f"### `{uri_pattern}`\n"
417
+ info += f"**Description:** {resource_info['description']}\n\n"
418
+
419
+ return info
420
+ except Exception as e:
421
+ return f"Error listing resources: {str(e)}"
422
+
423
  def analyze_herbicide_trends(year_start, year_end, plot_filter):
424
  """
425
  Analyze herbicide usage trends over time by calculating IFT (Treatment Frequency Index).
 
914
  static_btn5 = gr.Button("🧪 Usage herbicides", variant="secondary")
915
  static_btn6 = gr.Button("🔮 Prédictions 2025-2027", variant="secondary")
916
  static_btn7 = gr.Button("🌱 Recommandations", variant="secondary")
917
+ static_btn8 = gr.Button("📋 Liste Resources", variant="primary")
918
 
919
  with gr.Column():
920
  gr.Markdown("#### Resources paramétrées")
 
942
  static_btn5.click(lambda: get_herbicide_usage_summary(), outputs=[resource_output])
943
  static_btn6.click(lambda: get_predictions_summary(), outputs=[resource_output])
944
  static_btn7.click(lambda: get_recommendations_summary(), outputs=[resource_output])
945
+ static_btn8.click(lambda: list_mcp_resources(), outputs=[resource_output])
946
 
947
  plot_btn.click(lambda plot: get_plot_info(plot), inputs=[plot_input], outputs=[resource_output])
948
  crop_btn.click(lambda crop: get_crop_info(crop), inputs=[crop_input], outputs=[resource_output])
 
952
  return demo
953
 
954
 
955
+
test_final_mcp.py ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Test final de l'interface avec décorateurs MCP
3
+ """
4
+
5
+ from mcp_server import create_mcp_interface, mcp_registry
6
+
7
+ def test_final_mcp():
8
+ """Test final de l'interface avec décorateurs MCP"""
9
+ print("🧪 Test final de l'interface avec décorateurs MCP...")
10
+
11
+ try:
12
+ # Test de l'interface
13
+ demo = create_mcp_interface()
14
+ print("✅ Interface créée avec succès")
15
+
16
+ # Test du registre MCP
17
+ resources = mcp_registry.list_resources()
18
+ print(f"✅ {len(resources)} resources MCP enregistrées")
19
+
20
+ # Test de quelques resources
21
+ from mcp_server import (
22
+ get_available_plots_resource,
23
+ get_dataset_info,
24
+ get_plot_info,
25
+ list_mcp_resources
26
+ )
27
+
28
+ print("\n📋 Test des resources:")
29
+ print("Plots:", get_available_plots_resource()[:100] + "...")
30
+ print("Dataset:", get_dataset_info()[:100] + "...")
31
+ print("Plot info:", get_plot_info("Champ ferme W du sol")[:100] + "...")
32
+
33
+ print("\n🎯 Interface MCP complète prête !")
34
+ print("📋 5 onglets disponibles")
35
+ print("🔧 12 resources MCP avec décorateurs @mcp_resource")
36
+ print("🚀 Prêt pour déploiement sur Hugging Face Spaces")
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_final_mcp()
test_mcp_decorators.py ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Test des décorateurs MCP personnalisés
3
+ """
4
+
5
+ from mcp_server import mcp_registry, list_mcp_resources
6
+
7
+ def test_mcp_decorators():
8
+ """Test des décorateurs MCP personnalisés"""
9
+ print("🧪 Test des décorateurs MCP personnalisés...")
10
+
11
+ try:
12
+ # Test du registre MCP
13
+ resources = mcp_registry.list_resources()
14
+ print(f"✅ {len(resources)} resources MCP enregistrées")
15
+
16
+ # Afficher toutes les resources
17
+ for uri, info in resources.items():
18
+ print(f" 📋 {uri}: {info['description'][:50]}...")
19
+
20
+ # Test de la fonction list_mcp_resources
21
+ print("\n📋 Liste des resources MCP:")
22
+ resources_list = list_mcp_resources()
23
+ print(resources_list[:500] + "..." if len(resources_list) > 500 else resources_list)
24
+
25
+ # Test de pattern matching
26
+ print("\n🔍 Test de pattern matching:")
27
+ test_uri = "agricultural://plot/Champ ferme W du sol"
28
+ resource = mcp_registry.get_resource(test_uri)
29
+ if resource:
30
+ print(f"✅ Pattern match trouvé: {resource['uri_pattern']}")
31
+ else:
32
+ print("❌ Pattern match non trouvé")
33
+
34
+ print("\n🎯 Décorateurs MCP fonctionnels !")
35
+ return True
36
+
37
+ except Exception as e:
38
+ print(f"❌ Erreur: {e}")
39
+ import traceback
40
+ traceback.print_exc()
41
+ return False
42
+
43
+ if __name__ == "__main__":
44
+ test_mcp_decorators()