Skip to main content
Glama
cuolm

meteo-swiss-mcp

by cuolm

Swiss Weather MCP Server

Logo PyPI License Release Tests

A Model Context Protocol (MCP) server that exposes Swiss weather forecast data as callable tools. It reads the official MeteoSwiss local forecast collection, caches it locally, and serves predictions such as rainfall, sunshine, temperature, wind and a worded weather summary. MeteoSwiss publishes these forecasts for 5,614 Swiss locations (weather stations, postal code centers and points of interest), for today and the next 8 days, refreshed every hour. It also reads the latest measurements of the SwissMetNet weather stations, refreshed every 10 minutes, for the weather right now at a location and across Switzerland.

There is also an MCP client that can be run to test the server using the stdio transport.

Note: This project is not an official MeteoSwiss product. All forecast and measurement data are from the MeteoSwiss Open Data portal. Source: MeteoSwiss

Table of Contents

Related MCP server: wetter-mcp-server

Project Structure

swiss-weather-mcp/
├── src/swiss_weather_mcp/
│   ├── server.py           # MCP server
│   ├── errors.py           # The error for questions the data cannot answer
│   ├── log.py              # Logging setup for the server and the client
│   ├── locations.py        # Location lookup by name or postal code
│   ├── opendata.py         # Downloads and timestamps from MeteoSwiss Open Data
│   ├── formatting.py       # Swiss time and compass points in the answers
│   ├── other_language_place_names.csv  # Place names in other languages
│   ├── forecast/
│   │   ├── source.py       # MeteoSwiss forecast data source and caching
│   │   ├── parameters.py   # Forecast parameter codes and pictogram meanings
│   │   └── service.py      # Forecast values, units and aggregation
│   ├── measurements/
│   │   ├── source.py       # SwissMetNet measurements, stations and the nearest station
│   │   ├── parameters.py   # Measurement parameter codes
│   │   └── service.py      # The weather measured now
│   └── client.py           # MCP client (optional)
├── scripts/                # Builds other_language_place_names.csv
├── tests/swiss_weather_mcp/  # Tests
├── .github/workflows/      # CI and release pipelines
├── docs/                   # Documentation
├── pyproject.toml          # Project metadata and dependencies
├── uv.lock                 # Pinned, reproducible dependency set
└── Dockerfile

Caches live outside the project, under your OS's standard cache directory (see Installation).

Quick Start

1. Installation

Install the server globally to run it anywhere on your system:

uv tool install swiss-weather-mcp

2. Execution

swiss-weather-mcp-server

No configuration is needed, the server reads everything it requires from MeteoSwiss.

Installation

As a Global CLI Tool

uv tool install swiss-weather-mcp

# With the optional MCP client, which pulls in the OpenAI SDK
uv tool install 'swiss-weather-mcp[client]'

As a Library Dependency

# Using uv
uv add swiss-weather-mcp

# Using pip
pip install swiss-weather-mcp

# With the optional MCP client, which pulls in the OpenAI SDK
uv add 'swiss-weather-mcp[client]'
pip install 'swiss-weather-mcp[client]'

From Source

git clone https://github.com/cuolm/swiss-weather-mcp.git
cd swiss-weather-mcp

# Using uv (Recommended)
uv sync --extra client

# Using pip
python3 -m venv .venv
source .venv/bin/activate  # On Windows: .venv\Scripts\activate
pip install -e ".[client]"

Note: uv sync installs the server only. --extra client adds the OpenAI SDK needed by swiss-weather-mcp-client.

Note:

  • llama-server must already be running when you start swiss-weather-mcp-client, which talks to it but never starts it. The MCP server itself does not need it.

  • Caches live in your OS cache directory (via platformdirs, e.g. ~/Library/Caches/swiss-weather-mcp on macOS, ~/.cache/swiss-weather-mcp on Linux), independent of where the server runs. Set SWISS_WEATHER_MCP_CACHE_DIR to put them elsewhere, or delete the directory to clear them. Each data source has its own folder: locations, forecast and measurements. Forecast files sit in one folder per model run, named by the run's time in UTC, such as forecast/runs/202609231100.

Usage

Running the Server

If installed via uv tool install or pip:

# stdio (default)
swiss-weather-mcp-server

# streamable-http
swiss-weather-mcp-server --transport=streamable-http --host=localhost --port=8050

If running within the source repository cloned from GitHub:

# Using uv (Recommended)
uv run swiss-weather-mcp-server

# Using pip, with the virtual environment activated
swiss-weather-mcp-server

Optional flags: --help

Note: By default the server keeps only the rows for the location you asked about, a few kilobytes per parameter. Pass --cache-all-locations to keep the whole published file instead (about 31 MB per parameter), which makes questions about further locations need no new download.

Running the Server with Docker

