# KaiCast Developers — full documentation > Generated from https://kaicast.com/developers/. Each section below is one page; the same text is at its .md URL. # Ocean conditions API and MCP server for AI agents **KaiCast answers "how's the water?" for a place and a date** — water visibility, swell, wind, tide and water temperature, plus *where* and *which day* to dive, snorkel, freedive or spearfish. It is one API with two doors: a REST API and a remote MCP server that drops into Claude, ChatGPT, Cursor, VS Code or your own agent. Under the hood KaiCast is an ocean-conditions model: it fuses NOAA buoys and tides, PacIOOS ocean models, Open-Meteo Marine and satellite ocean-color data into a **water-visibility forecast** that is checked nightly against divers' logged observations. It is the same engine behind the KaiCast app. **Location is a parameter everywhere — ask about any coordinates on Earth.** Where KaiCast has calibrated data, you get numbers; where it doesn't, you get an explicit `not_covered` answer with the nearest covered spot, never a guess. Coverage grows region by region (see [Coverage](https://kaicast.com/developers/coverage)). There are two layers: | Layer | What it answers | Price | |---|---|---| | **Facts** | "What are conditions at this spot, these days?" — visibility, swell, wind, tide, water temperature, a 0–100 condition score, per 3-hour period. | **Free**, no key | | **Decisions** | "Where should I snorkel tomorrow?" "Which day this week is best for diving at Hanauma Bay?" — ranked picks with *why*, confidence and caveats, plus a branded KaiCast spot card. | **Free during early access** | Both layers are available two ways, with identical answers: - **REST API** at `https://kaicast.com/api/v1/…` — described by [`/openapi.json`](https://kaicast.com/openapi.json). See [REST API](https://kaicast.com/developers/rest). - **MCP server** at `https://kaicast.com/mcp` — four tools for any MCP client. See [MCP server](https://kaicast.com/developers/mcp). ## Quickstart **Facts — one spot, the next few days:** ```bash curl "https://kaicast.com/api/v1/conditions?spot=hanauma-bay&from=2026-10-06&to=2026-10-08" ``` **Decision — the best snorkel spots on Oʻahu tomorrow:** ```bash curl "https://kaicast.com/api/v1/recommendations?region=us-hi-oahu&activity=snorkel&date=2026-10-06" ``` **Decision — the best day this week to dive near a point:** ```bash curl "https://kaicast.com/api/v1/best-day?lat=21.27&lon=-157.82&radius_km=25&activity=dive" ``` **MCP — add KaiCast to Claude Code:** ```bash claude mcp add --transport http kaicast https://kaicast.com/mcp ``` Then ask: *"Where's the best place to snorkel on Oʻahu tomorrow morning?"* ## What makes the answers trustworthy - **Location is always a parameter.** Every endpoint takes a spot, a region, or a latitude/longitude. Places KaiCast doesn't cover get an honest `not_covered` answer with the nearest covered spot — never a guess. - **Decisions quote the facts.** A recommendation's score and visibility are exactly what `/api/v1/conditions` returns for that spot and period. The decision layer chooses and explains; it never re-derives a number. - **Confidence comes with its reasons** — satellite vs. estimated visibility, data freshness, forecast lead time, and how directly the model fits the activity. - **Caveats are first-class.** Estimated visibility, long lead times, marginal conditions, and activities scored by proxy (scuba, freediving, spearfishing) are all flagged in a stable, machine-readable form. ## Use cases Dive and snorkel trip planners, travel and concierge agents, dive-shop and charter tools, chat assistants, and anything that needs a marine forecast with a reason attached. Activity-by-activity support — scuba, snorkeling, freediving, spearfishing, surfing, kiteboarding — is on [Use cases](https://kaicast.com/developers/use-cases). ## Coverage KaiCast covers the main Hawaiian Islands today — Oʻahu, Maui, Kauaʻi, Hawaiʻi Island and Molokaʻi — and is built to expand region by region. See [Coverage](https://kaicast.com/developers/coverage) for the live list and data quality per region. ## Using the answers Forecasts are model estimates, not a safety guarantee. Every response carries a `source` object (`name`, `url`, `docs`, `citation`) — when you show KaiCast results, cite KaiCast with a link to kaicast.com, relay caveats, and link to the full forecast — every decision response includes a spot card with that link ready to render. Terms for commercial use are on the [Pricing](https://kaicast.com/developers/pricing) page. ## For agents reading this page A plain-text index of these docs is at [`/developers/llms.txt`](https://kaicast.com/developers/llms.txt), and every page has a Markdown twin (append `.md`, e.g. [`/developers/mcp.md`](https://kaicast.com/developers/mcp.md)). The full docs in one file: [`/developers/llms-full.txt`](https://kaicast.com/developers/llms-full.txt). ## FAQ ### Is there a free ocean conditions API? Yes. KaiCast's facts — water visibility, swell, wind, tide, water temperature and a condition score per spot in 3-hour periods up to 7 days ahead — are free and need no API key. Decisions (ranked spot picks and best-day answers) are also free during early access. ### Is there a dive visibility API? KaiCast forecasts underwater visibility in feet for each covered spot, from satellite ocean color where available and a calibrated estimate otherwise, and says which one each number came from. Call `GET /api/v1/conditions` or the `get_conditions` MCP tool. ### Is there an MCP server for marine or ocean conditions? Yes: `https://kaicast.com/mcp` is a remote, stateless Streamable HTTP MCP server with four tools — `get_conditions`, `list_coverage`, `recommend_spot` and `best_day`. No authentication is required. ### Which locations does KaiCast cover? Every endpoint accepts any latitude and longitude. Today the calibrated coverage is the main Hawaiian Islands (Oʻahu, Maui, Kauaʻi, Hawaiʻi Island and Molokaʻi); outside it you get an explicit not-covered answer. The live list is at `GET /api/v1/coverage`. ### Does KaiCast forecast surf or kiteboarding? It does not rank surf breaks or kite spots. It does publish the swell height, period and direction and the wind speed and direction for every covered spot, so an agent can reason about them; decision tools return `activity_not_modeled` for surf instead of guessing. ### How should I attribute KaiCast? Use the `source` object in each response: cite "KaiCast" and link to https://kaicast.com. Decision responses also include a spot card with a link to the full forecast. --- # Use cases by activity One model, many questions. This page says, activity by activity, what KaiCast answers and how directly it is modeled — so an agent never presents a proxy as a purpose-built forecast. The same levels are machine-readable in every decision response (`activity.model`) and in `GET /api/v1/coverage`. | Activity | `activity` id | Decisions | Model | Useful facts | |---|---|---|---|---| | Snorkeling | `snorkel` | Ranked spots, best day | **Native** | visibility, swell, wind, tide | | Scuba diving | `dive` | Ranked spots, best day | Proxy | visibility, swell, tide, water temp | | Freediving | `freedive` | Ranked spots, best day | Proxy | visibility, wind, swell | | Spearfishing | `spearfish` | Ranked spots, best day | Proxy | visibility, swell, tide | | Surfing | `surf` | Declined (`activity_not_modeled`) | Not modeled | swell height/period/direction, wind | | Kiteboarding, windsurfing, wing | — | Not offered | Not modeled | wind speed and direction, swell | **Native** means the condition score was built and calibrated for that activity. **Proxy** means the shared, snorkel-calibrated score is a reasonable stand-in: picks carry a caveat and a confidence haircut. **Not modeled** means the decision tools decline and point you at the facts. ## Dive visibility API The headline number is **underwater visibility in feet** per 3-hour period, up to 7 days ahead, with `visibility_source` telling you whether it came from satellite ocean color or a calibrated estimate. Visibility is also a hard ceiling on the condition rating, so a murky day can't be rated "great" because the wind is light. ```bash curl "https://kaicast.com/api/v1/conditions?spot=electric-beach&from=2026-10-06&to=2026-10-07" ``` ## Snorkel forecast API Snorkeling is KaiCast's native model. Ask for the best spots in a region on a day, or the best day at a spot: ```bash curl "https://kaicast.com/api/v1/recommendations?region=us-hi-maui&activity=snorkel&date=2026-10-06" ``` ## Scuba diving conditions `activity=dive` ranks with the shared condition score and breaks ties on visibility. Every pick carries a `proxy_activity_model` caveat; relay it. Conditions at depth (current, thermocline) are not scored. ## Freediving conditions `activity=freedive` favors visibility, then calm wind. Depth, current and thermocline are not scored, and every pick says so. Never dive alone. ## Spearfishing conditions `activity=spearfish` ranks on the shared score and visibility, and **excludes spots inside Hawaiʻi DAR marine managed areas closed to spearfishing** (Marine Life Conservation Districts such as Hanauma Bay, Molokini and Pūpūkea, Natural Area Reserves, and similar). Excluded spots are listed in `considered.excluded` with `reason: closed_to_spearfishing` and the closing areas named. Spots inside rules-only areas carry a `regulated_area` caveat. Closed seasons and bag limits are **not** modeled — every pick carries `verify_fishing_rules`. If the closure data can't be read, nothing is excluded and picks say `regulations_not_checked` instead. ## Surf forecast data KaiCast does not model surf breaks, so `recommend_spot` and `best_day` return `activity_not_modeled` for `surf` rather than inventing a ranking. The facts are still there: swell height, period and direction, and wind speed and direction, per spot and period, from `get_conditions`. ## Kiteboarding and wind sports There is no kite or windsurf decision model. Wind speed (knots) and direction (degrees, where it blows *from*) are published for every covered spot every 3 hours — enough for an agent to reason about a session, with the usual caveat that spot-level wind is a forecast, not a station reading. ## Trip planners, travel and concierge agents Give the agent the destination and the dates; let it call `best_day` and `recommend_spot`. Answers come with confidence, caveats and a spot card to render in chat, and with an honest `not_covered` when the destination is outside coverage — so the agent can say so instead of hallucinating a forecast. ## Marine conditions MCP server for AI assistants Connect `https://kaicast.com/mcp` to Claude, ChatGPT, Cursor, VS Code or your own agent — see [MCP server](https://kaicast.com/developers/mcp). Tools mirror the REST endpoints exactly. ## Anywhere in the world Every tool takes a latitude/longitude. KaiCast answers with numbers where it has calibrated coverage and says `not_covered` (with the nearest covered spot) everywhere else. Need a region? Email [dawson@kaicast.com](mailto:dawson@kaicast.com) — requests shape the expansion order. --- # REST API Base URL: `https://kaicast.com`. All endpoints are `GET`, return JSON, and allow cross-origin requests. The machine-readable spec is [`/openapi.json`](https://kaicast.com/openapi.json) (OpenAPI 3.1) — import it into Postman, generate a client, or hand it to an agent framework as a tool definition. | Endpoint | Layer | Purpose | |---|---|---| | `GET /api/v1/conditions` | Fact | Conditions for one spot over up to 7 days | | `GET /api/v1/spots` | Fact | Spot directory (metadata only), filter by region / text / nearest | | `GET /api/v1/coverage` | Fact | Covered regions, decision support, activities | | `GET /api/v1/recommendations` | Decision | Ranked spot picks for an activity, area and day | | `GET /api/v1/best-day` | Decision | Best day in a window for an activity at a destination | ## Conventions - **Attribution:** every JSON body — including errors — has a `source` object: `{ "name": "KaiCast", "url": "https://kaicast.com", "docs": "https://kaicast.com/developers", "citation": "…" }`. Cite it when you surface the data. Responses also carry a `Link` header pointing at these docs and `/openapi.json`. - **Units are in field names** — `visibility_ft`, `swell_height_m`, `wind_speed_kt`, `water_temp_c`. No separate unit strings to lose. - **Directions are where it comes FROM**, degrees true: `swell_from_deg`, `wind_from_deg`. Decision responses also give an 8-point compass (`swell_from: "NW"`). - **Times are ISO 8601 with the spot's UTC offset**, plus the IANA timezone on the spot. Dates (`from`, `to`, `date`) are **spot-local** calendar days. - **Every key is always present.** Unknown values are `null`, never omitted. - **Tide heights** are feet above MLLW (`tide_datum: "MLLW"`). - **Scores** are 0–100. Tiers: `excellent` ≥ 80, `great` ≥ 60, `good` ≥ 40, `fair` ≥ 20, else `no-go`. Low visibility caps the score — calm water can't buy back murky water. - **Location** is one of: `spot` (an id), `region` (an id like `us-hi-oahu`), or `lat` + `lon`. Never more than one. ## Authentication None required today. Facts are free and keyless; decisions are free during early access. Keys aren't issued yet ([API keys](https://kaicast.com/developers/keys)). When they are, they'll be required for decisions once metering starts, sent like this — the header is already accepted and ignored, so you can wire it in now: ```http Authorization: Bearer kc_live_xxxxxxxx_xxxxxxxxxxxxxxxx ``` or `X-KaiCast-Key: kc_live_…`. Test keys start with `kc_test_`. Keys never go in the URL. ## GET /api/v1/conditions Facts for **one** spot, up to 7 days, in 3-hour periods. | Parameter | Type | Notes | |---|---|---| | `spot` | string | Spot id, e.g. `electric-beach`. Or use `lat` + `lon`. | | `lat`, `lon` | number | Matched to the nearest covered spot within 30 km; the response echoes `query.resolved_from` with the distance. | | `from` | date | Spot-local `YYYY-MM-DD`. Default today. | | `to` | date | Inclusive. Default `from` + 6. At most 7 days. | ```bash curl "https://kaicast.com/api/v1/conditions?spot=electric-beach&from=2026-10-06&to=2026-10-06" ``` ```json { "schema": "kaicast.conditions.v1", "spot": { "id": "electric-beach", "name": "Electric Beach", "region": { "id": "us-hi-oahu", "name": "Oʻahu", "country": "US", "admin1": "Hawaii" }, "lat": 21.355, "lon": -158.131, "timezone": "Pacific/Honolulu" }, "query": { "spot": "electric-beach", "lat": null, "lon": null, "from": "2026-10-06", "to": "2026-10-06", "resolved_from": null }, "range": { "from": "2026-10-06", "to": "2026-10-06", "available_from": "2026-10-05", "available_to": "2026-10-12" }, "generated_at": "2026-10-05T10:00:00-10:00", "next_update_after": "2026-10-05T11:15:00-10:00", "data_quality": { "visibility_source": "satellite", "freshness": "live" }, "tide_datum": "MLLW", "now": null, "periods": [ { "kind": "forecast", "start": "2026-10-06T08:00:00-10:00", "end": "2026-10-06T11:00:00-10:00", "rating": "great", "score": 72, "visibility_ft": 45, "visibility_m": 13.7, "swell_height_ft": 2.1, "swell_height_m": 0.6, "swell_period_s": 12, "swell_from_deg": 310, "wind_speed_kt": 8, "wind_gust_kt": 12, "wind_from_deg": 65, "tide_state": "rising", "tide_height_ft": 0.9, "water_temp_c": 27.4, "confidence": 0.82, "visibility_source": "satellite", "why": "Clean water and light trades; small northwest swell wraps in but stays under the shelf." } ], "tide_events": [{ "type": "high", "time": "2026-10-06T13:42:00-10:00", "height_ft": 1.8 }], "sources": ["ndbc:51201", "noaa-tides:1612340", "pacioos-ww3", "openweather"], "links": { "page": "https://kaicast.com/conditions/electric-beach", "app": "https://kaicast.com/spot/electric-beach", "docs": "https://kaicast.com/openapi.json" }, "attribution": "KaiCast (https://kaicast.com). …" } ``` `now` is the current-hour nowcast when today is in range. The period containing *now* is kept; fully elapsed periods are dropped. ## GET /api/v1/spots The spot directory — ids, names, regions, coordinates. No conditions. | Parameter | Notes | |---|---| | `region` | Region id, e.g. `us-hi-maui` | | `q` | Name search | | `lat`, `lon` | Sort by distance; each spot gets `distance_km` | ```bash curl "https://kaicast.com/api/v1/spots?region=us-hi-maui" ``` ## GET /api/v1/coverage Which regions are covered, whether each supports **decisions** (`decision_support: "full"`) or facts only (`"conditions_only"`), its spots and bounding box, and the activities with how directly each is modeled. ```bash curl "https://kaicast.com/api/v1/coverage" ``` ## GET /api/v1/recommendations Ranked picks for one activity, in one area, on one spot-local day. | Parameter | Type | Notes | |---|---|---| | `activity` | enum | **Required.** `snorkel`, `dive`, `freedive`, `spearfish`, `surf` | | `region` / `spot` / `lat`+`lon` | | One location. Picks never span regions. | | `radius_km` | number | Around `lat`/`lon`. Default 40, max 100. | | `date` | date | Up to 6 days ahead. Default today — or tomorrow once today's daylight windows have passed, flagged by `date_rolled_forward: true` and a `message`. An explicit `date` is never moved. | | `limit` | integer | Default 3, max 5. | ```bash curl "https://kaicast.com/api/v1/recommendations?region=us-hi-oahu&activity=snorkel&date=2026-10-06&limit=2" ``` ```json { "schema": "kaicast.recommendation.v1", "status": "ok", "activity": { "id": "snorkel", "label": "Snorkeling", "model": "native", "note": "Scored by the KaiCast condition model, which is calibrated for snorkeling." }, "date": "2026-10-06", "date_label": "Tomorrow", "coverage": { "status": "covered", "regions": ["us-hi-oahu"] }, "picks": [ { "rank": 1, "spot": { "id": "electric-beach", "name": "Electric Beach", "region": { "id": "us-hi-oahu", "name": "Oʻahu" }, "lat": 21.355, "lon": -158.131 }, "distance_km": null, "date": "2026-10-06", "score": 72, "rating": "great", "best_window": { "start": "2026-10-06T08:00:00-10:00", "end": "2026-10-06T11:00:00-10:00", "start_local": "08:00", "end_local": "11:00", "score": 72, "rating": "great" }, "why": { "summary": "Great: 45 ft visibility, 2.1 ft swell at 12s from the NW, 8 kt wind from the NE.", "factors": ["45 ft visibility", "2.1 ft swell at 12s from the NW", "8 kt wind from the NE", "rising tide"], "model_note": "Clean water and light trades; small northwest swell wraps in but stays under the shelf." }, "metrics": { "visibility_ft": 45, "swell_height_ft": 2.1, "swell_period_s": 12, "swell_from": "NW", "wind_speed_kt": 8, "wind_from": "NE", "water_temp_f": 81, "tide_state": "rising" }, "confidence": { "value": 0.74, "label": "high", "factors": { "model": 0.82, "visibility_source": 1, "freshness": 1, "lead_time": 0.95, "activity_fit": 1 } }, "caveats": [], "card": { "…": "see Spot card below" } } ], "cards": ["…"], "card_markdown": "**Electric Beach** · Oʻahu — Tomorrow\nGreat (72/100) · best 08:00–11:00\n…", "disclaimer": "KaiCast forecasts are model estimates, not a safety guarantee. …", "pricing": { "layer": "decision", "plan": "free_early_access" } } ``` **How picks are made.** Each spot's value for the day is its best *daylight* period (starting 06:00–17:59 local). Spots are ranked by that period's score; ties break on what matters most for the activity (calmer surface for snorkeling, clearer water for diving). Spots unsuited to the activity are excluded and listed in `considered.excluded` (e.g. deep boat dives aren't snorkel picks). ## GET /api/v1/best-day Which day in a window is best at a destination. | Parameter | Type | Notes | |---|---|---| | `activity` | enum | **Required.** | | `spot` / `region` / `lat`+`lon` | | One destination. A region or radius picks the best spot *per day*. | | `from`, `to` | date | Default today … +6. At most 7 days; a past `from` is clamped to today. | ```bash curl "https://kaicast.com/api/v1/best-day?spot=hanauma-bay&activity=snorkel" ``` The response has `best` (a full pick, plus `why.versus_next` — e.g. *"9 points ahead of the next-best option (2026-10-05, 72/100)"*), `runner_up`, and `days[]` with every day's best score, spot and window. ## Statuses — honest answers, not errors Decision endpoints return **200** with a `status` for anything that isn't a malformed request: | `status` | Meaning | What to do | |---|---|---| | `ok` | Picks are present. | Show them, with caveats. | | `no_data` | Covered, but no daylight forecast for that date (e.g. an explicit `date` of today, asked after dark). | Try another date. | | `not_covered` | Outside KaiCast coverage. `coverage.nearest_covered` names the closest covered spot and distance. | Tell the user KaiCast doesn't cover it yet. | | `conditions_only` | Region has facts but no calibrated visibility model. | Use `/api/v1/conditions`. | | `activity_not_modeled` | E.g. `surf` — KaiCast doesn't model breaks. `facts_hint` points to the conditions endpoint. | Use swell/wind facts directly. | ## Activities | Activity | Model | Meaning | |---|---|---| | `snorkel` | native | The condition score is calibrated for snorkeling. | | `dive` | proxy | Same score; caveat added; confidence × 0.85. Conditions at depth aren't scored. | | `freedive` | proxy | Same score; visibility breaks ties. | | `spearfish` | proxy | Same score. **Spots in Hawaiʻi DAR areas closed to spearfishing are excluded** (`considered.excluded`, reason `closed_to_spearfishing`); bag limits and seasons are not modeled — a caveat says so. | | `surf` | unsupported | Not ranked. Use the facts. | ## Confidence and caveats `confidence.value` (0–1) is the product of its `factors` — the model's own visibility confidence, visibility source (satellite 1.0, estimated 0.8), input freshness, lead time (−5%/day), and activity fit (native 1.0, proxy 0.85). `label` is `high` ≥ 0.65, `medium` ≥ 0.4, else `low`. `caveats[]` items have a stable `code` and a human `message`: `visibility_estimated`, `stale_inputs`, `long_lead_time`, `marginal_conditions`, `location_snapped`, `proxy_activity_model`, `verify_fishing_rules`, `regulated_area`, `regulations_not_checked`. ## Spot card Every decision pick carries `card`, and the response carries `cards[]` and `card_markdown`. Render it as-is so users see the same card they'd see in the KaiCast app: ```json { "version": 1, "spot_id": "electric-beach", "spot_name": "Electric Beach", "region_name": "Oʻahu", "date": "2026-10-06", "date_label": "Tomorrow", "activity": "snorkel", "tier": "great", "tier_label": "Great", "tier_color": "#27D667", "score": 72, "headline": "Great: 45 ft visibility, 2.1 ft swell at 12s from the NW, 8 kt wind from the NE.", "best_window": { "start_local": "08:00", "end_local": "11:00" }, "metrics": { "visibility": "45 ft", "swell": "2.1 ft @ 12s", "wind": "8 kt NE", "water_temp": "81°F" }, "image_url": "https://kaicast.com/generateSpotShareImage?spot=electric-beach&date=2026-10-06", "links": { "share": "https://kaicast.com/s/electric-beach/2026-10-06?utm_source=agent&utm_medium=api&utm_campaign=spot_card_snorkel", "app": "kaicast://spot/electric-beach/2026-10-06", "forecast": "https://kaicast.com/spot/electric-beach?utm_source=agent&utm_medium=api&utm_campaign=spot_card_snorkel" }, "cta": { "label": "Full forecast in KaiCast", "text": "Hour-by-hour windows, 7-day outlook and live hazard alerts in the KaiCast app.", "url": "https://kaicast.com/spot/electric-beach?…" }, "attribution": "Conditions by KaiCast", "credits_text": "Weather data provided by OpenWeather · Buoy observations: NOAA NDBC · Data provided by PacIOOS (www.pacioos.org)" } ``` `credits_text` is the licence-required upstream credit line — render it with the card. Each pick also carries `data_credits` (structured), and the response carries their union. `image_url` is a 1200×630 PNG of the same card, for surfaces that show images. `tier_color` values: excellent `#09A1FB`, great `#27D667`, good `#FFD321`, fair `#FF9D25`, no-go `#F73726`. ## Errors Malformed requests return [RFC 9457](https://www.rfc-editor.org/rfc/rfc9457) `application/problem+json` with a stable `code`: ```json { "type": "https://kaicast.com/openapi.json#problem-unknown_activity", "title": "unknown activity", "status": 400, "detail": "Unknown activity \"kitesurf\". Use one of snorkel, dive, freedive, spearfish, surf.", "code": "unknown_activity" } ``` | Status | Codes | |---|---| | 400 | `missing_location`, `ambiguous_location`, `invalid_coordinates`, `invalid_date`, `invalid_range`, `range_too_long`, `missing_activity`, `unknown_activity`, `invalid_limit`, `invalid_radius`, `one_spot_per_request` | | 401 | `invalid_key`, `key_revoked`, `key_required` (decisions, once metering starts) | | 402 | `free_allotment_exhausted` — body includes `upgrade_url` | | 404 | `unknown_spot`, `no_spot_nearby` (conditions with lat/lon) | | 422 | `outside_horizon` — dates beyond the 7-day forecast | | 429 | `rate_limited` — honor `Retry-After` | | 503 | `data_unavailable` / `unavailable` — retry after `Retry-After` | ## Limits KaiCast serves answers **point by point** — one spot, one region, or one radius per call, for at most 7 days. There is no bulk export. - **Facts:** generous per-caller hourly and daily caps, and a cap on how many *distinct* spots a caller can pull per hour and per day. Re-asking about a spot you already asked about today never counts against the distinct-spot cap. Requests from user-triggered AI fetchers (ChatGPT-User, Claude-User, Perplexity-User…) get higher caps because they share IPs. - **Decisions:** at most 20 spots considered, 5 picks returned and 7 days per call; short bursts are throttled per IP. Data refreshes hourly (`next_update_after`); polling faster than that returns the same answer. --- # MCP server KaiCast runs a remote [Model Context Protocol](https://modelcontextprotocol.io) server: ```text https://kaicast.com/mcp ``` - **Transport:** Streamable HTTP, stateless, `POST` only. Supports MCP **2026-07-28** clients and **2025-era** clients (2025-06-18, 2025-11-25) from the same URL. - **Auth:** none required. (Keys arrive with metering; the `Authorization: Bearer kc_live_…` / `X-KaiCast-Key` headers are already accepted and ignored.) - **Discovery:** server card at [`/.well-known/mcp/server-card.json`](https://kaicast.com/.well-known/mcp/server-card.json); registry manifest at [`/server.json`](https://kaicast.com/server.json). - **Tools:** read-only, idempotent, with JSON output schemas and `structuredContent`. - **Spot cards:** decision tools render a branded KaiCast card in hosts that support [MCP Apps](https://github.com/modelcontextprotocol/ext-apps), and fall back to Markdown everywhere else. It is a thin layer over the [REST API](https://kaicast.com/developers/rest): the same services, the same numbers, the same logging. Anything a tool returns, the matching endpoint returns too. ## Tools | Tool | Layer | Does | REST twin | |---|---|---|---| | `get_conditions` | Fact · free | Conditions for one spot (or nearest to lat/lon) over up to 7 days | `GET /api/v1/conditions` | | `list_coverage` | Fact · free | Covered regions, decision support, spots, activities | `GET /api/v1/coverage` | | `recommend_spot` | Decision · free in early access | Ranked picks (≤ 5) for an activity in one area on one day, with why, confidence, caveats and a spot card | `GET /api/v1/recommendations` | | `best_day` | Decision · free in early access | Best day in a window (≤ 7 days) at a spot, region or radius, versus the runner-up, with a spot card | `GET /api/v1/best-day` | ### get_conditions ```json { "spot": "electric-beach", "from": "2026-10-06", "to": "2026-10-08" } ``` `spot` **or** `lat` + `lon` (snapped to the nearest covered spot within 30 km). `from`/`to` are spot-local dates, at most 7 days. Returns `kaicast.conditions.v1` — see [REST API](https://kaicast.com/developers/rest#get-apiv1conditions). ### list_coverage ```json { "region": "us-hi-maui" } ``` All arguments optional. Call this first when you don't know whether a place is covered. ### recommend_spot ```json { "region": "us-hi-oahu", "activity": "snorkel", "date": "2026-10-06", "limit": 3 } ``` | Argument | Required | Notes | |---|---|---| | `activity` | yes | `snorkel` · `dive` · `freedive` · `spearfish` · `surf` | | `region` / `spot` / `lat`+`lon` | one of | Location is always a parameter | | `radius_km` | | Around lat/lon, default 40, max 100 | | `date` | | Spot-local, up to 6 days ahead. Default today, or tomorrow once today's daylight has passed (`date_rolled_forward: true`) | | `limit` | | 1–5, default 3 | ### best_day ```json { "spot": "hanauma-bay", "activity": "snorkel", "from": "2026-10-06", "to": "2026-10-12" } ``` Same location arguments; `from`/`to` default to today … +6. ### What a decision result looks like The text content is a ready-to-show Markdown card; `structuredContent` is the full `kaicast.recommendation.v1` / `kaicast.best_day.v1` object (schemas in [`/openapi.json`](https://kaicast.com/openapi.json)): ```markdown **Electric Beach** · Oʻahu — Tomorrow Great (72/100) · best 08:00–11:00 Vis 45 ft · Swell 2.1 ft @ 12s · Wind 8 kt NE · Water 81°F Great: 45 ft visibility, 2.1 ft swell at 12s from the NW, 8 kt wind from the NE. [Full forecast in KaiCast →](https://kaicast.com/spot/electric-beach?utm_source=agent&utm_medium=mcp&utm_campaign=spot_card_snorkel) ``` When a place isn't covered, or the activity isn't modeled (surf), the tool returns a normal result with `status: "not_covered"`, `"conditions_only"` or `"activity_not_modeled"` and a message to relay — not an error. Bad arguments and quota denials are tool errors (`isError: true`) with a stable `structuredContent.error.code`, and an `upgrade_url` when a plan limit is reached. ## Connect a client ### Claude (claude.ai, Claude Desktop) Add KaiCast as a **custom connector**: open **Settings → Connectors**, choose to add a custom connector, name it *KaiCast* and paste `https://kaicast.com/mcp`. No sign-in is needed. Claude connects from Anthropic's cloud, so the URL must be public — it is. See Anthropic's guide: [Custom connectors](https://support.claude.com/en/articles/11175166). Claude renders KaiCast spot cards inline for `recommend_spot` and `best_day`. ### Claude Code ```bash claude mcp add --transport http kaicast https://kaicast.com/mcp ``` With a key, or shared with your team via the project's `.mcp.json`: ```bash claude mcp add --transport http kaicast https://kaicast.com/mcp --header "Authorization: Bearer kc_live_…" ``` ```json { "mcpServers": { "kaicast": { "type": "http", "url": "https://kaicast.com/mcp" } } } ``` ### ChatGPT In ChatGPT on the web, enable **Developer mode** (Settings → Apps → Advanced), then create an app with the URL `https://kaicast.com/mcp` and **No authentication**. Available on plans that support developer mode. See OpenAI's [developer mode guide](https://developers.openai.com/api/docs/guides/developer-mode). ### Cursor `~/.cursor/mcp.json` (or `.cursor/mcp.json` in a project): ```json { "mcpServers": { "kaicast": { "url": "https://kaicast.com/mcp" } } } ``` ### VS Code (GitHub Copilot agent mode) `.vscode/mcp.json`: ```json { "servers": { "kaicast": { "type": "http", "url": "https://kaicast.com/mcp" } } } ``` ### Other clients Any client that supports remote MCP servers over Streamable HTTP works — point it at `https://kaicast.com/mcp`. Clients that only speak stdio can bridge with a remote-proxy such as [`mcp-remote`](https://www.npmjs.com/package/mcp-remote): ```json { "mcpServers": { "kaicast": { "command": "npx", "args": ["-y", "mcp-remote", "https://kaicast.com/mcp"] } } } ``` ## Use it from your own agent ### Anthropic Messages API (MCP connector) Claude calls KaiCast directly — no tool plumbing on your side: ```bash curl https://api.anthropic.com/v1/messages \ -H "x-api-key: $ANTHROPIC_API_KEY" \ -H "anthropic-version: 2023-06-01" \ -H "anthropic-beta: mcp-client-2025-11-20" \ -H "content-type: application/json" \ -d '{ "model": "claude-opus-5-5", "max_tokens": 1500, "mcp_servers": [{ "type": "url", "url": "https://kaicast.com/mcp", "name": "kaicast" }], "tools": [{ "type": "mcp_toolset", "mcp_server_name": "kaicast" }], "messages": [{ "role": "user", "content": "Best day this week to snorkel Hanauma Bay?" }] }' ``` ### OpenAI Responses API ```python from openai import OpenAI client = OpenAI() resp = client.responses.create( model="gpt-5", tools=[{ "type": "mcp", "server_label": "kaicast", "server_url": "https://kaicast.com/mcp", "require_approval": "never", }], input="Where should I dive on Maui tomorrow?", ) print(resp.output_text) ``` All KaiCast tools are read-only, so `require_approval: "never"` is safe. ### MCP TypeScript SDK (any agent framework) ```ts import { Client, StreamableHTTPClientTransport } from '@modelcontextprotocol/client'; const client = new Client({ name: 'my-trip-planner', version: '1.0.0' }); await client.connect(new StreamableHTTPClientTransport(new URL('https://kaicast.com/mcp'))); const result = await client.callTool({ name: 'recommend_spot', arguments: { region: 'us-hi-oahu', activity: 'snorkel' }, }); console.log(result.structuredContent.picks[0].card); ``` ### Without MCP Every tool has a REST twin — use [`/openapi.json`](https://kaicast.com/openapi.json) as the tool definition in any framework that imports OpenAPI (LangChain, LlamaIndex, Semantic Kernel, custom function-calling). ## Raw protocol For debugging, a 2026-07-28 call is a single POST: ```bash curl https://kaicast.com/mcp \ -H 'content-type: application/json' \ -H 'accept: application/json, text/event-stream' \ -H 'MCP-Protocol-Version: 2026-07-28' \ -H 'Mcp-Method: tools/call' \ -H 'Mcp-Name: list_coverage' \ -d '{"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"name":"list_coverage","arguments":{}, "_meta":{"io.modelcontextprotocol/protocolVersion":"2026-07-28", "io.modelcontextprotocol/clientCapabilities":{}, "io.modelcontextprotocol/clientInfo":{"name":"curl","version":"1"}}}}' ``` 2025-era clients send `initialize` first, as usual; the server answers each request statelessly. `GET /mcp` returns 405 (there is no server-push stream). ## Showing results - Relay `caveats` — they carry the safety-relevant context (estimated visibility, proxy models, unchecked fishing rules). - Keep the card's **Full forecast in KaiCast** link, and cite KaiCast: every result's `structuredContent.source` holds `name`, `url` and a ready-made `citation`, and the text content ends with a `Source: KaiCast — https://kaicast.com` line. - Show the upstream **data credits**. Conditions and decisions carry `data_credits` (provider, required credit text, link, licence), and each spot card has a one-line `credits_text`. Several sources (OpenWeather, Open-Meteo) require that credit wherever their data is shown. - Don't present a forecast as a guarantee; each response includes a `disclaimer` you can show verbatim. --- # Coverage KaiCast forecasts named **spots** — dive and snorkel sites with a calibrated location on the water — grouped into **regions** with global ids (`{country}-{admin1}-{area}`, e.g. `us-hi-oahu`). Location is always a parameter, and coverage grows region by region; anywhere not listed is **not covered yet**, and every endpoint says so explicitly instead of guessing. The live list is always at [`GET /api/v1/coverage`](https://kaicast.com/api/v1/coverage) (or the `list_coverage` MCP tool). The map and table below are rendered from it. > Live list: https://kaicast.com/api/v1/coverage (JSON). ## Regions today | Region | Id | Decisions | Notes | |---|---|---|---| | Oʻahu | `us-hi-oahu` | Full | Largest spot set; south, west and north shores | | Maui | `us-hi-maui` | Full | Includes Molokini | | Kauaʻi | `us-hi-kauai` | Full | | | Hawaiʻi Island | `us-hi-hawaii` | Full | Kona and Kohala coasts | | Molokaʻi | `us-hi-molokai` | Full | One spot | ## Decision support levels | Level | Meaning | |---|---| | **Full** | Calibrated visibility model plus the composite condition score. `recommend_spot` / `best_day` rank here. | | **Conditions only** | Wind, swell, tide and temperature are published, but there's no credible clarity model yet, so decision tools decline with `status: "conditions_only"` and point to the facts. Any newly published region starts here. | | **Not covered** | No public data. Decision tools return `status: "not_covered"` with the nearest covered spot and its distance; `get_conditions` returns `no_spot_nearby`. | ## Data quality, per answer Quality isn't uniform even inside a covered region, so every answer says what it's built on: - **`visibility_source`** — `satellite` when a recent satellite water-clarity reading feeds the estimate; `heuristic` when visibility is estimated from wind, swell, rain and tide alone (satellite ocean color is often clouded out). Decisions add a `visibility_estimated` caveat and lower confidence for heuristic estimates. - **`freshness`** — `live`, `recent`, `aging`, `stale` or `none`, for the model's inputs. Stale inputs add a `stale_inputs` caveat. - **`confidence`** — per period (0–1), from the model. - **Calibration** — visibility predictions are corrected nightly against divers' logged observations (bias per spot and condition bucket). Spots with more logged dives are better calibrated. ## Inputs NOAA NDBC buoys and CO-OPS tides, PacIOOS ROMS and WaveWatch III ocean models, Open-Meteo Marine, OpenWeather, NOAA CoastWatch satellite ocean color (Kd490, chlorophyll), USGS stream gauges and Hawaiʻi DOH advisories for runoff, CUDEM bathymetry for exposure. Each conditions response lists the `sources` that fed it. ## Forecast horizon 7 days, in 3-hour periods, refreshed hourly. No historical conditions are served. ## Requesting a region Building for somewhere KaiCast doesn't cover yet? Tell us where and what you need at [dawson@kaicast.com](mailto:dawson@kaicast.com) — demand from agent builders shapes where coverage goes next. --- # Pricing > **Early access.** Everything below is free today. Paid tiers are coming; we'll give registered developers notice before anything changes. | | Facts | Decisions | |---|---|---| | **What** | Conditions per spot: visibility, swell, wind, tide, water temperature, condition score, data quality | Ranked spot picks, best day in a window, confidence + caveats, branded spot card | | **Endpoints / tools** | `/api/v1/conditions`, `/api/v1/spots`, `/api/v1/coverage` · `get_conditions`, `list_coverage` | `/api/v1/recommendations`, `/api/v1/best-day` · `recommend_spot`, `best_day` | | **Price** | **Free**, no key | **Free during early access** | | **Key** | Not needed | Optional now; will be required when metering starts | | **Limits** | Fair-use caps per caller; point-by-point only | Per-call bounds (≤ 20 spots, ≤ 5 picks, ≤ 7 days); burst throttling | ## Coming - **Developer tier** — a monthly allotment of free decision calls with an API key. - **Paid tiers** — higher decision volumes, priority support, and commercial-use terms for products that resell or embed KaiCast decisions. - **Enterprise / partners** — custom regions, SLAs, white-label cards. Facts stay free. They exist so any agent can answer "what's the water like?" with real numbers. ## Keys Keys will look like `kc_live_…` (production) and `kc_test_…` (testing) and will work for both the REST API and the MCP server. Send one as `Authorization: Bearer ` or `X-KaiCast-Key: `; never in a URL. Keys aren't issued yet — nothing needs one today. See [API keys](https://kaicast.com/developers/keys) for what changes when they arrive. To talk volume, partnerships or a region you need, email [dawson@kaicast.com](mailto:dawson@kaicast.com). ## Fair use - Serve answers to people's questions; don't mirror or resell the dataset. There is no bulk endpoint by design. - Keep attribution — cite KaiCast from each response's `source` object — and the spot card's link to the full forecast. - Forecasts are estimates, not a safety guarantee — don't present them as one. --- # API keys **You don't need a key today.** Facts are free and keyless, and decisions are free during early access. Just call the [REST API](https://kaicast.com/developers/rest) or connect the [MCP server](https://kaicast.com/developers/mcp). ## When keys arrive Keys come with metering for the decision layer. When they do: - One key will work for **both** the REST API and the MCP server. - Send it as `Authorization: Bearer kc_live_…` or `X-KaiCast-Key: kc_live_…` — never in a URL. `kc_test_…` keys will be for testing and never billed. - Facts stay free and keyless. - Registered developers get notice before anything changes, and sign-up will be plain HTTP so an agent can complete it with one human step (reading an emailed code). Both endpoints already accept and ignore those headers, so you can wire the header in now without breaking anything. ## Get notified Email [dawson@kaicast.com](mailto:dawson@kaicast.com) with what you're building. Volume, partnerships and new regions go to the same address.