Vanlife Planner
by EvkohLand
README.md
<p align="center">
<img src="./docs/banner.jpg" alt="A car with an open rooftop tent and a dog at sunset in the Dolomites" width="100%">
</p>
# Vanlife Planner
**Plan a road trip in a rooftop tent, a campervan or a motorhome, and let your assistant check
every night, route and arrival time against the rules of the road and the hour of sunset.**
[](./LICENSE)
[](https://github.com/liketrek/TREK)
[](#mcp-tools)
[](#works-with-chatgpt-and-claude)
A plugin for [TREK](https://github.com/liketrek/TREK), the self-hosted trip planner: legal nights, sunset-safe arrivals, routes, schedules,
supplies, the price and amenities of every place, the hosts' contacts and where each night stands
(spotted, in discussion, booked, dropped) — as MCP tools any assistant connected to TREK can call
(ChatGPT, Claude…), as warnings in the planner, as chips on each place (hover card, places list)
and as a small panel at the foot of the place view.

## Screenshots

<table>
<tr>
<td width="50%" valign="top">
<img src="./docs/shot-hover-card.png" alt="Hover card of a campsite with price and amenity chips">
<br><sub><b>Hover card on the map.</b> The price per person and one chip per amenity
(dog, water, electricity, toilets, shower, dump station, wifi, bins, laundry, pool, mobile data),
filled from park4night and OpenStreetMap.</sub>
</td>
<td width="50%" valign="top">
<img src="./docs/shot-dolomites-routes.jpg" alt="Colored day routes between the trip's places in the Dolomites">
<br><sub><b>Day routes.</b> Each day's road computed with Valhalla for the vehicle's height,
length and weight, drawn in the day's color.</sub>
</td>
</tr>
<tr>
<td width="50%" valign="top">
<img src="./docs/shot-route-overview.jpg" alt="Whole trip on the map, from the coast to the Alps">
<br><sub><b>The whole trip.</b> Driving days from the coast to the mountains, with every
candidate night clustered on the map.</sub>
</td>
<td width="50%" valign="top">
<img src="./docs/shot-chatgpt-tools.png" alt="ChatGPT listing the Vanlife tools it can call">
<br><sub><b>From ChatGPT.</b> Asked which Vanlife tools it has, ChatGPT lists them through
TREK's MCP server (captured with version 0.3.0; since 0.4.0 the day and place tools are
merged into <code>vanlife_day</code> and <code>vanlife_place</code>).</sub>
</td>
</tr>
<tr>
<td width="50%" valign="top">
<img src="./docs/shot-admin-plugin.png" alt="The plugin card in TREK's Admin, Plugins tab, with its permissions">
<br><sub><b>Admin → Plugins.</b> Every permission and outbound host shown on the card
before the admin turns the plugin on.</sub>
</td>
<td width="50%" valign="top">
<img src="./docs/shot-admin-settings.png" alt="Instance settings of the plugin: park4night and default language">
<br><sub><b>Instance settings.</b> park4night on or off, and the default language of
warnings and columns.</sub>
<br><br>
<img src="./docs/shot-warnings.png" alt="Warning chips in the planner's top bar">
<br><sub><b>Warnings in the planner.</b> Blocking points in red, points to verify in amber
(place names blurred here).</sub>
</td>
</tr>
</table>
## What it does
<img src="./docs/vehicles.jpg" alt="A car with a rooftop tent, a campervan and a motorhome parked at sunset" width="420" align="right">
The vehicle is a setting, and the rules follow it, anywhere in the world. A rooftop tent has to be
unfolded in daylight, and opening it is camping: by default the plugin applies the strictest rule
it knows (the Alps): a campsite, a farm, or private ground with the owner's consent, never public
ground. A night it cannot place is flagged to check. Sleeping
inside a van or a motorhome without setting anything out is parking in Italy (Codice della strada
art. 185), so a motorhome area is fine there, while many South Tyrol municipalities forbid
camping on public ground.
- **Trip check** — day by day, at four levels (blocking / to fix / to verify / info): arrival at
the night later than *sunset minus a margin* (computed for the exact date and place); a stop
visited on its closing day or outside the hours quoted in its notes; a check-in window or minimum
stay not met; a night the vehicle may not use; a farm night to check where local rules apply
(South Tyrol: allowed on private ground with the owner's consent, except in protected areas or
where a municipal rule forbids it); impossible times; a shopping detour over the limit; nights in a row without water; a
place that refuses the vehicle, the dog, or the vehicle's height, length or weight. Every evening
of a stay over several nights is a night: the day starts and ends at camp. Bookings are checked
against the plan: a booking tied to no night (TREK shows the place booked, but nobody sleeps
there), a cancelled night still planned, a booked price unlike the plan, notes still saying
"waiting" on a confirmed booking.
- **One rule per question** — where you sleep on an evening (`trip.js` `stayOn`), which booking
speaks for a night (`night-status.js`), what a night costs the whole party and in which currency
(`place-info.js` `nightCost`, `currencyOf`): the check, the night list, the budget, the schedule,
the place panel, the planner columns and the host message all ask the same functions.
- **Walking routes** — each hike's walk drawn dotted on the trip map, from the car park that gives
access to it (see *Hikes and their car park*).
- **One look everywhere** — every colour and pictogram the plugin shows comes from one catalogue,
`server/lib/design.js`. See *Map legend* below.
- **Nights** — candidates between the evening's last visit and the next morning's first stop, from
OpenStreetMap and, if the instance enables it, park4night; ranked by legality, price and real
detour.
- **Routes** — the road of each day with [Valhalla](https://valhalla1.openstreetmap.de) for the
vehicle (height; length and weight for a motorhome), no tolls except on the motorway days you
choose; also two route profiles in the planner's route toggle.
- **Schedules** — start and end of every stop from real drive times.
- **Supplies** — supermarkets, fuel and drinking water along the day's route, with that day's hours.
- **Price and amenities** — per night or per person, dog fee, height/length/weight limits, and the
amenities park4night and OpenStreetMap know (dog, water, electricity, toilets, shower, dump
station, wifi, bins, laundry, pool, shop, bakery, restaurant or snack bar, bar, mobile data,
playground, barbecue, gas bottles, LPG, vehicle wash, open in winter, rooftop tent accepted).
The price is TREK's own field; the rest is kept on the place by the plugin. Unknown amenities are
**filled automatically** from OpenStreetMap (the campsite mapped within 150 m) and from
park4night (the place a park4night link points to), citing the source; a value a person recorded
is never overwritten.
- **Hosts and nights** — the contact of each host (e-mail, phone, WhatsApp, website, name,
languages, preferred channel), filled from the place's own notes, OpenStreetMap and park4night;
a log of what was asked and answered; the status of every night, kept in TREK's own bookings
(a night's status is the strongest of its bookings: booked > in discussion > cancelled);
and a ready-to-send information request to a host, in English then French (Italian or German
on request), built from what is still unknown about the place. The plugin never sends it: the
assistant shows the exact text and waits for your go.
### Timed access, booking, toll
Some places can only be reached at certain hours, or not without a ticket: a mountain toll road
closed to cars after 09:00, a valley road open to cars only in the evening, a car park with
time slots to book. Each place can record, in the widget or with `vanlife_place` `set` (`null`
clears a field):
| Field | Meaning |
|---|---|
| `access_before` | `"HH:MM"`: arrive before this time, the road is closed after it. |
| `access_after` | `"HH:MM"`: cars allowed only after this time. With both fields, `access_before` earlier than `access_after` means closed in between; later means open in between. |
| `booking_required`, `booking_url`, `booking_note` | A booking is required (slot, car park, road permit), where to make it, and a short note (120 characters at most). |
| `toll_amount`, `toll_currency` | Toll or ticket per vehicle, and its 3-letter currency (default: the trip's). |
Example, a mountain toll road: `{"access_before": "09:00", "booking_required": true,
"booking_url": "https://toll-road.example.com", "toll_amount": 30, "toll_currency": "EUR"}`.
The trip check then says, from the arrival time of the stop (its own time, or the time the day's
drives give): an arrival after `access_before` is *blocking*, an arrival before `access_after`
is *to fix*, a booking with none recorded in TREK's bookings or the stop's notes is *to verify*,
with the link. `vanlife_day` `schedule` moves the proposed departure so every access is met.
The toll is added to that day's budget in `vanlife_plan_trip`. Each recorded field shows as a
small chip on the place: *Before 9:00 AM*, *Booking*, *Toll €30.00*.
## Map legend
The plugin draws no marker of its own: TREK's place markers already carry the state and the
kind, and a second marker on top would hide them and steal their clicks. The plugin draws one
thing on the map, the dotted walking route of each hike. It keeps every place readable this way:
The colour of a marker is its state, and nothing else:
| | State | Meaning |
|---|---|---|
| <img src="./docs/icons/state-booked.svg" width="32" alt="green"> | **Booked** | the host confirmed the night |
| <img src="./docs/icons/state-discussion.svg" width="32" alt="amber"> | **In discussion** | the host was asked, no answer or no confirmation yet |
| <img src="./docs/icons/state-cancelled.svg" width="32" alt="red"> | **Cancelled** | the night was dropped |
| <img src="./docs/icons/state-available.svg" width="32" alt="blue"> | **Available** | planned or not, but neither booked, in discussion nor cancelled — an activity is always blue |
The pictogram is the kind of place: the icon of its TREK category. `vanlife_place` `kind` (and
`vanlife_night` `kind`) moves a place to the trip's category for that kind, so its pictogram
follows. Three signs have one meaning each, and nothing else ever uses them:
| | Rule |
|---|---|
| <img src="./docs/icons/parking.svg" width="32" alt="P"> | **"P" = parking.** A car park is always and only the "P" (TREK icon `ParkingSquare` on the car park category), and the "P" never means anything else. |
| <img src="./docs/icons/walk-dotted.svg" width="96" alt="dotted line"> | **Dotted line = the walking route of a hike, from its car park.** The plugin draws nothing else dotted or dashed. |
| <img src="./docs/icons/day-route-grey.svg" width="32" alt="grey"> | **Grey = a day's drive route** (the "day route" places): it carries no state, and grey is used for nothing else. |
A hike is a walker (TREK icon `Footprints` on the hike category), never a camera: a viewpoint, a
lake or a village whose name says it is reached on foot (*"… on foot"*, *"… à pied"*) counts as a
hike for the plugin, and `kind: "hike"` files it in the hike category. The departure and return
of the trip is a home.
### Hikes and their car park
A walk always starts at a car park "P" and comes back to that same P: a **loop**, or an
**out-and-back** to a turnaround point and back the same way. Never from one P to another. The
walk is drawn dotted on footpaths ([Valhalla](https://valhalla1.openstreetmap.de) pedestrian
routing, cached for 30 days); an out-and-back is drawn one way and counted twice.
The car park is the one set on the hike (`vanlife_place` `set.walk.parking_place_id`), else a
car park planned right before or after the hike that day, else the nearest one planned that day
within 3 km, or any day within 2 km. The shape is `set.walk.shape` (`loop` or `out_and_back`),
else a loop when the hike's place is the car park itself, else an out-and-back to the hike's
place. `set.walk.via` (points `[lat, lng]`) shapes a loop. The tool refuses a walk with no car
park, with no shape, from a place that is not a car park, or whose turnaround is another car
park; the check (`vanlife_check_trip`) reports such walks, and they are not drawn. A hike that is
not planned gets its walk as soon as it is tied to a planned car park.
Hovering the dotted line gives the hike, the walk and its car park; a click opens the **hike
card**: loop or out-and-back, the total distance, the climb (from the terrain model), the hiking
time (DIN 33466 rule: 4 km/h, 300 m/h up, 500 m/h down), the start car park, and a link to the
page with the full track and elevation profile: the one set with `set.walk.url`, else the first
Outdooractive, Komoot, Wikiloc or AllTrails address written in the hike's website or notes. No
link is ever made up: with none, the card says so. `vanlife_place` `filter: "hikes"` lists every
hike with its car park, shape, walk and any rule it breaks. Off with the *walking routes* setting.
### Car parks
One `vanlife_place` call sets a car park: its price on TREK's own price with its unit
(`per: "day"` or `"hour"`), its height limit (`max_height_m`), and `set.parking` — opening hours,
payment, motorhomes allowed, overnight allowed, notes and the source address. They show as a
"P" chip on the place (*15,00 €/jour*, *05:00–23:00 · online booking only · no overnight*) and in
the place panel, with the source link.
From an assistant, one sentence sets both: *"the farm on day 2, we booked it"* is
`vanlife_night` with `kind: "farm"` and `status: "booked"` — the place moves to the trip's farm
category (leaf) and its marker turns green.
## MCP tools
Advertised to assistants as `plugin_vanlife_<name>`; the MCP client needs the opt-in
`plugins:use` scope.
| Tool | What it does |
|---|---|
| `vanlife_plan_trip` | All-in-one: check, cheaper legal nights nearby, routes, schedules, budget; proposes by default, writes only when asked. |
| `vanlife_check_trip` | Read-only check of a trip for the user's vehicle, including nights with no contact and hosts who have not answered for over 3 days. |
| `vanlife_find_nights` | Night candidates for one evening (OpenStreetMap, park4night when enabled), with the contacts OpenStreetMap knows. |
| `vanlife_day` | One tool, three actions: `routes` (road route of each day), `schedule` (times of one day from real drive times), `supplies` (groceries, fuel and water along a day's route). |
| `vanlife_place` | Everything about a place: list by filter (`hikes`: each hike with its car park and walk), read, `create` a place (kind, price, planned on a day), `kind` (its pictogram), `set` (amenities, price details, timed access, booking, toll, contacts, a car park's details, a hike's `walk`), `sheet_set`, `log` an exchange, `clear` / `clear_fields`, `fill` from open sources. |
| `vanlife_night` | `list` where each night stands; `set` a night as spotted, contacted, booked or dropped (a TREK booking pending, confirmed or cancelled). |
| `vanlife_host_message` | Drafts the information request to a host (English, then French); sends nothing. |
A whole hike in one call — the hike, its car park and its dotted walk:
```json
{ "tripId": 1,
"create": { "name": "Example lake loop", "lat": 46.69, "lng": 12.08, "kind": "hike", "day_number": 7 },
"set": { "walk": { "parking": { "name": "Example lake car park", "lat": 46.70, "lng": 12.085, "price_amount": 18, "per": "day" },
"shape": "loop", "via": [[46.692, 12.087], [46.689, 12.083]],
"url": "https://www.outdooractive.com/en/route/..." } } }
```
A walk always starts at a car park "P" and comes back to it, as a loop or an out-and-back;
a walk from one car park to another is refused.
TREK accepts 8 tools per plugin: the eighth place is left free on purpose.
No tool books, pays or writes to anyone. `vanlife_night` records what you declare, and every tool
that deals with hosts tells the assistant to show you the exact text and wait for your explicit
validation.
## Works with ChatGPT and Claude
The tools live on TREK's own MCP server, so any assistant that speaks MCP can use them.
1. In TREK, an admin installs and turns on the plugin (Admin → Plugins) and enables MCP access.
2. In the assistant, add TREK as a connector or custom app: the MCP address is
`https://<your-trek>/mcp`, and the sign-in must grant the **`plugins:use`** scope (no client
preset asks for it, so request it by name). ChatGPT: Settings → Apps → create an app with that
address. Claude: Settings → Connectors → add a custom connector.
3. Ask in plain words: *"Check trip 1 for my rooftop tent"*, *"Find a cheaper legal night near
day 4"*, *"Draft a message to the host of day 6 asking about dogs and late arrival"*.
After a plugin update, the assistant keeps the old tool list until it is told to reload it: in
ChatGPT, open the app's settings and click **Refresh tools** (*Actualiser les outils*); in Claude,
reconnect the connector.
## Where it shows in TREK
<img src="./docs/nights.jpg" alt="Illustrated map of a mountain valley with a dotted route and lit tents marking each night" width="380" align="right">
- **Warnings banner** — blocking and to-verify points, in the user's language.
- **Place chips** — the night's status first (*Booked ✓* green, *In discussion* orange, *Dropped*
red, on places that have a booking), then the price and one chip per amenity the place has (dog,
water, electricity first), plus a red chip listing what it lacks when that rules it out: the
places list, and the map's hover card
once an admin lets the plugin in (Admin → Default user settings → *Plugin info on places*).
<img src="./docs/hover-card.png" alt="Hover card of a booked farm night: green marker, Booked chip, price per night and one chip per amenity" width="254"> <img src="./docs/hover-card-discussion.png" alt="Hover card of a campsite in discussion: amber tent marker, In discussion chip, price and amenities" width="299">
- **Place card** — the widget reads the place's description and notes ("Key : value" lines and
bullet lists, in English, French, Italian, German or Spanish) into one card per kind, with fixed
sections in a fixed order: a hike shows its key figures (distance, climb, descent, time,
difficulty, loop), how to get there, the itinerary as steps, conditions, highlights and links
(track page, map, GPX); a night its price, stay, services, access and reviews; a visit its
figures, opening and access. What it cannot classify stays under *Other notes*; nothing is
stored twice (the notes stay the record, `vanlife_place` `sheet_set` rewrites a line). TREK then
folds its raw copy of the text. The host's contact shows on nights only.
- **Place panel** — a widget at the foot of the place view shows the night's status, the host's
contact (e-mail, phone and website as links, last exchange) and the amenities; it edits the
amenities and the contact, and has a *Fill the trip's amenities* button. Opening a place with
nothing recorded looks it up once. It sets the night's state too: pick the evening, then
*Available* (blue: the booking is deleted, the stay stays planned), *In discussion* (amber),
*Booked* (green, with an optional confirmation number) or *Cancelled* (red, with an optional
reason); TREK's map follows at once. Where TREK mounts place-detail widgets in its own place
edit form (`mode: 'edit'` in the frame context), the widget opens there on its editor.
- **Settings** — TREK's native forms: per user (vehicle, dimensions, dog, target and ceiling price,
sunset margin, language…) and per instance (park4night on or off, default language).
## Setup
1. Install the plugin from its zip (Admin → Plugins), and grant the permissions it asks for.
2. Optionally, in Admin → Plugins → Instance settings: turn park4night off, choose the default
language.
3. Each traveller sets the vehicle, its dimensions, the dog, the prices and the sunset margin in
Settings → Plugins.
4. To use the tools from ChatGPT, Claude or another assistant, connect it to TREK's MCP server
with the `plugins:use` scope; the tools appear as `plugin_vanlife_vanlife_*`.
## Permissions
| Permission | Why |
|---|---|
| `db:read:trips`, `db:read:categories`, `db:read:costs`, `db:read:todos` | Read the trip being checked or planned. |
| `db:write:places`, `db:write:itinerary` | Write a route place or a chosen night, only when an assistant is asked to apply; copy a host's website or phone into the place's empty TREK fields. |
| `db:write:reservations` | Record the status of a night as a TREK booking (pending, confirmed, cancelled), only when you say so. |
| `db:meta` | Keep the price details, amenities, host contacts and exchange log on the place itself (the plugin's own namespaced data; writes need the user's place edit right). |
| `db:own` | Cache of drive times and OpenStreetMap answers; which places were already looked up (ids and dates only). |
| `mcp:tools` | Publish the tools above. |
| `hook:trip-warning-provider` | The warnings banner (cache only, no network). |
| `hook:route-provider` | The two route profiles. |
| `hook:table-contributor` | The night status, price and amenities chips on each place. |
| `hook:map-layer-provider` | The walking route of each hike, drawn dotted on the trip map from the car park that gives access to it. Off with the *walking routes* setting. |
| `http:outbound:valhalla1.openstreetmap.de` | Drive times and road geometry; walking routes of the hikes. |
| `http:outbound:overpass-api.de` | Campsites, shops, fuel, water and campsite amenities from OpenStreetMap. |
| `http:outbound:park4night.com` | park4night search and amenities, only if the instance enables it. |
Only coordinates (and, for park4night, the language) leave the server — never names, notes,
contacts or anything about the travellers. Host contacts stay in your TREK instance.
### About park4night
park4night publishes no API: the plugin reads the endpoint its own web map uses, which may change
without notice. Calls are rate-limited (20 per hour, 3 s apart). Search results are not stored;
when amenities are filled, only the yes/no amenities of a place that is already in your trip are
copied onto that place, with its park4night number as the source. An admin can turn park4night
off in the instance settings, and the plugin then uses OpenStreetMap alone.
## Limits worth knowing
Zone outlines are coarse (check a night near a border by hand). The public Overpass server allows
few queries per server, so searches may answer "busy, try again in a minute". OpenStreetMap often
lacks amenities, and has no reviews. Nothing here books, pays or messages anyone.
## Development
```bash
npm install
npm test # vitest, mock TREK host, no network
npm run coverage
npm run sync-manifest # copy server/lib/tool-specs.js into trek-plugin.json
npm run validate && npm run pack
```
Data: © OpenStreetMap contributors (ODbL), routing by Valhalla on the FOSSGIS servers,
park4night data © park4night.
## Contributing
See [CONTRIBUTING.md](./CONTRIBUTING.md).
## License
MIT — see [LICENSE](./LICENSE).
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessUnresponsive