Open-source sailing planner for the French coastline (Atlantic + Mediterranean), powered by an MCP server and a public web app. The user describes their trip in natural language to an MCP client (e.g. Claude Desktop, or any other). The client orchestrates marine data fetches, estimates passage time and complexity via MCP tools, and renders a precomputed plan in the standalone web app.
Primary scope: French Atlantic coast (Manche + Atlantique) and Mediterranean. Both regions are first-class.
The web app is strictly standalone — it never proposes "talk to an assistant". Conversational entry happens client-side.
packages/web/— React 19 + TypeScript + Vite, deployed to Cloudflare Pages on ohmywind.fr (previewdev→ dev.ohmywind.fr)packages/data-adapters/— Python lib, pure domain logic (marine data adapters, polars, routing, complexity)packages/mcp-core/— Python lib, FastMCP server definition (cloud-agnostic, redeployable anywhere)packages/hf-space/— Wrapper Hugging Face Spaces : app Starlette + uvicorn en Docker (aucun Gradio), porte la landing, les endpoints REST, CORS et le rate-limit. Servi aujourd'hui surqdonnars-openwind-mcp.hf.space; le passage à mcp.ohmywind.fr (Worker Cloudflare) est la phase B du plan de rebrand.
Plan d'exécution détaillé : plan/ (local, non-tracké).
Deux environnements auto-déployés. Un push détermine où ça ship :
main= PROD. Un merge/push surmainexpédie la prod : build Cloudflare surohmywind.fretsync-hf-space.ymlresynchronise le Space prodqdonnars/openwind-mcp. Ne jamais pousser directement surmainsans intention de shipper ; toujours passer par une PR.dev= PREVIEW. Un push surdevdéploiedev.ohmywind.fr(preview Cloudflare) et synchronise le Space devqdonnars/openwind-mcp-dev. C'est le bac à sable d'intégration, isolé de la prod. Pour tester un changement live avant prod, le faire passer pardevd'abord.- Le workflow
.github/workflows/sync-hf-space.ymlmappe la branche → Space (main→ prod,dev→ dev ; overridable via vars repoHF_SPACE_ID/HF_SPACE_ID_DEV). Il se déclenche sur push touchanthf-space/,mcp-core/,data-adapters/ou le workflow lui-même. - Toujours bosser sur une branche feature → PR vers
main.delete_branch_on_mergeest activé (les branches mergées sont supprimées auto côté GitHub). - Web : l'URL backend est variabilisée via
VITE_API_BASE, source uniquepackages/web/src/api/config.ts. Ne JAMAIS re-hardcoder l'URL du Space ailleurs. Cloudflare poseVITE_API_BASEpar environnement (Production → Space prod, Preview → Space dev).
The MCP server core (mcp-core) MUST stay deployment-agnostic.
The hf-space/ package is a thin wrapper for HF Spaces.
We could re-deploy on Fly.io, Modal, or self-hosted by writing a different wrapper without touching mcp-core or data-adapters.
→ Concrètement : aucun import de gradio ou de huggingface_hub dans mcp-core ou data-adapters. Si tu en vois un, c'est un bug.
- Wind speeds always in knots, never km/h
- Wind directions in TWD/TWA/AWA/AWS conventions, document explicitly when used
- True heading vs magnetic heading — V1 uses true throughout
- AROME is the default high-resolution model (1.3 km, covers Atlantic French coast + Mediterranean, captures thermal and local winds). When
modelsnot specified, return AROME first / use AROME for passage estimation. - Tides and currents: surfaced from Open-Meteo Marine (SMOC, 8 km global) and reported only when materially relevant per leg (currents ≥ 0.3 kt, tidal range ≥ 0.5 m). High-precision MARC atlases (250 m) for critical Atlantic passes are a later enhancement.
- Mediterranean specifics:
- Tides typically < 40 cm and currents typically < 0.3 kt → most legs will not surface tide/current data, by design
- Local wind names: mistral (NW), tramontane (NW), sirocco (SE), marin (SE-S), levante (E), libeccio (SW)
- Atlantic specifics:
- Tidal range can exceed 10 m (Manche), tidal currents > 5 kt in narrow passes (Goulet de Brest, Raz de Sein, Raz Blanchard) — tide and current data are first-class
- Open-Meteo SMOC at 8 km is sufficient for open-water planning but insufficient for narrow passes — flag this in user-facing copy
- Open-Meteo Forecast API — wind, multi-model (AROME, ICON, GFS, ECMWF), keyless
- Open-Meteo Marine API — wave height, period, direction, wind wave vs swell, keyless
- (V2 candidates) Météo-France API for official BMS bulletins
- Python: ruff for lint + format, pytest for tests, uv for env management
- TypeScript: ESLint flat config (existing)
- Commits: conventional commits (
feat:,fix:,refacto:,docs:,chore:,test:) - All adapters implement
MarineDataAdapterProtocol fromadapters/base.py - Async everywhere (httpx, asyncio.gather)
- ❌ Don't add heavy backend deps to
packages/web/— must stay GH Pages-deployable - ❌ Don't ship API keys in the bundle (Open-Meteo is keyless, keep it that way)
- ❌ Don't break
mainbranch deployment during refactos — work on branches - ❌ Don't replace LLM qualitative judgment with numerical scoring (no
find_best_windowin V1) - ❌ Don't try to compete with real routing tools (Predict Wind, qtVlm) on optimization
- ❌ Don't couple
mcp-coreto Gradio or HF Spaces — those belong only inhf-space/ - ❌ Don't make the web app propose to chat with an assistant — it stays standalone
- ❌ Don't réintroduire les zones d'accélération côtière (retiré V1)
- ❌ Don't shipper de mapping "Sun Odyssey 32 → cruiser_30ft" en dur côté serveur — le LLM décide à partir des descriptions de
list_boat_archetypes()
- Local
plan/files (gitignored) are the source of truth for scope and decisions - Tests must pass before commit
- Isolation par worktree : au début de toute session qui va modifier du code, créer un worktree dédié pour éviter qu'une autre session Claude qui tournerait en parallèle sur une autre branche te marche dessus (cf. incident où un commit a atterri sur la mauvaise branche parce que
git checkoutavait été fait par l'autre session).- Naming :
../open_wind-<topic>(kebab-case, court, descriptif). - Création :
git worktree add ../open_wind-<topic> <branche-existante>ougit worktree add ../open_wind-<topic> -b feat/<topic>pour une nouvelle branche. cddans le worktree,npm cicôté web si besoin (node_modules n'est pas partagé entre worktrees), travailler.- À la fin de la session (après push), proposer explicitement à l'utilisateur : « Worktree
../open_wind-<topic>peut être supprimé viagit worktree remove ../open_wind-<topic>, je le fais ? » Ne JAMAIS supprimer sans confirmation. - Exceptions où le worktree n'est pas requis : tâches purement read-only (exploration, Q&A, lecture de doc) ou fix ultra-rapide single-file quand on est déjà sur la bonne branche (à vérifier via
git branch --show-currentavant le premier edit).
- Naming :