Skip to main content
Glama
WindWise MCP

WindWise MCP

by kacaben

WindWise MCP

CI TypeScript MCP SDK License: MIT

A portfolio project, not production weather advice. Forecasts are model output; see Limitations.

An MCP server that lets AI agents answer "should I go out on the water?" It combines Open-Meteo's weather and marine forecasts and adds wind-sport logic on top. It finds rideable wind windows, rates sea state, compares spots and gives a go / marginal / no-go call with reasons.

It is not just a thin API wrapper. The API returns raw hourly columns. WindWise turns them into the answers a wing-foiler, kiter or dinghy sailor actually needs, and is honest about forecast uncertainty.

A real conversation in Claude, using the dinghy_sailing preset:


Tools

Tool

What it answers

Business logic beyond the API

get_weather

"What's the weather in Sayulita?"

Weather codes → words, degrees → compass points, trims to the next N hours

get_wind_forecast

"When is the wind strongest between 10 and 5?"

Per-day rollup: average/peak/peak time, strongest gust, circular-mean prevailing direction, Beaufort

get_marine_conditions

"Are the waves OK for a beginner?"

Hour-by-hour sea-state rating; short-period chop rates one level rougher than swell; detects inland points

find_wind_windows

"When is it 12–22 kn with gusts under 25?"

Finds continuous windows across days, filters by direction and thunderstorms, explains why when nothing matches

compare_locations

"Bucerías or La Cruz tomorrow?"

Ranks 2–5 spots, tolerates partial failures, flags spots in the same model grid cell

get_weather_summary

"Should I go out tomorrow?"

Combines weather and marine into a go / marginal / no-go verdict with human-readable reasons

All tools are read-only (readOnlyHint) and validate their input with Zod. Each one publishes an outputSchema, returns both text and structuredContent, and includes Open-Meteo attribution and a forecast caveat. Forecasts always start from the current local hour, so a tool never offers a wind window that has already passed.

Activity presets

find_wind_windows, compare_locations and get_weather_summary accept an activity instead of raw knots. Any explicit min_wind_knots, max_wind_knots or max_gust_knots overrides the preset.

activity

Wind range

wing_foiling

12 kn and up

dinghy_sailing

10–18 kn

With no activity and no min_wind_knots, the minimum defaults to 12 kn. All three tools share min_window_hours (default 2), so they agree on what counts as a usable session. To add a preset, add one line to ACTIVITY_PRESETS in src/domain/activities.ts. The tool schemas and descriptions pick it up automatically.

A preset's maximum is not silently dropped: activity: "dinghy_sailing" with min_wind_knots: 20 fails with a message saying the 18 kn cap comes from the preset, and asks you to pass max_wind_knots as well.

Example: find_wind_windows

// Input
{
  "location": "Bucerias, Mexico",
  "min_wind_knots": 12,
  "max_wind_knots": 22,
  "max_gust_knots": 25,
  "start_hour": 9,
  "end_hour": 18,
  "directions": ["W", "WSW", "WNW", "SW"]   // onshore/cross-shore for this beach
}

// Output (example values, abridged)
{
  "found": true,
  "bestWindow": {
    "date": "2026-11-09", "start": "2026-11-09T13:00", "end": "2026-11-09T17:00",
    "durationHours": 4, "avgWindKn": 14.6, "maxWindKn": 16.8, "maxGustKn": 21.3,
    "direction": "WSW", "gusty": false
  },
  "windows": [ ... ],
  "attribution": "Weather data by Open-Meteo.com (CC BY 4.0) - https://open-meteo.com/"
}

When nothing matches, the tool still gives the agent something useful to say (example values):

{
  "found": false,
  "whyNoWindows": { "too_light": 27 },
  "strongestHourInRange": { "time": "2026-11-06T15:00", "windKn": 7.8, "gustKn": 21, "direction": "NNW" }
}

Related MCP server: Weather MCP Server

How it's built

AI agent ──MCP over stdio──▶ tools/      6 tools · Zod input/output schemas · error mapping
                               ├──▶ domain/     pure functions: wind windows, sea state, verdicts, ranking
                               └──▶ openmeteo/  HTTP with timeouts and retry · response validation · geocoding cache

The highlights:

  • Tools depend on an interface, not on HTTP. The integration tests run a real MCP client against the real server with a fake WeatherService, so there's no network and no fetch mocks.

  • Business logic is pure. Everything in domain/ takes arrays and returns data, and that is where most of the tests are.

  • Errors are for the model, bugs are for the developer. Known failures return actionable isError results. Anything unexpected is logged and hidden from the model. Open-Meteo responses are validated with Zod, so an API change becomes one clear error rather than silent nulls.

    When Open-Meteo could not be reached, the tool returned an isError result the model can read, not a crash or a stack trace. Claude read it and called the tools again on its own.

  • Forecasts start now, in the spot's local time. Hours that have already passed are never offered, days means 24-hour periods from now, and times never pass through the server's time zone.

  • Honest about uncertainty. Every answer carries a caveat, and compare_locations says when two towns share a forecast grid cell instead of pretending one is better.

