Tracy André commited on
Commit
eb1a639
·
1 Parent(s): 53bf046
Files changed (1) hide show
  1. app.py +236 -22
app.py CHANGED
@@ -29,18 +29,219 @@ def _check_auth(authorization: str | None):
29
  if token != MCP_BEARER:
30
  raise HTTPException(status_code=401, detail="Unauthorized")
31
 
32
- # ========= Endpoint MCP (exemple minimal) =========
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
  @api.post("/mcp")
34
  async def mcp_endpoint(request: Request, authorization: str | None = Header(None)):
35
  _check_auth(authorization)
36
  try:
37
  payload = await request.json()
38
  except Exception:
39
- raise HTTPException(status_code=400, detail="Invalid JSON")
40
-
41
- # TODO: remplace ici par ta logique MCP réelle (dispatch, tools, resources, etc.)
42
- # Pour démarrer, on renvoie simplement le payload reçu.
43
- return JSONResponse({"status": "ok", "echo": payload})
 
 
 
 
 
 
 
 
 
 
44
 
45
  # ========= UI Gradio =========
46
  # Use your existing comprehensive agricultural analysis interface
@@ -55,29 +256,42 @@ if __name__ == "__main__":
55
  import uvicorn
56
  uvicorn.run(app, host="0.0.0.0", port=PORT)
57
 
58
- # ========= Tests curl (exemples en commentaires) =========
59
  # Healthcheck (public, GET)
60
- # curl -s https://<ORG>-<SPACE>.hf.space/health
 
 
 
 
 
61
 
62
- # MCP sans auth (si MCP_BEARER non défini)
63
- # curl -s -X POST https://<ORG>-<SPACE>.hf.space/mcp \
64
  # -H "Content-Type: application/json" \
65
- # -d '{"ping":"pong"}'
66
 
67
- # MCP avec Bearer (si MCP_BEARER défini)
68
- # curl -s -X POST https://<ORG>-<SPACE>.hf.space/mcp \
69
- # -H "Authorization: Bearer <TON_TOKEN>" \
70
  # -H "Content-Type: application/json" \
71
- # -d '{"ping":"pong"}'
72
 
73
- # Test de l'UI Gradio
74
- # curl -s https://<ORG>-<SPACE>.hf.space/
 
 
 
 
 
 
 
75
 
76
- # Exemples de payloads MCP pour tester les fonctionnalités agricoles:
77
- # curl -s -X POST https://<ORG>-<SPACE>.hf.space/mcp \
78
  # -H "Content-Type: application/json" \
79
- # -d '{"method": "analyze_weed_pressure", "params": {"years": [2020, 2021, 2022], "plots": ["P1", "P2"]}}'
80
 
81
- # curl -s -X POST https://<ORG>-<SPACE>.hf.space/mcp \
 
 
82
  # -H "Content-Type: application/json" \
83
- # -d '{"method": "predict_future_pressure", "params": {"target_years": [2025, 2026], "max_ift": 1.0}}'
 
29
  if token != MCP_BEARER:
30
  raise HTTPException(status_code=401, detail="Unauthorized")
31
 
