Skip to main content
Glama
modern-data-engineering-lab

Weather-Prediction MCP Server

Weather-Prediction MCP Server + Agent Bricks Agent

A weather MCP server built with FastMCP that exposes weather-forecast tools to a Databricks Agent Bricks agent, so the agent can answer natural-language weather questions and make simple predictions/recommendations (e.g. "Will it rain in Chicago tomorrow?", "Should I bring a jacket to Austin this weekend?").

Built as a homework for Day 3 (Agent Bricks + Alpaca paper-trading MCP server), using that repo's mcp_server/ split as the reference pattern: thin @mcp.tool functions on top of a separate adapter module that owns all the HTTP/parsing.

Live deployment

Two Databricks Apps (mirroring Day 3's server + agent split):

App

Role

URL

mcp-weather-server

MCP server (the 5 tools)

https://mcp-weather-server-7474650707148987.aws.databricksapps.com

agent-weather-app

The agent (chat UI, calls the MCP tools)

https://agent-weather-app-7474650707148987.aws.databricksapps.com

  • MCP endpoint (what the agent connects to): https://mcp-weather-server-7474650707148987.aws.databricksapps.com/mcp

  • Confirmed running from the server logs: Starting MCP server 'weather-prediction' with transport 'http' on http://0.0.0.0:8000/mcp. (Opening /mcp in a browser returns Not Acceptable: Client must accept text/event-stream — that's expected; only an MCP client can speak to it.)

  • agent-weather-app declares mcp-weather-server as an app resource, and its service principal has CAN_USE on the server, so the agent can call all 5 tools.

Related MCP server: Weather Prediction MCP Server

Weather API + auth

Open-Meteo — chosen because it needs no signup, no API key, and no credit card (free for non-commercial use, ~10k calls/day). Two key-less endpoints are used:

  • Geocoding API (geocoding-api.open-meteo.com) — turns a city name into latitude/longitude.

  • Forecast API (api.open-meteo.com) — current conditions + daily forecast.

Because there are no credentials, there is no Databricks secret to manage for this project. (If you swap in a keyed provider like WeatherAPI.com, fetch the key in weather_adapter.py via WorkspaceClient().secrets.get_secret() — the same pattern as Day 3's alpaca_broker.py — and nothing else changes.)

Architecture

Agent Bricks agent  --(MCP tool calls, streamable HTTP)-->  weather_mcp_server.py
        |                                                            |
        | natural-language weather Q&A                               | (thin @mcp.tool funcs)
        v                                                            v
   final answer  <----------------------------------------  weather_adapter.py
                                                                     |
                                                                     | (all HTTP + parsing)
                                                                     v
                                                       Open-Meteo geocoding + forecast APIs
  • weather_mcp_server.py — FastMCP server; each tool is a thin wrapper that delegates to the adapter and returns a clean dict (or {"status": "error", ...} on failure).

  • weather_adapter.py — the adapter/broker module (like alpaca_broker.py): all requests calls and response parsing live here, plus the WMO weather-code → text mapping. No raw requests calls exist inside any @mcp.tool function.

Tools

Minimum three capabilities (current / forecast / prediction), plus two stretch tools:

Tool

Kind

What it does

get_current_weather(location)

current

Temperature, feels-like, humidity, precipitation, wind, conditions for now.

get_forecast(location, days=3)

forecast

Daily high/low, precip chance & amount, max wind, conditions for the next N days (1–16).

predict_umbrella_needed(location, day_offset=0)

prediction

Derived yes/no: umbrella if precip chance ≥ 40% or ≥ 0.1 in expected. Returns the reasoning + thresholds.

get_travel_recommendation(location, day_offset=0)

prediction

Multi-factor packing advice (umbrella / jacket / sunscreen / wind), each with its threshold.

compare_cities_weather(locations, day_offset=0)

stretch

Side-by-side forecast for several cities + picks the "nicest" via a simple score.

location accepts a city name ("Chicago", "Austin, TX", "London") or a "lat,lon" pair ("41.8781,-87.6298"). day_offset is 0 = today, 1 = tomorrow, etc.

Why the prediction tools aren't just passthroughs

predict_umbrella_needed and get_travel_recommendation apply explicit thresholds (configurable via env vars in app.yaml) to the raw forecast and return the decision plus the reason, rather than echoing the API. Defaults:

Threshold

Default

Meaning

UMBRELLA_PRECIP_CHANCE_PCT

40

Umbrella if max precip probability ≥ this

UMBRELLA_PRECIP_AMOUNT

0.1 in

…or total precip ≥ this

JACKET_TEMP_F

55

Jacket if the day's high ≤ this

SUNSCREEN_TEMP_F

75

Sunscreen if sunny and high ≥ this

WIND_ADVISORY_MPH

25

Wind warning if max wind ≥ this

Error handling & edge cases (with examples)

Every tool catches errors and returns a structured {"status": "error", "message": ...} dict — the MCP-appropriate analog of an HTTP 4xx — so a bad input or an API outage never reaches the agent as a stack trace. Concrete cases:

Input

Result

Where

Unknown city — get_current_weather("Xyzzyville")

{"status":"error","message":"Could not find a location matching 'Xyzzyville'. Try a 'City, ST' form or a 'lat,lon' pair."}

adapter raises ValueError (weather_adapter.py:130), tool catches (weather_mcp_server.py:86)

Empty location — get_current_weather("")

ValueError("Location is required …") → clean error dict

weather_adapter.py:110

Out-of-range daysget_forecast("Chicago", 999)

clamped to 1..16

weather_adapter.py:213

Out-of-range day_offsetpredict_umbrella_needed("Chicago", 99)

{"status":"error","message":"day_offset 99 is out of range - only N day(s) … (0 = today)."}

weather_mcp_server.py:60

Too few cities — compare_cities_weather(["Austin"])

{"status":"error","message":"Provide at least two locations to compare."}

weather_mcp_server.py:272

One bad city in a compare

that city goes into an errors[] array; the rest still return

weather_mcp_server.py:302

API / network outage

generic Exception caught + logger.exception(...), returns {"status":"error","message":"Could not fetch …: <e>"}

e.g. weather_mcp_server.py:88

lat,lon input — get_current_weather("41.88,-87.63")

resolved directly, no geocoding call

weather_adapter.py (_LATLON_RE)

Agent behavior on error: the system prompt tells the agent to relay the error and ask the user to clarify rather than guess — see the guardrail screenshot (will it rain? with no location → the agent asks for a location and invents nothing).

Code map (nothing left to inference)

Requirement

Exact location

FastMCP + streamable-HTTP transport

weather_mcp_server.py:324mcp.run(transport="http", host="0.0.0.0", port=port)

Tools via @mcp.tool (thin wrappers)

weather_mcp_server.py lines 69, 93, 117, 186, 252

No raw requests in tools — all HTTP in the adapter

weather_adapter.py: _get():95, geocode():100, get_current():151, get_forecast():201. The server imports weather_adapter and never calls requests.

Docstrings (Args/Returns)

every tool function in weather_mcp_server.py

Prediction = threshold logic, not a passthrough

constants at weather_mcp_server.py:47-51; applied in predict_umbrella_needed:117 and get_travel_recommendation:186; overridable via app.yaml

Clean error handling

try/except in every tool; adapter raises typed ValueError with messages

System prompt + guardrails

agent/system_prompt.md

No secrets

Open-Meteo is key-less; .env git-ignored; no WorkspaceClient().secrets used

This MCP server is stateless — no model or DB loaded per request. The only external calls are two key-less Open-Meteo endpoints, all confined to weather_adapter.py.

Files

  • weather_mcp_server.py — FastMCP server exposing the 5 tools

  • weather_adapter.py — adapter: all Open-Meteo HTTP calls + parsing + WMO code mapping

  • requirements.txt / app.yaml — Databricks App config for the MCP server

  • .env.example — local dev env template (all optional; no key required)

  • agent/system_prompt.md — the Agent Bricks system prompt

  • agent/agent_setup.md — how to register the MCP server + build the agent, and the tool list

Setup

1. Run locally

pip install -r requirements.txt
python weather_mcp_server.py      # serves MCP over streamable HTTP on :8000

Sanity-check the adapter without MCP:

python -c "import json, weather_adapter as w; print(json.dumps(w.get_current('Chicago'), indent=2))"

Or point an MCP Inspector at http://localhost:8000 to list and call the tools.

2. Deploy as a Databricks App

Same flow as Day 3's mcp_server/:

  1. Put this folder in a Databricks Git folder.

  2. Compute > Apps > Create app > Custom, name it e.g. weather-mcp, and point its source at this folder (so it picks up app.yaml).

  3. Deploy, then copy the app URL — you'll register it as an external MCP server next.

No secrets step is needed (Open-Meteo is key-less).

3. Register the MCP server + build the agent

See agent/agent_setup.md for the full steps (mirrors Day 3's README): register the app URL as an external MCP, create an Agent Bricks agent, add the 5 tools, and paste the system prompt from agent/system_prompt.md.

Demonstration (agent Q&A)

Real tool outputs from the live Open-Meteo API (captured 2026-08-09; your numbers will differ):

Q1 — "What's the weather like in Chicago right now?"

Agent calls get_current_weather("Chicago")

{"location": "Chicago, Illinois, United States", "temperature": 83.9, "feels_like": 90.6,
 "humidity_pct": 68, "wind_speed": 11.2, "conditions": "Overcast", "as_of": "2026-08-09T12:15"}

Answer: "Right now in Chicago it's about 84°F (feels like 91°F because of the humidity — 68%), overcast, with an 11 mph wind."

Q2 — "Should I bring an umbrella to Seattle tomorrow?"

Agent calls predict_umbrella_needed("Seattle", day_offset=1)

{"location": "Seattle, Washington, United States", "date": "2026-08-10", "umbrella_needed": false,
 "precip_chance_pct": 2, "conditions": "Overcast",
 "reason": "No umbrella needed. 2026-08-10 in Seattle... only a 2% chance of precipitation... below the 40% / 0.1 in threshold."}

Answer: "No need — tomorrow in Seattle is overcast but only a 2% chance of rain, well below the 40% umbrella threshold."

Q3 — "Should I pack a jacket for Seattle this weekend?"

Agent calls get_travel_recommendation("Seattle", day_offset=1)

{"location": "Seattle, Washington, United States", "date": "2026-08-10",
 "recommendations": ["No special gear needed - mild conditions expected."],
 "forecast": {"temp_high": 74.7, "temp_low": 55.2, "conditions": "Overcast"}}

Answer: "You should be fine without a jacket during the day — highs around 75°F. Evenings dip to the mid-50s, so a light layer wouldn't hurt after sunset."

Q4 (stretch) — "Which has nicer weather this weekend: Austin, Seattle, or Denver?"

Agent calls compare_cities_weather(["Austin","Seattle","Denver"], day_offset=1) → ranks by score and reports the best city with its high/low, conditions, and precip chance.

Notes / guardrails

  • Clean errors, not stack traces. A bad location (get_current_weather("Xyzzy")) returns {"status": "error", "message": "Could not find a location matching 'Xyzzy'. Try a 'City, ST' form or a 'lat,lon' pair."}, and the system prompt tells the agent to ask the user to clarify rather than guess.

  • No hallucinated weather. The system prompt requires the agent to base every weather claim on a tool result and to say so if a tool fails.

  • No secrets committed. Open-Meteo needs none; .env is git-ignored.

Related MCP Connectors

Related MCP Servers