open-meteo-mcp-server
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@open-meteo-mcp-serverweather forecast for Tokyo"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Public Hosted Server: https://open-meteo.caseyjhand.com/mcp
Overview
Global weather from Open-Meteo: forecasts, historical archive, marine conditions, air quality, probabilistic ensembles, river discharge, and CMIP6 climate projections. Geocode place names, pull hourly and daily variables, and run SQL over large results staged to DataCanvas. Runs as a stdio process, a local Streamable HTTP server, or the public hosted endpoint above.
Tools
Tool | Description |
| Resolve a place name to ranked coordinate matches with country, region, elevation, timezone, and population |
| Weather forecast for coordinates: current conditions and/or hourly and daily variables for up to 16 days, with optional recent past data; wide windows spill to DataCanvas |
| Historical weather from the Open-Meteo reanalysis archive (1940–present); Best Match by default, or pin a |
| Marine wave and ocean conditions for coastal or ocean coordinates: wave height, period, direction, swell, and sea-surface temperature; up to 8 forecast days, |
| Modeled CAMS air quality: PM2.5, PM10, NO2, O3, CO, dust, pollen, and European/US AQI indices; current conditions, up to 7 forecast days, |
| Terrain elevation from Copernicus DEM (~90m resolution) for up to 100 coordinate pairs per call |
| Probabilistic ensemble forecast: per-member hourly/daily time series (up to 51 members, 16 days) for exceedance and uncertainty analysis |
| GloFAS river discharge forecast (up to 210 days) or reanalysis (1984–present); coordinate-based, resolving to the largest river within 5 km; large ranges spill to DataCanvas |
| Bias-corrected daily CMIP6 climate projections (1950–2050) across up to 7 models; large ranges spill to DataCanvas |
| List tables and columns on a DataCanvas staged by |
| Run a read-only SQL SELECT against tables staged on a DataCanvas |
Related MCP server: weather-mcp
Capability reference
openmeteo_search_locations tool
Returns name, country, admin1/admin2, latitude, longitude, elevation, IANA timezone, population, and GeoNames feature code
Search by a bare place name — a city, region, or landmark ("Baoding", not "Baoding Hebei"; "Paris", not "Paris, France"); a compound "City Region" or "City, Country" string matches nothing
Disambiguate same-named places (e.g., "Springfield") with the optional
countryfilter (ISO 3166-1 alpha-2) or by raisingcount(default 5, up to 10) and readingadmin1/countryon each resultPass the timezone from a result directly to weather tools as the
timezoneparameterFails with a
no_resultserror (not an empty array) when nothing matchesWhen the top match has null or sub-100,000 population, the response carries an advisory
noticenaming that place, its country, and its feature code — historic and colonial exonyms ("Bangalore", "Calcutta") can resolve to an unrelated small feature rather than the modern city
openmeteo_get_forecast tool
Up to 16 forecast days (
forecast_days, default 7) plus optionalpast_days(0–92) for recent history — preferpast_daysoveropenmeteo_get_historicalfor the last ~5 days, where the archive's ERA5 components lagAt least one of
current_variables,hourly_variables, ordaily_variablesis required;current_variablesalone satisfies it and returns acurrentobject pluscurrent_unitsfrom Open-Meteo's 15-minute current-conditions dataHourly and daily are separate variable sets — a variable documented under the other cadence is rejected before the request, naming the field it belongs in and same-cadence alternatives
Configurable temperature, wind-speed, and precipitation units; reshapes the columnar API response into per-timestamp records with parallel
hourly_units/daily_unitsmapsA wide window (large
past_daysplus many hourly variables) spills to DataCanvas whenCANVAS_PROVIDER_TYPE=duckdb— output carriescanvas_idandtruncated: true; an over-wide request Open-Meteo refuses outright fails asrequest_too_large, naming the levers to narrow it
openmeteo_get_historical tool
Requires
start_dateandend_date(YYYY-MM-DD); archive covers 1940-01-01 to presentOmitting
modelsreads Best Match (blends IFS HRES, ERA5, and ERA5-Land — source varies by date); passmodelsto pin one:era5/era5_land/era5_ensembleupdate daily with about a 5-day delay,ecmwf_ifshas none,cerracovers Europe only (elsewhere it fails as a coverage-gap error). Not an allowlist — an unlisted name still goes upstreamWith 2+ models each variable column is suffixed with the model name
Same variable vocabulary as
openmeteo_get_forecastfor direct past/forecast comparison; at least one ofhourly_variablesordaily_variablesis required, and the two are separate sets — a wrong-cadence name is rejected before the requestLarge date ranges (multi-year hourly) spill to DataCanvas when
CANVAS_PROVIDER_TYPE=duckdb— output carriescanvas_idandtruncated: true; query viaopenmeteo_dataframe_describethenopenmeteo_dataframe_query
openmeteo_get_marine tool
Up to 8 forecast days (
forecast_days, upstream default 7) with optionalpast_days(0–92), or an archive range viastart_date/end_date(real wave values go back to at least 2022)One window per call — a date range is mutually exclusive with
forecast_days/past_days, and needs both ends (a lonestart_dateorend_dateis rejected)At least one of
hourly_variablesordaily_variablesis required; the two are separate sets — a wrong-cadence name is rejected before the requestInland or sheltered-water points return near-zero wave values (physically correct, not an error);
ocean_current_velocityis null for non-open-ocean coordinatesWide windows spill to DataCanvas when
CANVAS_PROVIDER_TYPE=duckdb— output carriescanvas_idandtruncated: true; query withopenmeteo_dataframe_query
openmeteo_get_air_quality tool
Up to 7 forecast days (
forecast_days, upstream default 5) with optionalpast_days(0–92), or an archive range viastart_date/end_date— the CAMS global archive begins August 2022; earlier dates return nulls, andus_aqistarts a day later than the pollutant seriesOne window per call — a date range is mutually exclusive with
forecast_days/past_days, and needs both endsAt least one of
current_variablesorhourly_variablesis required;current_variablesalone answers "right now" (acurrentobject pluscurrent_units, interval 3600s on this endpoint)Grid-modeled CAMS data, coarser than ground stations — cross-reference
openaq-mcp-serverfor measured readings; output carriesdata_source: "CAMS"to distinguish the twoWide windows spill to DataCanvas when
CANVAS_PROVIDER_TYPE=duckdb— output carriescanvas_idandtruncated: true; query withopenmeteo_dataframe_query
openmeteo_get_elevation tool
Accepts parallel
latitudes[]/longitudes[]arrays of equal length, up to 100 pairs per callReturns results in input order:
{ latitude, longitude, elevation_m }from the Copernicus DEM (~90m resolution)Useful for geographic context, elevation-adjusted weather interpretation, or route planning
openmeteo_get_ensemble tool
Up to 16 forecast days (
forecast_days, default 7) with optionalpast_days(0–92); at least one ofhourly_variablesordaily_variablesis required, and the two are separate sets (though this endpoint's own catalog publishestemperature_2m_max/_minunder both)Each requested variable returns as per-member columns (
temperature_2m_member01,temperature_2m_member02, …) across up to 64 members — use the spread for exceedance probabilities and uncertainty rangesmodelsselects one global or regional ensemble (member counts vary, e.g.ecmwf_ifs025_ensemble51,gem_global_ensemble21); omit for the API default blend. Not an allowlist — an unlisted name still goes upstreamA regional model queried outside its coverage area fails as a non-retryable input error naming the gap — switch to a global model rather than retrying
Large multi-member, multi-day pulls spill to DataCanvas when
CANVAS_PROVIDER_TYPE=duckdb— output carriescanvas_idandtruncated: true; query withopenmeteo_dataframe_query
openmeteo_get_flood tool
Coordinate-based — no river ID needed; discharge comes from the largest modeled river within 5 km of the point, which is not always the closest one. Vary the coordinate by about 0.1° and compare when a result looks unrepresentative
Forecast horizon up to 210 days (
forecast_days); reanalysis history from 1984-01-01 to present viastart_date/end_dateOne mode per call —
forecast_daysis mutually exclusive with astart_date/end_daterange, and the range needs both endsDaily variables:
river_discharge(ensemble mean),river_discharge_mean/_min/_max/_median,river_discharge_p25/_p75— all in m³/s; returns null for coordinates outside GloFAS coverageWide reanalysis ranges spill to DataCanvas when
CANVAS_PROVIDER_TYPE=duckdb— output carriescanvas_idandtruncated: true; query withopenmeteo_dataframe_query
openmeteo_get_climate tool
Coverage 1950-01-01 to 2050-12-31, daily resolution only — the future-projection counterpart to
openmeteo_get_historicalUp to 7 bias-corrected CMIP6 models (e.g.
CMCC_CM2_VHR4,MRI_AGCM3_2_S); not an allowlist — an unlisted name still goes upstream, and a multi-model rejection names only the offending modelWith 2+ models each variable appears once per model, suffixed with the model name; a single or omitted model returns plain variable names
Not all models carry all variables — missing combinations return null rather than an error
Multi-decade daily pulls across several models spill to DataCanvas when
CANVAS_PROVIDER_TYPE=duckdb— output carriescanvas_idandtruncated: true; query withopenmeteo_dataframe_query
openmeteo_dataframe_describe tool
Lists tables and columns on a DataCanvas staged by any of the seven spillover tools (
openmeteo_get_forecast,_historical,_marine,_air_quality,_ensemble,_flood,_climate) — call this first, since table and column names are generated per requestFails with
canvas_not_enabledwhenCANVAS_PROVIDER_TYPEis notduckdb, orcanvas_not_foundwhencanvas_idis unknown or past its 24-hour sliding TTLOutput includes each table's row count, column types, and nullability, plus the canvas's
expires_at
openmeteo_dataframe_query tool
Runs a read-only SQL
SELECTagainst tables staged by the seven spillover tools — pass thecanvas_idand reference the exacttable_namethey return, or discover both viaopenmeteo_dataframe_describeFails with
canvas_not_enabledwhenCANVAS_PROVIDER_TYPEis notduckdb,canvas_not_foundwhencanvas_idis unknown or past its 24-hour sliding TTL, ormissing_tablewhen the SQL references a table not staged on the canvasSystem catalogs (
information_schema,sqlite_master,pg_catalog,duckdb_*()functions) are blocked so callers cannot enumerate other staged canvases — fails assystem_catalog_accessResult rows are capped at 100 inline;
row_countreports the full total — page further results withLIMIT/OFFSETin the SQL
Features
Built on @cyanheads/mcp-ts-core: stdio and Streamable HTTP transports, pluggable auth (none / jwt / oauth), swappable storage (in-memory, filesystem, Supabase, Cloudflare KV/R2/D1), structured logging with optional OpenTelemetry tracing.
Open-Meteo-specific:
No API key required for non-commercial use — zero-config out of the box; commercial use requires Open-Meteo's paid API tier
Self-contained geocoding —
openmeteo_search_locationsresolves place names so agents don't need a separate geocoderHistorical archive from 1940 to present on the same variable schema as the forecast API, with a
modelsselector for pinning the reanalysis sourceAutomatic columnar-to-record reshape — Open-Meteo's parallel time/variable arrays become per-timestamp records with a
*_unitsmapDataCanvas spillover for all seven forecast/archive tools — an over-budget result stages a DuckDB dataframe for SQL querying; with
CANVAS_PROVIDER_TYPE=none(the default) those tools return a bounded preview withtruncated: trueinstead
Agent-friendly output:
Location-first workflow —
openmeteo_search_locationsreturns the IANA timezone alongside coordinates, ready to pass straight to any weather tool'stimezoneparameterDiscriminated rejections — an over-wide request fails as
request_too_largenaming the levers to shrink it, distinct from a rate-limit rejection, which is not retriedCadence-aware validation — a variable documented under the wrong cadence (hourly vs. daily) is rejected before the upstream call, naming the field it belongs in and same-cadence alternatives
Notice on silent data changes — an unserved variable name or a snapped-to-grid coordinate surfaces in the response
noticerather than passing as an unremarked data gap
Getting started
Public Hosted Instance
A public instance is available at https://open-meteo.caseyjhand.com/mcp — no installation required. Point any MCP client at it via Streamable HTTP:
{
"mcpServers": {
"open-meteo-mcp-server": {
"type": "streamable-http",
"url": "https://open-meteo.caseyjhand.com/mcp"
}
}
}Self-Hosted / Local
Add the following to your MCP client configuration file.
{
"mcpServers": {
"open-meteo-mcp-server": {
"type": "stdio",
"command": "bunx",
"args": ["@cyanheads/open-meteo-mcp-server@latest"],
"env": {
"MCP_TRANSPORT_TYPE": "stdio",
"MCP_LOG_LEVEL": "info"
}
}
}
}Or with npx (no Bun required):
{
"mcpServers": {
"open-meteo-mcp-server": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@cyanheads/open-meteo-mcp-server@latest"],
"env": {
"MCP_TRANSPORT_TYPE": "stdio",
"MCP_LOG_LEVEL": "info"
}
}
}
}Or with Docker:
{
"mcpServers": {
"open-meteo-mcp-server": {
"type": "stdio",
"command": "docker",
"args": ["run", "-i", "--rm", "-e", "MCP_TRANSPORT_TYPE=stdio", "ghcr.io/cyanheads/open-meteo-mcp-server:latest"]
}
}
}For Streamable HTTP, set the transport and start the server:
MCP_TRANSPORT_TYPE=http MCP_HTTP_PORT=3010 bun run start:http
# Server listens at http://localhost:3010/mcpPrerequisites
Bun v1.4.0 or higher (or Node.js v24+).
No API key required. Non-commercial use is free and keyless.
Commercial use requires Open-Meteo's paid API tier.
Installation
Clone the repository:
git clone https://github.com/cyanheads/open-meteo-mcp-server.gitNavigate into the directory:
cd open-meteo-mcp-serverInstall dependencies:
bun installConfiguration
All configuration is validated at startup via Zod schemas. No API key is required for non-commercial use — all variables are optional.
Variable | Description | Default |
| Transport: |
|
| HTTP server port |
|
| HTTP server host |
|
| HTTP endpoint path |
|
| Maximum HTTP request body bytes; |
|
| Public origin for TLS-terminating reverse-proxy deployments | — |
| HTTP session mode: |
|
| Replay missed SSE events for stateful HTTP sessions. No effect on stateless mode or protocol revision 2026-07-28. |
|
| Events retained per stateful session for replay; oldest evicted first. |
|
| How long retained events remain replayable (ms). |
|
| Auth mode: |
|
| Log level ( |
|
| Maximum repeated emissions per level and message in each log window; |
|
| Repeated-log suppression window (ms). |
|
| Opt-in forced-GC interval (ms, Bun only). Set to |
|
| Directory for log files (Node.js only) |
|
| Storage backend: |
|
| Canvas engine for |
|
| Override for the main forecast + elevation API |
|
| Override for the historical archive API |
|
| Override for the marine forecast API |
|
| Override for the CAMS air quality API |
|
| Override for the geocoding API |
|
| Override for the ensemble forecast API |
|
| Override for the GloFAS flood API |
|
| Override for the CMIP6 climate projections API |
|
| Enable OpenTelemetry tracing and metrics |
|
See .env.example for the full list of optional overrides.
Running the server
Local development
Build and run the production version:
# One-time build bun run rebuild # Run the built server bun run start:http # or bun run start:stdioRun checks and tests:
bun run devcheck # Lint, format, typecheck, security bun run test # Vitest test suite
Docker
docker build -t open-meteo-mcp-server .
docker run --rm -p 3010:3010 open-meteo-mcp-serverThe Dockerfile defaults to HTTP transport, stateless session mode, and logs to /var/log/open-meteo-mcp-server. OpenTelemetry peer dependencies are installed by default — build with --build-arg OTEL_ENABLED=false to omit them.
Project structure
Directory | Purpose |
|
|
| Server-specific environment variable parsing and validation with Zod |
| Tool definitions ( |
| Open-Meteo HTTP client wrapping all nine endpoints with retry, error classification, and columnar reshape |
| DataCanvas accessor for |
| Unit and integration tests mirroring |
Development guide
See CLAUDE.md for development guidelines and architectural rules. The short version:
Handlers throw, framework catches — no
try/catchin tool logicUse
ctx.logfor request-scoped logging,ctx.statefor tenant-scoped storageRegister new tools in the
tools[]array insrc/index.tsWrap external API calls: validate raw → normalize to domain type → return output schema; never fabricate missing fields
Attribution
Weather data by Open-Meteo.com, licensed CC BY 4.0.
Contributing
Issues are welcome. Run checks and tests before submitting:
bun run devcheck
bun run testLicense
Apache-2.0 — see LICENSE for details.
This server cannot be deployed
Maintenance
Related MCP Connectors
Open-Meteo MCP — weather forecast + historical reanalysis + sister APIs
Climate MCP — wraps Open-Meteo Climate API (free, no auth)
Weather MCP — wraps Open-Meteo API (free, no auth)
MET Norway (api.met.no) MCP — global weather, Nordics specials.
Related MCP Servers
- FlicenseAqualityDmaintenanceA comprehensive MCP server providing tools for real-time, forecast, and historical weather data, alongside air quality, marine conditions, and climate projections. It also includes geocoding services to search for locations and retrieve precise coordinates for environmental analysis.7-
- AlicenseNot gradedqualityBmaintenanceExposes weather data from the free Open-Meteo API via decorator-driven MCP tools with Zod schemas, supporting stdio and HTTP transports.11 npmMIT
- AlicenseNot gradedqualityBmaintenanceMCP server that provides tools to retrieve weather information from a weather API, supporting both local stdio and remote Streamable HTTP transports.370 npm1MIT
- FlicenseNot gradedqualityCmaintenanceEnables querying weather, country information, exchange rates, public holidays, and dictionary definitions by wrapping public APIs as MCP tools over stdio.-