Images are built and published automatically by GitHub Actions to the project's GitHub Container Registry, tagged :latest (newest release) and by version.

  1. Run the published image, mapping port 8050:

    docker run -p 8050:8050 ghcr.io/cuolm/swiss-weather-mcp:latest
  2. Access the server at:

    http://localhost:8050/mcp/

Note: The cache lives inside the container and is lost when it stops. Mount a volume to keep it across restarts, for example -v swiss-weather-cache:/root/.cache/swiss-weather-mcp.

Manual Build

docker build -t swiss-weather-mcp .
docker run -p 8050:8050 swiss-weather-mcp

Running the MCP Client using Stdio Transport

The bundled MCP client can be used to test the server over the stdio transport. It requires the client extra (see Installation). The client starts the MCP server itself, but not the model server, which has to be running first.

  1. Install llama.cpp, which provides llama-server:

    brew install llama.cpp
  2. Start it in its own terminal, downloading the model on first use:

    llama-server --jinja --no-mmproj -hf unsloth/Qwen3.5-4B-GGUF --port 8080
  3. Run the client against it in a second terminal:

    swiss-weather-mcp-client --model=unsloth/Qwen3.5-4B-GGUF --base-url=http://localhost:8080/v1
    
    # From a source checkout, using uv
    uv run --extra client swiss-weather-mcp-client --model=unsloth/Qwen3.5-4B-GGUF --base-url=http://localhost:8080/v1

Note: --jinja applies the model's chat template, without which tool calling is unsupported, and --no-mmproj skips the vision projector that -hf downloads alongside some models. --base-url defaults to llama-server's address, and any other OpenAI compatible backend works by pointing it elsewhere, for example LM Studio or vLLM. Pass --model exactly as the server reports it under /v1/models.

Available Tools

The forecast tools take a location (a name such as "Zurich", or a Swiss postal code such as "8001") and a Swiss local time, or a date for daily_forecast. They return the value together with the location they resolved, its altitude, the time or day it applies to, and the model run the forecast came from.

Tool

Purpose

Example Call

current_date_and_time()

Today's weekday and the Swiss time now, in the form the tools accept

current_date_and_time()

daily_forecast(location, start_date, days)

One row per day, 1 to 9 days: lowest and highest hourly temperature, median rainfall with its 10th and 90th percentile, daytime weather in words with an emoji

daily_forecast("Zurich", "2026-09-26", 2)

hourly_forecast(location, start, end)

Hour by hour, up to 24 hours, or one hour without end: temperature (°C) with its 10th and 90th percentile, rain chance (%) and the weather in words with an emoji

hourly_forecast("Zurich", "2026-09-23T15:00")

rain_outlook(location, start, end)

Rain in 3-hour blocks, up to 48 hours: chance (%), median (mm) and how much the wettest hour may bring (mm)

rain_outlook("Zurich", "2026-09-23T12:00", "2026-09-24T00:00")

sunshine_hours(location, start, end)

Sunshine (h) summed over a period

sunshine_hours("Zurich", "2026-09-23T06:00", "2026-09-23T18:00")

wind(location, when)

Mean speed and strongest gust (km/h) in the hour up to that time, how strong that gust may get (its 90th percentile), and the direction as degrees and compass point

wind("Säntis", "2026-09-23T14:00")

total_cloud_cover(location, when)

Estimated total cloud cover (%) plus the low, medium and high layers

total_cloud_cover("Zurich", "2026-09-23T14:00")

freezing_level(location, when)

Height of the 0 °C line (m above sea level)

freezing_level("Zermatt", "2026-09-23T14:00")

current_conditions(location)

Measured now at the nearest weather station: temperature, humidity, dew point, rain and sunshine in the last 10 minutes, wind, gust, direction and sea-level pressure, plus the temperature and pressure change over the last 3 hours and the sunshine in the last hour

current_conditions("Zurich")

current_extremes(extreme)

Where in Switzerland it is warmest, coldest, windiest, wettest or sunniest now, from the measurements of all weather stations: the first 5 stations by temperature or gust, or how many stations are sunny or rainy in Switzerland and in each canton

current_extremes("sunniest")

Time

  • Send times as Swiss clock time in ISO 8601 without an offset, e.g. "2026-09-23T14:00". The server applies summer or winter time for that date itself. An explicit offset is honoured.

  • All times in the answers are ISO 8601 Swiss local time with the UTC offset, e.g. 2026-09-23T13:00+02:00. The +02:00 shows the difference to UTC: +02:00 in summer, +01:00 in winter.

  • MeteoSwiss stamps an hourly average or sum at the end of its hour, so 14:00 means 13:00 to 14:00, and a period from start to end covers exactly the hours in between. Cloud cover and the freezing level are values at that moment instead.

  • The forecast covers today and the next 8 days.

Note: Ask current_date_and_time() first when the question is relative, such as "tomorrow" or "tonight", because the tools take a real date rather than an offset.

