Tracy André commited on
Commit
953d6cd
·
1 Parent(s): fd8d3c6
Files changed (4) hide show
  1. README_MCP_DEPLOYMENT.md +138 -0
  2. app.py +61 -14
  3. mcp_http_server.py +22 -0
  4. mcp_server.py +109 -4
README_MCP_DEPLOYMENT.md ADDED
@@ -0,0 +1,138 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 🤖 Déploiement MCP HTTP sur Hugging Face Spaces
2
+
3
+ ## Vue d'ensemble
4
+
5
+ Cette configuration permet d'exposer le serveur MCP via HTTP en plus de l'interface Gradio sur Hugging Face Spaces.
6
+
7
+ ## 🎯 URLs d'accès
8
+
9
+ Après déploiement sur HF Spaces :
10
+
11
+ ### Interface Gradio
12
+ ```
13
+ https://huggingface.co/spaces/[votre-username]/agricultural-analysis
14
+ ```
15
+
16
+ ### Serveur MCP HTTP
17
+ ```
18
+ https://[votre-username]-agricultural-analysis.hf.space:8000
19
+ ```
20
+
21
+ ## 🔧 Endpoints MCP disponibles
22
+
23
+ ### Informations du serveur
24
+ ```http
25
+ GET https://[votre-username]-agricultural-analysis.hf.space:8000/
26
+ ```
27
+
28
+ ### Lister les ressources
29
+ ```http
30
+ GET https://[votre-username]-agricultural-analysis.hf.space:8000/mcp/resources
31
+ ```
32
+
33
+ ### Obtenir une ressource spécifique
34
+ ```http
35
+ GET https://[votre-username]-agricultural-analysis.hf.space:8000/mcp/resources/agricultural://data/summary
36
+ ```
37
+
38
+ ### Lister les outils
39
+ ```http
40
+ GET https://[votre-username]-agricultural-analysis.hf.space:8000/mcp/tools
41
+ ```
42
+
43
+ ### Appeler un outil
44
+ ```http
45
+ POST https://[votre-username]-agricultural-analysis.hf.space:8000/mcp/call/filter_data
46
+ Content-Type: application/json
47
+
48
+ {
49
+ "years": [2022, 2023, 2024],
50
+ "plots": ["P001", "P002"]
51
+ }
52
+ ```
53
+
54
+ ## 🛠️ Configuration MCP Inspector
55
+
56
+ Pour utiliser avec MCP Inspector :
57
+
58
+ 1. **URL du serveur** : `https://[votre-username]-agricultural-analysis.hf.space:8000`
59
+ 2. **Protocole** : HTTP/REST
60
+ 3. **Format** : JSON
61
+
62
+ ## 📋 Exemples d'utilisation
63
+
64
+ ### Test de connectivité
65
+ ```bash
66
+ curl https://[votre-username]-agricultural-analysis.hf.space:8000/
67
+ ```
68
+
69
+ ### Obtenir les outils disponibles
70
+ ```bash
71
+ curl https://[votre-username]-agricultural-analysis.hf.space:8000/mcp/tools
72
+ ```
73
+
74
+ ### Analyser la pression adventices
75
+ ```bash
76
+ curl -X POST https://[votre-username]-agricultural-analysis.hf.space:8000/mcp/call/analyze_weed_pressure \
77
+ -H "Content-Type: application/json" \
78
+ -d '{"years": [2022, 2023, 2024], "include_visualization": true}'
79
+ ```
80
+
81
+ ### Prédire la pression future
82
+ ```bash
83
+ curl -X POST https://[votre-username]-agricultural-analysis.hf.space:8000/mcp/call/predict_weed_pressure \
84
+ -H "Content-Type: application/json" \
85
+ -d '{"target_years": [2025, 2026, 2027]}'
86
+ ```
87
+
88
+ ### Identifier les parcelles adaptées
89
+ ```bash
90
+ curl -X POST https://[votre-username]-agricultural-analysis.hf.space:8000/mcp/call/identify_suitable_plots \
91
+ -H "Content-Type: application/json" \
92
+ -d '{"target_years": [2025, 2026, 2027], "max_ift_threshold": 1.0}'
93
+ ```
94
+
95
+ ## 🔍 Logs et Debug
96
+
97
+ Les logs des deux services (Gradio + MCP) apparaîtront dans les logs du Space Hugging Face.
98
+
99
+ ### Vérifier le statut
100
+ - **Gradio** : Interface web accessible
101
+ - **MCP HTTP** : Endpoint `/` répond avec infos du serveur
102
+
103
+ ## ⚙️ Variables d'environnement
104
+
105
+ ```bash
106
+ # Mode de déploiement
107
+ MCP_MODE=http
108
+
109
+ # Configuration réseau
110
+ MCP_HOST=0.0.0.0
111
+ MCP_PORT=8000
112
+ GRADIO_SERVER_NAME=0.0.0.0
113
+ GRADIO_SERVER_PORT=7860
114
+
115
+ # Configuration Hugging Face
116
+ HF_TOKEN=your_token_here
117
+ DATASET_ID=HackathonCRA/2024
118
+ ```
119
+
120
+ ## 🚀 Déploiement
121
+
122
+ 1. **Push vers HF Spaces** avec les fichiers modifiés
123
+ 2. **Attendre le démarrage** (vérifier les logs)
124
+ 3. **Tester la connectivité** sur les deux ports
125
+ 4. **Configurer MCP Inspector** avec l'URL
126
+
127
+ ## 🔧 Dépannage
128
+
129
+ ### Problème de port
130
+ Si le port 8000 n'est pas accessible, Hugging Face Spaces peut avoir des restrictions. Essayez :
131
+ - Port 8080 ou 3000
132
+ - Modifier `MCP_PORT` dans `app.py`
133
+
134
+ ### Problème de CORS
135
+ Les headers CORS sont configurés pour `allow_origins=["*"]`. Si nécessaire, ajuster dans `mcp_server.py`.
136
+
137
+ ### Timeout de démarrage
138
+ Le MCP server a 2 secondes pour démarrer avant Gradio. Augmentez si nécessaire dans `app.py`.
app.py CHANGED
@@ -1,36 +1,83 @@
1
  """
2
  Hugging Face Space compatible version of the agricultural analysis app.
3
  This is the main entry point for deployment on Hugging Face Spaces.
 
4
  """
