|
|
""" |
|
|
All MCP Server Implementations |
|
|
Deploy as: src/servers/__init__.py OR separate files |
|
|
|
|
|
Contains: |
|
|
- WeatherServer (Open-Meteo) |
|
|
- SoilPropertiesServer (SoilGrids) |
|
|
- WaterServer (GRACE) |
|
|
- ElevationServer (OpenElevation) |
|
|
- PestsServer (iNaturalist) |
|
|
""" |
|
|
|
|
|
import aiohttp |
|
|
import asyncio |
|
|
import os |
|
|
import xarray as xr |
|
|
import requests |
|
|
from datetime import datetime |
|
|
from typing import Dict, Any |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class PestsServer: |
|
|
"""iNaturalist Pest Observation Server""" |
|
|
|
|
|
async def get_data(self, lat: float, lon: float) -> Dict[str, Any]: |
|
|
try: |
|
|
url = "https://api.inaturalist.org/v1/observations" |
|
|
params = { |
|
|
"lat": lat, |
|
|
"lng": lon, |
|
|
"radius": 50, |
|
|
"order": "desc", |
|
|
"order_by": "observed_on", |
|
|
"per_page": 20, |
|
|
"quality_grade": "research", |
|
|
"iconic_taxa": "Insecta" |
|
|
} |
|
|
|
|
|
async with aiohttp.ClientSession() as session: |
|
|
async with session.get(url, params=params, timeout=aiohttp.ClientTimeout(total=10)) as response: |
|
|
if response.status == 200: |
|
|
data = await response.json() |
|
|
observations = data.get("results", []) |
|
|
|
|
|
pest_summary = [] |
|
|
for obs in observations[:10]: |
|
|
pest_summary.append({ |
|
|
"species": obs.get("taxon", {}).get("name", "Unknown"), |
|
|
"common_name": obs.get("taxon", {}).get("preferred_common_name", "N/A"), |
|
|
"observed_on": obs.get("observed_on"), |
|
|
"distance_km": obs.get("distance", "N/A") |
|
|
}) |
|
|
|
|
|
return { |
|
|
"status": "success", |
|
|
"data": { |
|
|
"recent_observations": pest_summary, |
|
|
"total_count": len(observations), |
|
|
"data_source": "iNaturalist Community Data" |
|
|
} |
|
|
} |
|
|
else: |
|
|
return {"status": "error", "error": f"HTTP {response.status}"} |
|
|
except Exception as e: |
|
|
return {"status": "error", "error": str(e)} |