Skywise
Skywise is a weather-prediction MCP server that provides current conditions, forecasts, historical data, severe weather alerts, air quality, and travel recommendations. It powers an Agent Bricks agent to answer natural-language weather questions and make simple predictions like 'Will it rain in Chicago tomorrow?'
README
Skywise — Weather-Prediction MCP Server + Agent Bricks Agent
A weather-forecast MCP server (FastMCP) plus a Databricks Agent Bricks agent that uses it to answer natural-language weather questions and make simple predictions ("Will it rain in Chicago tomorrow?", "Should I bring a jacket to Austin this weekend?"). An optional dashboard app shows the history of predictions the agent has made.
Built on the Day-3 reference pattern (an MCP-server app + a separate dashboard
app, thin @mcp.tool functions over a separate broker/adapter module), and
reusing the proven NWS + Lakebase code from an earlier weather app. As in the
reference repos, the dashboard Flask app sits at the repo root and the MCP
server is the mcp_server/ subfolder.
Weather API + auth used
| Provider | Used for | Auth | Notes |
|---|---|---|---|
| Open-Meteo | geocoding, current conditions, multi-day forecast | none (keyless) | worldwide; ~10k calls/day non-commercial |
| NWS (weather.gov) | active severe-weather alerts | none (keyless) | US-only; requires a descriptive User-Agent header |
There is no weather API key anywhere in this project — both providers are
free and keyless. The only secret is the Lakebase connection URL, and it is
only used to log predictions for the dashboard (see Secrets). No
secrets are committed to git; .env is gitignored.
Architecture
Natural-language question
│
▼
┌─────────────────────┐ registers as external MCP tool
│ Agent Bricks agent │◄───────────────────────────────────────┐
│ (system_prompt.md) │ │
└─────────┬───────────┘ │
│ calls MCP tools (streamable-HTTP) │
▼ │
┌──────────────────────────── Databricks App #1 ───────────────┐│
│ mcp_server/weather_mcp_server.py (FastMCP, thin @mcp.tool) ││
│ ├─ weather_broker.py → Open-Meteo (current/historical/ ││
│ │ forecast/air-quality + geocode) ││
│ ├─ recommendation.py → threshold logic (the judgment) ││
│ ├─ nws_broker.py → NWS active alerts (US) ││
│ └─ query_log.py ──────────────┐ (best-effort logging) ││
└────────────────────────────────────┼─────────────────────────┘│
▼ │
┌──────────────┐ │
│ Lakebase │ reads history ──┘
│ weather_queries│
└──────▲────────┘
│
┌───────────────────── Databricks App #2 (repo root) ──────────┐
│ app.py (Flask, read-only) │
│ recent predictions history + live conditions lookup │
└───────────────────────────────────────────────────────────────┘
Design rule (from the assignment): tools are thin, brokers are fat. No raw
requests calls live inside any @mcp.tool function — all HTTP + parsing is in
weather_broker.py / nws_broker.py.
MCP tools
| Tool | Kind | What it does |
|---|---|---|
get_current_weather(location, units) |
required | Current temp, feels-like, humidity, wind, conditions. |
get_historical_weather(location, date, units) |
required | Actual observed weather for a past date (Open-Meteo ERA5 archive) — not a forecast. Rejects future/malformed dates. |
get_travel_recommendation(location, date, units) |
required | Derived judgment — applies documented thresholds (umbrella if rain ≥ 40%, jacket/coat by low temp, sunscreen if UV ≥ 6, heat/wind cautions, air-quality caution) and returns the reasoning. Logs to Lakebase. |
get_forecast(location, days, units) |
additional | N-day (1–16) daily highs/lows, precip chance, UV, wind. Also satisfies the assignment's named forecast capability. |
get_severe_weather_alerts(location) |
stretch | Active NWS watches/warnings for a US location. |
get_air_quality(location) |
stretch | Current US AQI + PM2.5/PM10/ozone (Open-Meteo Air Quality API). |
compare_cities_weather(locations, units) |
stretch | Current conditions for several cities side by side. |
get_current_user() |
utility | End-user identity from the App's X-Forwarded-User header (mirrors the tutorial MCP server). |
The "prediction" tool does more than echo the API: the threshold logic lives
in mcp_server/recommendation.py and each rule
that fires is returned in a reasons list so the agent can explain why. It
also folds in air quality (limit strenuous outdoor activity when US AQI > 100)
as a best-effort signal that never breaks the core forecast advice.
A curated seed cache of common US cities (_SEED_COORDS in
weather_broker.py) resolves the demo cities
(Chicago, Austin, Oklahoma City, …) with zero network calls, and gracefully
falls through to live geocoding for anything else.
Error handling: brokers raise a WeatherError with a short message on a bad
location or provider outage; every tool catches it and returns
{"status": "error", "message": ...} — a clean sentence the agent can react to,
never a stack trace.
Repository layout
The dashboard app lives at the repo root (its app.py/app.yaml are the
root's), so it deploys by pointing a Databricks App at the repo root — the same
shape as the reference repos (root Flask app + an mcp_server/ subfolder). The
MCP server is the self-contained mcp_server/ subfolder.
skywise-mcp-agent/ ← ROOT = the read-only dashboard app (Databricks App #2)
├── app.py ← Flask: predictions history + live conditions
├── app.yaml ← dashboard App config (command + env)
├── requirements.txt ← dashboard deps (also what setup_secrets.py needs)
├── weather_display.py ← FE-support helpers (geocode/daily/AQI), ported
├── query_log.py ← (copy) reads recent predictions
├── lakebase.py ← (copy) Lakebase connection helper
├── templates/index.html ← "Nordic Cool" UI (ported from weather-intel app)
├── setup_secrets.py ← one-time: store LAKEBASE_URL secret
├── databricks.yml ← optional Asset Bundle for CLI deploys
├── .env.example ← local dev template (no real secrets)
├── .gitignore
├── README.md ← you are here
├── agent/
│ ├── system_prompt.md ← the agent's system prompt (a deliverable)
│ └── demo_transcripts.md ← fill in with 3+ demo Q&As after deploy
└── mcp_server/ ← Databricks App #1 (the MCP server) — self-contained
├── weather_mcp_server.py ← FastMCP entrypoint, thin @mcp.tool funcs
├── weather_broker.py ← Open-Meteo: geocode / current / historical / forecast
├── recommendation.py ← derived judgment (thresholds + reasoning)
├── nws_broker.py ← NWS active alerts (reused, User-Agent-safe)
├── query_log.py ← best-effort prediction logging to Lakebase
├── lakebase.py ← Lakebase connection helper (reused)
├── schema_weather_queries.sql ← DDL + app-role grants for the prediction-log table
├── test_weather.py ← runnable live smoke test (✓/✗ per capability)
├── app.yaml ← App config (env only; no key secret)
└── requirements.txt
The root (dashboard) app and the
mcp_server/app each carry their own copies of the shared Lakebase modules (lakebase.py,query_log.py) so each deploys as a self-contained Databricks App — the same convention as the reference repos, whose root app is likewise a Flask app beside anmcp_server/folder.
Dashboard UI (ported "Nordic Cool" design)
The dashboard's templates/index.html reuses the
polished front-end from the earlier weather-intelligence app — its
self-hosted fonts (Fraunces + Hanken Grotesk), scandi palette, geocode
typeahead, and live-conditions panel (yesterday/today/tomorrow cards + an
AQI badge and EPA scale). The RAG "sync + semantic search" half of that original
page (irrelevant to this assignment) is replaced with the Recent agent
predictions history read from Lakebase. Two small endpoints in
app.py — /weather/geocode and /weather/conditions —
feed the reused JS, backed by weather_display.py
(a trimmed port of that app's weather_client.py display functions).
Two Skywise touches layered on top of the ported design (palette-native, no new colors): a °F/°C segmented toggle that re-fetches the current city's conditions, and a data-derived insight line ("📈 4°F warmer than yesterday · rain rising to 47% tomorrow") computed only from the three days already on screen — so it never states anything the panel doesn't show.
Setup
1. (Optional) Lakebase for prediction logging
Only needed if you want the dashboard / prediction history. Skip if you just want the required MCP tools + agent.
- Create a Lakebase instance with a native-password role and copy its
connection URL (
postgresql://role:password@host:5432/databricks_postgres?sslmode=require). - Create the table and grant the app role access:
(or paste the file into a Databricks SQL editor connected to the instance). This createspsql "$LAKEBASE_URL" -f mcp_server/schema_weather_queries.sqlweather.weather_queriesin the sameweatherschema the weather-intel app uses, and grants schema/table/sequence privileges to the Lakebase rolestudent— the same role the day-1 tutorial and weather-intel app grant to. Review placeholder: if your Lakebase app role isn'tstudent, edit theGRANT ... TO studentlines at the bottom of that file first (otherwise the app hits "permission denied" on the first insert).
2. Secrets
The only secret is the Lakebase URL. Store it once by running the root script
(install its one dependency first — the root requirements.txt covers it):
pip install -r requirements.txt
python setup_secrets.py
This uses scope database-day2 and key lakebase-url — the same Lakebase
secret you already created for the weather-intel app — so no new secret is
needed (matching the LAKEBASE_SECRET_SCOPE / LAKEBASE_SECRET_KEY in both
app.yaml files). If that secret already exists, you can skip this step.
3. NWS User-Agent
NWS returns HTTP 403 without a descriptive User-Agent. Set your own contact
in mcp_server/app.yaml (WEATHER_USER_AGENT, e.g.
"(skywise-mcp-agent, you@example.com)"). It is a courtesy contact, not a
secret.
4. Run locally (optional)
cd mcp_server
pip install -r requirements.txt
python weather_mcp_server.py # serves MCP over HTTP on :8000
# dashboard app — run from the repo root (this is where app.py lives)
pip install -r requirements.txt
python app.py # serves the dashboard on :8001
5. Deploy the two Databricks Apps — mind the source path
The two apps deploy from different directories. Pointing an App at the wrong
directory is the #1 deploy failure: the repo root has an app.yaml (the
dashboard), but the mcp_server/ code has its own app.yaml, so each App must
point at its own directory.
| App | Point the Databricks App source at | Notes |
|---|---|---|
| Dashboard (Databricks App #2) | the repo root | root app.py + app.yaml |
| MCP server (Databricks App #1) | mcp_server/ subfolder |
name it mcp-skywise-weather |
Via the workspace UI (no CLI required):
- Create a Git folder pointing at this repo.
- Compute → Apps → Create app for the MCP server: set its source code
path to the
mcp_server/subfolder (not the repo root).- ⚠️ Name it with an
mcp-prefix — e.g.mcp-skywise-weather. Databricks only recognizes an App as an MCP server (for AI Playground / Agent Bricks discovery) when its name starts withmcp-(parallel to the tutorial'smcp-trading-server).
- ⚠️ Name it with an
- Create a second app for the dashboard: set its source code path to
the repo root (any name — the dashboard is not an MCP server). Because the
root has its own
app.yaml, the container runs the Flask app, notsetup_secrets.py. Open each app's URL and confirm it's serving.
Register the MCP server as an external MCP + build the agent
Written from Databricks' current MCP docs (host a custom MCP, MCPs and agent tools). UI labels move around — confirm against your workspace.
What to swap vs. the tutorial's mcp-trading-server: a new app name
(mcp-skywise-weather), the skywise mcp_server/ source folder, and no API-key
secret (Open-Meteo + NWS are keyless). The Lakebase secret is the same
database-day2/lakebase-url you already created — nothing new to store.
- Get the MCP endpoint. After the
mcp-skywise-weatherapp deploys, its MCP URL ishttps://<app-url>/mcp(FastMCP's streamable-HTTP endpoint — the server already runstransport="http", so no code change is needed). - Grant access. On the app → Permissions, grant yourself (and the agent's principal) access. Databricks governs MCP access through Apps permissions, not per-tool grants. (Governance also appears under AI Gateway → MCPs.)
- Attach it to an agent in AI Playground. Open AI Playground, pick an
LLM endpoint, open Tools, and add the
mcp-skywise-weatherMCP server. Its@mcp.toolfunctions appear for selection:get_current_weather,get_historical_weather,get_travel_recommendation,get_forecast,get_severe_weather_alerts,get_air_quality,compare_cities_weather,get_current_user. - Set the instructions. Paste
agent/system_prompt.mdas the agent's system prompt. - Test, then export. Run the prompts in
agent/demo_transcripts.md, capture the tool calls + answers for your submission, then Export / Create agent from the Playground.
Demonstrating the agent (required)
Fill in agent/demo_transcripts.md with at least 3
natural-language questions, the tool calls the agent made, and its final answers
(screenshots encouraged). Suggested prompts:
- "Will it rain in Chicago tomorrow — should I take an umbrella?"
- "What's the weather looking like in Austin this weekend?"
- "Are there any severe weather warnings for Oklahoma City right now?"
Assignment Requirements Checklist
Required MCP Tools (minimum 3) ✅
- ✅
get_current_weather(location, units)- current conditions - ✅
get_forecast(location, days, units)- multi-day forecast - ✅
get_travel_recommendation(location, date, units)- derived judgment with documented thresholds (not a passthrough)
Stretch Tools (5 bonus tools) ✅
- ✅
get_historical_weather- past weather lookup - ✅
get_severe_weather_alerts- NWS alerts - ✅
get_air_quality- AQI data - ✅
compare_cities_weather- multi-city comparison - ✅
get_current_user- identity utility
Technical Requirements ✅
- ✅ FastMCP with
@mcp.tooldecorators - ✅ Separate adapter modules -
weather_broker.py,recommendation.py,nws_broker.py(no HTTP in tool functions) - ✅ Secrets properly stored - Lakebase URL in Databricks secrets, no API keys needed (Open-Meteo & NWS are keyless)
- ✅ requirements.txt & app.yaml - present for both MCP server and dashboard apps
- ✅ Deployed as Databricks App -
mcp-skywise-weatherrunning withmcp-prefix - ✅ Clear docstrings - Args/Returns documented for all tools
- ✅ Error handling - returns clean error messages, not stack traces
- ✅ Prediction tool logic - applies explicit thresholds in
recommendation.pywith reasoning
Agent Requirements ✅
- ✅ Agent Bricks agent configured - registered in AI Playground with MCP server
- ✅ System prompt -
agent/system_prompt.mdwith tool selection rules and anti-hallucination guardrails - ✅ Demo transcripts -
agent/demo_transcripts.mdwith 4+ examples and screenshots
Documentation ✅
- ✅ README.md - architecture diagram, tool list, setup steps, API documentation
- ✅ Weather API documented - Open-Meteo (keyless) + NWS (keyless with User-Agent)
- ✅ No secrets in git -
.gitignoreconfigured,.env.exampleprovided
Bonus Features ✨
- ✅ Dashboard app - real-time prediction logging to Lakebase, "Nordic Cool" UI
- ✅ 8 tools total - 3 required + 5 stretch
- ✅ Comprehensive error handling - graceful degradation for API outages
- ✅ Air quality integration - enriches recommendations with AQI data
Notes / reuse
- NWS handling (
nws_broker.py) reuses the mandatory-User-Agent+ 4-decimal-coordinate-rounding pattern proven in an earlier weather app, so the common NWS 403/redirect gotchas are handled up front. - Air quality (
get_air_quality) reuses the Open-Meteo AQI adapter + EPA-category logic from that same weather app. - Seed cache (
_SEED_COORDS) reuses its curated city→coordinate map. - Dashboard UI reuses that app's "Nordic Cool" front-end (fonts, palette,
geocode typeahead, live-conditions + AQI panel); its display helpers are
ported into
weather_display.py(at the repo root). - Lakebase helper (
lakebase.py) is the standard single-LAKEBASE_URLpattern from the reference apps — no token refresh needed. - Test script (
test_weather.py) mirrors the reference'stest_watchlist.pystyle — a runnable ✓/✗ smoke test over the live free APIs, no secrets needed. - Prediction logging is best-effort: if Lakebase is down or unconfigured, the weather answer is still returned and only the dashboard history is affected.
Recommended Servers
playwright-mcp
A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.
Magic Component Platform (MCP)
An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.
Audiense Insights MCP Server
Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.
VeyraX MCP
Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.
graphlit-mcp-server
The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.
Kagi MCP Server
An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.
E2B
Using MCP to run code via e2b.
Neon Database
MCP server for interacting with Neon Management API and databases
Exa Search
A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.
Qdrant Server
This repository is an example of how to create a MCP server for Qdrant, a vector search engine.