Skip to main content
Glama
shreyaschhabra

Weather MCP Server

README.md
# Weather MCP Server

**Live demo: [instaweather.streamlit.app](https://instaweather.streamlit.app/)**

A production-grade **Model Context Protocol (MCP)** server that exposes 12 real-time weather tools to large language models. Built in Python using the FastMCP framework, integrated with Google Gemini 3.1 Flash Lite, and deployable as a Streamlit web application or a FastAPI REST service.

All weather data is sourced from the [Open-Meteo](https://open-meteo.com/) family of free APIs. No API keys are required for weather data.

---

## Table of Contents

1. [What is MCP](#what-is-mcp)
2. [How MCP and LLM Work Together](#how-mcp-and-llm-work-together)
3. [Project Architecture](#project-architecture)
4. [Features](#features)
5. [Tech Stack](#tech-stack)
6. [Project Structure](#project-structure)
7. [Available Tools](#available-tools)
8. [Prerequisites](#prerequisites)
9. [Installation](#installation)
10. [Configuration](#configuration)
11. [Running the Project](#running-the-project)
12. [Deployment](#deployment)
    - [Streamlit Community Cloud](#streamlit-community-cloud)
    - [Railway](#railway)
    - [Render](#render)
13. [Data Sources](#data-sources)
14. [Environment Variables](#environment-variables)
15. [Roadmap](#roadmap)

---

## What is MCP

The **Model Context Protocol (MCP)** is an open standard introduced by Anthropic in late 2024 that defines how large language models communicate with external tools and data sources. It provides a uniform interface so that any MCP-compatible LLM host (Claude Desktop, VS Code Copilot, custom agents) can discover and invoke tools without being tightly coupled to a specific implementation.

An MCP server is a process that:

- Declares a list of tools, each with a name, description, and typed parameter schema
- Listens for JSON-RPC 2.0 requests over a transport (stdio in this project)
- Executes the requested tool and returns structured results

The key benefit is **separation of concerns**: the LLM decides which tool to call and with what arguments; the MCP server handles the actual execution. This makes tools reusable across any LLM that supports the protocol.

---

## How MCP and LLM Work Together

The following sequence describes what happens when a user sends a message to the assistant:

```
User: "Should I bring an umbrella in London today?"
        |
        v
Client (streamlit_app.py / client_gemini.py)
  -- Sends the user message to Gemini API
  -- Attaches the full list of 12 tool definitions (name, description, parameter schema)
        |
        v
Gemini 3.1 Flash Lite
  -- Reads the message and the tool list
  -- Determines that "should_i_bring_umbrella" with city="London" is the correct tool
  -- Returns a tool_use response (not a text answer)
        |
        v
Client
  -- Receives the tool_use response
  -- Extracts the tool name and arguments
  -- Calls the MCP server via stdio: call_tool("should_i_bring_umbrella", {"city": "London"})
        |
        v
MCP Server (main.py)
  -- Runs the tool function
  -- Geocodes London via Open-Meteo geocoding API
  -- Fetches hourly precipitation probability from Open-Meteo forecast API
  -- Calculates max rain probability and total expected rainfall
  -- Returns: "Maybe bring one just in case. (max rain chance: 45%, 3h with >50% chance)"
        |
        v
Client
  -- Sends the tool result back to Gemini
        |
        v
Gemini 3.1 Flash Lite
  -- Reads the tool result
  -- Generates a natural language response based on the data
        |
        v
User sees: "There is a 45% chance of rain in London today. You may want
            to carry an umbrella, particularly in the afternoon."
```

The LLM is never directly fetching weather data. It only decides which tool to call and synthesises the final response. The MCP server is the data layer.

---

## Project Architecture

```
+---------------------+       stdio (JSON-RPC 2.0)      +-------------------+
|                     | <------------------------------> |                   |
|  Client Layer       |                                  |  MCP Server       |
|                     |                                  |  (main.py)        |
|  streamlit_app.py   |   1. list_tools()                |                   |
|  client_gemini.py   |   2. call_tool(name, args)       |  12 tools         |
|  app.py (FastAPI)   |   3. receive result              |  FastMCP          |
|  client-gemini.ts   |                                  |  httpx async      |
|                     |                                  |                   |
+---------------------+                                  +--------+----------+
          |                                                       |
          | Anthropic / Google API calls                         | HTTP requests
          v                                                       v
+---------------------+                                  +-------------------+
|  Gemini 3.1 Flash   |                                  |  Open-Meteo APIs  |
|  Lite               |                                  |                   |
|  (Tool-use loop)    |                                  |  Forecast         |
|                     |                                  |  Archive          |
+---------------------+                                  |  Air Quality      |
                                                         |  Marine           |
                                                         |  Geocoding        |
                                                         +-------------------+
```

---

## Features

- 12 real-time weather tools accessible to Gemini via MCP
- Agentic tool-use loop: Gemini calls one or more tools per query, processes the results, and generates a natural language response
- Streamlit web UI with sidebar tool list, example prompts, and live tool call display
- FastAPI REST server with server-sent events (SSE) streaming for the same functionality
- Python CLI client and TypeScript CLI client
- Dark, formal UI theme with no decorative icons
- All weather data from Open-Meteo — no paid API keys required
- MCP server compatible with Claude Desktop, VS Code Copilot, and any other MCP host

---

## Tech Stack

| Layer | Technology |
|---|---|
| MCP Server | Python, [FastMCP](https://github.com/jlowin/fastmcp) (`mcp` package v1.x) |
| HTTP Requests | `httpx` (async) |
| LLM | Google Gemini 3.1 Flash Lite via `google-generativeai` |
| Web UI | Streamlit |
| Weather Data | Open-Meteo (free, no key required) |

---

## Project Structure

```
weather-mcp-server/
|
|-- main.py              # MCP server — all 12 tool definitions
|-- streamlit_app.py     # Streamlit web UI + Gemini agentic loop
|-- requirements.txt     # Python dependencies
|-- .gitignore
|
`-- .streamlit/
    `-- config.toml      # Streamlit dark theme configuration
```

---

## Available Tools

The MCP server exposes the following 12 tools. All tools accept a `city` parameter (the city name as a plain string). The server geocodes the city automatically using the Open-Meteo Geocoding API.

---

### 1. get_weather

Returns the current weather conditions for a city.

**Parameters**

| Parameter | Type | Required | Description |
|---|---|---|---|
| city | string | Yes | Name of the city |

**Returns**

Current temperature (actual and feels-like), humidity, wind speed, precipitation, cloud cover, and a WMO weather condition description.

**Example prompt:** "What is the current weather in Tokyo?"

---

### 2. get_forecast

Returns a daily weather forecast for up to 7 days.

**Parameters**

| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
| city | string | Yes | — | Name of the city |
| days | integer | No | 7 | Number of forecast days (1–7) |

**Returns**

Per-day summary including temperature range, precipitation total, max wind speed, and weather condition.

**Example prompt:** "Give me a 5-day forecast for Paris."

---

### 3. get_hourly_forecast

Returns an hour-by-hour weather breakdown for the current day.

**Parameters**

| Parameter | Type | Required | Description |
|---|---|---|---|
| city | string | Yes | Name of the city |

**Returns**

24 rows of hourly data: time, condition, temperature, precipitation probability, and wind speed.

**Example prompt:** "Show me hourly weather for Singapore today."

---

### 4. compare_cities_weather

Fetches current conditions for multiple cities in parallel and presents them side by side.

**Parameters**

| Parameter | Type | Required | Description |
|---|---|---|---|
| cities | array of strings | Yes | 2 to 5 city names |

**Returns**

Per-city summary with temperature, feels-like temperature, humidity, wind speed, and weather condition.

**Example prompt:** "Compare the weather in New York, London, Dubai, and Sydney."

---

### 5. should_i_bring_umbrella

Analyses today's hourly precipitation probabilities and advises whether an umbrella is needed.

**Parameters**

| Parameter | Type | Required | Description |
|---|---|---|---|
| city | string | Yes | Name of the city |

**Returns**

A clear recommendation (yes / maybe / no) with the maximum rain probability, expected total rainfall, and the number of hours with greater than 50% rain chance.

**Thresholds**

- Max probability >= 70% or total rain > 5mm: Definite umbrella
- Max probability >= 30%: Possibly needed
- Below 30%: No umbrella required

**Example prompt:** "Should I bring an umbrella in Mumbai today?"

---

### 6. get_air_quality

Returns current air quality measurements including the European AQI and key pollutant concentrations.

**Parameters**

| Parameter | Type | Required | Description |
|---|---|---|---|
| city | string | Yes | Name of the city |

**Returns**

European AQI with label (Good / Fair / Moderate / Poor / Very Poor / Extremely Poor), PM2.5, PM10, NO2, O3, and CO values in µg/m³.

**Example prompt:** "What is the air quality in Beijing?"

---

### 7. get_weather_alerts

Scans the 7-day forecast for potentially hazardous conditions and returns structured alerts.

**Parameters**

| Parameter | Type | Required | Description |
|---|---|---|---|
| city | string | Yes | Name of the city |

**Returns**

A list of alerts grouped by severity (WARNING / WATCH / ADVISORY), each with a date, category, and detail message. Returns a clear all-clear message if no hazardous conditions are detected.

**Alert categories**

| Category | Trigger condition |
|---|---|
| Thunderstorm | WMO codes 95, 96, 99 |
| Heavy Rain / Storms | WMO codes 65, 67, 82 |
| Heavy Snow | WMO codes 75, 77, 85, 86 |
| High Wind | Wind speed >= 75 km/h |
| Wind | Wind speed >= 50 km/h |
| Extreme Heat | Max temperature >= 40°C |
| Heat | Max temperature >= 35°C |
| Extreme Cold | Min temperature <= -15°C |
| Cold | Min temperature <= -5°C |
| Flooding Risk | Daily precipitation >= 30mm |

**Example prompt:** "Are there any weather warnings for Florida this week?"

---

### 8. get_uv_index

Returns the maximum UV index forecast and corresponding sun protection advice for up to 3 days.

**Parameters**

| Parameter | Type | Required | Description |
|---|---|---|---|
| city | string | Yes | Name of the city |

**Returns**

Daily UV index maximum with a protection recommendation.

**UV scale**

| UV Index | Level | Advice |
|---|---|---|
| 0–2 | Low | No protection needed |
| 3–5 | Moderate | SPF 30+, seek shade at noon |
| 6–7 | High | SPF 50+, hat and sunglasses required |
| 8–10 | Very High | Minimise midday exposure |
| 11+ | Extreme | Avoid going outside 10am–4pm |

**Example prompt:** "What is the UV index in Sydney for the next 3 days?"

---

### 9. get_sunrise_sunset

Returns sunrise time, sunset time, and total daylight duration for the next 7 days, adjusted to the city's local timezone.

**Parameters**

| Parameter | Type | Required | Description |
|---|---|---|---|
| city | string | Yes | Name of the city |

**Returns**

7-day table with sunrise time (HH:MM), sunset time (HH:MM), and daylight duration in hours.

**Example prompt:** "What time does the sun rise in Oslo this week?"

---

### 10. get_historical_weather

Queries the Open-Meteo Archive API to retrieve weather data for a specific past date. Data is available from 1940 onwards for most locations.

**Parameters**

| Parameter | Type | Required | Description |
|---|---|---|---|
| city | string | Yes | Name of the city |
| date | string | Yes | Date in YYYY-MM-DD format |

**Returns**

Daily summary for the given date: temperature high and low, precipitation total, maximum wind speed, and weather condition.

**Example prompt:** "What was the weather in London on 2024-07-04?"

---

### 11. get_pollen_forecast

Returns today's peak pollen concentrations by type. Coverage is best in Europe and parts of North America.

**Parameters**

| Parameter | Type | Required | Description |
|---|---|---|---|
| city | string | Yes | Name of the city |

**Returns**

Peak concentration (grains/m³) and level (None / Low / Moderate / High / Very High) for each of: Alder, Birch, Grass, Mugwort, Olive, and Ragweed.

**Example prompt:** "What are the pollen levels in Berlin today?"

---

### 12. get_marine_weather

Returns current wave conditions and a 3-day wave forecast. Best results for coastal cities and island locations.

**Parameters**

| Parameter | Type | Required | Description |
|---|---|---|---|
| city | string | Yes | Name of a coastal city |

**Returns**

Current wave height with sea state label (Glassy / Calm / Slight / Moderate / Rough / Very Rough / High), wave direction and period, wind wave height, swell height and period, and a 3-day daily maximum forecast.

**Example prompt:** "What are the wave conditions in Lisbon?"

---

## Prerequisites

- Python 3.11 or higher
- A Google Gemini API key (free tier available at [aistudio.google.com](https://aistudio.google.com))
- Git

---

## Installation

```bash
git clone https://github.com/shreyaschhabra/weather-mcp-server.git
cd weather-mcp-server
pip install -r requirements.txt
```

---

## Configuration

The only required configuration is the Gemini API key. Set it as an environment variable before running any client or server.

```bash
export GEMINI_API_KEY="your_gemini_api_key_here"
```

To get a free Gemini API key:
1. Go to [https://aistudio.google.com/apikey](https://aistudio.google.com/apikey)
2. Sign in with a Google account
3. Click "Create API key"

The MCP server (`main.py`) does not require any API key. It only calls Open-Meteo endpoints which are free and unauthenticated.

---

## Running the Project

```bash
GEMINI_API_KEY="your_key" streamlit run streamlit_app.py
```

Open [http://localhost:8501](http://localhost:8501) in your browser.

**What the UI shows:**
- Left sidebar: all 12 tools with descriptions and one-click example prompts per tool
- Chat area: user messages, a collapsible "Tools used" section showing tool name, arguments as metrics, and raw tool output, followed by Gemini's final response
- Input field at the bottom with Enter-to-send support

---

### Running the MCP Server Standalone

To run the MCP server by itself for testing or integration with another MCP host (Claude Desktop, VS Code Copilot, etc.):

```bash
python3 main.py
```

The server communicates over stdio using JSON-RPC 2.0. To connect it to Claude Desktop, add the following to `~/Library/Application Support/Claude/claude_desktop_config.json`:

```json
{
  "mcpServers": {
    "weather": {
      "command": "python3",
      "args": ["/absolute/path/to/weather-mcp-server/main.py"]
    }
  }
}
```

To connect it to VS Code Copilot, the `.vscode/mcp.json` configuration is already included in the repository.

---

## Deployment

### Streamlit Community Cloud

The recommended deployment path. Free, permanent public URL, no server management required.

1. Fork or push this repository to your GitHub account.
2. Go to [share.streamlit.io](https://share.streamlit.io) and sign in with GitHub.
3. Click **New app**.
4. Select your repository, set the branch to `main`, and set the main file path to `streamlit_app.py`.
5. Click **Advanced settings** and add the following secret:
   ```
   GEMINI_API_KEY = "your_gemini_api_key"
   ```
6. Click **Deploy**.

The app will be available at a URL of the form `https://your-app-name.streamlit.app`.
This repository is deployed at **<https://instaweather.streamlit.app/>**.

> **Note on dependency pinning.** This project targets the **v1** `mcp` API
> (`mcp.server.fastmcp.FastMCP`). In `mcp` 2.x that module was removed and
> `FastMCP` was renamed to `MCPServer`. Because Streamlit Community Cloud
> installs dependencies fresh on every deploy, an unpinned `mcp[cli]>=1.0.0`
> will pull in 2.x and the server subprocess will die at import time — the
> client then fails with `MCPError: Connection closed` during
> `session.initialize()`. `requirements.txt` pins `mcp[cli]>=1.9.0,<2` to
> prevent this. If you reboot the app and it breaks after a working deploy,
> check the installed versions in the app logs first.

---

### Railway

1. Push the repository to GitHub.
2. Create a new project at [railway.app](https://railway.app) and select **Deploy from GitHub repo**.
3. Add the environment variable `GEMINI_API_KEY` in the Variables tab.
4. Create a `Procfile` in the project root with the following content:
   ```
   web: uvicorn app:app --host 0.0.0.0 --port $PORT
   ```
5. Railway will detect Python, install dependencies from `requirements.txt`, and deploy automatically.

---

### Render

1. Push the repository to GitHub.
2. Create a new **Web Service** at [render.com](https://render.com).
3. Connect your GitHub repository.
4. Set the following:
   - **Build command:** `pip install -r requirements.txt`
   - **Start command:** `uvicorn app:app --host 0.0.0.0 --port $PORT`
5. Add the environment variable `GEMINI_API_KEY` under the Environment tab.
6. Click **Create Web Service**.

Note: the free tier on Render spins down inactive services after 15 minutes of inactivity. The first request after a period of inactivity will take longer while the service restarts.

---

## Data Sources

All weather data is provided by the Open-Meteo project. Open-Meteo is a free, open-source weather API with no rate limits for non-commercial use.

| API | Endpoint | Used by tools |
|---|---|---|
| Geocoding | `geocoding-api.open-meteo.com/v1/search` | All tools (city name to coordinates) |
| Weather Forecast | `api.open-meteo.com/v1/forecast` | get_weather, get_forecast, get_hourly_forecast, compare_cities_weather, should_i_bring_umbrella, get_uv_index, get_sunrise_sunset, get_weather_alerts |
| Historical Archive | `archive-api.open-meteo.com/v1/archive` | get_historical_weather |
| Air Quality | `air-quality-api.open-meteo.com/v1/air-quality` | get_air_quality, get_pollen_forecast |
| Marine | `marine-api.open-meteo.com/v1/marine` | get_marine_weather |

Full Open-Meteo documentation: [https://open-meteo.com/en/docs](https://open-meteo.com/en/docs)

---

## Environment Variables

| Variable | Required | Description |
|---|---|---|
| `GEMINI_API_KEY` | Yes | Google Gemini API key. Required by all client-side files (`streamlit_app.py`, `app.py`, `client_gemini.py`, `client-gemini.ts`). Not required by `main.py`. |

---

## Roadmap

- Add support for multi-turn conversation history in the Streamlit UI
- Add get_uv_index hourly breakdown for the current day
- Add weather widgets / data visualisation charts using Plotly or Altair
- Add a caching layer to reduce repeated API calls for the same city
- Publish the MCP server to the MCP registry so it can be installed by other MCP hosts without cloning the repository
- Add support for units switching (Fahrenheit / mph)
- Extend pollen coverage using additional data sources for Asia and South America