See docs/ARCHITECTURE.md for the layer diagram, the source tree, every design decision and how to add a tool.


Getting started

Requires Node.js 22 or newer. No API key is needed.

git clone https://github.com/kacaben/windwise-mcp.git
cd windwise-mcp
npm install
npm run build

Use it from Claude Code

claude mcp add windwise -- node /absolute/path/to/windwise-mcp/dist/index.js

Use it from Claude Desktop (or any stdio MCP client)

Add this to claude_desktop_config.json:

{
  "mcpServers": {
    "windwise": {
      "command": "node",
      "args": ["/absolute/path/to/windwise-mcp/dist/index.js"]
    }
  }
}

Then try asking:

  • "Will there be enough wind for wing foiling in Bucerías tomorrow?"

  • "Show me the wind forecast for the next three days between 10 AM and 5 PM."

  • "Find 2-hour windows of 15–25 knots from the west in Cabarete this week."

  • "When could I go dinghy sailing in Bucerías this week?"

  • "Compare Bucerías, La Cruz de Huanacaxtle and Sayulita for tomorrow afternoon."

  • "Are the waves likely to be manageable for a beginner in Sayulita?"

You can also explore the tools interactively with the MCP Inspector:

npx @modelcontextprotocol/inspector node dist/index.js

Development

Command

What it does

npm run dev

Run the server from source with tsx

npm run lint

Biome lint and format check, failing on warnings (npm run format fixes what it can)

npm run typecheck

Strict TypeScript check of src, tests and scripts

npm run coverage

Tests with a V8 coverage report (about 97% of statements)

npm test

Unit and integration tests (Vitest, no network)

npm run build

Compile to dist/

npm run smoke

Live end-to-end test: spawns dist/index.js over stdio and calls every tool against the real Open-Meteo API

Tests

  • Domain unit tests: wind windows (gaps, limits, direction filters, thunderstorms, midnight crossings, missing data), sea-state ratings including chop and worst-hour rating, verdict reasons, ranking, activity presets, units, local time and session-hour selection.

  • Integration-layer tests: query building, Open-Meteo error payloads, retry/backoff, timeouts, response validation (missing variables, mismatched lengths, changed time format), cancellation, geocoding disambiguation (the "Sydney, US" regression and aliases such as "UK" and "USA"), "did you mean" suggestions, LRU eviction and inland detection. These use a fake fetch.

  • MCP server tests: a real Client connected through InMemoryTransport checks every tool's output, input and output schemas, validation errors, past-hour filtering, grid-cell choice, partial failures in compare_locations, time-zone date mismatches and hidden internal errors, with a fake WeatherService.

There is one test file per module. npm run coverage prints a per-file report.

CI runs lint, typecheck, tests and build on Node 22 and 24 for every push and pull request. A separate weekly workflow runs the live smoke test to catch upstream API changes.

Adding a tool

See Adding a tool in the architecture notes.


Limitations and roadmap

  • These are model forecasts. Sea breezes and thermal winds, which matter a lot in Banderas Bay, are often under-forecast by global models.

  • Marine data has ~5 km resolution and is less reliable inside bays and near the shore.

  • Wave ratings are deliberately simple and conservative: height bands, with short-period chop rated one level rougher than swell of the same height. They are a hint, not safety advice, and they don't account for wave direction or the shape of the beach.

  • The go / marginal / no-go verdict looks at wind, gusts, thunderstorms and waves. It ignores rain, temperature and the rider's skill level.

  • Any thunderstorm hour in the session makes the whole day no_go, even when a clean window comes before it. That is deliberately conservative; the windows are still listed.

  • The wind tools use the sea grid cell, so the temperature and rain in get_weather_summary describe the water cell, not the town. get_weather uses the land cell.

  • compare_locations takes place names only, not coordinates, and waves are just a tie-breaker in its ranking.

  • Forecasts are not cached (only place lookups are), and a rate-limited request is retried once after a short delay, ignoring Retry-After.

  • Prevailing direction is an unweighted vector mean, so a few calm hours from another direction can pull it around.

Ideas for later:

  • Short-TTL forecast cache and Retry-After handling

  • Coordinates and a direction filter for compare_locations; weigh waves in the ranking

  • Skill-level input for the sea-state rating and verdict

  • Compare several weather models (ECMWF, GFS, ICON) side by side to show forecast spread

  • Verify past forecasts against Open-Meteo's historical data to measure local bias

  • Tide data, and "offshore wind" warnings computed from each spot's beach orientation

  • Streamable HTTP transport for remote deployment


