Mydatavalue
FreeNot checkedMCP server for querying MyDataValue's Booking.com and Airbnb property data, including pricing, promotions, reviews, and performance metrics, via a read-only con
About
MCP server for querying MyDataValue's Booking.com and Airbnb property data, including pricing, promotions, reviews, and performance metrics, via a read-only connector with automatic OAuth token rotation.
README
A small remote MCP (Model Context Protocol) server that lets Claude query the MyDataValue API — Booking.com + Airbnb properties, pricing, promotions, ranking, reviews, demand, compsets, performance, and the change log — as a native connector, instead of copying curl commands around.
It is read-only by design (GET requests only), matching the scope of the MyDataValue API key it was issued.
Why this exists / how the OAuth dance is handled
MyDataValue issues a refresh_token that:
- is exchanged for a 1-hour
access_tokenviaPOST /oauth/token - rotates on every exchange — the old
refresh_tokenstops working the instant a new one is issued - gets your whole integration shut off if an old, already-used
refresh_tokenis replayed (their theft-detection)
That means the refresh token can never live only in a chat transcript, a
.env file on a laptop, or this repo — it has to be persisted durably by
whatever is running the service, updated atomically on every rotation, and
never reused. src/tokenStore.js + src/mdvClient.js do exactly that:
- the current
refresh_token/access_token/ expiry live in a JSON file atTOKEN_STORE_PATH(defaults to/data/token-store.json— point this at a mounted persistent volume in production) - writes are atomic (temp file + rename) so a crash mid-write can't corrupt the store or leave you holding a half-written token
- concurrent requests are coalesced onto a single in-flight refresh, so two simultaneous tool calls can never race each other into burning the refresh token twice
- 401s trigger exactly one forced refresh + retry; 429s honor
Retry-After
A note on durability
The first version of this service persisted the rotating refresh_token to
a Railway volume mounted at /data. In testing, that turned out not to
reliably survive redeploys on this service — a plain redeploy with zero code
or config changes came back up unable to find the file it had written
seconds earlier, twice in a row. Railway's own docs say this shouldn't
happen for a volume-attached service, so the exact cause is unconfirmed
(possibly an interaction with this service's multiRegionConfig), but it's
reproducible here, and reusing a stale refresh_token is exactly the
failure mode MyDataValue treats as a stolen-token replay and locks accounts
over — so this service does not rely on the volume for correctness.
Instead, MDV_SEED_REFRESH_TOKEN is the durable source of truth, and it's
kept current in place: every time the service rotates the token, it calls
Railway's public API (variableUpsert, with skipDeploys: true so it
doesn't force a redeploy) to overwrite MDV_SEED_REFRESH_TOKEN with the new
value. Env vars have reliably round-tripped through every redeploy in
testing, unlike the volume. The next boot - whenever that happens - reads
the current value straight from that env var. src/railwayVariable.js
implements this; it needs RAILWAY_PROJECT_TOKEN, RAILWAY_PROJECT_ID, and
RAILWAY_ENVIRONMENT_ID (RAILWAY_SERVICE_ID optional) to be set. Without
those, the service still runs (rotations just live only in memory), but it
logs a loud warning on every rotation, because a restart before persistence
succeeds means the next boot replays a now-stale token.
The local file at TOKEN_STORE_PATH is still written on a best-effort
basis (useful for poking at from Railway's file browser while debugging),
but nothing in this service depends on it being there.
Endpoints wired up
Confirmed against MyDataValue's own OpenAPI reference (Booking.com uses
property_id path params under /booking/...; Airbnb uses listing_id
path params under /airbnb/..., and its collection is specifically named
listings, not properties):
list_properties—/booking/properties/,/airbnb/listings/get_property— single property/listing detail, by idget_pricing—/{channel}/pricing/{id}/get_compset—/{channel}/compset/{id}/get_promotions—/{channel}/promotions/, filterable by id, paginatedget_ranking—/{channel}/ranking/get_reviews—/{channel}/reviews/get_performance—/{channel}/performance/get_demand—/booking/demand/(Booking.com only — MyDataValue doesn't expose an Airbnb demand endpoint)get_change_log—/change-log/(a single team-wide feed, not per-channel)get_cancellation_policy_options—/airbnb/cancellation-policy-options/{listing_id}/(Airbnb only)mdv_raw_get— a generic, read-only passthrough for anything above that's missing a param, plus endpoints intentionally left out of scope (auto-refresh state, sync-jobs, tags, webhooks — these are either write-oriented or not part of what this connector's token was issued to read)
A note on MyDataValue's own hosted MCP server
MyDataValue's docs mention they also run an official hosted MCP server at
https://mcp.mydatavalue.com/mcp — sign in with your MyDataValue account
directly, no Railway service needed. It's a legitimate alternative, but per
their own docs it only covers properties, pricing, promotions, and the
write levers to act on them; it doesn't expose ranking, reviews, demand,
compsets, or the change log the way the REST API (and this connector) does.
Worth knowing about if you ever want to act on pricing/promotions from
chat, not just read — this connector is deliberately read-only.
Running locally
cp .env.example .env # fill in MDV_CLIENT_SECRET, MDV_SEED_REFRESH_TOKEN, MCP_ACCESS_TOKEN
npm install
npm start
Deploying
Designed to run on Railway (or any host that gives you a persistent volume):
- Deploy this repo.
- Attach a persistent volume, mounted at
/data. - Set env vars:
MDV_CLIENT_ID,MDV_CLIENT_SECRET,MDV_SEED_REFRESH_TOKEN(kept current automatically after the first rotation - see "A note on durability" above),TOKEN_STORE_PATH=/data/token-store.json(best-effort only),MCP_PUBLIC_URL(the exact public HTTPS URL Railway gives the service, no trailing slash),MCP_OAUTH_CLIENT_ID,MCP_OAUTH_CLIENT_SECRET(generate withopenssl rand -hex 32), andMCP_ACCESS_TOKEN(alsoopenssl rand -hex 32— this is now the one-time consent passphrase, see below, not a request header). - For durable refresh-token persistence, create a Railway Project
Token (Project Settings → Tokens, scoped to this project) and set it
as
RAILWAY_PROJECT_TOKEN, plusRAILWAY_PROJECT_IDandRAILWAY_ENVIRONMENT_ID(andRAILWAY_SERVICE_IDif you want variable updates scoped to just this service). Skipping this makes rotations memory-only - fine for a quick test, risky for anything left running. - Generate a public domain.
Connecting it in Claude
Claude's "Add custom connector" dialog only takes a server URL plus,
optionally, an OAuth Client ID and OAuth Client Secret in Advanced
settings — there's no field for a raw bearer header. So this server
implements a minimal (but spec-following) OAuth 2.1 authorization server in
front of /mcp: RFC 9728 protected resource metadata, RFC 8414
authorization server metadata, and the authorization_code + PKCE grant plus
refresh_token, per the MCP Authorization spec.
There's exactly one pre-shared client (this connector) - no Dynamic Client
Registration, which the spec allows as an alternative.
To connect:
- In Claude, add a custom connector with URL
https://<your-domain>/(either the bare domain orhttps://<your-domain>/mcpworks - both are handled, since it's easy to paste one or the other into that one field). - In Advanced settings, enter
MCP_OAUTH_CLIENT_IDas the OAuth Client ID andMCP_OAUTH_CLIENT_SECRETas the OAuth Client Secret. - Claude will open
/authorizein a browser tab. Enter theMCP_ACCESS_TOKENvalue as the passphrase to approve the connection. This is a one-time step (until the issued token expires/is revoked).
MCP_OAUTH_ALLOWED_REDIRECT_URIS defaults to * (accept any redirect_uri)
on first deploy, since Claude's exact callback URL isn't documented. Once
you've connected once, check the deploy logs for the redirect_uri that
was actually used ([oauthServer] /authorize request - redirect_uri=...)
and set MCP_OAUTH_ALLOWED_REDIRECT_URIS to that exact value (comma-separate
if there's more than one) to close the open-redirect surface.
Security notes
MCP_ACCESS_TOKENgates the one-time/authorizeconsent step — anyone who has it (and the client ID) can complete the OAuth flow and mint a token. Treat it like a password. It is no longer sent as a request header.MCP_OAUTH_CLIENT_SECRETand the issued access/refresh tokens are the keys to/mcpitself going forward - keep them as secret asMCP_ACCESS_TOKEN.MDV_CLIENT_SECRETand the rotating MyDataValuerefresh_tokennever leave the server process, Railway's env var store, and (best-effort) its volume. They are not logged in full (only the last 6 characters, for audit purposes).RAILWAY_PROJECT_TOKENcan rewrite this project's env vars - treat it with the same care as the other secrets here.- If MyDataValue ever locks you out for a suspected replayed token, contact
them to get re-issued, then set a fresh
MDV_SEED_REFRESH_TOKENbefore redeploying.
Installing Mydatavalue
This server has no published package — it is built from source. Open the repository and follow its README.
▸ github.com/Elev8-OS/mydatavalue-mcpFAQ
Is Mydatavalue MCP free?
Yes, Mydatavalue MCP is free — one-click install via Unyly at no cost.
Does Mydatavalue need an API key?
No, Mydatavalue runs without API keys or environment variables.
Is Mydatavalue hosted or self-hosted?
A hosted option is available: Unyly runs the server in the cloud, no local setup required.
How do I install Mydatavalue in Claude Desktop, Claude Code or Cursor?
Open Mydatavalue on unyly.org, pick your client tab (Claude Desktop, Claude Code, Cursor) and press Install — the config is generated automatically, no JSON editing.
Related MCPs
GitHub
PRs, issues, code search, CI status
by GitHubFilesystem
Secure file operations with configurable access controls.
Memory
Knowledge graph-based persistent memory system.
Template MCP Server
A CLI tool to create a new Model Context Protocol server project with TypeScript support, dual transport options, and an extensible structure
by mcpdotdirectCompare Mydatavalue with
Not sure what to pick?
Find your stack in 60 seconds
Author?
Embed badge for your README
Browse similar
All development MCPs