32
+ def handle_mcp_request(payload: dict) -> dict:
33
+ """Handle MCP JSON-RPC 2.0 requests."""
34
+ # Validate JSON-RPC 2.0 format
35
+ if payload.get("jsonrpc") != "2.0":
36
+ return {
37
+ "jsonrpc": "2.0",
38
+ "id": payload.get("id"),
39
+ "error": {
40
+ "code": -32600,
41
+ "message": "Invalid Request",
42
+ "data": "Missing or invalid jsonrpc version"
43
+ }
44
+ }
45
+
46
+ request_id = payload.get("id")
47
+ method = payload.get("method")
48
+ params = payload.get("params", {})
49
+
50
+ if not method:
51
+ return {
52
+ "jsonrpc": "2.0",
53
+ "id": request_id,
54
+ "error": {
55
+ "code": -32600,
56
+ "message": "Invalid Request",
57
+ "data": "Missing method"
58
+ }
59
+ }
60
+
61
+ # Handle MCP standard methods
62
+ if method == "initialize":
63
+ return {
64
+ "jsonrpc": "2.0",
65
+ "id": request_id,
66
+ "result": {
67
+ "protocolVersion": "2024-11-05",
68
+ "capabilities": {
69
+ "tools": {
70
+ "listChanged": False
71
+ },
72
+ "resources": {
73
+ "subscribe": False,
74
+ "listChanged": False
75
+ }
76
+ },
77
+ "serverInfo": {
78
+ "name": "agricultural-mcp-server",
79
+ "version": "1.0.0"
80
+ }
81
+ }
82
+ }
83
+
84
+ elif method == "tools/list":
85
+ return {
86
+ "jsonrpc": "2.0",
87
+ "id": request_id,
88
+ "result": {
89
+ "tools": [
90
+ {
91
+ "name": "analyze_weed_pressure",
92
+ "description": "Analyze weed pressure trends using IFT herbicide data",
93
+ "inputSchema": {
94
+ "type": "object",
95
+ "properties": {
96
+ "years": {
97
+ "type": "array",
98
+ "items": {"type": "integer"},
99
+ "description": "Years to analyze"
100
+ },
101
+ "plots": {
102
+ "type": "array",
103
+ "items": {"type": "string"},
104
+ "description": "Plot names to analyze"
105
+ }
106
+ }
107
+ }
108
+ },
109
+ {
110
+ "name": "predict_future_pressure",
111
+ "description": "Predict future weed pressure for target years",
112
+ "inputSchema": {
113
+ "type": "object",
114
+ "properties": {
115
+ "target_years": {
116
+ "type": "array",
117
+ "items": {"type": "integer"},
118
+ "description": "Years to predict"
119
+ },
120
+ "max_ift": {
121
+ "type": "number",
122
+ "description": "Maximum IFT threshold for sensitive crops"
123
+ }
124
+ }
125
+ }
126
+ },
127
+ {
128
+ "name": "analyze_crop_rotation",
129
+ "description": "Analyze crop rotation impact on weed pressure",
130
+ "inputSchema": {
131
+ "type": "object",
132
+ "properties": {}
133
+ }
134
+ }
135
+ ]
136
+ }
137
+ }
138
+
139
+ elif method == "tools/call":
140
+ tool_name = params.get("name")
141
+ tool_args = params.get("arguments", {})
142
+
143
+ if tool_name == "analyze_weed_pressure":
144
+ return {
145
+ "jsonrpc": "2.0",
146
+ "id": request_id,
147
+ "result": {
148
+ "content": [
149
+ {
150
+ "type": "text",
151
+ "text": f"Analyse de la pression adventices pour les années {tool_args.get('years', [])} et parcelles {tool_args.get('plots', [])}\n\nCette fonction analyserait normalement les données IFT herbicides de votre dataset agricultural."
152
+ }
153
+ ]
154
+ }
155
+ }
156
+
157
+ elif tool_name == "predict_future_pressure":
158
+ return {
159
+ "jsonrpc": "2.0",
160
+ "id": request_id,
161
+ "result": {
162
+ "content": [
163
+ {
164
+ "type": "text",
165
+ "text": f"Prédiction de pression adventices pour {tool_args.get('target_years', [])} avec seuil IFT max {tool_args.get('max_ift', 1.0)}\n\nCette fonction utiliserait vos modèles d'apprentissage automatique pour prédire les futures pressions."
166
+ }
167
+ ]
168
+ }
169
+ }
170
+
171
+ elif tool_name == "analyze_crop_rotation":
172
+ return {
173
+ "jsonrpc": "2.0",
174
+ "id": request_id,
175
+ "result": {
176
+ "content": [
177
+ {
178
+ "type": "text",
179
+ "text": "Analyse de l'impact des rotations culturales sur la pression adventices\n\nCette fonction analyserait les patterns de rotation dans votre dataset."
180
+ }
181
+ ]
182
+ }
183
+ }
184
+
185
+ else:
186
+ return {
187
+ "jsonrpc": "2.0",
188
+ "id": request_id,
189
+ "error": {
190
+ "code": -32601,
191
+ "message": "Method not found",
192
+ "data": f"Unknown tool: {tool_name}"
193
+ }
194
+ }
195
+
196
+ elif method == "resources/list":
197
+ return {
198
+ "jsonrpc": "2.0",
199
+ "id": request_id,
200
+ "result": {
201
+ "resources": [
202
+ {
203
+ "uri": "agricultural://dataset/summary",
204
+ "name": "Agricultural Dataset Summary",
205
+ "description": "Summary of the Kerguéhennec experimental station dataset",
206
+ "mimeType": "text/plain"
207
+ }
208
+ ]
209
+ }
210
+ }
211
+
212
+ else:
213
+ return {
214
+ "jsonrpc": "2.0",
215
+ "id": request_id,
216
+ "error": {
217
+ "code": -32601,
218
+ "message": "Method not found",
219
+ "data": f"Unknown method: {method}"
220
+ }
221
+ }
222
+
223
+ # ========= Endpoint MCP (conforme JSON-RPC 2.0) =========
224
  @api.post("/mcp")
