io. github. cyanheads/ open-meteo-mcp-server
by cyanheads · remote · local (npm) · no sign-in to list tools · v0.4.1 · published by GitHub user cyanheads
Global weather via Open-Meteo: forecast, historical, marine, air quality, geocoding, elevation.
11 tools: 1 change things and 10 only read.
Scanned Oct 21, 2026
- Deletes or cancels0Claude Code: deny
- Can't be undone0Claude Code: ask
- Changes things1Claude Code: ask: see these tools
- Only reads10Claude Code: allow: see these tools
2 checks matched something in the tool definitions. No finding is not a safety guarantee. How the checks work.
claude mcp add --transport http open-meteo https://open-meteo.caseyjhand.com/mcpFindings
Matches of fixed checks, with the text they matched.
The server marks "openmeteo_dataframe_query" read-only (readOnlyHint: true), and its own name, description or input give the class "write".
What this check looks forMatched text
annotations: {"readOnlyHint":true}; The name "openmeteo_dataframe_query" says "query": a word for reading.
The description is 2,131 characters long, over the 2,000 this check looks for.
What this check looks forMatched text
description ends: "...preview instead. At least one of hourly_variables or daily_variables is required."
Tools
Open a tool for its reasons and full description. Descriptions are the publisher's own words, shown as they are.
11 tools
Changes things 1Claude Code: ask
openmeteo_dataframe_queryRun a read-only SQL SELECT against tables staged on a DataCanvas by openmeteo_get_forecast, openmeteo_get_historical, openmeteo_get_marine, openmeteo_get_air_quality, openmeteo_get…1 finding
Why it is in this group
- The name "openmeteo_dataframe_query" says "query": a word for reading.
- The description's first verb is "run": a word for a change.
- The input takes a statement or command to run (input.sql), which can change anything it can reach.
- The server marks it read-only (readOnlyHint: true), but an annotation never lowers a class.
Findings
- Warn
The server marks "openmeteo_dataframe_query" read-only (readOnlyHint: true), and its own name, description or input give the class "write".
What this check looks forMatched text
annotations: {"readOnlyHint":true}; The name "openmeteo_dataframe_query" says "query": a word for reading.
Description, as the server wrote it Run a read-only SQL SELECT against tables staged on a DataCanvas by openmeteo_get_forecast, openmeteo_get_historical, openmeteo_get_marine, openmeteo_get_air_quality, openmeteo_get_ensemble, openmeteo_get_flood, or openmeteo_get_climate. Pass the canvas_id returned when any of those tools spills (truncated: true), and reference the exact table_name those tools return alongside it. Call openmeteo_dataframe_describe to list staged tables and their columns when you need to discover names. Returns up to 100 leading rows — fewer when wide rows would pass the 80,000-character inline ceiling — plus row_count — exact, or a lower bound flagged by count_error when counting fails or runs past 5 seconds; page the rest with LIMIT / OFFSET.
Annotations the server declares
- readOnlyHint: true
Only reads 10Claude Code: allow
openmeteo_dataframe_describeList the tables and their columns on a DataCanvas staged by openmeteo_get_forecast, openmeteo_get_historical, openmeteo_get_marine, openmeteo_get_air_quality, openmeteo_get_ensembl…No findings
Why it is in this group
- The name "openmeteo_dataframe_describe" says "describe": a word for reading.
Description, as the server wrote it List the tables and their columns on a DataCanvas staged by openmeteo_get_forecast, openmeteo_get_historical, openmeteo_get_marine, openmeteo_get_air_quality, openmeteo_get_ensemble, openmeteo_get_flood, or openmeteo_get_climate. Call this first to discover table names before querying with openmeteo_dataframe_query.
Annotations the server declares
- readOnlyHint: true
- idempotentHint: true
openmeteo_get_air_qualityModeled CAMS (Copernicus Atmosphere Monitoring Service) air quality: PM2.5, PM10, nitrogen dioxide, sulphur dioxide, ozone, carbon monoxide, dust, pollen, and European/US AQI indic…No findings
Why it is in this group
- The name "openmeteo_get_air_quality" says "get": a word for reading.
Description, as the server wrote it Modeled CAMS (Copernicus Atmosphere Monitoring Service) air quality: PM2.5, PM10, nitrogen dioxide, sulphur dioxide, ozone, carbon monoxide, dust, pollen, and European/US AQI indices. This is modeled grid data, not measured station readings — for measured data, use openaq-mcp-server. Forecast horizon up to 7 days, with optional past_days (up to 92) for recent history — or start_date and end_date together for an archive range; the CAMS global archive begins in August 2022, and earlier dates return rows of nulls. One window per call: a date range is mutually exclusive with forecast_days and past_days, and needs both ends — a lone start_date or end_date is rejected. Common variables: pm2_5, pm10, carbon_monoxide, nitrogen_dioxide, sulphur_dioxide, ozone, dust, european_aqi, us_aqi, alder_pollen, birch_pollen, grass_pollen, mugwort_pollen, olive_pollen, ragweed_pollen. Pollen is modeled for Europe only, and only in pollen season — elsewhere the pollen values come back null, and the notice names them. Set current_variables for pollutant and AQI values at this instant — returned as a current object plus a current_units map, and enough on its own without hourly_variables; the block’s interval field is its aggregation interval in seconds (3600 on this endpoint). A wide window — a large past_days or date range plus many variables — produces thousands of records; these spill to a DataCanvas when canvas is enabled, returning canvas_id and table_name with truncated: true — inspect the staged columns with openmeteo_dataframe_describe, then query the full set with openmeteo_dataframe_query. With canvas disabled they return a bounded preview instead.
Annotations the server declares
- readOnlyHint: true
- idempotentHint: true
openmeteo_get_climateLong-range climate projections from bias-corrected daily CMIP6 models, covering 1950-01-01 to 2050-12-31 at any coordinate.No findings
Why it is in this group
- The name "openmeteo_get_climate" says "get": a word for reading.
Description, as the server wrote it Long-range climate projections from bias-corrected daily CMIP6 models, covering 1950-01-01 to 2050-12-31 at any coordinate. Answers "what will conditions look like through 2050?" — the future-projection counterpart to openmeteo_get_historical (the observed archive, what happened). Daily resolution only. Available models: CMCC_CM2_VHR4, FGOALS_f3_H, HiRAM_SIT_HR, MRI_AGCM3_2_S, EC_Earth3P_HR, MPI_ESM1_2_XR, NICAM16_8S. A model name outside that list is sent upstream rather than rejected here, so a model Open-Meteo adds later still works; if upstream rejects the request, the error names the offending model on its own rather than the whole requested list. With 2+ models each variable appears once per model with the model name as suffix (e.g. temperature_2m_max_CMCC_CM2_VHR4); a single or omitted model returns plain variable names. Not all models carry all variables — missing combinations return null. Multi-decade daily pulls across several models produce thousands of records and spill to a DataCanvas when canvas is enabled, returning canvas_id and table_name with truncated: true — inspect the staged columns with openmeteo_dataframe_describe, then query the full set with openmeteo_dataframe_query. With canvas disabled they return a bounded preview instead.
Annotations the server declares
- readOnlyHint: true
- idempotentHint: true
openmeteo_get_elevationTerrain elevation from the Copernicus Digital Elevation Model (~90m resolution) for one or more coordinate pairs.No findings
Why it is in this group
- The name "openmeteo_get_elevation" says "get": a word for reading.
Description, as the server wrote it Terrain elevation from the Copernicus Digital Elevation Model (~90m resolution) for one or more coordinate pairs. Accepts up to 100 pairs per call. Useful for geographic context, elevation-adjusted weather interpretation, or route planning. A point the DEM has no value for (latitude 90) returns elevation_m null; the other points in the call are unaffected.
Annotations the server declares
- readOnlyHint: true
- idempotentHint: true
openmeteo_get_ensembleProbabilistic ensemble weather forecast — up to 64 ensemble members, up to 35 days ahead with optional past_days (0–92).1 finding
Why it is in this group
- The name "openmeteo_get_ensemble" says "get": a word for reading.
Findings
- Info
The description is 2,131 characters long, over the 2,000 this check looks for.
What this check looks forMatched text
description ends: "...preview instead. At least one of hourly_variables or daily_variables is required."
Description, as the server wrote it Probabilistic ensemble weather forecast — up to 64 ensemble members, up to 35 days ahead with optional past_days (0–92). Most models end within 16 days; a model's columns are null past its horizon, and the response notice names where its data ends. Each member's values appear as separate columns named with a member suffix (e.g. temperature_2m_member01, temperature_2m_member02). Use the spread across members to compute exceedance probabilities, quantify forecast uncertainty, and build decision thresholds. Available models: ecmwf_ifs025_ensemble (51 members, global 0.25°), ecmwf_aifs025_ensemble (51, global 0.25°), ecmwf_ifs_europe_ensemble (51, Europe 9 km), ecmwf_aifs_europe_ensemble (51, Europe 31 km), google_weathernext2_ensemble (64, global 0.25°), ncep_gefs_seamless (31, global blend), ncep_gefs025 (31, global 0.25°), ncep_gefs05 (31, global 50 km, 35 days), ncep_aigefs025 (31, global 0.25°), icon_seamless_eps (20–40, global/Europe blend), icon_global_eps (40, global 26 km), icon_eu_eps (40, Europe 13 km), icon_d2_eps (20, Central Europe 2 km), gem_global_ensemble (21, global 0.25°), bom_access_global_ensemble (18, global 40 km), ukmo_global_ensemble_20km (18, global 20 km), ukmo_uk_ensemble_2km (3, UK 2 km), meteoswiss_icon_ch1_ensemble (11, Central Europe 1 km), meteoswiss_icon_ch2_ensemble (21, Central Europe 2 km). Omit models to use ecmwf_ifs025_ensemble. A regional model returns no data outside the area it covers; that comes back as an input error naming the coverage gap, not a transient failure, so pick a global model or move the coordinate inside the region rather than retrying. A model name this list does not carry is still sent upstream, so a newly added one keeps working. Large multi-member, multi-day pulls produce thousands of records and spill to a DataCanvas when canvas is enabled, returning canvas_id and table_name with truncated: true — inspect the staged columns with openmeteo_dataframe_describe, then query the full set with openmeteo_dataframe_query. With canvas disabled they return a bounded preview instead. At least one of hourly_variables or daily_variables is required.
Annotations the server declares
- readOnlyHint: true
- idempotentHint: true
openmeteo_get_floodGloFAS (Global Flood Awareness System) river discharge forecast and historical reanalysis.No findings
Why it is in this group
- The name "openmeteo_get_flood" says "get": a word for reading.
Description, as the server wrote it GloFAS (Global Flood Awareness System) river discharge forecast and historical reanalysis. Returns daily river discharge (m³/s) for the largest modeled river within 5 km of the given coordinates — no river ID needed. That river is not always the closest one: at 5 km resolution a point near a confluence or a pair of parallel channels can resolve to an unintended reach. When the returned discharge looks unrepresentative for the intended river, Open-Meteo suggests varying the coordinate by about 0.1° and comparing the values. Forecast horizon up to 210 days ahead; reanalysis history back to 1984-01-01. One mode per call: forecast_days for the future outlook, or start_date and end_date together for reanalysis history. The two modes are mutually exclusive, and a date range needs both ends — a lone start_date or end_date is rejected. Available daily variables: "river_discharge" (the daily discharge value; the mean across ensemble members is "river_discharge_mean"), and the ensemble statistics "river_discharge_mean", "river_discharge_median", "river_discharge_min", "river_discharge_max", "river_discharge_p25" (25th percentile), and "river_discharge_p75" (75th percentile). The statistics are computed across ensemble members in forecast mode only; in reanalysis mode they repeat river_discharge and are not uncertainty estimates. Returns null for coordinates far from any river or in areas without GloFAS coverage. A wide reanalysis range produces thousands of daily records and spills to a DataCanvas when canvas is enabled, returning canvas_id and table_name with truncated: true — inspect the staged columns with openmeteo_dataframe_describe, then query the full set with openmeteo_dataframe_query. With canvas disabled it returns a bounded preview instead.
Annotations the server declares
- readOnlyHint: true
- idempotentHint: true
openmeteo_get_forecastWeather forecast for coordinates: hourly and/or daily variables for up to 16 days ahead, with optional past_days (up to 92) for recent history.No findings
Why it is in this group
- The name "openmeteo_get_forecast" says "get": a word for reading.
Description, as the server wrote it Weather forecast for coordinates: hourly and/or daily variables for up to 16 days ahead, with optional past_days (up to 92) for recent history. Use past_days instead of openmeteo_get_historical for dates within the last 1–5 days, since the archive’s ERA5 components lag by up to ~5 days. Returns per-timestamp records — each hourly entry contains a "time" field (ISO 8601) plus one key per requested variable; each daily entry contains a "time" field (YYYY-MM-DD) plus requested variables. Common hourly variables: temperature_2m, precipitation, wind_speed_10m, relative_humidity_2m, cloud_cover, uv_index, apparent_temperature, precipitation_probability, weather_code, surface_pressure, visibility, wind_direction_10m, wind_gusts_10m, dew_point_2m. Common daily variables: temperature_2m_max, temperature_2m_min, precipitation_sum, wind_speed_10m_max, sunrise, sunset, uv_index_max, precipitation_hours, weather_code. Set current_variables for conditions at this instant — Open-Meteo serves those from 15-minute model data, which is more precise than picking the nearest hourly row, and the response carries a current object plus a current_units map. A wide window — a large past_days plus many hourly variables — produces thousands of records; these spill to a DataCanvas when canvas is enabled, returning canvas_id and table_name with truncated: true — inspect the staged columns with openmeteo_dataframe_describe, then query the full set with openmeteo_dataframe_query. With canvas disabled they return a bounded preview instead. At least one of current_variables, hourly_variables, or daily_variables is required.
Annotations the server declares
- readOnlyHint: true
- idempotentHint: true
openmeteo_get_historicalHistorical weather from the Open-Meteo reanalysis archive (1940–present).No findings
Why it is in this group
- The name "openmeteo_get_historical" says "get": a word for reading.
Description, as the server wrote it Historical weather from the Open-Meteo reanalysis archive (1940–present). Requires start_date and end_date (ISO 8601 date, e.g., "2024-07-01"). With models omitted the archive answers from Best Match, which blends IFS HRES, ERA5, and ERA5-Land seamlessly — so the source varies by date and no single update lag describes the response. Set models to pin a consistent source for a multi-decade series: the ERA5 family updates daily with about a 5-day delay, while IFS HRES has none, so for the last few days either request models: ["ecmwf_ifs"] or use openmeteo_get_forecast with past_days. Available models: best_match (default, blends IFS HRES + ERA5 + ERA5-Land), ecmwf_ifs (global 9 km, updated every 6 hours, no delay), ecmwf_ifs_analysis_long_window (global 9 km, daily, 2 days delay), era5_seamless (ERA5 and ERA5-Land combined), era5 (global 0.25° (~25 km), daily, 5 days delay), era5_land (global 0.1° (~11 km), daily, 5 days delay), era5_ensemble (global 0.5° (~55 km), daily, 5 days delay), cerra (Europe only, 5 km, no real-time updates). Uses the same variable names as the forecast API for direct comparison. Large date ranges (multi-year hourly) produce thousands of records — these spill to a DataCanvas when canvas is enabled, returning canvas_id and table_name with truncated: true; inspect the staged columns with openmeteo_dataframe_describe, then query the full set with openmeteo_dataframe_query. With canvas disabled they return a bounded preview instead. At least one of hourly_variables or daily_variables is required.
Annotations the server declares
- readOnlyHint: true
- idempotentHint: true
openmeteo_get_marineMarine wave and ocean conditions for a coastal or ocean coordinate: wave height, wave period, wave direction, wind-wave height, swell height, sea-surface temperature.No findings
Why it is in this group
- The name "openmeteo_get_marine" says "get": a word for reading.
Description, as the server wrote it Marine wave and ocean conditions for a coastal or ocean coordinate: wave height, wave period, wave direction, wind-wave height, swell height, sea-surface temperature. Forecast horizon up to 8 days, with optional past_days (up to 92) for recent history — or start_date and end_date together for an archive range, which returns real wave values back to at least 2022. One window per call: a date range is mutually exclusive with forecast_days and past_days, and needs both ends — a lone start_date or end_date is rejected. Returns per-timestamp records — each entry contains a "time" field plus one key per requested variable. Best for open-ocean and coastal exposed points — sheltered water such as a sound or bay returns near-zero wave values, and a land point returns null values, which the notice reports. Common hourly variables: wave_height, wave_direction, wave_period, wind_wave_height, wind_wave_direction, wind_wave_period, swell_wave_height, swell_wave_direction, swell_wave_period. Common daily: wave_height_max, wave_direction_dominant, wave_period_max. Note: ocean_current_velocity is null for non-open-ocean coordinates. A wide window — a large past_days or date range plus many variables — produces thousands of records; these spill to a DataCanvas when canvas is enabled, returning canvas_id and table_name with truncated: true — inspect the staged columns with openmeteo_dataframe_describe, then query the full set with openmeteo_dataframe_query. With canvas disabled they return a bounded preview instead.
Annotations the server declares
- readOnlyHint: true
- idempotentHint: true
openmeteo_search_locationsResolve a place name to ranked coordinate matches with country, region, elevation, timezone, and population.No findings
Why it is in this group
- The name "openmeteo_search_locations" says "search": a word for reading.
Description, as the server wrote it Resolve a place name to ranked coordinate matches with country, region, elevation, timezone, and population. Required prerequisite for name-based queries — all weather tools take latitude/longitude, not place names. Search by a bare place name (city, region, or landmark); never fold a qualifier into it — pass "Baoding", not "Baoding Hebei", and "Paris", not "Paris, France". To disambiguate places that share a name, set the country input (ISO 3166-1 alpha-2, e.g. "US") and/or read the admin1 and country fields on each ranked result — admin1 is a result field for choosing among matches, not a search input. Returns up to 10 matches ranked by population/relevance.
Annotations the server declares
- readOnlyHint: true
- idempotentHint: true
This is not a safety rating, and no finding is not a safety guarantee. The groups and findings come from fixed checks of the tool definitions the server returned on the date shown, not from its code or what it does when called. How the checks work.
Install
Pinned to version 0.4.1. Values in angle brackets are yours to fill in.
claude mcp add --transport http open-meteo https://open-meteo.caseyjhand.com/mcpRun in a terminal.
Claude Code permissions
Allow the reads, ask before a change, deny the deletes. Paste into .claude/settings.json.
{
"permissions": {
"allow": [
"mcp__open-meteo__openmeteo_search_locations",
"mcp__open-meteo__openmeteo_get_elevation",
"mcp__open-meteo__openmeteo_get_forecast",
"mcp__open-meteo__openmeteo_get_historical",
"mcp__open-meteo__openmeteo_get_marine",
"mcp__open-meteo__openmeteo_get_air_quality",
"mcp__open-meteo__openmeteo_get_ensemble",
"mcp__open-meteo__openmeteo_get_flood",
"mcp__open-meteo__openmeteo_get_climate",
"mcp__open-meteo__openmeteo_dataframe_describe"
],
"ask": [
"mcp__open-meteo__openmeteo_dataframe_query"
],
"deny": []
}
}Tools that only read are allowed, tools that change things ask first, tools that delete or cancel are denied. It assumes the server is installed as open-meteo. Review it before you use it.
