weather-mcp / app.py
PulkitT01
Update so gradio calls forecast function, not ui. Eliminate ui function
b7e84d4
import os
os.system("pip install geopy -q")
import gradio as gr
from datetime import datetime
import requests
from geopy.geocoders import Nominatim
from datetime import datetime, date, timedelta
import pandas as pd
import json
from pathlib import Path
WEATHER_CODES = {
0: "Clear sky", 1: "Mainly clear", 2: "Partly cloudy", 3: "Overcast",
45: "Fog", 48: "Depositing rime fog", 51: "Light drizzle", 53: "Moderate drizzle", 55: "Dense drizzle",
61: "Slight rain", 63: "Moderate rain", 65: "Heavy rain", 71: "Slight snowfall", 73: "Moderate snowfall",
75: "Heavy snowfall", 80: "Slight rain showers", 81: "Moderate rain showers", 82: "Violent rain showers",
95: "Thunderstorm", 96: "Thunderstorm with hail"
}
def get_weather_forecast_range(location_name, start_date, end_date):
"""
Fetches weather forecast for a given location and date range, returns a Markdown summary.
Also saves the forecast as a JSON file under the 'logs' directory.
Parameters:
location_name (str): Name of the location (e.g., city or landmark).\n
start_date (str or date): Start date of the forecast in 'YYYY-MM-DD' format or as a date object.\n
end_date (str or date): End date of the forecast in 'YYYY-MM-DD' format or as a date object.\n
Returns:
str: Markdown-formatted string with weather details or an error message.\n
"""
try:
if isinstance(start_date, str):
start_date = datetime.strptime(start_date, "%Y-%m-%d").date()
if isinstance(end_date, str):
end_date = datetime.strptime(end_date, "%Y-%m-%d").date()
today = date.today()
days_ahead = (end_date - today).days
if days_ahead > 15:
return f"**โš ๏ธ Error:** Weather data only available up to 15 days from today."
geolocator = Nominatim(user_agent="weather_api")
location = geolocator.geocode(location_name)
if not location:
return f"**โš ๏ธ Error:** Could not find coordinates for '{location_name}'."
lat, lon = location.latitude, location.longitude
include_hourly = days_ahead <= 6
url = "https://api.open-meteo.com/v1/forecast"
params = {
"latitude": lat,
"longitude": lon,
"daily": "sunrise,sunset,uv_index_max,temperature_2m_max,temperature_2m_min,weather_code",
"temperature_unit": "celsius",
"windspeed_unit": "kmh",
"timeformat": "iso8601",
"timezone": "auto",
"start_date": start_date.isoformat(),
"end_date": end_date.isoformat()
}
if include_hourly:
params["hourly"] = "temperature_2m,weather_code,uv_index,visibility"
response = requests.get(url, params=params)
if response.status_code != 200:
return f"**โš ๏ธ Error:** API error {response.status_code}: {response.text}"
raw = response.json()
forecasts = []
for idx, d in enumerate(raw["daily"]["time"]):
day_result = {
"date": d,
"sunrise": raw["daily"]["sunrise"][idx].split("T")[1],
"sunset": raw["daily"]["sunset"][idx].split("T")[1],
"uv_max": round(raw["daily"]["uv_index_max"][idx], 1),
"temp_min": round(raw["daily"]["temperature_2m_min"][idx]),
"temp_max": round(raw["daily"]["temperature_2m_max"][idx]),
"weather": WEATHER_CODES.get(int(raw["daily"]["weather_code"][idx]), "Unknown")
}
if include_hourly and "hourly" in raw:
hourly_df = pd.DataFrame({
"time": raw["hourly"]["time"],
"temp": raw["hourly"]["temperature_2m"],
"code": raw["hourly"]["weather_code"],
"uv": raw["hourly"]["uv_index"],
"visibility": [v / 1000 for v in raw["hourly"]["visibility"]]
})
hourly_df["time"] = pd.to_datetime(hourly_df["time"])
target_date = datetime.strptime(d, "%Y-%m-%d").date()
df_day = hourly_df[hourly_df["time"].dt.date == target_date]
df_day["weather"] = df_day["code"].apply(lambda c: WEATHER_CODES.get(int(c), "Unknown"))
day_result["hourly"] = [
{
"time": t.strftime("%Y-%m-%d %H:%M"),
"temp": f"{round(temp)}ยฐC",
"weather": w,
"uv": round(uv, 1),
"visibility": f"{round(vis, 1)} km"
}
for t, temp, w, uv, vis in zip(
df_day["time"], df_day["temp"], df_day["weather"],
df_day["uv"], df_day["visibility"]
)
]
else:
day_result["note"] = "Hourly weather data is only available for the next 7 days."
forecasts.append(day_result)
# Save the forecast JSON
save_dir = Path("logs")
save_dir.mkdir(exist_ok=True)
save_path = save_dir / f"weather_{location_name}_{start_date}_to_{end_date}.json"
with open(save_path, "w", encoding="utf-8") as f:
json.dump(forecasts, f, indent=2)
# Generate Markdown output
md = f"## Weather Forecast for {location_name}\n\n"
for day in forecasts:
md += f"### ๐Ÿ“… {day['date']}\n"
md += f"- ๐ŸŒ… Sunrise: {day['sunrise']}\n"
md += f"- ๐ŸŒ‡ Sunset: {day['sunset']}\n"
md += f"- ๐ŸŒก๏ธ Temp: {day['temp_min']}ยฐC โ€“ {day['temp_max']}ยฐC\n"
md += f"- ๐ŸŒค๏ธ Weather: {day['weather']}\n"
md += f"- โ˜€๏ธ UV Index Max: {day['uv_max']}\n"
if "hourly" in day:
md += f"**Hourly Forecast:**\n"
for h in day["hourly"]:
md += f" - {h['time']}: {h['temp']}, {h['weather']}, UV {h['uv']}, Visibility {h['visibility']}\n"
elif "note" in day:
md += f"_{day['note']}_\n"
md += "\n"
return md
except Exception as e:
return f"**โš ๏ธ Error:** {str(e)}"
with gr.Blocks(title="TravAI Weather Forecast", theme=gr.themes.Soft(primary_hue="indigo")) as demo:
gr.Markdown("""
# ๐ŸŒฆ๏ธ TravAI: Weather Forecast Tool
_Your smart travel weather companion, powered by Open-Meteo API._
""")
with gr.Group():
with gr.Row():
location = gr.Textbox(label="๐Ÿ“ Location", placeholder="Enter a city or landmark", scale=2)
start = gr.Textbox(label="๐Ÿ—“๏ธ Start Date", value=str(date.today()), scale=1)
end = gr.Textbox(label="๐Ÿ“† End Date", value=str(date.today() + timedelta(days=6)), scale=1)
submit_btn = gr.Button("๐Ÿ” Get Forecast")
output = gr.Markdown(label="๐Ÿ“œ Forecast Output")
submit_btn.click(fn=get_weather_forecast_range,
inputs=[location, start, end],
outputs=output)
gr.Markdown("""
---
โš™๏ธ _Built with [Gradio](https://www.gradio.app) & [Open-Meteo](https://open-meteo.com) โ€ข Developed during MCP Hackathon 2025_
""")
if __name__ == "__main__":
demo.launch(mcp_server=True, share=True)