225
  async def mcp_endpoint(request: Request, authorization: str | None = Header(None)):
226
  _check_auth(authorization)
227
  try:
228
  payload = await request.json()
229
  except Exception:
230
+ return JSONResponse(
231
+ status_code=400,
232
+ content={
233
+ "jsonrpc": "2.0",
234
+ "id": None,
235
+ "error": {
236
+ "code": -32700,
237
+ "message": "Parse error",
238
+ "data": "Invalid JSON"
239
+ }
240
+ }
241
+ )
242
+
243
+ response = handle_mcp_request(payload)
244
+ return JSONResponse(response)
245
 
246
  # ========= UI Gradio =========
247
  # Use your existing comprehensive agricultural analysis interface
 
256
  import uvicorn
257
  uvicorn.run(app, host="0.0.0.0", port=PORT)
258
 
259
+ # ========= Tests curl (exemples conformes JSON-RPC 2.0) =========
260
  # Healthcheck (public, GET)
261
+ # curl -s https://hackathoncra-mcp.hf.space/health
262
+
263
+ # Test d'initialisation MCP
264
+ # curl -s -X POST https://hackathoncra-mcp.hf.space/mcp \
265
+ # -H "Content-Type: application/json" \
266
+ # -d '{"jsonrpc": "2.0", "id": 1, "method": "initialize", "params": {"protocolVersion": "2024-11-05", "capabilities": {}}}'
267
 
268
+ # Liste des outils disponibles
269
+ # curl -s -X POST https://hackathoncra-mcp.hf.space/mcp \
270
  # -H "Content-Type: application/json" \
271
+ # -d '{"jsonrpc": "2.0", "id": 2, "method": "tools/list", "params": {}}'
272
 
273
+ # Appel d'outil - Analyse pression adventices
274
+ # curl -s -X POST https://hackathoncra-mcp.hf.space/mcp \
 
275
  # -H "Content-Type: application/json" \
276
+ # -d '{"jsonrpc": "2.0", "id": 3, "method": "tools/call", "params": {"name": "analyze_weed_pressure", "arguments": {"years": [2020, 2021, 2022], "plots": ["P1", "P2"]}}}'
277
 
278
+ # Appel d'outil - Prédiction future
279
+ # curl -s -X POST https://hackathoncra-mcp.hf.space/mcp \
280
+ # -H "Content-Type: application/json" \
281
+ # -d '{"jsonrpc": "2.0", "id": 4, "method": "tools/call", "params": {"name": "predict_future_pressure", "arguments": {"target_years": [2025, 2026], "max_ift": 1.0}}}'
282
+
283
+ # Appel d'outil - Analyse rotation
284
+ # curl -s -X POST https://hackathoncra-mcp.hf.space/mcp \
285
+ # -H "Content-Type: application/json" \
286
+ # -d '{"jsonrpc": "2.0", "id": 5, "method": "tools/call", "params": {"name": "analyze_crop_rotation", "arguments": {}}}'
287
 
288
+ # Liste des ressources
289
+ # curl -s -X POST https://hackathoncra-mcp.hf.space/mcp \
290
  # -H "Content-Type: application/json" \
291
+ # -d '{"jsonrpc": "2.0", "id": 6, "method": "resources/list", "params": {}}'
292
 
293
+ # Avec authentification Bearer (si MCP_BEARER défini)
294
+ # curl -s -X POST https://hackathoncra-mcp.hf.space/mcp \
295
+ # -H "Authorization: Bearer VOTRE_TOKEN" \
296
  # -H "Content-Type: application/json" \
297
+ # -d '{"jsonrpc": "2.0", "id": 1, "method": "initialize", "params": {"protocolVersion": "2024-11-05", "capabilities": {}}}'