night-margin-mcp
Click on "Install 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., "@night-margin-mcpWhere is my portfolio leaking money?"
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.
night-margin-mcp — Margin Engine
Wheelhouse optimizes what guests pay. night-margin protects what you keep.
Built by a property manager who runs 73 listings in Lapland — for operators like us.
An MCP server that shows every pricing decision for a short-term rental portfolio net of turnover costs — the margin guard for Wheelhouse pricing, the profit layer underneath its revenue optimization (ADR, RevPAR). The key metric: net per available night.
Quick start
claude mcp add margin -- npx -y night-margin-mcpThat's it — works with zero config on synthetic demo data. Open Claude and ask:
Where is my portfolio leaking money in June 2026?
You'll get the leak figure, the net-per-night metric, and your best and worst properties in under a minute. When you're ready to run the analysis against your own Wheelhouse portfolio, see Wheelhouse API key below.
On live data the first call is slower: one request per listing, serial, to stay inside Wheelhouse's 60 req/min limit — 3 listings ≈ 5 s, 73 listings ≈ 90 s. Results are then cached for 10 minutes.
Related MCP server: tokencost-mcp-server
What you get
The first line is the pain, in euros — this is actual zero-config output (June 2026, demo data):
€132 is leaking from 8 bookings that don't cover their own turnover cost. (4.0% of booked nights are net-negative) Net per available night: €74.7 Occupancy 72.9% (175 booked, 65 gap nights) Gross €22,821 − turnover costs €4,900 = net €17,921
Property | Net/night | Booked nights | Gap nights | Net € |
demo-1br-03 | €21.4 | 15 | 15 | €643 |
demo-1br-01 | €32 | 18 | 12 | €960 |
demo-3br-08 | €178.1 | 30 | 0 | €5,344 |
Same math, your numbers: set WHEELHOUSE_API_KEY and the analysis runs on your live portfolio.
Live demo
Want to see the output before installing anything? https://mikki321.github.io/night-margin-mcp/ — portfolio KPIs, a per-property heatmap of net per available night, and a triage table of net-negative bookings, generated by this server from the synthetic demo portfolio that ships with the package.
Example prompts
The server provides ten tools: three read-only analysis tools, a four-tool decision loop (see The decision loop), a proactive alerts tool (see Proactive alerts), review_history — a read-only mirror of your own past months net of estimated turnover cost (a description of what happened, never a forecast) — and margin_copilot, which ranks the below-floor gap nights into money-moves, weighs the options for each and marks the recommended one (read-only; staging routes through propose_decisions → apply_decision). No dates needed — analyze_portfolio and compare_strategies default to the last 30 + next 90 days (recent turnovers + your booking horizon), propose_decisions to the next 30 days; pass from/to to change. Try these directly in Claude:
analyze_portfolio— where is the money leaking?Where is my portfolio leaking money?
compare_strategies— gross vs. net in the same picture:Compare two strategies for June 2026: (A) fill gap nights at a 40% discount and (B) raise the minimum stay to three nights with a 10% price increase. Which one delivers the better net per night?
gap_night_check— is a single gap night worth filling?Property demo-1br-01 has a gap night on 2026-06-23. What's the floor price, and is the night worth filling?
propose_decisions— which upcoming nights are at risk of selling below cost?Which gap nights are about to sell below my cost floor?
apply_decision— act on a proposal:Apply decision d3
Without explicit confirmation this is a dry run: it prints the exact rate payload that would be written and changes nothing. Prices change only when you confirm the write, and every applied decision can be undone with
revert_decision.Risk presets —
propose_decisionsandgap_night_checkboth take an optionalriskparameter (conservative|recommended|aggressive, defaultrecommended) that scales the floor's minimum margin — Wheelhouse's own CON/REC/AGG language:What should I do this week? Use the aggressive risk preset.
check_alerts— proactive check for gap-floor breaches and new bookings (see Proactive alerts):Check for any pricing alerts.
The core message of the demo: filling gaps at a discount can grow revenue while shrinking profit — a 2-night and a 7-night booking consume the same cleaning.
The decision loop
New in 0.3.0 — analysis turns into safe, reversible action:
propose_decisionsfinds upcoming gap nights (next 30 days by default) where the current price recommendation sits below your cost floor (turnover + travel +MIN_MARGIN, scaled by an optionalriskpreset — see Example prompts) and proposes fixing those nights at the floor so they can't sell below cost. Proposals go into a local decision log — proposing never touches prices.apply_decisionwrites one proposal to Wheelhouse as fixed custom rates — but only with explicitconfirm: true. The default is a dry run showing the exact payload without writing anything. Before the first real write, the prior custom rates are snapshotted to the decision log, sorevert_decisioncan always delete what was written and restore what was there before.set_targetstores a monthly gross revenue target per property;analyze_portfoliothen reports progress toward any targets whose month overlaps the analysis window.
The safety model in one line: the analysis tools (analyze_portfolio, compare_strategies, gap_night_check) and propose_decisions never change any prices; writes happen only through apply_decision/revert_decision with an explicit confirm: true, always target the listing's own channel, and every write is revertible. Without a WHEELHOUSE_API_KEY you can run the whole loop up to the dry run on demo data — real writes require the key and a fresh propose against your own portfolio. The decision log and targets live locally in NM_STATE_DIR (default ~/.night-margin).
Proactive alerts (watch mode)
check_alerts is read-only and checks two things: (1) upcoming gap nights priced below your cost floor (same data as propose_decisions, always the recommended risk preset, but this tool never saves proposals to the decision log), and (2) new bookings since the last check, each shown net of turnover costs — the same twist as the rest of the server, now in a booking notification. The first run establishes a baseline (records existing bookings without alerting on them); later runs alert on bookings that were not present in the previous check. The Wheelhouse reservation payload carries no creation timestamp, so a booking made earlier will surface the first time its dates enter the 120-day horizon — "new to this check", not "booked since yesterday".
By default check_alerts is silent — it just returns the report as text, same as every other tool. To get pushed notifications, configure a channel:
Variable | Description |
| Send alerts via a Telegram bot (checked first). |
| Send alerts to a generic incoming webhook (Slack-compatible |
Neither is required — without either, check_alerts still works and simply says no channel is configured. Notifications are off unless you set one of these — nothing is ever sent by default, and the token/URL are never logged or echoed back in error messages.
For unattended checks (e.g. a daily cron job), run the server with --watch: it runs check_alerts once (sending a notification if a channel is configured), prints the report to stdout, and exits — no MCP server, no stdio transport:
0 8 * * * WHEELHOUSE_API_KEY=... NM_TELEGRAM_BOT_TOKEN=... NM_TELEGRAM_CHAT_ID=... npx -y night-margin-mcp --watchConfiguration (env)
Every setting is optional — without them, the server runs the manual cost model on synthetic demo data. Environment variables are passed via claude mcp add's -e flags:
claude mcp add margin -e WHEELHOUSE_API_KEY=xxx -e COST_SOURCE=csv -e CSV_PATH=/path/to/costs.csv -- npx -y night-margin-mcpVariable | Default | Description |
| – | Your own Wheelhouse integration key. When set, bookings are fetched from the Wheelhouse RM API; without it, synthetic demo data is used. |
| – | Optional override for the |
|
| Source of turnover costs: |
|
| Manual mode: € per turnover (cleaning). Also serves as the fallback in CSV mode when a booking has no cost row. |
| – | Optional tiering, e.g. |
| – | CSV mode: path to the cost CSV (required when |
| – | CleanHub mode: actual turnover costs from the CleanHub field-operations platform over HTTP (both required when |
|
| Minimum margin € for the gap-night floor: floor = turnover + travel + |
|
| Local state directory for the decision log, monthly targets, and the seen-bookings set (used by |
| – | Optional: send |
| – | Optional: send |
CSV mode
If you know your actual turnover costs per booking, a CSV gives you the most accurate analysis. Schema (header row required):
reservation_id,property_id,checkin,checkout,nights,gross_revenue,cleaning_cost,travel_cost,laundry_cost,turnover_date,is_sunday_or_holiday
demo-1br-01-r1,demo-1br-01,2026-06-03,2026-06-10,7,571,56,16,9,2026-06-10,falseA synthetic sample file (887 rows, covering the full 2026 calendar year of the demo data) ships with the package at examples/sample-costs.csv, and can be downloaded directly:
https://raw.githubusercontent.com/mikki321/night-margin-mcp/main/examples/sample-costs.csvUsage:
curl -o costs.csv "https://raw.githubusercontent.com/mikki321/night-margin-mcp/main/examples/sample-costs.csv"
claude mcp add margin -e COST_SOURCE=csv -e CSV_PATH="$PWD/costs.csv" -- npx -y night-margin-mcpCost rows are matched to bookings primarily by reservation_id, and secondarily by the composite key property_id|checkin|checkout. The tool response reports the attribution quality — e.g. "Cost attribution: 289/289 bookings matched by reservation_id", or with mixed sources "Cost attribution: 41 by reservation_id, 6 by composite key, 3 by average fallback (50 total)". If a row is missing and no AVG_TURNOVER_COST fallback is set, you get a clear error listing the affected bookings.
Wheelhouse API key
Without a key, the server runs on synthetic demo data (and says so in every response). With a key, you get your real bookings from the Wheelhouse RM API: the server fetches your listings and reservations, and every metric is computed from your own portfolio.
The key is your own Wheelhouse integration key (the API's
X-Integration-Api-Keyheader). Get it from your Wheelhouse account's integration settings or from Wheelhouse support — API documentation: https://api.usewheelhouse.com/wheelhouse_rm_api.Pass it as an environment variable:
claude mcp add margin -e WHEELHOUSE_API_KEY=xxx -- npx -y night-margin-mcp.Never commit the key to git — not in a
.envfile in the repo, not in README examples, not in commits. The key travels only as an environment variable.
If the key is invalid or the network flakes, the server returns a clear error with instructions — it does not silently fall back to demo data, so you'll never mistake mock numbers for your own.
For developers
Local development:
npm install
npm test
npm run build
claude mcp add margin -- node "<repo path>/dist/index.js"If the npm package doesn't work for any reason, the same server installs directly from GitHub:
claude mcp add margin -- npx -y github:mikki321/night-margin-mcpExploring the Wheelhouse API with curl
Use these commands to verify auth and inspect response shapes (redact guest names/emails before sharing; field names and formats are preserved):
# 1) Listings — confirms auth + provides listing_id and channel
curl -s -H "X-Integration-Api-Key: $WHEELHOUSE_API_KEY" \
"https://api.usewheelhouse.com/ss_api/v1/listings?per_page=5&page=1" | head -c 4000
# 2) Reservations — THE blocking one (full JSON for 2–3 reservations)
curl -s -H "X-Integration-Api-Key: $WHEELHOUSE_API_KEY" \
"https://api.usewheelhouse.com/ss_api/v1/listings/<LISTING_ID>/reservations?channel=<CHANNEL>" | head -c 6000
# 3) Price recommendations — for gap_night_check, lower priority
curl -s -H "X-Integration-Api-Key: $WHEELHOUSE_API_KEY" \
"https://api.usewheelhouse.com/ss_api/v1/listings/<LISTING_ID>/price_recommendations?channel=<CHANNEL>" | head -c 4000The rate limit is 60 requests/min — the server runs requests serially and backs off on 429 responses (2 s / 4 s / 8 s).
Core math
net_per_night = (gross − cleaning − laundry − travel) / available nights
available = booked nights + gap nights in the analysis window
gap_floor = turnover cost + travel + MIN_MARGIN
leak = Σ(€ of net-negative bookings) and % of booked nightsAll data shipped with the repo and the package is synthetic — no real customer or property data is included.
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Latest Blog Posts
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/mikki321/night-margin-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server