5
 
6
  import os
7
  import sys
8
  import gradio as gr
 
 
 
 
9
 
10
  # Add current directory to Python path
11
  sys.path.append(os.path.dirname(os.path.abspath(__file__)))
12
 
13
- # Import the main Gradio app
14
  from gradio_app import create_gradio_app
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15
 
16
  def main():
17
- """Main function for Hugging Face deployment."""
 
 
 
 
18
  # Set up environment
19
  os.environ.setdefault("GRADIO_SERVER_NAME", "0.0.0.0")
20
  os.environ.setdefault("GRADIO_SERVER_PORT", "7860")
21
 
22
- # Create and launch the app
23
- app = create_gradio_app()
24
-
25
- # Launch with Hugging Face compatible settings
26
- app.launch(
27
- server_name="0.0.0.0",
28
- server_port=7860,
29
- share=False, # Don't share in HF Spaces
30
- debug=False, # Disable debug in production
31
- show_error=True,
32
- quiet=False
33
- )
 
 
 
34
 
35
  if __name__ == "__main__":
36
  main()
 
1
  """
2
  Hugging Face Space compatible version of the agricultural analysis app.
3
  This is the main entry point for deployment on Hugging Face Spaces.
4
+ Launches both Gradio interface and MCP HTTP server.
5
  """
6
 
7
  import os
8
  import sys
9
  import gradio as gr
10
+ import asyncio
11
+ import threading
12
+ import time
13
+ import logging
14
 
15
  # Add current directory to Python path
16
  sys.path.append(os.path.dirname(os.path.abspath(__file__)))
17
 
18
+ # Import the main Gradio app and MCP server
19
  from gradio_app import create_gradio_app
