Skip to main content
Glama
AG0860-Mohammad-Anas

Weather Travel Advisory MCP Server

README.md
# P004 Case Study 2 — Weather and Travel Advisory MCP Server

An MCP (Model Context Protocol) server that exposes weather-related **tools**,
**resources**, and **prompts**, using the public [wttr.in](https://wttr.in)
weather API to generate a structured, deterministic travel advisory report.

This implements the PRD: *P004 Case Study 2: Weather and Travel Advisory MCP
Server Using wttr.in API*. Section references below (e.g. "PRD 10.3") point
back to that document.

---

## 1. Project Overview

The server accepts a destination city, fetches live weather data, normalizes
it into a clean schema, deterministically calculates a travel weather risk
(LOW / MEDIUM / HIGH), and produces a JSON travel advisory report — all
through well-defined MCP primitives rather than a generic chatbot loop.

## 2. Business Use Case

A traveler wants a quick, structured answer to questions like:
- "Should I travel to Jaipur this weekend?"
- "What should I pack for Pune based on the weather?"
- "Is Mumbai risky for outdoor travel?"

**Out of scope** (PRD Section 5): flight/hotel/train booking, medical advice,
disaster alerts, visa/immigration rules, paid travel services. This is a
weather-based advisory only, not a guaranteed travel decision.

## 3. Technology Stack

| Layer            | Choice                                   |
|-------------------|-------------------------------------------|
| MCP framework      | `mcp` (FastMCP)                            |
| HTTP client        | `requests`                                 |
| Data validation    | `pydantic`                                 |
| Testing            | `pytest`, `pytest-mock`                    |
| Config             | `python-dotenv`                            |
| Language           | Python 3.10+                               |

## 4. wttr.in API Usage Details (PRD Section 6–7)

- Endpoint: `GET https://wttr.in/{city_name}?format=j1`
- No API key required.
- Fallback domain: `https://wttr.is/{city_name}?format=j1`
- City names with spaces: replace spaces with `+` (e.g. `New+Delhi`).
- Timeout: 10 seconds.
- Numeric fields in the raw response arrive as **strings** and are converted
  to `int`/`float` during normalization (PRD 7.6).

All networking lives in `src/api_client.py`. Per PRD Section 9, it is the
**only** module that talks to the wttr.in API.

## 5. MCP Tools (PRD Section 10)

| Tool | Purpose |
|------|---------|
| `validate_city_input_tool` | Validates/cleans the destination city name. No API call. |
| `get_weather_forecast_tool` | Calls wttr.in (only tool allowed to hit the network). |
| `normalize_weather_data_tool` | Converts raw wttr.in JSON into the normalized schema. |
| `calculate_weather_risk_tool` | Deterministic LOW/MEDIUM/HIGH risk calculation. No LLM. |
| `save_travel_advisory_tool` | Validates + saves the final report as JSON. |

## 6. MCP Resources (PRD Section 11)

| Resource URI | Content |
|---|---|
| `resource://travel/checklist` | Static travel-readiness checklist |
| `resource://travel/advisory-rules` | Static LOW/MEDIUM/HIGH interpretation rules |
| `resource://weather/normalized-forecast-schema` | JSON description of the normalized schema |

Resources are static reference content only — they never call APIs.

## 7. MCP Prompts (PRD Section 12)

| Prompt | Purpose |
|---|---|
| `travel_readiness_prompt` | Template for a concise travel-readiness advisory |
| `weather_risk_summary_prompt` | Template explaining why the risk level was assigned |
| `packing_recommendation_prompt` | Template for practical packing suggestions |

Prompts only render natural-language template text — they never call APIs
and never perform risk math (that's `calculate_weather_risk_tool`'s job).

## 8. Setup Instructions

```bash
git clone <this-repo>
cd p004_mcp_weather_travel_advisory
python3 -m venv .venv && source .venv/bin/activate   # optional but recommended
pip install -r requirements.txt
cp .env.example .env   # no API key needed, defaults already work
```

## 9. How to Run the MCP Server

```bash
python src/server.py
```

This starts the MCP server on stdio transport, ready to be connected from any
MCP-compatible client (e.g. Claude Desktop, an MCP Inspector, or a custom
client configured to launch this command).

## 10. How to Run Tests

```bash
pytest tests/
```

This runs all unit tests (37 tests) using **mocked** wttr.in responses — no
network access or API key required. `pytest.ini` excludes integration tests
by default (`-m "not integration"`).

## 11. How to Run Integration Tests

```bash
pytest -m integration
```

This runs `test_real_wttr_api_for_jaipur`, which makes a real HTTP call to
wttr.in. **Status: written but unverified** in this delivery — the
development sandbox used to build this project only allowed network egress
to package registries (PyPI/npm/GitHub), not to wttr.in/wttr.is, so this
specific test could not be executed live here. It is ready to run in any
environment with normal internet access.

## 12. How to Generate Sample Reports

```bash
python src/server.py --sample-city Jaipur
python src/server.py --sample-city Pune
```

Each command runs the full PRD Section 13 pipeline once and prints the
resulting JSON report, saving it to `outputs/travel_advisory_report.json`.
Pre-generated examples (built from mocked weather data, since this sandbox
cannot reach wttr.in) are checked in at:
- `sample_outputs/sample_jaipur_advisory.json` (MEDIUM risk scenario)
- `sample_outputs/sample_pune_advisory.json` (HIGH risk scenario)

## 13. Final Report Schema (PRD Section 14)

```json
{
  "destination": "",
  "region": "",
  "country": "",
  "forecast_days": 3,
  "current_weather": {
    "temperature_c": 0,
    "humidity": 0,
    "precipitation_mm": 0,
    "wind_speed_kmph": 0,
    "weather_description": ""
  },
  "daily_forecast": [
    {
      "date": "",
      "max_temp_c": 0,
      "min_temp_c": 0,
      "avg_temp_c": 0,
      "total_precipitation_mm": 0,
      "max_wind_kmph": 0,
      "max_chance_of_rain": 0,
      "weather_description": ""
    }
  ],
  "weather_risk": "LOW | MEDIUM | HIGH",
  "risk_factors": [],
  "recommended_actions": [],
  "packing_suggestions": [],
  "travel_readiness_advisory": "",
  "weather_risk_explanation": "",
  "resources_used": [],
  "tools_used": [],
  "prompts_used": []
}
```

The report is validated against this schema (via `src/schemas.py`, a
pydantic model) **before** it is written to disk.

## 14. Known Limitations

1. **No live LLM call for prompt outputs.** `travel_readiness_advisory`,
   `weather_risk_explanation`, and `packing_suggestions` are conceptually
   meant to be produced by feeding the rendered prompt templates (PRD
   Section 12) to an LLM. This environment has no LLM API key / network
   access to an LLM provider, so `src/server.py::_render_llm_field` contains
   the intended (commented-out) Anthropic API call, clearly marked
   **"written but unverified — no API key/network access to test live"**,
   and falls back to a small deterministic text generator so the pipeline
   still produces a complete, schema-valid report end to end.
2. **No live wttr.in access from the build sandbox.** The environment used
   to build and test this project could only reach package registries
   (PyPI, npm, GitHub), not wttr.in/wttr.is. All unit tests therefore use
   realistic mocked fixtures (`tests/conftest.py`), and the one real-network
   test is marked `@pytest.mark.integration` and flagged as unverified here.
3. **Risk model is a fixed rule set.** Thresholds (e.g. `max_temp_c >= 40`)
   come directly from PRD Section 10.4 and are not configurable via
   environment variables in this version.
4. **3-day forecast cap.** Per PRD 10.3 rule 5, only the first 3 days
   returned by wttr.in are normalized, even if more are available.

## 15. Future Improvements

- Wire up the real Anthropic Messages API call in `_render_llm_field` once
  an API key is available, and add a live/offline toggle via an env var.
- Add response caching for repeated queries to the same city within a short
  time window, to reduce load on wttr.in.
- Make risk thresholds configurable via `.env` for easier tuning.
- Add more cities to `sample_outputs/` (New Delhi, Mumbai, Bengaluru) once
  live API access is available.
- Add an MCP resource that returns the list of supported/validated example
  cities for quick client-side testing.

---

## Project Structure

```
p004_mcp_weather_travel_advisory/
├── README.md
├── requirements.txt
├── .env.example
├── pytest.ini
├── src/
│   ├── server.py          # MCP server wiring + orchestration flow (PRD 13)
│   ├── tools.py            # 5 mandatory MCP tools (PRD 10)
│   ├── resources.py        # 3 mandatory MCP resources (PRD 11)
│   ├── prompts.py          # 3 mandatory MCP prompts (PRD 12)
│   ├── api_client.py       # wttr.in HTTP client (PRD 6, 7, 18)
│   ├── schemas.py          # pydantic schemas (PRD 10.3, 14)
│   └── report_writer.py    # JSON persistence + schema validation (PRD 10.5)
├── tests/
│   ├── conftest.py             # mocked wttr.in fixtures
│   ├── test_api_client.py
│   ├── test_tools.py
│   ├── test_resources.py
│   ├── test_prompts.py
│   └── test_report_schema.py
├── outputs/
│   └── travel_advisory_report.json   # generated at runtime
└── sample_outputs/
    ├── sample_jaipur_advisory.json
    └── sample_pune_advisory.json
```