Note: A location must be one of the places MeteoSwiss publishes. Names are matched exactly, ignoring case, accents and periods, so zurich finds Zürich and St Moritz finds St. Moritz, but a region such as Tessin does not match and is rejected rather than guessed at. Names in the other national languages and in English, such as Genf or Geneva for Genève, work too. They come from Wikipedia lists (CC BY-SA 4.0), collected by scripts/build_other_language_place_names.py into other_language_place_names.csv. A city covers several postal code areas and resolves to the lowest one, which is not always its centre (Bern's lowest is 3004, its old town 3011). The answer names the point it used, and a postal code picks a specific district.

Note: current_conditions uses the nearest of the about 160 SwissMetNet stations that measures the temperature, not the location itself. It is usually a few kilometres away, but can be up to about 20 km away and several hundred metres higher or lower: Braunwald is measured in Glarus, 801 m lower. Every answer starts with one sentence that says so, such as "Glarus (517 m) is the nearest MeteoSwiss station to Braunwald 8784 (1318 m), 11.8 km away and 801 m lower. It measured the following values at 20:50.", so the values are not mistaken for the location's own. The file of a station is about 6 KB and is kept for 5 minutes. A station that has not published a temperature in the last hour is passed over for the next nearest one, at most three stations. The changes over the last 3 hours and the sunshine in the last hour come from the earlier rows of that file. They are empty when the station has not published all the rows they need, such as shortly after midnight UTC.

Note: current_extremes compares the newest values of all about 160 SwissMetNet stations, which MeteoSwiss publishes in one file of about 17 KB, kept for 5 minutes. The warmest, coldest and windiest stations are ranked by the temperature and by the strongest gust, and the first 5 are listed. For the sunniest and the wettest a ranking says little, because many stations share the highest value, so the answer counts stations instead: a station is sunny when the sun shone for at least 5 of the last 10 minutes, and rainy when it measured rain in the last 10 minutes. The counts are given for Switzerland and for each canton with a measuring station, always as "2 of 3 stations", with the canton with the highest share first, because a small canton has few stations. A canton without a sunny or rainy station is listed with 0, so it is not mistaken for missing. Cantons are named in full, and Liechtenstein, whose station is Vaduz, is listed like a canton. A station that does not measure a value, or has not delivered it yet, is left out of both numbers. The wettest also lists the 5 stations with the most rain. These are the values of stations, so the coldest and the windiest are usually high mountain stations.

Note: daily_forecast is by far the cheapest tool, about 8 MB for all its files against about 31 MB for each hourly file, so prefer it when the question is about a day rather than an hour. hourly_forecast reads five hourly files, wind four, and rain_outlook and total_cloud_cover three each. A tool downloads its files at the same time.

Note: MeteoSwiss calculates many slightly different possible outcomes, not just one forecast. Temperature and rain are the median of these outcomes: half lie below it and half above. If the possible temperatures at 14:00 are 18, 19, 20, 21 and 23 °C, the median is 20 °C. It is not the most likely value. Medians do not add up: when showers are possible but unlikely in any single hour, every hourly amount is 0, while the day as a whole still has a median of several millimetres. So daily_forecast gives the rain of a day, and rain_outlook shows the chance and how much a wet hour may bring.

Example Usage with LMStudio

Using the streamable-http transport layer

Configure the mcp.json file in LMStudio:

{
  "mcpServers": {
    "swiss_weather_mcp_server": {
      "url": "http://localhost:8050/mcp/"
    }
  }
}

Run the MCP server with the streamable-http transport layer:

uv run swiss-weather-mcp-server --transport=streamable-http --host=localhost --port=8050

Using the stdio transport layer

Configure the mcp.json file in LMStudio. Replace <path-to-the-project> with your actual local path:

{
  "mcpServers": {
    "swiss_weather_mcp_server": {
      "command": "<path-to-the-project>/.venv/bin/swiss-weather-mcp-server"
    }
  }
}

LMStudioMCPServer

Tests

Run the tests from the project root with:

uv run pytest

# Or, with an activated virtual environment
pytest

Check the code style with ruff and the type hints with mypy. mypy also checks the client, so install its extra first:

uv sync --group dev --extra client
uv run ruff check
uv run mypy

Every push and pull request runs the tests, ruff, mypy and a Docker build check via the Tests workflow.

Releasing

Versions are derived from Git tags by hatch-vcs — there is no version string to bump by hand.

  • Pushing a pre-release tag (e.g. 0.2.0rc1) triggers release_test.yaml: tests, publish to TestPyPI, push a versioned image to GHCR, and create a prerelease GitHub Release.

  • Pushing a final tag (e.g. 0.2.0) triggers release.yaml: tests, publish to PyPI, push :<version> and :latest images to GHCR, and create a GitHub Release.

Both publish jobs use PyPI trusted publishing via the pypi / testpypi GitHub environments — no API tokens are stored in the repository.

Resources

License

Licensed under the Apache License 2.0.

Available Tools

8 tools
current_date_and_timeA
        Get the current weekday and Swiss local time.

        Call this first when the question is relative, such as "tomorrow" or "tonight", because
        the forecast tools take a real date rather than an offset. The time is written the way
        the tools read it, ISO 8601 without an offset, so it can be sent back to them as it is.

        Returns:
            str: For example "Today is Wednesday, 2026-09-23T13:02 (Swiss time)".
        
ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A4.7/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full transparency burden. It discloses the return type (str), a concrete example ('Today is Wednesday, 2026-09-23T13:02 (Swiss time)'), and the ISO 8601 without-offset format, noting it can be sent directly to the forecast tools. Minor gaps remain about edge cases like DST or system-clock dependence, but they are low-risk.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is four concise sentences, each earning its place: purpose, usage timing, format rationale, and return example. It is front-loaded with the essential 'what' and keeps the supporting reasoning tight.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a zero-parameter utility tool, the description is complete. It covers what it returns, when to call it, how the output should be interpreted, and how it feeds into sibling forecast tools. Nothing needed to invoke it correctly is left out.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool has zero parameters, so the baseline is 4. The description correctly avoids inventing parameter details and instead focuses on the returned value and its use. No further parameter documentation is needed.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a clear verb and object: 'Get the current weekday and Swiss local time.' This unambiguously defines the tool's purpose. It also differentiates from the weather-forecast siblings by being the date/time anchor tool rather than a forecast tool.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly tells the agent when to call it: 'Call this first when the question is relative, such as "tomorrow" or "tonight"'. It also explains why, saying the forecast tools expect a real date rather than an offset. This provides concrete, actionable routing guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

daily_forecastA
        Get the whole-day forecast for a location, for one day or several days in a row: the
        cheapest way to answer "how is the weather" for a day, the weekend or the week.

        Each day covers a Swiss calendar day, 00:00 to 24:00 local time. The minimum and maximum
        are the lowest and highest hourly mean temperature of that day, and the weather words
        describe the daytime. Always use this for the rain of a whole day.

        Args:
            location (str): Location name (e.g., "Zurich") or Swiss postal code (e.g., "8001").
            start_date (str): The first Swiss calendar day in ISO 8601, e.g. "2026-09-23". Today or up to 8 days ahead.
            days (int): How many days in a row, from 1 to 9. Default 1.

        Returns:
            dict: The resolved location with its altitude, one row per day, and the model run.
                Each row has the date and weekday, the minimum and maximum temperature in
                Celsius, the day's rainfall in millimetres as its median and its 10th and 90th
                percentile (in 8 of 10 possible outcomes, the rain lies between the two
                percentiles), and the weather in words with a matching emoji. A value is None
                when MeteoSwiss does not publish it for that location.

        Examples:
            daily_forecast("Zurich", "2026-09-23")
            daily_forecast("Lugano", "2026-09-26", 2)   # a weekend
        
ParametersJSON Schema
NameRequiredDescriptionDefault
daysNo
locationYes
start_dateYes

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden, and it delivers richly: it defines the Swiss calendar day boundary (00:00–24:00), clarifies that min/max are hourly mean temperatures, states the weather words describe daytime, and explains that values can be None when MeteoSwiss does not publish them. This goes far beyond a generic 'get forecast' statement.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is well-organized with Args, Returns, and Examples sections. It is detailed but every sentence adds needed information about semantics, constraints, or output interpretation, especially given that no annotations and no output schema exist to supplement it.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

The tool has no output schema, so the description must cover return values, and it does thoroughly: the resolved location, altitude, per-day rows, model run, temperature min/max, rainfall percentiles, weather words, and None semantics. Considering the moderate complexity and the absence of structured metadata, the description is complete enough for correct invocation.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The input schema provides zero parameter descriptions (0% coverage), so the description must compensate. It fully does so by giving formats ('ISO 8601', e.g. '2026-09-23'), valid ranges ('today or up to 8 days ahead', 'days from 1 to 9'), a default ('Default 1'), and concrete examples for both location and days.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb and resource: 'Get the whole-day forecast for a location, for one day or several days in a row.' It clearly differentiates from siblings like hourly_forecast by framing itself as the day-level view and explicitly notes it is the 'cheapest way' to answer daily weather questions and 'Always use this for the rain of a whole day.'

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives clear context for when to use this tool: for a day, weekend, or week, and specifically for whole-day rainfall. It does not explicitly name sibling alternatives and say 'use hourly_forecast instead when...', but the 'always use this for the rain of a whole day' guidance and the day-level framing strongly imply the boundary versus other forecast tools.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

freezing_levelA
        Get the height of the 0 degree line at a location at a specific time.

        This is the height where the air is at 0 degrees. Snow usually reaches somewhat below it,
        so it answers questions about snow in the mountains, such as how high a ski area has to be.

        Args:
            location (str): Location name (e.g., "Zurich") or Swiss postal code (e.g., "8001").
            when (str): Swiss local time in ISO 8601 without offset, e.g. "2026-09-23T14:00". Today or up to 8 days ahead.

        Returns:
            dict: The freezing level in metres above sea level, the resolved location with its
                altitude, the time it is valid for, and the model run.

        Examples:
            freezing_level("Zermatt", "2026-09-23T14:00")
            freezing_level("Davos", "2026-09-25T06:00")
        
ParametersJSON Schema
NameRequiredDescriptionDefault
whenYes
locationYes

TDQS

A4.6/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full behavioral burden. It explains the output concept (height where air is 0 degrees), the relationship to snow, the required time format and range, and the return dict contents. It does not disclose edge cases or data sources, but it is transparent about the core behavior and result.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is concise yet complete: one opening sentence, a clarifying context sentence, then clear Args/Returns/Examples sections. Every sentence earns its place, and information is front-loaded with the core purpose before details.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given two required parameters, no output schema, and no annotations, the description provides everything an agent needs: parameter formats, time constraints, return value structure, and usage examples. It is fully complete for correct invocation.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 0%, but the description fully compensates. It defines location as a name or Swiss postal code with examples, and when as Swiss local time in ISO 8601 without offset with a concrete example and validity window. This adds all necessary meaning beyond the bare parameter names.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a specific verb and resource: 'Get the height of the 0 degree line at a location at a specific time.' It clearly differentiates from weather siblings like hourly_forecast or rain_outlook by focusing on the freezing level, and the snow-context sentence reinforces its purpose.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explains when this tool is relevant: 'Snow usually reaches somewhat below it, so it answers questions about snow in the mountains.' It does not explicitly name alternative tools or say when not to use it, but the context is clear enough for an agent to select it appropriately.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

hourly_forecastA
        Get the weather hour by hour for a location: temperature with its likely range, chance
        of rain and the weather in words.

        Use this for one hour, such as "how warm is it at 15:00", or part of a day, such as "how
        is the afternoon" or "when is the best time for a walk". Without end it returns the one
        hour starting at start. Each row covers the hour from its "from" to its "to": the
        temperature is the mean of that hour, while the rain chance and the weather cover the 3
        hours up to "to", as MeteoSwiss publishes them only per 3 hours. For whole days use
        daily_forecast, for rain amounts rain_outlook.

        Args:
            location (str): Location name (e.g., "Zurich") or Swiss postal code (e.g., "8001").
            start (str): Swiss local time in ISO 8601 without offset, e.g. "2026-09-23T15:00". Today or up to 8 days ahead.
            end (str): Optional. Swiss local time in ISO 8601 without offset, at most 24 hours after start.

        Returns:
            dict: The resolved location with its altitude, one row per hour (from, to,
                temperature in Celsius as its median and its 10th and 90th percentile, rain
                chance in percent, weather in words with a matching emoji), and the model run.
                In 8 of 10 possible outcomes, the temperature lies between the two percentiles.

        Examples:
            hourly_forecast("Zurich", "2026-09-23T15:00")                        # one hour
            hourly_forecast("Zurich", "2026-09-23T12:00", "2026-09-23T18:00")    # the afternoon
        
ParametersJSON Schema
NameRequiredDescriptionDefault
endNo
startYes
locationYes

TDQS

A5/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description fully discloses behavioral details: the default single-hour return when end is omitted, the exact time semantics (from/to, 3-hour coverage for rain/weather per MeteoSwiss), and the percentile interpretation for temperature. This goes far beyond a generic summary.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is structured with clear sections (description, args, returns, examples) and front-loads the purpose. Every sentence adds value, and the length is justified by the complexity of the data semantics. No redundancy or filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Despite no output schema and no annotations, the description covers everything an agent needs to call the tool correctly: input formats, time bounds, return structure, and the meaning of the data fields. It even explains the 3-hour aggregation rationale. Nothing essential is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 0%, but the description compensates completely: it specifies the location format (name or Swiss postal code), start format with example and valid range (today up to 8 days ahead), and end constraints (optional, at most 24 hours after start). Examples illustrate real calls.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a specific verb+resource ('Get the weather hour by hour for a location') and enumerates the exact data delivered (temperature range, rain chance, weather in words). It also names the sibling tools it is not (daily_forecast, rain_outlook), so an agent can distinguish it immediately.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Provides explicit use cases ('Use this for one hour... or part of a day') and gives concrete natural-language queries ('how is the afternoon'). It also states when to use alternatives ('For whole days use daily_forecast, for rain amounts rain_outlook'), leaving no ambiguity.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

rain_outlookA
        Get when and how much it may rain at a location, in 3-hour blocks over a period.

        Use this for "will it rain this afternoon", "do I need an umbrella" or "when does the
        rain stop". Each block gives the chance of rain, the median rainfall of the 3 hours, and
        how much the wettest hour of the block may bring (its 90th percentile). The median is
        often 0 when showers are possible, so read the chance and the "up to" amount as well.

        Args:
            location (str): Location name (e.g., "Zurich") or Swiss postal code (e.g., "8001").
            start (str): Swiss local time the period starts, ISO 8601 without offset, e.g. "2026-09-23T12:00". Today or up to 8 days ahead.
            end (str): Swiss local time the period ends, ISO 8601 without offset, at most 48 hours after start.

        Returns:
            dict: The resolved location with its altitude, one row per 3-hour block (from, to,
                rain chance in percent, median rainfall and wettest hour's "up to" amount in
                millimetres), and the model run.

        Examples:
            rain_outlook("Zurich", "2026-09-23T12:00", "2026-09-24T00:00")
            rain_outlook("Lugano", "2026-09-24T06:00", "2026-09-24T18:00")
        
ParametersJSON Schema
NameRequiredDescriptionDefault
endYes
startYes
locationYes

TDQS

A5/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries full responsibility. It discloses the return structure (dict with resolved location, altitude, per-block rows, and model run), explains that the median is often 0 when showers are possible and advises reading the chance and 'up to' amount, and states time constraints (start today or up to 8 days ahead, end at most 48 hours after). This is thorough and proactive.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is well-structured with Args, Returns, and Examples sections. It front-loads the core purpose and usage, then provides concise yet complete parameter details. Every sentence adds value—no filler. The examples are illustrative and directly useful for an agent.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's moderate complexity and lack of annotations/output schema, the description is exceptionally complete. It explains the output format, parameter constraints, interpretation guidance, and even provides two example calls. An agent has everything needed to select and invoke it correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 0%, but the description fully documents each parameter: location (name or Swiss postal code), start (Swiss local time ISO 8601 without offset, with valid range), and end (same format, max 48 hours after start). It adds essential format and constraint details that the schema completely lacks.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb ('Get') and a clear resource: when and how much it may rain in 3-hour blocks. It differentiates from siblings like hourly_forecast and daily_forecast by focusing exclusively on rain probability and amounts, and it names the exact use cases ('will it rain this afternoon', 'do I need an umbrella'). The purpose is unmistakable.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly says 'Use this for...' and lists three concrete scenarios. It also implicitly distinguishes from alternative forecast tools by focusing on rain-specific data and 3-hour resolution. While it doesn't name alternatives, the guidance is clear and actionable, leaving no ambiguity about when to invoke this tool.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

sunshine_hoursA
        Get the sunshine hours for a location over a period.

        MeteoSwiss publishes sunshine as minutes per hour, which are added up over the period
        and reported as hours.

        Args:
            location (str): Location name (e.g., "Zurich") or Swiss postal code (e.g., "8001").
            start (str): Swiss local time the period starts, ISO 8601 without offset, e.g. "2026-09-23T06:00". Today or up to 8 days ahead.
            end (str): Swiss local time the period ends, ISO 8601 without offset, e.g. "2026-09-23T18:00".

        Returns:
            dict: Sunshine in hours, the resolved location with its altitude, the period, and
                the model run.

        Examples:
            sunshine_hours("Zurich", "2026-09-23T00:00", "2026-09-24T00:00")   # the whole day
            sunshine_hours("Zurich", "2026-09-23T12:00", "2026-09-23T18:00")   # the afternoon
        
ParametersJSON Schema
NameRequiredDescriptionDefault
endYes
startYes
locationYes

TDQS

A4.2/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden. It discloses the data source (MeteoSwiss), the unit conversion (minutes summed to hours), time-zone and offset requirements, the valid time window (today to 8 days ahead), and the return structure (sunshine hours, resolved location with altitude, period, and model run). This is substantial behavioral context beyond a bare call signature, though it does not mention error behavior or rate limits.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is well-structured with Args, Returns, and Examples sections. Every sentence contributes information: the purpose, the unit conversion, parameter constraints, return shape, and two use cases. It is slightly verbose but appropriately so for a tool with no schema documentation. The main purpose is front-loaded.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given there is no output schema, the description is adequately complete: it describes the return dict and its fields, explains the period constraints, and gives examples. It covers everything an agent needs to select and call the tool correctly for a typical read-only weather data request. Minor omissions like invalid-location handling are acceptable for this complexity level.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The input schema has no descriptions (0% coverage), so the description must explain each parameter. It does so thoroughly: location accepts a name or Swiss postal code; start and end are Swiss local times in ISO 8601 without offset, with example values and the restriction that the period starts today or up to 8 days ahead. Examples illustrate valid calls, fully compensating for the schema gap.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's purpose: 'Get the sunshine hours for a location over a period.' The verb 'get', the resource 'sunshine hours', and the scope (location + period) are specific and distinct from sibling tools like hourly_forecast or daily_forecast, which serve different weather metrics. No ambiguity about what it does.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides useful context, such as the MeteoSwiss data source and the minute-to-hour conversion, and constrains start/end times to Swiss local time and a forward-only range. However, it does not explicitly state when to prefer this tool over alternatives or mention any exclusions (e.g., 'use daily_forecast for temperature'). Usage guidance is implied by the specific metric but not made explicit.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

total_cloud_coverA
        Get how cloudy it is at a location at a specific time.

        MeteoSwiss publishes low, medium and high cloud separately, and they overlap, so the
        total is an estimate that assumes the layers are independent. All three layers are
        returned as well, which tells low fog apart from thin high cloud.

        When a number is not needed, hourly_forecast answers "how cloudy" in plain words.

        Args:
            location (str): Location name (e.g., "Zurich") or Swiss postal code (e.g., "8001").
            when (str): Swiss local time in ISO 8601 without offset, e.g. "2026-09-23T14:00". Today or up to 8 days ahead.

        Returns:
            dict: Estimated total cloud cover in percent, the low, medium and high layers in
                percent, the resolved location with its altitude, the time it is valid for,
                and the model run.

        Examples:
            total_cloud_cover("Zurich", "2026-09-23T14:00")
            total_cloud_cover("Locarno", "2026-09-24T09:00")
        
ParametersJSON Schema
NameRequiredDescriptionDefault
whenYes
locationYes

TDQS

A4.5/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full burden. It discloses that the total is an estimate assuming independent layers, and explains that all three layers are returned to distinguish low fog from thin high cloud. It also describes the return dict contents (location, altitude, valid time, model run), offering good behavioral transparency beyond the basic purpose.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is well-structured with a clear opening, an explanatory note about the estimation, explicit Args/Returns/Examples sections. It is slightly longer than necessary but each sentence provides value, including the estimation nuance and the alternative routing. The structure is front-loaded with the core purpose, making it easy to scan.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a tool with no output schema and no annotations, the description covers everything an agent needs: parameter format and examples, the return value structure, the estimation logic, and the differentiation from a sibling. It even includes usage examples. Nothing is missing for correct invocation and interpretation of results.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must fully explain the parameters. It does: location is defined as a location name or Swiss postal code with examples, and when is defined as Swiss local time in ISO 8601 without offset, with an example and a valid range (today to 8 days ahead). This completely compensates for the lack of schema descriptions.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb and resource: 'Get how cloudy it is at a location at a specific time.' It clearly distinguishes itself from the sibling hourly_forecast by explaining that this tool returns a numeric total and layer details, whereas hourly_forecast answers in plain words. The purpose is unambiguous and differentiates from siblings.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides a clear alternative: 'When a number is not needed, hourly_forecast answers "how cloudy" in plain words.' This implies that this tool is for numeric cloud cover data. While it doesn't explicitly enumerate all when-not scenarios, it gives a concrete condition for choosing the alternative, which is sufficient guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

windA
        Get the wind at a location at a specific time: mean speed, strongest gust and direction.

        The speed is the mean over the hour up to that time, and the gust the strongest
        one-second gust in that hour, which is what makes wind hazardous. The gust's 90th
        percentile is a cautious high estimate of that strongest gust: in 9 of 10 possible
        outcomes the gust stays below it, and in 1 of 10 it is stronger, so it is not the
        highest gust possible. The direction is where the wind blows from, in degrees clockwise
        from north (0 is a north wind, 180 a south wind) and as a compass point.

        Args:
            location (str): Location name (e.g., "Zurich") or Swiss postal code (e.g., "8001").
            when (str): Swiss local time in ISO 8601 without offset, e.g. "2026-09-23T14:00". Today or up to 8 days ahead.

        Returns:
            dict: Wind speed, gust and the gust's 90th percentile in kilometres per hour, the
                direction in degrees and as a compass point such as "SW", the resolved location
                with its altitude, the time it is valid for, and the model run.

        Examples:
            wind("Zurich", "2026-09-23T14:00")
            wind("Säntis", "2026-09-24T12:00")
        
ParametersJSON Schema
NameRequiredDescriptionDefault
whenYes
locationYes

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden and does so excellently. It explains the averaging period (mean over the hour), the gust definition (one-second gust), the probabilistic meaning of the 90th percentile, and the direction convention (degrees clockwise from north, compass point). This gives the agent insight beyond the bare operation.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is well-structured with a concise summary, then a detailed explanation of output semantics, followed by Args, Returns, and Examples sections. Every sentence adds value — the probabilistic note and directional convention are important for correct interpretation. It is longer than a bare one-liner but not bloated.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a tool with only two parameters, no output schema, and no annotations, the description is exceptionally complete. It documents parameter formats, constraints, the exact return fields, and gives usage examples. An agent has everything it needs to call this tool correctly and interpret the result.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must fully document both parameters. It does: location accepts a place name or Swiss postal code with examples, and when is specified as Swiss local ISO 8601 without offset with constraints (today to 8 days ahead) and an example. This compensates completely for the schema's lack of descriptions.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a clear verb and resource: 'Get the wind at a location at a specific time', followed by the specific outputs (mean speed, strongest gust, direction). This is unambiguously distinct from sibling tools like hourly_forecast or rain_outlook, so an agent can identify it without needing to open the schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description clearly conveys the tool's intended use case — retrieving wind data for a single location at a particular point in time — which is enough to infer when to select it. However, it does not explicitly mention alternatives or provide exclusions, so it falls short of the full explicit-routing bar.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 13 tool updatesv0.2.0
    • Addeddaily_forecast
    • Addedfreezing_level
    • Addedhourly_forecast
    • Removedprecipitation_rate
    • Removedpressure_msl
    • Addedrain_outlook
    • Removedsnow_depth
    • Changedsunshine_hours6 fields changed
      • addedInput schema / properties / end
        Added value: +{
        +  "title": "End",
        +  "type": "string"
        +}
      • removedInput schema / properties / lead_time_end_swiss
        Removed value: -{
        -  "title": "Lead Time End Swiss",
        -  "type": "integer"
        -}
      • removedInput schema / properties / lead_time_start_swiss
        Removed value: -{
        -  "title": "Lead Time Start Swiss",
        -  "type": "integer"
        -}
      • addedInput schema / properties / start
        Added value: +{
        +  "title": "Start",
        +  "type": "string"
        +}
      • changedInput schema / required
        Previous value: -[
        -  "location",
        -  "lead_time_start_swiss",
        -  "lead_time_end_swiss"
        -]New value: +[
        +  "location",
        +  "start",
        +  "end"
        +]
      • changedOutput schema / (root)
        Previous value: -{
        -  "properties": {
        -    "result": {
        -      "title": "Result",
        -      "type": "number"
        -    }
        -  },
        -  "required": [
        -    "result"
        -  ],
        -  "title": "sunshine_hoursOutput",
        -  "type": "object"
        -}New value: +null
    • Removedtemperature
    • Changedtotal_cloud_cover4 fields changed
      • removedInput schema / properties / lead_time_swiss
        Removed value: -{
        -  "title": "Lead Time Swiss",
        -  "type": "integer"
        -}
      • addedInput schema / properties / when
        Added value: +{
        +  "title": "When",
        +  "type": "string"
        +}
      • changedInput schema / required
        Previous value: -[
        -  "location",
        -  "lead_time_swiss"
        -]New value: +[
        +  "location",
        +  "when"
        +]
      • changedOutput schema / (root)
        Previous value: -{
        -  "properties": {
        -    "result": {
        -      "title": "Result",
        -      "type": "number"
        -    }
        -  },
        -  "required": [
        -    "result"
        -  ],
        -  "title": "total_cloud_coverOutput",
        -  "type": "object"
        -}New value: +null
    • Removedtotal_rainfall
    • Addedwind
    • Removedwind_speed
  2. 9 tool updatesv0.1.0
    • First observedcurrent_date_and_time
    • First observedprecipitation_rate
    • First observedpressure_msl
    • First observedsnow_depth
    • First observedsunshine_hours
    • First observedtemperature
    • First observedtotal_cloud_cover
    • First observedtotal_rainfall
    • First observedwind_speed

TDQS

A4.6/5.0

Scored across 8 tools

Disambiguation5/5

Each tool targets a distinct weather variable or time scale: hourly vs daily vs rain-specific, wind, freezing level, sunshine, cloud cover, plus a date helper. The descriptions explicitly cross-reference each other (e.g., 'for whole days use daily_forecast, for rain amounts rain_outlook'), eliminating ambiguity.

Naming Consistency5/5

All tool names use clear, lowercase snake_case noun phrases that describe the data product (hourly_forecast, rain_outlook, freezing_level, total_cloud_cover). Though not verb_noun, the convention is perfectly consistent and readable, with no mixing of styles.

Tool Count5/5

8 tools is well-scoped for a weather data server. Each tool covers a distinct aspect of the domain without redundancy, and the count is comfortably within the ideal 3–15 range.

Completeness4/5

The server covers temperature, precipitation, wind, cloud, sunshine, and freezing level, plus a date helper. Minor gaps exist—no explicit weather alerts or current-conditions snapshot—but hourly_forecast can approximate current conditions, so agents can work around these limitations.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    A
    maintenance
    Enables AI models to access Swiss weather and climate data from MeteoSwiss, including current observations, forecasts, and warnings.
    6
    68 PyPI
    MIT
  • A
    license
    Not graded
    quality
    A
    maintenance
    MCP server for querying MeteoSwiss open weather data, providing access to station data, forecasts, radar composites, and more via natural language.
    38 PyPI
    44
    MIT
  • F
    license
    Not graded
    quality
    C
    maintenance
    Provides real-time weather forecasts, current conditions, and smart umbrella recommendations through MCP tools, backed by the Open-Meteo API.
    -