20
+ from mcp_server import main_http
21
+
22
+ # Set up logging
23
+ logging.basicConfig(level=logging.INFO)
24
+ logger = logging.getLogger("hf-space-launcher")
25
+
26
+ def run_mcp_server():
27
+ """Run MCP HTTP server in a separate thread."""
28
+ logger.info("🤖 Starting MCP HTTP Server...")
29
+ os.environ["MCP_MODE"] = "http"
30
+ os.environ["MCP_PORT"] = "8000"
31
+ os.environ["MCP_HOST"] = "0.0.0.0"
32
+
33
+ try:
34
+ asyncio.run(main_http())
35
+ except Exception as e:
36
+ logger.error(f"❌ MCP Server failed: {e}")
37
+
38
+ def run_gradio_app():
39
+ """Run Gradio app."""
40
+ logger.info("🌐 Starting Gradio Interface...")
41
+
42
+ try:
43
+ app = create_gradio_app()
44
+ app.launch(
45
+ server_name="0.0.0.0",
46
+ server_port=7860,
47
+ share=False, # Don't share in HF Spaces
48
+ debug=False, # Disable debug in production
49
+ show_error=True,
50
+ quiet=False,
51
+ prevent_thread_lock=True # Important for threading
52
+ )
53
+ except Exception as e:
54
+ logger.error(f"❌ Gradio failed: {e}")
55
 
56
  def main():
57
+ """Main function for Hugging Face deployment - runs both services."""
58
+ logger.info("🚀 Starting Agricultural Analysis Space...")
59
+ logger.info("📊 Gradio Interface will be on port 7860")
60
+ logger.info("🤖 MCP HTTP Server will be on port 8000")
61
+
62
  # Set up environment
63
  os.environ.setdefault("GRADIO_SERVER_NAME", "0.0.0.0")
64
  os.environ.setdefault("GRADIO_SERVER_PORT", "7860")
65
 
66
+ try:
67
+ # Start MCP server in a separate thread
68
+ mcp_thread = threading.Thread(target=run_mcp_server, daemon=True)
69
+ mcp_thread.start()
70
+ logger.info("✅ MCP Server thread started")
71
+
72
+ # Give MCP server time to start
73
+ time.sleep(2)
74
+
75
+ # Start Gradio app in main thread
76
+ run_gradio_app()
77
+
78
+ except Exception as e:
79
+ logger.error(f"❌ Failed to start services: {e}")
80
+ raise
81
 
82
  if __name__ == "__main__":
83
  main()
