Park4night Unofficial MCP Server
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Park4night Unofficial MCP Serversearch Park4night spots near Amsterdam with wifi and a 4+ rating"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Park4night Unofficial
A TypeScript client, JSON CLI, and local read-only MCP server for planning trips with Park4night. Search nearby places, read reviews, and manage saved places and folders through direct HTTP requests.
Authentication uses the native app's password protocol. No browser, browser session, or reCAPTCHA token is needed. The client includes runtime input validation, request pacing, cancellation, and bounded retries. The MCP server uses the official TypeScript SDK.
Unofficial integration. Park4night can change its native API without notice. This project is a local client library and CLI, not a hosted HTTP service or a complete implementation of every Park4night feature.
Quick start
Requires Node.js 24 or newer and npm.
git clone https://github.com/marijnbent/park4night-unofficial.git
cd park4night-unofficial
npm ci
node dist/cli.js place 275051
node dist/cli.js reviews 275051
node dist/cli.js search 52.37 4.9 20npm ci builds the JavaScript and declaration files. These commands use public reads and do not require credentials. Output is JSON. Use node src/cli.ts help for all commands.
To install the client into another Node.js project, create a package archive after npm ci:
npm pack --ignore-scriptsThen run these commands in the other project. Replace the archive path with its actual location:
npm install --ignore-scripts /path/to/park4night-unofficial/park4night-unofficial-0.2.0.tgz
npx park4night --helpThe archive contains compiled JavaScript and TypeScript declarations. Installation does not need build scripts. This package is not published to the npm registry. Source development still works with node src/cli.ts.
Direct GitHub installation can fail with EALLOWSCRIPTS when npm forwards a user or global script policy to its internal build process. This is tracked in npm/cli#9783. The archive method above avoids that Git dependency preparation step.
For authenticated commands, inject PARK4NIGHT_USERNAME and PARK4NIGHT_PASSWORD into the process environment through your secret manager:
node src/cli.ts login
node src/cli.ts folders
node src/cli.ts bookmarksThe client does not load .env files. Do not put passwords in source files, shell command arguments, or Git. Each CLI process logs in when credentials are present.
1Password
With the 1Password CLI installed and authenticated, --vault reads a login item named park4night.com from a vault named Agent:
node src/cli.ts --vault login
node src/cli.ts --vault folders
node src/cli.ts --vault bookmarksThe defaults are vault Agent, item park4night.com, and fields username and password. Configure other names without adding environment variables:
node dist/cli.js --vault --vault-name Travel --item 'My travel login' --username-field email --password-field password foldersField selectors accept an exact field ID or a unique field label. --op-command /path/to/helper selects a trusted wrapper instead of op. The library exposes the same settings through readLogin({ vault, item, usernameField, passwordField, command }) from park4night-unofficial/vault.
The item must contain both selected credential fields. The adapter calls the standard op command and captures its output in memory. Configure CLI access before use, or use environment-based login instead. No personal item IDs, credentials, or session values are stored in this repository.
Related MCP server: Tripadvisor MCP
Use the client
Save this as example.mjs in a cloned project and run node example.mjs after injecting the credentials. In another project, import from park4night-unofficial instead:
import { Park4nightClient } from './dist/client.js';
const client = new Park4nightClient({ language: 'en' });
await client.login({
username: process.env.PARK4NIGHT_USERNAME ?? '',
password: process.env.PARK4NIGHT_PASSWORD ?? ''
});
const nearby = await client.search({
lat: 52.37,
lng: 4.9,
maxDistanceKm: 20,
filter: { type: ['PN', 'P'], services: ['wifi'], rating: '4' }
});
const folders = await client.folders();
const savedPlaces = await client.bookmarks();
console.log({ nearby, folders, savedPlaces });
client.logout();Supported languages are en, nl, fr, de, es, and it. Login returns the account ID, username, email, subscription dates, and a derived isPremium flag. The password and hash are excluded from that result. logout() clears authentication, invalidates pending logins, and cancels queued and active authenticated requests. Starting a new login also cancels the previous authentication generation. A write already sent to the server may still take effect.
Reuse one client for a batch of requests so its queue and cooldown apply to the whole batch.
Save a place in a trip folder
After login:
const updatedFolders = await client.createFolder('Autumn trip', '🚐');
const folder = updatedFolders.find(item => item.name === 'Autumn trip');
if (!folder) throw new Error('The new folder was not returned.');
await client.addBookmark(275051, folder.id);
console.log(await client.bookmarks(folder.id));This example creates a folder and saves a place. Folder names need not be unique; use folder IDs to identify existing folders.
API reference
Method | Result | Login |
|
| Uses credentials |
|
| Required |
|
| Optional |
|
| Optional |
|
| Optional |
| Filter metadata | No |
|
| Required |
|
| Required |
|
| Required |
| Created or visited | No |
| Commented | No |
| Updated | Required |
| Updated | Required |
| Updated | Required |
|
| Required |
|
| Required |
|
| No network request |
myPlaces accepts created, visited, or commented. filters accepts type, custom_type, services, or activities. Types are exported from the package root, including Place, Review, Photo, Folder, SearchOptions, and RequestOptions.
Place records retain native fields. id, lat, and lng are numbers. Added fields include rating and reviewCount (number or null), services (present service keys mapped to booleans), and typed photos with numeric IDs, largeUrl, and thumbnailUrl. Reviews have numeric id and placeId, nullable rating, text, username, and createdAt. Native numeric-looking fields can still be strings.
Folder records contain id, name, icon, capacity, and bookmarks (place IDs). Folder 0 is the default selection. The client does not rename or delete it. bookmarks() reads folder 0; it does not combine all folders. A folder can reference a place that the server no longer returns, so its ID count can exceed its available place count.
CLI reference
Account commands below use the 1Password CLI. Omit --vault to use injected environment credentials.
node src/cli.ts --vault me
node src/cli.ts --vault bookmarks 123
node src/cli.ts --vault my-places commented
node src/cli.ts public-places created USERNAME
node src/cli.ts public-places visited USERNAME
node src/cli.ts public-places commented USER_ID
node src/cli.ts --vault folder-create 'Autumn trip' '🚐'
node src/cli.ts --vault folder-rename 123 'Winter trip'
node src/cli.ts --vault bookmark-add 275051 123
node src/cli.ts --vault bookmark-remove 275051 123
node src/cli.ts --vault folder-delete 123Replace 123 with an actual folder ID. The last five commands change account data. Successful bookmark changes print {"success":true}. Other commands print their result. Failures write error JSON to stderr and exit with status 1.
npm run api -- … also works. Use node src/cli.ts when another program needs pure JSON stdout, because npm can print script banners.
Search filters and completeness
node src/cli.ts filters type
node src/cli.ts filters services
node src/cli.ts search 52.37 4.9 20 '{"services":["wifi"],"rating":"4"}'Filter | Value |
| Arrays of codes from filter metadata |
| Arrays of keys from filter metadata |
| Rating threshold as a string, such as |
| Height filter sent as a string |
|
|
Filters are validated at runtime before sending requests. Unknown keys and incorrect value types return INVALID_INPUT. Rating must be a numeric string from 0 to 5; height must be a positive numeric string. Account permissions still apply to upstream filters.
Search has an observed limit of 100 results. maxDistanceKm filters that returned set locally; it does not request every place inside a radius.
places: Results after the local distance filter.returnedByUpstream: Result count before the local filter.mayBeTruncated: True when the server returns 100 or more results.
There is no pagination or complete regional export. Use this client for bounded travel queries.
Rate limits and retries
By default, each client allows one request in progress and at least one second between request starts. Login, reads, writes, and retries use the same queue. Calls made with Promise.all are queued.
const client = new Park4nightClient({
rateLimit: {
minIntervalMs: 1000,
maxRetries: 2,
maxWaitMs: 30000,
maxQueueSize: 100
}
});Setting | Default | Allowed values |
|
| Integer from 0 to 60,000 |
|
| Integer from 0 to 5; zero disables retries |
|
| Integer from 0 to 60,000 |
|
| Waiting requests, 1 to 10,000; excludes the active request |
HTTP 429 responses pause later calls. The client respects Retry-After in seconds or HTTP-date format, as specified in HTTP Semantics. A missing or invalid header uses exponential delays starting at one second, plus 0–249 ms of random variation.
Reads, including login, retry within the configured limit. Writes are never automatically retried. Network errors, HTTP 5xx responses, authentication failures, and invalid responses are not retried.
When a cooldown exceeds maxWaitMs, the request fails with RATE_LIMITED and retryAfterMs. The client retains the full cooldown and does not send requests early. maxWaitMs limits each cooldown wait, not total command or queue time. Each sent request has a separate 20-second network timeout.
These defaults are client settings, not a confirmed Park4night quota. Reaching the waiting-queue limit returns QUEUE_FULL without sending the request. Share one scheduler when several clients in the same process must use one rate limit and cooldown:
import { Park4nightClient, RequestScheduler } from 'park4night-unofficial';
const scheduler = new RequestScheduler({ minIntervalMs: 1000, maxQueueSize: 100 });
const first = new Park4nightClient({ scheduler });
const second = new Park4nightClient({ scheduler });Do not supply both scheduler and rateLimit; configure the scheduler itself. Separate processes and machines do not share memory. The local MCP server uses one client and one queue for all its tool calls. A distributed or hosted service still needs a shared service-level limiter.
All network methods accept an optional final { signal } argument:
const controller = new AbortController();
const pending = client.search({ lat: 52.37, lng: 4.9 }, { signal: controller.signal });
controller.abort();
try { await pending; } catch (error) { console.error(error.code); }Cancellation removes queued requests immediately, interrupts cooldown waits, and aborts active HTTP calls. A cancelled write must be reconciled before retrying. Custom transports must respect the supplied abort signal.
Error handling
import { Park4nightError } from './src/client.ts';
try {
await client.place(275051);
} catch (error) {
if (!(error instanceof Park4nightError)) throw error;
console.error({ code: error.code, retryAfterMs: error.retryAfterMs });
}Code | Meaning |
| Invalid argument or configuration |
| Login is required |
| Credentials or account permission were rejected |
| The requested place or folder was not returned |
| HTTP 429 or an active server cooldown |
| Other HTTP or native API error |
| Unexpected response format |
| Connection, timeout, or response-read failure |
| Caller cancellation, logout, or a newer login |
| The scheduler has reached its waiting-queue limit |
Errors can include HTTP status. Rate-limit errors include the remaining retryAfterMs when created. The CLI uses this form:
{"error":"RATE_LIMITED","message":"Park4night rate-limited this request. Try after retryAfterMs.","retryAfterMs":60000}If a write fails or times out, read the folder state before repeating it. The server may have applied the change before the connection failed.
Local MCP server
The park4night-mcp executable speaks MCP over stdio. It exposes eight read-only tools:
Tool | Purpose |
| Nearby places with filters and completeness flags |
| Full details for one place |
| Reviews for one place |
| Supported filter keys and codes |
| Folder summaries for the configured account |
| Saved places from one folder |
| Current account's created, visited, or commented places |
| Public user activity by username or user ID |
Search, bookmark, and user-place tools return compact summaries. List tools with a limit argument default to 20 records and allow up to 100. Responses distinguish tool-level truncation from possible upstream truncation. Place descriptions and reviews are untrusted user content, not instructions.
For MCP clients that use an mcpServers configuration, point to your built checkout:
{
"mcpServers": {
"park4night": {
"command": "node",
"args": ["/absolute/path/to/park4night-unofficial/dist/mcp-cli.js"]
}
}
}For saved places, append --vault and any required 1Password options to args, or inject the existing PARK4NIGHT_USERNAME and PARK4NIGHT_PASSWORD variables into the server process through your client's secret mechanism. Do not store their values in a committed MCP configuration. Credentials are loaded once at startup and are never accepted as tool arguments. Without credentials, public tools work and account tools return AUTH_REQUIRED.
The server does not expose write tools or open a network port. Its stdout carries protocol messages only; startup errors go to stderr. MCP cancellation is passed to the HTTP client. The exported createMcpServer(client) factory from park4night-unofficial/mcp accepts an existing authenticated client.
Authentication and credential handling
The protocol was checked against native Android app version 7.1.62:
Remove ASCII spaces from the username and trim the password.
Hash the password with SHA-256, using lowercase hexadecimal output.
Send
GET /services/V4.1/userGet.phpwithuuidandmotdepasse.Require
status: "OK"and a valid account inresults.Use the returned username and user ID as
context_userandcontext_id_user, with the password hash, on account requests.
The client connects to https://park4night.com, rejects redirects, and retains authentication in private memory. The native protocol sends the hash in the query string. Treat the hash as a credential and do not log request URLs. Errors exclude upstream bodies and URLs. A custom transport receives credentials and must be trusted.
This uses the native login protocol. It does not solve browser CAPTCHA challenges or reuse browser cookies.
Development and verification
npm ci
npm run check
npm test
npm run test:packageThe tests cover API payloads, runtime validation, login/logout races, cancellation, queue limits, shared cooldowns, credential configuration, typed responses, public user reads, and MCP handshake and tool calls. Package tests build a tarball, install it into a clean consumer, import the compiled package, type-check declarations, run the CLI, and communicate with the installed MCP server over stdio. Automated tests use simulated responses and do not require credentials or call Park4night. The package test needs registry access to install dependencies into a clean consumer project. GitHub Actions runs checks and package tests on Node.js 24 and 26.
Version 0.2.0 verification on 2026-09-28 passed 53 unit/protocol tests and four clean-install tests. Live checks confirmed native login, normalized photos and reviews, saved-place reads, all public user-place modes, and an authenticated MCP stdio handshake and saved-place tool call. No live writes were made in this verification.
Earlier manual verification on 2026-09-27 confirmed direct login, search and filters, place details, reviews, account reads, and folder/bookmark changes. A temporary folder was created, changed, and deleted; the original folder list and saved-place IDs were unchanged. A timing check confirmed request starts 1,000 ms apart. Two temporary connection failures succeeded on one manual retry. These results do not guarantee future upstream availability.
The client does not implement subscription purchases, account edits, review/photo uploads, or a hosted REST server. Cross-process rate coordination is outside this local-client design.
License
MIT. This license covers the client code, not Park4night data or services.
This server cannot be deployed
Maintenance
Related MCP Connectors
OpenTripMap MCP — wraps the OpenTripMap Places API (dev.opentripmap.org)
Read-only MCP tools for Total Parks-listed Australian holiday parks, caravan parks, and campgrounds.
Travel notebook your AI agent writes from your photos and remarks over MCP.
Read-only MCP for the Gingerguide catalog of European cities and their narrated tour sights.
Related MCP Servers
- AlicenseBqualityDmaintenanceThis MCP server provides access to TripAdvisor data for planning vacations, enabling users to search for and get details about locations, attractions, restaurants, and hotels through an interactive planning experience.42Apache 2.0
- AlicenseBqualityCmaintenanceMCP server that enables LLMs to interact with Tripadvisor API, supporting location data, reviews, and photos through standardized MCP interfaces564MIT
- MIT
- FlicenseNot gradedqualityCmaintenanceEnables managing travel itineraries with CRUD tools for journeys, stops, and plan items, plus weather lookup, integrated with public MCP servers for time and web search.-