Tracy André commited on
Commit
a764c8f
·
1 Parent(s): e73e903
Files changed (2) hide show
  1. app.py +56 -58
  2. requirements.txt +1 -1
app.py CHANGED
@@ -1,24 +1,46 @@
1
  import os
2
- from contextlib import asynccontextmanager
3
  from fastapi import FastAPI
4
- from fastmcp import FastMCP
5
  from gradio.routes import mount_gradio_app
6
 
 
 
 
 
 
 
 
 
7
  # Import your existing Gradio app and analysis tools
8
  from gradio_app import create_gradio_app
9
  from data_loader import AgriculturalDataLoader
10
  from analysis_tools import AgriculturalAnalyzer
11
 
12
- # ========= Configuration =========
13
  PORT = int(os.environ.get("PORT", 7860))
 
14
 
15
  # Initialize agricultural components
16
  data_loader = AgriculturalDataLoader()
17
  analyzer = AgriculturalAnalyzer(data_loader)
18
 
19
- # ========= Create FastMCP Server =========
 
 
 
 
 
 
 
 
 
 
 
 
 
20
  mcp = FastMCP("Agricultural Analysis Tools")
21
 
 
22
  @mcp.tool
23
  def analyze_weed_pressure(years: list[int] | None = None, plots: list[str] | None = None) -> str:
24
  """Analyze weed pressure trends using IFT herbicide data from Kerguéhennec experimental station."""
@@ -140,76 +162,52 @@ def dataset_resource() -> str:
140
  """Agricultural dataset summary resource."""
141
  return get_dataset_summary()
142
 
143
- # ========= Create MCP HTTP App =========
144
- mcp_http_app = mcp.http_app()
145
-
146
- # ========= FastAPI App Setup =========
147
- @asynccontextmanager
148
- async def lifespan(app: FastAPI):
149
- # Initialize MCP session manager
150
- async with mcp_http_app.lifespan(app):
151
- yield
152
-
153
- app = FastAPI(
154
- title="Agricultural Analysis - MCP + Gradio",
155
- description="Agricultural data analysis with MCP tools and Gradio interface",
156
- version="1.0.0",
157
- lifespan=lifespan
158
  )
159
 
 
 
 
 
160
  @app.get("/health")
161
  def health():
162
- """Health check endpoint."""
163
  return {"ok": True, "service": "agricultural-mcp-server", "version": "1.0.0"}
164
 
165
- @app.get("/debug/routes")
166
- def debug_routes():
167
- """Debug endpoint to see all routes."""
168
- routes = []
169
- for route in app.routes:
170
- if hasattr(route, 'path'):
171
- routes.append({
172
- "path": route.path,
173
- "methods": getattr(route, 'methods', None),
174
- "name": getattr(route, 'name', None)
175
- })
176
- elif hasattr(route, 'path_regex'):
177
- routes.append({
178
- "path": str(route.path_regex.pattern),
179
- "type": "mount",
180
- "name": getattr(route, 'name', None)
181
- })
182
- return {"routes": routes, "mcp_tools": len(mcp.tools)}
183
-
184
- # ========= Mount MCP Server directly =========
185
- app.mount("/mcp", mcp_http_app)
186
-
187
- # ========= Mount Gradio UI =========
188
  demo = create_gradio_app()
189
- final_app = mount_gradio_app(app, demo, path="/")
190
 
191
- # ========= Launch Configuration =========
 
 
 
192
  if __name__ == "__main__":
193
  import uvicorn
194
- uvicorn.run(final_app, host="0.0.0.0", port=PORT)
195
 
196
  # ========= Tests FastMCP (exemples curl) =========
197
- # Health check
198
  # curl -s https://hackathoncra-mcp.hf.space/health
199
 
200
- # Test MCP tools avec FastMCP
201
- # curl -s -X POST https://hackathoncra-mcp.hf.space/mcp \
202
- # -H "Content-Type: application/json" \
203
- # -d '{"jsonrpc": "2.0", "id": 1, "method": "initialize", "params": {"protocolVersion": "2024-11-05", "capabilities": {}}}'
204
-
205
- # curl -s -X POST https://hackathoncra-mcp.hf.space/mcp \
206
  # -H "Content-Type: application/json" \
207
- # -d '{"jsonrpc": "2.0", "id": 2, "method": "tools/list", "params": {}}'
208
 
209
- # curl -s -X POST https://hackathoncra-mcp.hf.space/mcp \
 
210
  # -H "Content-Type: application/json" \
211
- # -d '{"jsonrpc": "2.0", "id": 3, "method": "tools/call", "params": {"name": "analyze_weed_pressure", "arguments": {"years": [2020, 2021, 2022]}}}'
212
 
213
- # curl -s -X POST https://hackathoncra-mcp.hf.space/mcp \
 
 
214
  # -H "Content-Type: application/json" \
215
- # -d '{"jsonrpc": "2.0", "id": 4, "method": "tools/call", "params": {"name": "get_dataset_summary", "arguments": {}}}'
 
1
  import os
2
+ import gradio as gr
3
  from fastapi import FastAPI
 
4
  from gradio.routes import mount_gradio_app
5
 
