Skip to main content
Glama
steph76k

surf-mcp-server

by steph76k

Surf MCP Server

Ein MCP Server fuer Surfspot-Daten, Spot-Suche, Marine Forecasts und Tide-Daten. Die Spot-Daten liegen als JSON-Dateien unter src/data/. Forecasts kommen ueber Open-Meteo Marine, Tide-Daten ueber WorldTides.

Der Server laeuft lokal per Python oder als Container in Kubernetes ueber das mitgelieferte Helm Chart.

Architektur

flowchart LR
    Client[MCP Client] -->|streamable-http| Server[Surf MCP Server]
    Server --> Tools[MCP Tools]
    Tools --> Spots[src/data/*.json]
    Tools --> Forecast[Forecast Orchestrierung]
    Forecast --> OpenMeteo[Open-Meteo Marine API]
    Forecast --> WorldTides[WorldTides API]
    WorldTides --> Secret[WORLDTIDES_API_KEY]

Related MCP server: Surf Forecast MCP

Voraussetzungen

  • Python >=3.10

  • Zugriff auf dieses Repo

  • Installation der Python-Abhaengigkeiten aus pyproject.toml

  • Optional: uv

  • Fuer Tide-Daten: WorldTides API Key als WORLDTIDES_API_KEY

Abhaengigkeiten:

  • mcp[cli]

  • requests

Lokales Setup

Vom Repo-Root aus:

cd surf-mcp-server
python -m venv .venv
source .venv/bin/activate
python -m pip install -U pip
python -m pip install -e .

Alternativ mit uv:

cd surf-mcp-server
uv sync

Konfiguration

Open-Meteo braucht keinen API Key. Fuer WorldTides muss ein API Key gesetzt sein, wenn get_tides oder get_conditions genutzt werden:

export WORLDTIDES_API_KEY="..."

Server starten

python src/server.py

Der Server startet mit streamable-http auf 0.0.0.0:8000:

mcp.run(transport="streamable-http")

Wenn du ihn auf einem anderen System testest, reicht normalerweise:

  1. Repo kopieren oder klonen

  2. Python-Umgebung erstellen

  3. Dependencies installieren

  4. WORLDTIDES_API_KEY setzen, falls Tide-Daten genutzt werden

  5. python src/server.py aus dem Repo-Root ausfuehren

Projektstruktur

surf-mcp-server/
|-- .github/workflows/build.yml   # GitHub Actions Build Pipeline
|-- charts/surf-mcp/              # Helm Chart
|-- src/
|   |-- server.py                  # MCP Tools und Server-Start
|   |-- spots.py                   # Laden der Spot-Dateien
|   |-- forecast.py                # Forecast- und Tide-Orchestrierung
|   |-- data/                      # Surfspot JSON-Dateien
|   `-- providers/
|       |-- open_meteo.py          # Open-Meteo Marine API
|       |-- worldtides.py          # WorldTides API
|       `-- stormglass.py          # Vorbereiteter Provider
|-- tests/                         # Unit Tests
|-- Dockerfile
|-- pyproject.toml
|-- uv.lock
`-- README.md

Forecast Flow

sequenceDiagram
    participant User as MCP Client
    participant Server as Surf MCP Server
    participant Data as Spot JSON
    participant OM as Open-Meteo
    participant WT as WorldTides

    User->>Server: get_conditions(spot_id)
    Server->>Data: get_spot(spot_id)
    Data-->>Server: coordinates
    Server->>OM: wave_height, wave_period, wave_direction
    OM-->>Server: hourly marine forecast
    Server->>WT: tide heights with WORLDTIDES_API_KEY
    WT-->>Server: tide heights
    Server-->>User: combined forecast + tide_state

Verfuegbare MCP Tools

list_regions()

Gibt alle verfuegbaren Regionen zurueck.

Beispiel-Resultat:

["Airport Reefs", "Lakey", "Uluwatu", "Ungasan", "West Sumbawa"]

list_spots(region: string | null = null)

Listet Spots. Optional kann nach Region gefiltert werden.

Beispiel:

{
  "region": "Uluwatu"
}

Resultat enthaelt:

{
  "id": "uluwatu_the_peak",
  "name": "Uluwatu - The Peak",
  "country": "Indonesia",
  "island": "Bali",
  "region": "Uluwatu"
}

get_spot_info(spot_id: string)

Gibt alle Details fuer einen Spot zurueck.

Beispiel:

{
  "spot_id": "lakey_peak"
}

find_spots(...)

Findet Spots anhand mehrerer Kriterien.

Parameter:

  • skill: z.B. intermediate, advanced, expert

  • country: z.B. Indonesia

  • island: z.B. Bali oder Sumbawa

  • region: z.B. Uluwatu, Lakey, West Sumbawa

  • tide: z.B. low, mid, high

  • wind: z.B. SE, N, NW

  • swell_direction: z.B. S, SW, W

  • swell_height_ft: z.B. 4

Beispiel:

{
  "skill": "intermediate",
  "island": "Bali",
  "region": "Uluwatu",
  "tide": "mid",
  "wind": "SE",
  "swell_direction": "SW",
  "swell_height_ft": 4
}

count_spots()

Gibt die Anzahl der Spot-Dateien zurueck.

search_spots(query: string)

Sucht Spots nach Name.

Beispiel:

{
  "query": "uluwatu"
}

get_forecast(spot_id: string)

Gibt den normalisierten Marine Forecast fuer einen Spot zurueck.

Die Daten kommen aus Open-Meteo Marine:

  • wave_height_ft

  • wave_period_s

  • swell_direction

  • swell_direction_deg

Beispiel-Resultat:

[
  {
    "time": "2026-06-11T00:00",
    "wave_height_ft": 4.6,
    "wave_period_s": 12.1,
    "swell_direction": "SW",
    "swell_direction_deg": 225
  }
]

get_tides(spot_id: string)

Gibt Tide-Daten fuer einen Spot zurueck.

Die Daten kommen aus WorldTides. Dafuer muss WORLDTIDES_API_KEY gesetzt sein.

Beispiel-Resultat:

[
  {
    "time": "2026-06-11T00:00+0000",
    "tide_height_ft": 3.2,
    "tide_state": "mid"
  }
]

get_conditions(spot_id: string)

Kombiniert Marine Forecast und Tide-Daten. Pro Forecast-Zeitpunkt wird die naechste Tide gesucht und als tide_height_ft und tide_state ergaenzt.

Beispiel-Resultat:

[
  {
    "time": "2026-06-11T00:00",
    "wave_height_ft": 4.6,
    "wave_period_s": 12.1,
    "swell_direction": "SW",
    "swell_direction_deg": 225,
    "tide_height_ft": 3.2,
    "tide_state": "mid"
  }
]

Docker

Image lokal bauen:

docker build -t surf-mcp-server .

Container starten:

docker run --rm \
  -p 8000:8000 \
  -e WORLDTIDES_API_KEY="$WORLDTIDES_API_KEY" \
  surf-mcp-server

GitHub Build Pipeline

Die Pipeline liegt unter .github/workflows/build.yml.

flowchart TD
    PushMain[Push auf main] --> Checkout[Checkout]
    Checkout --> Login[Login bei GHCR]
    Login --> Metadata[Docker Metadata]
    Metadata --> Build[Docker Build]
    Build --> PushGhcr[Push nach GHCR]
    PushGhcr --> Tags[latest + Git SHA]

Bei jedem Push auf main wird ein Container Image gebaut und nach GHCR gepusht:

  • ghcr.io/steph76k/surf-mcp-server:latest

  • ghcr.io/steph76k/surf-mcp-server:<git-sha>

Die Pipeline nutzt GITHUB_TOKEN mit packages: write.

Kubernetes Deployment mit Helm

Das Helm Chart liegt unter charts/surf-mcp/.

flowchart LR
    Helm[helm upgrade --install] --> Deployment[Deployment]
    Helm --> Service[Service]
    Helm --> Ingress[Ingress]
    Secret[Kubernetes Secret surf-mcp] --> Deployment
    Deployment --> Pod[surf-mcp Pod]
    Pod --> GHCR[GHCR Image]
    Pod --> WorldTides[WorldTides API]

WorldTides Secret erstellen

Vor dem Helm Deployment muss der WorldTides API Key als Kubernetes Secret angelegt werden:

kubectl create secret generic surf-mcp \
  --from-literal=worldtides-api-key="$WORLDTIDES_API_KEY"

Das Chart liest den Key standardmaessig aus diesem Secret:

env:
  - name: WORLDTIDES_API_KEY
    valueFrom:
      secretKeyRef:
        name: surf-mcp
        key: worldtides-api-key

Installieren oder aktualisieren

helm upgrade --install surf-mcp charts/surf-mcp \
  --namespace surf-mcp \
  --create-namespace

Standardwerte aus charts/surf-mcp/values.yaml:

  • Image: ghcr.io/steph76k/surf-mcp-server:latest

  • Service: ClusterIP auf Port 8000

  • Ingress: Traefik

  • Host: surf-mcp.rke2-cluster1.berger.ph

  • Secret: surf-mcp mit Key worldtides-api-key

Image Tag ueberschreiben

helm upgrade --install surf-mcp charts/surf-mcp \
  --namespace surf-mcp \
  --create-namespace \
  --set image.tag="<git-sha>"

Manifest lokal pruefen

helm template surf-mcp charts/surf-mcp

Spot JSON Schema

Jeder Spot ist eine eigene JSON-Datei in src/data/. Der Dateiname sollte zur spot_id passen.

Beispiel:

{
  "spot_id": "uluwatu_the_peak",
  "name": "Uluwatu - The Peak",
  "country": "Indonesia",
  "island": "Bali",
  "region": "Uluwatu",
  "coordinates": {
    "lat": -8.816633,
    "lon": 115.08625
  },
  "conditions": {
    "swell": {
      "directions": ["S", "SW"],
      "min_ft": 1,
      "max_ft": 6
    },
    "wind": {
      "offshore": ["SE"]
    },
    "tide": ["mid", "high"]
  },
  "wave": {
    "direction": "left",
    "type": "reef"
  },
  "ratings": {
    "crowd": 10,
    "localism": 9,
    "risk": 6,
    "fun": 9
  },
  "surfer_level": ["intermediate", "advanced"],
  "notes": "At high tide, aim south of the cave when coming in.",
  "description": "Short description of the spot.",
  "hazards": [
    "sharp coral reef",
    "strong currents"
  ]
}

Wichtige Daten-Konventionen

surfer_level

surfer_level ist immer ein Array, damit die Suche einzelne Levels sauber matchen kann:

{
  "surfer_level": ["intermediate", "advanced"]
}

Nicht mehr verwenden:

{
  "surfer_level": "intermediate / advanced"
}

Empfohlene Werte:

  • beginner

  • intermediate

  • advanced

  • expert

Location Felder

country, island und region sind getrennte Felder:

{
  "country": "Indonesia",
  "island": "Bali",
  "region": "Uluwatu"
}

Nicht mehr verwenden:

{
  "region": "Bali - Uluwatu Area"
}

Aktuelle Regionen

  • Airport Reefs

  • Lakey

  • Uluwatu

  • Ungasan

  • West Sumbawa

Neue Spots hinzufuegen

  1. Neue Datei in src/data/ erstellen, z.B. my_spot.json

  2. spot_id passend zum Dateinamen setzen, z.B. my_spot

  3. Schema wie oben verwenden

  4. JSON validieren:

jq empty src/data/my_spot.json

Alle Spot-Dateien validieren:

for f in src/data/*.json; do jq empty "$f" || exit 1; done

Tests und Checks

python -m compileall src
python -m unittest discover -s tests
helm template surf-mcp charts/surf-mcp

Hinweise

  • Spot-Daten werden bei jedem Tool-Aufruf direkt aus src/data/*.json gelesen.

  • get_forecast nutzt Open-Meteo Marine und braucht keinen API Key.

  • get_tides und get_conditions nutzen WorldTides und brauchen WORLDTIDES_API_KEY.

  • src/providers/stormglass.py ist als optionaler Provider vorbereitet.

  • Das Helm Chart nutzt standardmaessig die im Docker Image enthaltenen Spot-Daten.

  • Ein externer ConfigMap-Mount fuer Spot-Daten kann ueber spotData.configMap.enabled=true aktiviert werden.

Related MCP Connectors

Related MCP Servers

  • F
    license
    B
    quality
    D
    maintenance
    A minimal Python MCP server that provides surf forecasts for a single Surfline spot. It fetches both conditions and rating data through the get_surf_forecast tool.
    1
    -
  • F
    license
    Not graded
    quality
    C
    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.
    20
    -
  • A
    license
    A
    quality
    D
    maintenance
    MCP server for wind forecasts, live measurements, wave forecasts, and tides, enabling kite and surf spot checks via AI assistants.
    5
    6 npm
    MIT