# 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.