6
+ from starlette.middleware import Middleware
7
+ from starlette.middleware.base import BaseHTTPMiddleware
8
+ from starlette.requests import Request
9
+ from starlette.responses import JSONResponse
10
+
11
+ # ==== FastMCP ====
12
+ from fastmcp import FastMCP
13
+
14
  # Import your existing Gradio app and analysis tools
15
  from gradio_app import create_gradio_app
16
  from data_loader import AgriculturalDataLoader
17
  from analysis_tools import AgriculturalAnalyzer
18
 
19
+ # --------- Config ---------
20
  PORT = int(os.environ.get("PORT", 7860))
21
+ MCP_BEARER = os.getenv("MCP_BEARER", "") # ajouter dans Settings > Variables & secrets si besoin
22
 
23
  # Initialize agricultural components
24
  data_loader = AgriculturalDataLoader()
25
  analyzer = AgriculturalAnalyzer(data_loader)
26
 
27
+ # --------- Auth middleware (optionnel) ---------
28
+ class BearerAuthMiddleware(BaseHTTPMiddleware):
29
+ def __init__(self, app, token: str | None):
30
+ super().__init__(app)
31
+ self.token = token
32
+
33
+ async def dispatch(self, request: Request, call_next):
34
+ if self.token:
35
+ auth = request.headers.get("authorization", "")
36
+ if not auth.startswith("Bearer ") or auth.split(" ", 1)[1] != self.token:
37
+ return JSONResponse({"detail": "Unauthorized"}, status_code=401)
38
+ return await call_next(request)
39
+
40
+ # --------- Déclare le serveur MCP ---------
41
  mcp = FastMCP("Agricultural Analysis Tools")
42
 
43
+ # --------- Outils MCP agricoles ---------
44
  @mcp.tool
45
  def analyze_weed_pressure(years: list[int] | None = None, plots: list[str] | None = None) -> str:
46
  """Analyze weed pressure trends using IFT herbicide data from Kerguéhennec experimental station."""
 
162
  """Agricultural dataset summary resource."""
163
  return get_dataset_summary()
164
 
165
+ # Crée l'app ASGI FastMCP et protège-la éventuellement
166
+ mcp_app = mcp.http_app( # cf. doc: http_app() retourne une app ASGI montable
167
+ path="/", # endpoint interne de l'app MCP => "/"
168
+ custom_middleware=[
169
+ Middleware(BearerAuthMiddleware, token=MCP_BEARER)
170
+ ] if MCP_BEARER else []
 
 
 
 
 
 
 
 
 
171
  )
172
 
173
+ # --------- App FastAPI parente (lifespan important) ---------
174
+ app = FastAPI(title="Agricultural Analysis - Gradio + FastMCP", lifespan=mcp_app.lifespan)
175
+
176
+ # Health simple
177
  @app.get("/health")
178
  def health():
 
179
  return {"ok": True, "service": "agricultural-mcp-server", "version": "1.0.0"}
180
 
181
+ # Monte MCP sous /mcp/ (final: /mcp/…)
182
+ app.mount("/mcp", mcp_app)
183
+
184
+ # --------- UI Gradio (utilise ton interface existante) ---------
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
185
  demo = create_gradio_app()
 
186
 
187
+ # Monte Gradio à la racine
188
+ app = mount_gradio_app(app, demo, path="/")
189
+
190
+ # --------- Entrée locale facultative ---------
191
  if __name__ == "__main__":
192
  import uvicorn
193
+ uvicorn.run(app, host="0.0.0.0", port=PORT)
194
 
195
  # ========= Tests FastMCP (exemples curl) =========
196
+ # Health
197
  # curl -s https://hackathoncra-mcp.hf.space/health
198
 
199
+ # MCP tools list
200
+ # curl -s -X POST https://hackathoncra-mcp.hf.space/mcp/ \
 
 
 
 
201
  # -H "Content-Type: application/json" \
202
+ # -d '{"jsonrpc": "2.0", "id": 1, "method": "tools/list", "params": {}}'
203
 
204
+ # MCP tool call
205
+ # curl -s -X POST https://hackathoncra-mcp.hf.space/mcp/ \
206
  # -H "Content-Type: application/json" \
207
+ # -d '{"jsonrpc": "2.0", "id": 2, "method": "tools/call", "params": {"name": "get_dataset_summary", "arguments": {}}}'
208
 
209
+ # MCP avec Bearer (si MCP_BEARER défini)
210
+ # curl -s -X POST https://hackathoncra-mcp.hf.space/mcp/ \
211
+ # -H "Authorization: Bearer VOTRE_TOKEN" \
212
  # -H "Content-Type: application/json" \
213
+ # -d '{"jsonrpc": "2.0", "id": 1, "method": "tools/list", "params": {}}'
requirements.txt CHANGED
@@ -1,7 +1,7 @@
1
  fastapi>=0.112
2
  uvicorn[standard]>=0.30
3
  gradio>=4.43
4
- fastmcp>=2.11.0
5
  pandas>=2.0.0
6
  numpy>=1.24.0
7
  matplotlib>=3.6.0
 
1
  fastapi>=0.112
2
  uvicorn[standard]>=0.30
3
  gradio>=4.43
4
+ fastmcp>=2.3.2
5
  pandas>=2.0.0
6
  numpy>=1.24.0
7
  matplotlib>=3.6.0