get_forecast_by_city
Retrieve weather forecast data for any US city by providing the city name. This tool accesses National Weather Service APIs to deliver location-specific weather information.
Instructions
Get weather forecast for a city by name.
Args:
city_name: Name of the city (e.g., "San Francisco", "New York", "Chicago")
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| city_name | Yes |
Implementation Reference
- weather.py:193-227 (handler)The core handler function for the 'get_forecast_by_city' tool. Decorated with @mcp.tool() for automatic registration and schema generation from signature and docstring. Geocodes the city name, retrieves forecast data using helper functions, and returns a formatted string with forecast details for up to 10 periods.@mcp.tool() async def get_forecast_by_city(city_name: str) -> str: """Get weather forecast for a city by name. Args: city_name: Name of the city (e.g., "San Francisco", "New York", "Chicago") """ coords = await geocode_location(city_name) if not coords: return f"Error: Could not find coordinates for '{city_name}'. Please check the city name and try again." latitude, longitude = coords forecast_data = await get_forecast_data(latitude, longitude) if not forecast_data: return f"Unable to fetch forecast data for {city_name}. The location may be outside NWS coverage area (US only)." periods = forecast_data["properties"]["periods"] location = forecast_data.get("_location", {}) location_str = f"{location.get('city', city_name)}, {location.get('state', 'Unknown')}" forecasts = [] for period in periods[:10]: forecast = f""" {period['name']}: Temperature: {period['temperature']}°{period['temperatureUnit']} Wind: {period['windSpeed']} {period['windDirection']} {f"Humidity: {period.get('relativeHumidity', {}).get('value', 'N/A')}%" if period.get('relativeHumidity') else ""} {f"Precipitation: {period.get('probabilityOfPrecipitation', {}).get('value', 0)}%" if period.get('probabilityOfPrecipitation') else ""} Forecast: {period['detailedForecast']} """ forecasts.append(forecast) return f"Weather Forecast for {location_str}:\n" + "\n---\n".join(forecasts)
- weather.py:33-71 (helper)Helper function used by get_forecast_by_city to convert city name to latitude/longitude coordinates, prioritizing US locations using Open-Meteo geocoding API.async def geocode_location(city_name: str) -> tuple[float, float] | None: """Geocode a city name to latitude and longitude coordinates. Returns: Tuple of (latitude, longitude) or None if geocoding fails """ async with httpx.AsyncClient() as client: try: # Try to get US results first by adding country code # Open-Meteo supports country_code parameter params = { "name": city_name, "count": 10, # Get more results to find US cities "language": "en", "format": "json" } response = await client.get( GEOCODE_API_BASE, params=params, timeout=10.0 ) response.raise_for_status() data = response.json() if data.get("results") and len(data["results"]) > 0: # Prefer US results (country_code == "US") for result in data["results"]: if result.get("country_code") == "US": return (result["latitude"], result["longitude"]) # If no US result found, return the first result anyway # (user might be looking for a non-US city, or it might still work) result = data["results"][0] return (result["latitude"], result["longitude"]) return None except httpx.RequestError: return None except Exception: return None
- weather.py:129-155 (helper)Helper function called by get_forecast_by_city to fetch detailed forecast data from the National Weather Service (NWS) API for given coordinates, including location metadata.async def get_forecast_data(latitude: float, longitude: float) -> dict[str, Any] | None: """Get forecast data for coordinates. Returns None on error.""" if not validate_coordinates(latitude, longitude): return None points_url = f"{NWS_API_BASE}/points/{latitude},{longitude}" points_data = await make_nws_request(points_url) if not points_data or "properties" not in points_data: return None forecast_url = points_data["properties"].get("forecast") if not forecast_url: return None forecast_data = await make_nws_request(forecast_url) if not forecast_data or "properties" not in forecast_data: return None # Add location info to the forecast data location_info = points_data["properties"].get("relativeLocation", {}) forecast_data["_location"] = { "city": location_info.get("properties", {}).get("city", "Unknown"), "state": location_info.get("properties", {}).get("state", "Unknown") } return forecast_data
- weather.py:28-31 (helper)Utility helper for validating latitude and longitude coordinates used indirectly in forecast retrieval.def validate_coordinates(latitude: float, longitude: float) -> bool: """Validate that coordinates are within valid ranges.""" return -90 <= latitude <= 90 and -180 <= longitude <= 180