Attribution and license

Weather data by Open-Meteo.com, licensed under CC BY 4.0. The free Open-Meteo API is for non-commercial use.

Code licensed under the MIT License.

Available Tools

6 tools
compare_locationsCompare spotsA
Read-onlyIdempotent

Compare wind (and optionally waves) across 2-5 places for one day and rank them for a session. Ranking: most hours in usable windows, then longest continuous window, then steadier wind, then smaller waves. Set activity (e.g. dinghy_sailing) to rank for that wind range. Use for 'Bucerias or La Cruz tomorrow?' questions.

Inputs: locations (required), day, activity, end_hour, +6 more. Returns: date, hours, notes, ranking, +4 more

find_wind_windowsFind rideable wind windowsA
Read-onlyIdempotent

Find continuous time windows from now on when the wind matches the criteria: an activity preset (e.g. dinghy_sailing) or explicit min/max knots and max gusts, plus allowed directions and time of day. Returns each window with its average/peak wind and direction, the best window, and, when nothing matches, why each hour was rejected. Use for questions like 'will there be enough wind for wing foiling tomorrow?' or 'when can I go dinghy sailing this week?'.

Inputs: days, activity, end_hour, latitude, +9 more. Returns: found, notes, windows, criteria, +7 more

get_marine_conditionsWaves, swell and sea temperatureA
Read-onlyIdempotent

Marine forecast for a coastal location: wave height and period, swell height/direction/period, sea surface temperature and ocean current. Each day includes a sea-state rating and whether it is beginner-friendly. Only works for coastal or offshore points; inland places return an error.

Inputs: days, end_hour, latitude, location, +2 more. Returns: daily, hours, units, caveat, +3 more

get_weatherCurrent weather and hourly forecastA
Read-onlyIdempotent

Current conditions plus an hourly forecast (temperature, rain, cloud, wind) for a place. Use for general 'what's the weather' questions. Wind is in knots, times are local to the location. For wind-sport planning prefer get_wind_forecast or find_wind_windows.

Inputs: hours, latitude, location, longitude. Returns: units, caveat, hourly, current, +2 more

get_weather_summarySession summary for a dayA
Read-onlyIdempotent

One-call briefing for a water-sports day at a place: weather, temperature range, rain and thunderstorm risk, wind summary and best wind window, waves and sea state, and a go / marginal / no_go verdict with reasons. Combines the weather and marine forecasts. For today, only the remaining hours count. Set activity (e.g. dinghy_sailing) to judge the day for that wind range. Start here for 'should I go out tomorrow?' questions.

Inputs: day, activity, end_hour, latitude, +8 more. Returns: date, wind, hours, notes, +10 more

get_wind_forecastDetailed wind forecastA
Read-onlyIdempotent

Hourly wind speed, gusts and direction in knots for up to 7 days, plus a per-day summary (average, peak and peak time, strongest gust, prevailing direction). Only hours from now on are included. Restrict to daylight with start_hour/end_hour (local time). Use this to answer 'when is the wind strongest' questions.

Inputs: days, end_hour, latitude, location, +2 more. Returns: daily, hours, units, caveat, +3 more

Tool Schema Changelog

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

Loading tool schema changelog…

    TDQS

    A4.1/5.0

    Scored across 6 tools

    Disambiguation4/5

    The tools are mostly distinct, but get_weather, get_wind_forecast, and get_weather_summary all overlap in weather/wind reporting. The descriptions clarify when to use each, though get_weather_summary as a 'start here' briefing could still be confused with the narrower forecast tools.

    Naming Consistency5/5

    All names use consistent snake_case and follow a predictable verb_noun pattern: get_weather, get_wind_forecast, get_marine_conditions, get_weather_summary, find_wind_windows, compare_locations. The mix of get/find/compare reflects real action differences without becoming inconsistent.

    Tool Count5/5

    Six tools is well-scoped for a wind and weather planning server. Each tool has a distinct role: general weather, detailed wind forecast, marine conditions, window search, location comparison, and a combined briefing.

    Completeness4/5

    The surface covers the core wind-sports workflow well: forecast data, marine conditions, window finding, location comparison, and a go/no-go summary. Minor gaps remain around tides, weather alerts, or location search, but agents can work around these for most stated use cases.

    Maintenance

    ActivityMaintained
    ResponsivenessNo recent community issues

    Related MCP Connectors

    Related MCP Servers

    • F
      license
      Not graded
      quality
      D
      maintenance
      Provides surf forecast data including swell height, period, direction, and wind conditions for any location worldwide using the Open-Meteo Marine API. It also includes tools to find the best day to surf and integrates with MCP clients via a Python server.
      21
      -