Skip to main content
Glama
server.py12.4 kB
#!/usr/bin/env python3 """ Weather MCP Server A FastMCP server that provides weather data and forecasts. """ import asyncio import json import os from typing import Any, Dict, List, Optional import httpx from fastmcp import FastMCP # Create the FastMCP server mcp = FastMCP("Weather MCP Server") # National Weather Service API configuration NWS_API_BASE = "https://api.weather.gov" def get_coordinates(location: str) -> tuple: """Get coordinates for a location using a simple geocoding approach.""" # For now, return coordinates for major cities # In a real implementation, you'd use a geocoding service city_coords = { "new york": (40.7128, -74.0060), "nyc": (40.7128, -74.0060), "london": (51.5074, -0.1278), "paris": (48.8566, 2.3522), "tokyo": (35.6762, 139.6503), "los angeles": (34.0522, -118.2437), "chicago": (41.8781, -87.6298), "houston": (29.7604, -95.3698), "phoenix": (33.4484, -112.0740), "philadelphia": (39.9526, -75.1652), "san antonio": (29.4241, -98.4936), "san diego": (32.7157, -117.1611), "dallas": (32.7767, -96.7970), "san jose": (37.3382, -121.8863), "austin": (30.2672, -97.7431), "jacksonville": (30.3322, -81.6557), "fort worth": (32.7555, -97.3308), "columbus": (39.9612, -82.9988), "charlotte": (35.2271, -80.8431), "seattle": (47.6062, -122.3321), "denver": (39.7392, -104.9903), "washington": (38.9072, -77.0369), "boston": (42.3601, -71.0589), "el paso": (31.7619, -106.4850), "nashville": (36.1627, -86.7816), "detroit": (42.3314, -83.0458), "oklahoma city": (35.4676, -97.5164), "portland": (45.5152, -122.6784), "las vegas": (36.1699, -115.1398), "memphis": (35.1495, -90.0490), "louisville": (38.2527, -85.7585), "baltimore": (39.2904, -76.6122), "milwaukee": (43.0389, -87.9065), "albuquerque": (35.0844, -106.6504), "tucson": (32.2226, -110.9747), "fresno": (36.7378, -119.7871), "sacramento": (38.5816, -121.4944), "mesa": (33.4152, -111.8315), "kansas city": (39.0997, -94.5786), "atlanta": (33.7490, -84.3880), "long beach": (33.7701, -118.1937), "colorado springs": (38.8339, -104.8214), "raleigh": (35.7796, -78.6382), "miami": (25.7617, -80.1918), "virginia beach": (36.8529, -75.9780), "omaha": (41.2565, -95.9345), "oakland": (37.8044, -122.2712), "minneapolis": (44.9778, -93.2650), "tulsa": (36.1540, -95.9928), "cleveland": (41.4993, -81.6944), "wichita": (37.6872, -97.3301), "arlington": (32.7357, -97.1081) } location_lower = location.lower().strip() if location_lower in city_coords: return city_coords[location_lower] # Default to New York if location not found return (40.7128, -74.0060) def get_weather_station(lat: float, lon: float) -> str: """Get the nearest weather station for given coordinates.""" try: # Get the grid point for the coordinates response = httpx.get(f"{NWS_API_BASE}/points/{lat},{lon}", timeout=10.0) response.raise_for_status() data = response.json() # Get the observation station station_response = httpx.get(data["properties"]["observationStations"], timeout=10.0) station_response.raise_for_status() stations = station_response.json() # Return the first (closest) station if stations["features"]: return stations["features"][0]["properties"]["stationIdentifier"] return None except Exception: return None @mcp.tool() def get_current_weather(location: str, units: str = "metric") -> str: """Get current weather for a specific location using National Weather Service data. Args: location: City name (e.g., "New York", "Los Angeles", "Chicago") units: Units of measurement: metric or imperial (default: metric) Returns: JSON string with current weather data from National Weather Service """ try: # Get coordinates for the location lat, lon = get_coordinates(location) # Get the grid point for the coordinates response = httpx.get(f"{NWS_API_BASE}/points/{lat},{lon}", timeout=10.0) response.raise_for_status() grid_data = response.json() # Get the current forecast (first period) as a fallback for current conditions forecast_response = httpx.get(grid_data["properties"]["forecast"], timeout=10.0) forecast_response.raise_for_status() forecast_data = forecast_response.json() if not forecast_data.get("properties", {}).get("periods"): return json.dumps({"error": "No forecast data available for this location"}, indent=2) # Use the first forecast period as current conditions current_period = forecast_data["properties"]["periods"][0] # Convert temperature based on units temp_f = current_period.get("temperature") if temp_f is not None: if units == "metric": temp_c = (temp_f - 32) * 5/9 temp = round(temp_c, 1) temp_unit = "°C" else: temp = temp_f temp_unit = "°F" else: temp = None temp_unit = "°C" if units == "metric" else "°F" weather_data = { "location": location.title(), "temperature": temp, "temperature_unit": temp_unit, "conditions": current_period.get("shortForecast", "Unknown"), "detailed_forecast": current_period.get("detailedForecast", ""), "wind_speed": current_period.get("windSpeed", ""), "wind_direction": current_period.get("windDirection", ""), "humidity": current_period.get("relativeHumidity", {}).get("value") if current_period.get("relativeHumidity") else None, "precipitation_chance": current_period.get("probabilityOfPrecipitation", {}).get("value") if current_period.get("probabilityOfPrecipitation") else None, "start_time": current_period.get("startTime", ""), "end_time": current_period.get("endTime", ""), "source": "National Weather Service", "note": "Current conditions based on forecast data" } return json.dumps(weather_data, indent=2) except httpx.RequestError as e: return json.dumps({"error": f"Request failed: {str(e)}"}, indent=2) except Exception as e: return json.dumps({"error": f"Unexpected error: {str(e)}"}, indent=2) @mcp.tool() def get_forecast(location: str, days: int = 5, units: str = "metric") -> str: """Get weather forecast for a location using National Weather Service data. Args: location: City name (e.g., "New York", "Los Angeles", "Chicago") days: Number of days to forecast (default: 5, max: 5) units: Units of measurement: metric or imperial (default: metric) Returns: JSON string with detailed forecast data from National Weather Service """ try: days = min(max(days, 1), 5) # Clamp between 1 and 5 # Get coordinates for the location lat, lon = get_coordinates(location) # Get the grid point for the coordinates response = httpx.get(f"{NWS_API_BASE}/points/{lat},{lon}", timeout=10.0) response.raise_for_status() grid_data = response.json() # Get the forecast forecast_response = httpx.get(grid_data["properties"]["forecast"], timeout=10.0) forecast_response.raise_for_status() forecast_data = forecast_response.json() forecasts = [] for period in forecast_data["properties"]["periods"][:days * 2]: # 2 periods per day (day/night) # Convert temperature based on units temp_f = period.get("temperature") if temp_f is not None: if units == "metric": temp_c = (temp_f - 32) * 5/9 temp = round(temp_c, 1) temp_unit = "°C" else: temp = temp_f temp_unit = "°F" else: temp = None temp_unit = "°C" if units == "metric" else "°F" forecasts.append({ "name": period.get("name", ""), "start_time": period.get("startTime", ""), "end_time": period.get("endTime", ""), "temperature": temp, "temperature_unit": temp_unit, "conditions": period.get("shortForecast", ""), "detailed_forecast": period.get("detailedForecast", ""), "wind_speed": period.get("windSpeed", ""), "wind_direction": period.get("windDirection", ""), "humidity": period.get("relativeHumidity", {}).get("value") if period.get("relativeHumidity") else None, "precipitation_chance": period.get("probabilityOfPrecipitation", {}).get("value") if period.get("probabilityOfPrecipitation") else None }) forecast_result = { "location": location.title(), "forecast": forecasts, "units": units, "source": "National Weather Service", "updated": forecast_data.get("properties", {}).get("updated", "") } return json.dumps(forecast_result, indent=2) except httpx.RequestError as e: return json.dumps({"error": f"Request failed: {str(e)}"}, indent=2) except Exception as e: return json.dumps({"error": f"Unexpected error: {str(e)}"}, indent=2) @mcp.tool() def get_weather_alerts(location: str) -> str: """Get weather alerts for a location using National Weather Service data. Args: location: City name (e.g., "New York", "Los Angeles", "Chicago") Returns: JSON string with official weather alerts from National Weather Service """ try: # Get coordinates for the location lat, lon = get_coordinates(location) # Get alerts for the area using the direct alerts endpoint alerts_response = httpx.get(f"{NWS_API_BASE}/alerts?point={lat},{lon}", timeout=10.0) alerts_response.raise_for_status() alerts_data = alerts_response.json() alerts = [] for alert in alerts_data.get("features", []): properties = alert.get("properties", {}) alerts.append({ "event": properties.get("event", ""), "headline": properties.get("headline", ""), "description": properties.get("description", ""), "instruction": properties.get("instruction", ""), "severity": properties.get("severity", ""), "urgency": properties.get("urgency", ""), "certainty": properties.get("certainty", ""), "area_desc": properties.get("areaDesc", ""), "effective": properties.get("effective", ""), "expires": properties.get("expires", ""), "sender": properties.get("senderName", ""), "sender_short": properties.get("sender", "") }) alert_result = { "location": location.title(), "alerts": alerts, "count": len(alerts), "source": "National Weather Service", "updated": alerts_data.get("updated", "") } return json.dumps(alert_result, indent=2) except httpx.RequestError as e: return json.dumps({"error": f"Request failed: {str(e)}"}, indent=2) except Exception as e: return json.dumps({"error": f"Unexpected error: {str(e)}"}, indent=2) if __name__ == "__main__": port = int(os.environ.get("PORT", 8000)) mcp.run( transport="http", host="0.0.0.0", port=port, stateless_http=True )

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/akarnik23/mcp-weather'

If you have feedback or need assistance with the MCP directory API, please join our Discord server