mcp_http_server.py ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Standalone HTTP MCP Server for Hugging Face Spaces deployment.
3
+ This file launches only the MCP HTTP server on a different port.
4
+ """
5
+
6
+ import os
7
+ import asyncio
8
+ import logging
9
+ from mcp_server import main_http
10
+
11
+ # Set up logging
12
+ logging.basicConfig(level=logging.INFO)
13
+ logger = logging.getLogger("mcp-http-server")
14
+
15
+ if __name__ == "__main__":
16
+ # Force HTTP mode
17
+ os.environ["MCP_MODE"] = "http"
18
+ os.environ["MCP_PORT"] = "8000"
19
+ os.environ["MCP_HOST"] = "0.0.0.0"
20
+
21
+ logger.info("🤖 Starting MCP HTTP Server on port 8000...")
22
+ asyncio.run(main_http())
mcp_server.py CHANGED
@@ -15,6 +15,10 @@ import pandas as pd
15
  from data_loader import AgriculturalDataLoader
16
  from analysis_tools import AgriculturalAnalyzer
17
  import plotly.io as pio
 
 
 
 
18
 
19
 
20
  # Set up logging
@@ -412,9 +416,85 @@ async def call_tool(name: str, arguments: Dict[str, Any]) -> List[TextContent]:
412
  )]
413
 
414
 
415
- async def main():
416
- """Main function to run the MCP server."""
417
- logger.info("Starting Agricultural MCP Server...")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
418
 
419
  # Initialize the server
420
  async with stdio_server() as (read_stream, write_stream):
@@ -428,6 +508,31 @@ async def main():
428
  )
429
  )
430
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
431
 
432
  if __name__ == "__main__":
433
- asyncio.run(main())
 
15
  from data_loader import AgriculturalDataLoader
16
  from analysis_tools import AgriculturalAnalyzer
17
  import plotly.io as pio
18
+ import os
19
+ from fastapi import FastAPI, HTTPException
20
+ from fastapi.middleware.cors import CORSMiddleware
21
+ import uvicorn
22
 
23
 
24
  # Set up logging
 
416
  )]
417
 
418
 
419
+ # FastAPI app for HTTP interface
420
+ fastapi_app = FastAPI(
421
+ title="Agricultural MCP Server",
422
+ description="MCP Server for Agricultural Data Analysis",
423
+ version="1.0.0"
424
+ )
425
+
426
+ # Add CORS middleware for web access
427
+ fastapi_app.add_middleware(
428
+ CORSMiddleware,
429
+ allow_origins=["*"],
430
+ allow_credentials=True,
431
+ allow_methods=["*"],
432
+ allow_headers=["*"],
433
+ )
434
+
435
+ @fastapi_app.get("/")
436
+ async def root():
437
+ """Root endpoint with server info."""
438
+ return {
439
+ "name": "Agricultural MCP Server",
440
+ "version": "1.0.0",
441
+ "description": "MCP Server for Agricultural Data Analysis",
442
+ "endpoints": {
443
+ "resources": "/mcp/resources",
444
+ "tools": "/mcp/tools",
445
+ "call": "/mcp/call/{tool_name}"
446
+ }
447
+ }
448
+
449
+ @fastapi_app.get("/mcp/resources")
450
+ async def get_resources():
451
+ """Get available MCP resources."""
452
+ try:
453
+ resources = await list_resources()
454
+ return {"resources": [resource.model_dump() for resource in resources]}
455
+ except Exception as e:
456
+ logger.error(f"Error listing resources: {e}")
457
+ raise HTTPException(status_code=500, detail=str(e))
458
+
459
+ @fastapi_app.get("/mcp/resources/{resource_uri:path}")
460
+ async def get_resource(resource_uri: str):
461
+ """Get a specific resource."""
462
+ try:
463
+ content = await read_resource(resource_uri)
464
+ return {"uri": resource_uri, "content": content}
465
+ except Exception as e:
466
+ logger.error(f"Error reading resource {resource_uri}: {e}")
467
+ raise HTTPException(status_code=500, detail=str(e))
468
+
469
+ @fastapi_app.get("/mcp/tools")
470
+ async def get_tools():
471
+ """Get available MCP tools."""
472
+ try:
473
+ tools = await list_tools()
474
+ return {"tools": [tool.model_dump() for tool in tools]}
475
+ except Exception as e:
476
+ logger.error(f"Error listing tools: {e}")
477
+ raise HTTPException(status_code=500, detail=str(e))
478
+
479
+ @fastapi_app.post("/mcp/call/{tool_name}")
480
+ async def call_mcp_tool(tool_name: str, arguments: Dict[str, Any] = None):
481
+ """Call an MCP tool."""
482
+ try:
483
+ if arguments is None:
484
+ arguments = {}
485
+ result = await call_tool(tool_name, arguments)
486
+ return {
487
+ "tool": tool_name,
488
+ "arguments": arguments,
489
+ "result": [content.model_dump() for content in result]
490
+ }
491
+ except Exception as e:
492
+ logger.error(f"Error calling tool {tool_name}: {e}")
493
+ raise HTTPException(status_code=500, detail=str(e))
494
+
495
+ async def main_stdio():
496
+ """Main function to run the MCP server via stdio."""
497
+ logger.info("Starting Agricultural MCP Server (stdio mode)...")
498
 
499
  # Initialize the server
500
  async with stdio_server() as (read_stream, write_stream):
 
508
  )
509
  )
510
 
511
+ async def main_http():
512
+ """Main function to run the MCP server via HTTP."""
513
+ logger.info("Starting Agricultural MCP Server (HTTP mode)...")
514
+
515
+ port = int(os.getenv("MCP_PORT", "8000"))
516
+ host = os.getenv("MCP_HOST", "0.0.0.0")
517
+
518
+ config = uvicorn.Config(
519
+ fastapi_app,
520
+ host=host,
521
+ port=port,
522
+ log_level="info",
523
+ access_log=True
524
+ )
525
+ server_instance = uvicorn.Server(config)
526
+ await server_instance.serve()
527
+
528
+ def main():
529
+ """Main entry point - choose mode based on environment."""
530
+ mode = os.getenv("MCP_MODE", "stdio")
531
+
532
+ if mode == "http":
533
+ asyncio.run(main_http())
534
+ else:
535
+ asyncio.run(main_stdio())
536
 
537
  if __name__ == "__main__":
538
+ main()