""" 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 # ============================================================================ # PESTS SERVER (iNaturalist) # ============================================================================ 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, # 50km radius "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)}