# Directory Structure
```
└── weather
    ├── .gitignore
    ├── .python-version
    ├── claude-config.json
    ├── pyproject.toml
    ├── README.md
    ├── run_claude.sh
    ├── uv.lock
    └── weather.py
```
# Files
--------------------------------------------------------------------------------
/weather/.python-version:
--------------------------------------------------------------------------------
```
1 | 3.13
2 | 
```
--------------------------------------------------------------------------------
/weather/.gitignore:
--------------------------------------------------------------------------------
```
 1 | # Python-generated files
 2 | __pycache__/
 3 | *.py[oc]
 4 | build/
 5 | dist/
 6 | wheels/
 7 | *.egg-info
 8 | 
 9 | # Virtual environments
10 | .venv
11 | 
```
--------------------------------------------------------------------------------
/weather/claude-config.json:
--------------------------------------------------------------------------------
```json
1 | {
2 |   "name": "weather",
3 |   "transports": ["http"],
4 |   "runtime": {
5 |     "transport": "http",
6 |     "persistent": true,
7 |     "port": 8000
8 |   }
9 | } 
```
--------------------------------------------------------------------------------
/weather/pyproject.toml:
--------------------------------------------------------------------------------
```toml
 1 | [project]
 2 | name = "weather"
 3 | version = "0.1.0"
 4 | description = "Add your description here"
 5 | readme = "README.md"
 6 | requires-python = ">=3.13"
 7 | dependencies = [
 8 |     "httpx>=0.28.1",
 9 |     "mcp[cli]>=1.4.1",
10 | ]
11 | 
```
--------------------------------------------------------------------------------
/weather/weather.py:
--------------------------------------------------------------------------------
```python
 1 | from typing import Any
 2 | import httpx
 3 | from mcp.server.fastmcp import FastMCP
 4 | 
 5 | # Initialize FastMCP server
 6 | mcp = FastMCP("weather")
 7 | 
 8 | # Constants
 9 | NWS_API_BASE = "https://api.weather.gov"
10 | USER_AGENT = "weather-app/1.0"
11 | 
12 | 
13 | async def make_nws_request(url: str) -> dict[str, Any] | None:
14 |     """Make a request to the NWS API with proper error handling."""
15 |     headers = {
16 |         "User-Agent": USER_AGENT,
17 |         "Accept": "application/geo+json"
18 |     }
19 |     async with httpx.AsyncClient() as client:
20 |         try:
21 |             response = await client.get(url, headers=headers, timeout=30.0)
22 |             response.raise_for_status()
23 |             return response.json()
24 |         except Exception:
25 |             return None
26 | 
27 | def format_alert(feature: dict) -> str:
28 |     """Format an alert feature into a readable string."""
29 |     props = feature["properties"]
30 |     return f"""
31 | Event: {props.get('event', 'Unknown')}
32 | Area: {props.get('areaDesc', 'Unknown')}
33 | Severity: {props.get('severity', 'Unknown')}
34 | Description: {props.get('description', 'No description available')}
35 | Instructions: {props.get('instruction', 'No specific instructions provided')}
36 | """
37 | 
38 | 
39 | @mcp.tool()
40 | async def get_alerts(state: str) -> str:
41 |     """Get weather alerts for a US state.
42 | 
43 |     Args:
44 |         state: Two-letter US state code (e.g. CA, NY)
45 |     """
46 |     url = f"{NWS_API_BASE}/alerts/active/area/{state}"
47 |     data = await make_nws_request(url)
48 | 
49 |     if not data or "features" not in data:
50 |         return "Unable to fetch alerts or no alerts found."
51 | 
52 |     if not data["features"]:
53 |         return "No active alerts for this state."
54 | 
55 |     alerts = [format_alert(feature) for feature in data["features"]]
56 |     return "\n---\n".join(alerts)
57 | 
58 | @mcp.tool()
59 | async def get_forecast(latitude: float, longitude: float) -> str:
60 |     """Get weather forecast for a location.
61 | 
62 |     Args:
63 |         latitude: Latitude of the location
64 |         longitude: Longitude of the location
65 |     """
66 |     # First get the forecast grid endpoint
67 |     points_url = f"{NWS_API_BASE}/points/{latitude},{longitude}"
68 |     points_data = await make_nws_request(points_url)
69 | 
70 |     if not points_data:
71 |         return "Unable to fetch forecast data for this location."
72 | 
73 |     # Get the forecast URL from the points response
74 |     forecast_url = points_data["properties"]["forecast"]
75 |     forecast_data = await make_nws_request(forecast_url)
76 | 
77 |     if not forecast_data:
78 |         return "Unable to fetch detailed forecast."
79 | 
80 |     # Format the periods into a readable forecast
81 |     periods = forecast_data["properties"]["periods"]
82 |     forecasts = []
83 |     for period in periods[:5]:  # Only show next 5 periods
84 |         forecast = f"""
85 | {period['name']}:
86 | Temperature: {period['temperature']}°{period['temperatureUnit']}
87 | Wind: {period['windSpeed']} {period['windDirection']}
88 | Forecast: {period['detailedForecast']}
89 | """
90 |         forecasts.append(forecast)
91 | 
92 |     return "\n---\n".join(forecasts)
93 | 
94 | 
95 | if __name__ == "__main__":
96 |     # Initialize and run the server
97 |     mcp.run(transport='stdio')
```