mirror of
https://github.com/furyhawk/home_stack.git
synced 2026-07-21 02:06:47 +00:00
feat: add weather tool with NWS API integration and update dependencies
This commit is contained in:
@@ -17,6 +17,7 @@ dependencies = [
|
||||
"fastapi[standard]>=0.115.0",
|
||||
"filelock>=3.16.1",
|
||||
"gguf>=0.13.0",
|
||||
"httpx>=0.28.1",
|
||||
"huggingface-hub[hf-xet]>=0.32.0",
|
||||
"importlib-metadata>=8.6.1 ; python_full_version < '3.10'",
|
||||
"ipykernel>=6.29.5",
|
||||
@@ -28,6 +29,7 @@ dependencies = [
|
||||
"llama-cpp-python>=0.3.9",
|
||||
"llguidance>=0.7.11,<0.8.0 ; platform_machine == 'aarch64' or platform_machine == 'arm64' or platform_machine == 'x86_64'",
|
||||
"lm-format-enforcer>=0.10.11,<0.11",
|
||||
"mcp[cli]>=1.9.4",
|
||||
"mistral-common[opencv]>=1.5.4",
|
||||
"msgspec>=0.19.0",
|
||||
"ninja>=1.11.1.4",
|
||||
|
||||
Generated
+4
@@ -2067,6 +2067,7 @@ dependencies = [
|
||||
{ name = "fastapi", extra = ["standard"] },
|
||||
{ name = "filelock" },
|
||||
{ name = "gguf" },
|
||||
{ name = "httpx" },
|
||||
{ name = "huggingface-hub", extra = ["hf-xet"] },
|
||||
{ name = "ipykernel" },
|
||||
{ name = "ipywidgets" },
|
||||
@@ -2077,6 +2078,7 @@ dependencies = [
|
||||
{ name = "llama-cpp-python" },
|
||||
{ name = "llguidance", marker = "platform_machine == 'aarch64' or platform_machine == 'arm64' or platform_machine == 'x86_64'" },
|
||||
{ name = "lm-format-enforcer" },
|
||||
{ name = "mcp", extra = ["cli"] },
|
||||
{ name = "mistral-common", extra = ["opencv"] },
|
||||
{ name = "msgspec" },
|
||||
{ name = "ninja" },
|
||||
@@ -2133,6 +2135,7 @@ requires-dist = [
|
||||
{ name = "fastapi", extras = ["standard"], specifier = ">=0.115.0" },
|
||||
{ name = "filelock", specifier = ">=3.16.1" },
|
||||
{ name = "gguf", specifier = ">=0.13.0" },
|
||||
{ name = "httpx", specifier = ">=0.28.1" },
|
||||
{ name = "huggingface-hub", extras = ["hf-xet"], specifier = ">=0.32.0" },
|
||||
{ name = "importlib-metadata", marker = "python_full_version < '3.10'", specifier = ">=8.6.1" },
|
||||
{ name = "ipykernel", specifier = ">=6.29.5" },
|
||||
@@ -2144,6 +2147,7 @@ requires-dist = [
|
||||
{ name = "llama-cpp-python", specifier = ">=0.3.9" },
|
||||
{ name = "llguidance", marker = "platform_machine == 'aarch64' or platform_machine == 'arm64' or platform_machine == 'x86_64'", specifier = ">=0.7.11,<0.8.0" },
|
||||
{ name = "lm-format-enforcer", specifier = ">=0.10.11,<0.11" },
|
||||
{ name = "mcp", extras = ["cli"], specifier = ">=1.9.4" },
|
||||
{ name = "mistral-common", extras = ["opencv"], specifier = ">=1.5.4" },
|
||||
{ name = "msgspec", specifier = ">=0.19.0" },
|
||||
{ name = "ninja", specifier = ">=1.11.1.4" },
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
from typing import Any
|
||||
import httpx
|
||||
from mcp.server.fastmcp import FastMCP
|
||||
|
||||
# Initialize FastMCP server
|
||||
mcp = FastMCP("weather")
|
||||
|
||||
# Constants
|
||||
NWS_API_BASE = "https://api.weather.gov"
|
||||
USER_AGENT = "weather-app/1.0"
|
||||
|
||||
|
||||
async def make_nws_request(url: str) -> dict[str, Any] | None:
|
||||
"""Make a request to the NWS API with proper error handling."""
|
||||
headers = {"User-Agent": USER_AGENT, "Accept": "application/geo+json"}
|
||||
async with httpx.AsyncClient() as client:
|
||||
try:
|
||||
response = await client.get(url, headers=headers, timeout=30.0)
|
||||
response.raise_for_status()
|
||||
return response.json()
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
|
||||
def format_alert(feature: dict) -> str:
|
||||
"""Format an alert feature into a readable string."""
|
||||
props = feature["properties"]
|
||||
return f"""
|
||||
Event: {props.get('event', 'Unknown')}
|
||||
Area: {props.get('areaDesc', 'Unknown')}
|
||||
Severity: {props.get('severity', 'Unknown')}
|
||||
Description: {props.get('description', 'No description available')}
|
||||
Instructions: {props.get('instruction', 'No specific instructions provided')}
|
||||
"""
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
async def get_alerts(state: str) -> str:
|
||||
"""Get weather alerts for a US state.
|
||||
|
||||
Args:
|
||||
state: Two-letter US state code (e.g. CA, NY)
|
||||
"""
|
||||
url = f"{NWS_API_BASE}/alerts/active/area/{state}"
|
||||
data = await make_nws_request(url)
|
||||
|
||||
if not data or "features" not in data:
|
||||
return "Unable to fetch alerts or no alerts found."
|
||||
|
||||
if not data["features"]:
|
||||
return "No active alerts for this state."
|
||||
|
||||
alerts = [format_alert(feature) for feature in data["features"]]
|
||||
return "\n---\n".join(alerts)
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
async def get_forecast(latitude: float, longitude: float) -> str:
|
||||
"""Get weather forecast for a location.
|
||||
|
||||
Args:
|
||||
latitude: Latitude of the location
|
||||
longitude: Longitude of the location
|
||||
"""
|
||||
# First get the forecast grid endpoint
|
||||
points_url = f"{NWS_API_BASE}/points/{latitude},{longitude}"
|
||||
points_data = await make_nws_request(points_url)
|
||||
|
||||
if not points_data:
|
||||
return "Unable to fetch forecast data for this location."
|
||||
|
||||
# Get the forecast URL from the points response
|
||||
forecast_url = points_data["properties"]["forecast"]
|
||||
forecast_data = await make_nws_request(forecast_url)
|
||||
|
||||
if not forecast_data:
|
||||
return "Unable to fetch detailed forecast."
|
||||
|
||||
# Format the periods into a readable forecast
|
||||
periods = forecast_data["properties"]["periods"]
|
||||
forecasts = []
|
||||
for period in periods[:5]: # Only show next 5 periods
|
||||
forecast = f"""
|
||||
{period['name']}:
|
||||
Temperature: {period['temperature']}°{period['temperatureUnit']}
|
||||
Wind: {period['windSpeed']} {period['windDirection']}
|
||||
Forecast: {period['detailedForecast']}
|
||||
"""
|
||||
forecasts.append(forecast)
|
||||
|
||||
return "\n---\n".join(forecasts)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Initialize and run the server
|
||||
mcp.run(transport="stdio")
|
||||
Reference in New Issue
Block a user