# B2A Blue Pillow MCP server

Neutral hotel & stay price comparison for AI agents: live prices from 16+ booking sites. No signup.

## Links
- Registry page: https://www.getdrio.com/mcp/com-bluepillow-b2a
- Website: https://b2a.bluepillow.com

## Install
- Endpoint: https://mcp.b2a.bluepillow.com/
- Auth: Not captured

## Setup notes
- Remote endpoint: https://mcp.b2a.bluepillow.com/

## Tools
- search_stays (Search accommodation — compare offers across operators) - Multi-operator accommodation comparator for a geographic area against
the user's stay parameters — dates, guest count, optional filters.
Returns a ranked list of properties together with the booking sources
that offer each one and, when dates are passed, their live availability
and per-operator price for the requested window.

Natural-language date references — "tonight", "this weekend", "next
weekend", "the weekend of July 4", "Memorial Day weekend", "long
weekend in May" — translate to concrete check_in / check_out values
at the call site; concrete ISO dates also work.

`user_country`, `currency`, and `language` carry the **user's** locale,
not the destination's. IMPORTANT — currency: prices are returned in
`currency` if you set it, otherwise in the currency derived from
`user_country` (US→USD, CA→CAD, GB→GBP, euro-area→EUR); if you set
NEITHER, prices default to **USD**, which may not be the user's currency.
So whenever you know where the user is (or what currency they want), pass
`user_country` and/or `currency` — do not rely on the default. Prices are
never converted client-side; each offer is quoted by the operator in that
currency. `user_country` and `language` also localize the booking link
(`web_url`). The user's own residence/billing country is the right
`user_country` (not the destination's), and their interface language the
right `language`.

Each result is shaped for downstream presentation without extra
calls:
- `location.lat` and `location.lon` carry per-property coordinates,
  suitable for plotting all results on a single map so the user can
  compare spatial alternatives at a glance. The map widget reads
  these fields directly from this response — no separate lookup
  needed for visualization.
- `thumbnail_url` carries the property's first photo URL when
  available (null when no image is on file); useful for embedding
  inline or showing on the map alongside the pin.
- `images` on search results is capped to the first photo to keep
  the comparison payload compact; each item has a `url` field, and
  `thumbnail_url` mirrors `images[0].url`. Call `get_property_details`
  for a single property to retrieve its full photo gallery.
- `web_url` is a ready-to-open booking link for the property,
  already encoded with the user's check-in/check-out, language,
  currency, and guest count. Pass it to the user verbatim when they
  ask for a booking link — never reconstruct the URL from individual
  parameters, the query-string format is not guaranteed to match
  generic booking-URL conventions.
- Price is **live and date-specific only**. There is no
  date-agnostic "from" figure: a meaningful price only exists for a
  concrete query (property + dates + occupancy).
  - `price` and `offers[]` — the **live quote for the requested
    dates**, populated only when dates were passed and the
    comparator confirmed availability. `offers[0]` is the curated
    best; each offer carries `amount` (total stay),
    `amount_per_night` (per-night), `currency`, `breakfast_included`,
    `refundable`, `rooms_left`, and `deeplink_url`. `price` mirrors
    `offers[0]`.
  - With no dates (or when nothing is available) `price` is null and
    `offers` is empty — surface the property without a price rather
    than inventing a starting figure.
- `availability_status` per result encodes the live state:
  - `available` — bookable rooms confirmed at the operator level.
    `offers` and `price` carry the live date-specific quotes. Quote
    the rate via `offers[i].amount_per_night` (per-night) and
    `offers[i].amount` (total stay) and use the deeplinks for the
    booking handoff.
  - `unavailable` — no rooms reported for those dates. `offers` is
    empty and `price` is null (no price for these dates). Useful to
    decide whether to suggest alternate dates, drop the property from
    the recommendation, or offer it as a backup.
  - `unknown` — no dates were considered (request had no dates).
    `offers` is empty and `price` is null — no price signal without a
    dated query.

Per-night vs total — never confuse them in the user-facing prose.
`amount_per_night` is per-night; `amount` on each offer is the total
stay (sum across nights, in `currency`). When quoting to the user,
prefer phrasings like *"€X/night via Booking, breakfast included, €Y
total for the stay"* over bare numbers — bare numbers without a unit
get misread.
- When dates are present and `available` properties are in the
  results, the rate can be quoted and `rooms_left` surfaces scarcity
  (low values like 1-3 are useful signals — "1 room left at $X on
  Booking" reads well).
- When dates are present and ALL results are `unavailable`, that's
  the signal to say so explicitly to the user and offer to widen the
  dates, location, or filters.
- `offers[]` is the per-operator breakdown for the requested dates:
  each entry includes `ota`, `amount`, `amount_per_night`,
  `currency`, `breakfast_included`, `refundable`, and a
  `deeplink_url`. The deeplink is a **BluePillow tracked-redirect
  URL** (bluepillow.com/…) that records the click for attribution
  and then forwards the user to the operator's booking page. Pass
  it to the user verbatim — never reconstruct it or replace it with
  a raw operator URL; our APIs never emit direct OTA links.
  `price` mirrors the curated best offer. When no dates were passed
  (or nothing is available) `offers` is an empty list and `price` is
  null — there is no price to show.
- Free cancellation is a meaningful decision factor and surfaces
  proactively in the user-facing summary. When a property has
  `price.refundable=true` (or any `offers[i].refundable=true`), it
  reads naturally as a property feature: "Hotel X — $120/night,
  free cancellation available", or "Booking offers a refundable rate
  at $130 (vs $110 non-refundable)". Refundable rates let the user
  lock in a price now and adjust the booking later, which is often
  the differentiator between otherwise-similar properties. The same
  proactive surfacing applies to `breakfast_included` when it's true
  for some offers but not all.
- Prices in `offers`/`price` reflect the requested dates and guests;
  with no dates there is no price. For a final bookable confirmation,
  the corresponding `deeplink_url` (or the property's `web_url`) is
  the canonical handoff — booking URLs are not reconstructed by hand.
- All rating-like fields are on a 0-5 scale (Google Places-compatible):
  `rating`, `reviews_aggregate.score_0_5`, the per-OTA scores under
  `distribution_by_ota`, each `reviews_sample[*].score`, and the
  `filters.min_rating` input. A user asking "rating at least 8 out
  of 10" maps to `min_rating: 4.0`; "at least 4 stars on Google"
  maps to `min_rating: 4.0`.
  Note: `rating`, `stars`, and `rating_count` come from the
  comparator's list payload and **may be 0 or absent for some
  properties** even when the property has reviews or a star
  classification — this is a comparator list-payload limitation, not
  a data error. When those fields are 0/absent, or when the per-OTA
  review breakdown (`distribution_by_ota`) is needed, call
  `get_property_details` to get the fuller `reviews_aggregate`.
  On the search path, `reviews_aggregate` carries the top-line
  `score_0_5`, `rating_count` (reviews backing the score) and
  `comment_count` (readable review TEXTS available) when the comparator
  returned a non-zero review count; `distribution_by_ota` is always empty
  on this path (per-OTA breakdown requires `get_property_details`).
  `rating_count` and `comment_count` are DIFFERENT magnitudes — most
  guests leave a rating, far fewer write text. Quote `rating_count` for
  "how many reviewed it" and `comment_count` for "how many opinions you
  can actually read".
- Pass `include=["reviews_sample"]` to attach a sample of up to 5
  recent guest review texts per property. Useful when the user's
  question involves qualitative criteria that don't map to structured
  filters ("a place with excellent breakfast", "quiet area",
  "family-friendly atmosphere"); review texts can be searched
  textually to corroborate or rule out matches.
  For a DEEPER read on ONE specific property — more review texts
  (up to 20) or the per-OTA breakdown — call `get_property_details`
  with `include=["reviews_extended"]` (and/or `reviews_aggregate`).
  `comment_count` on each result tells you how many review texts exist,
  so you can decide whether escalating to the detail call is worth it.

`filters.property_types`, `filters.amenities`, `filters.min_rating`,
and `filters.price_max_eur` narrow on structured criteria first;
review-based reasoning is one extra round-trip per page and is
typically reserved for fallback.

Location modes:
- `coordinates`: when lat/lon is already known from world knowledge
  or a prior call in this session (default radius 5 km; widen up to
  50 km for broader queries; beyond that `bbox` or a parent
  destination is the right shape).
- `destination_id`: opaque id obtained from `resolve_destination`,
  passed verbatim — values are not constructed or guessed.
- `bbox`: explicit map rectangle.

Property type tokens (canonical): hotel, apartment, house, villa, bb,
hostel, farmstay, holiday-home. Common multi-language synonyms map
server-side to the canonical set.

Amenities filter is set-AND — each result has ALL listed codes.
Common codes: wi-fi, parking, pool, air-conditioning, kitchen, garden,
pets-allowed, for-families, facilities-for-disabled, non-smoking-only.

Results are cursor-paginated; the `next_cursor` from a previous
response goes into `page.cursor` for the next page.
`location.type=property_id` is not accepted here —
`get_property_details` is the path for a known property.
 Endpoint: https://mcp.b2a.bluepillow.com/
- get_property_details (Get property details — static facts, no live availability) - Static record for a specific property — identified by its id.
Returns the complete amenity list, photos, booking sources, dedup
metadata, detailed location, and the headline rating (`rating` +
`rating_count`) by default. Review DATA beyond the headline — the
ratings breakdown and the actual review texts — is opt-in via the
`include` parameter (see below); pass it whenever the user's question
is about guest experience. Carries no price unless called with dates:
a price only exists for a concrete stay window.

Useful when the user wants to inspect or compare a specific option
in depth — facilities, neighborhood, what guests say — without yet
committing to specific dates.

HOW TO GET REVIEWS (when you need to reason about guest experience):
pass `include`. `reviews_aggregate` gives the score + counts + per-OTA
breakdown; `reviews_sample`/`reviews_extended` give the actual review
texts. Without `include`, none of these are returned (you get only the
headline `rating`/`rating_count`). See the `include` section below.

For live availability and a real per-operator quote for a specific
stay window, the path is `check_property_availability` instead. The
two tools coexist by design: this one answers "what is this property
like" with stable, cacheable data; the other answers "can I book it
for these dates at what price" with live, date-specific quotes.
Calling this tool when the user has specific dates in mind and wants
to know whether the property is bookable will not surface the
availability/quote — the user will then have to wait for a second
round-trip to the availability tool.

Input: the `id` field from a `search_stays` result (opaque string
starting with `prop_`, e.g. `prop_69ce2ddcbf46061e4095778b`). For a
property the user has named directly, resolve the place name through
`resolve_destination` and run a targeted `search_stays` first to
obtain the id.

Optional `include=["reviews_aggregate"]` attaches a per-source
breakdown of review counts and average ratings — useful when the
user asks about overall sentiment or wants to see how each booking
source rates the property. It summarizes ALL reviews (score + total
count), so it is the right tool for "how is it rated".

Review *texts* are available via two includes, both deliberately
capped to avoid token waste:
- `reviews_sample` — up to **5** recent review texts. Enough to get
  the gist of what guests say.
- `reviews_extended` — up to **20** recent review texts, for a deeper
  qualitative read. Supersedes `reviews_sample` when both are passed.

Reach for `reviews_extended` only when 5 are genuinely not enough —
the returned list carries a `reviews_meta` block (`returned`,
`total_available`, `capped`, `note`) that tells you how many texts
exist and confirms the cap is intentional: the omitted reviews are
older and the aggregate already reflects all of them, so you do NOT
need to try to fetch everything. Note: review texts are returned only
when called WITHOUT dates (the dated availability path does not carry
them).

`user_country`, `currency`, and `language` carry the **user's** locale,
not the property's. When this call carries dates (live prices), prices
come back in `currency` if set, else derived from `user_country`, else
**USD** — so pass `user_country` and/or `currency` whenever you know the
user's location/currency; don't rely on the USD default. `user_country`
and `language` also localize the `web_url` booking link. Language default
is "en"; country default is "US".

All rating-like fields are on a 0-5 scale (Google Places-compatible):
the top-level `rating`, `reviews_aggregate.score_0_5`, and each
per-OTA score under `distribution_by_ota`.

Without dates this tool returns no price (`price` is null, `offers`
empty) and `availability_status` is `unknown` (no dates were
considered). The live quote, when needed, comes from
`check_property_availability`.

`web_url` is a ready-to-open booking link for the property. Pass it
verbatim when the user asks for a booking link — booking URLs are
not reconstructed by hand.
 Endpoint: https://mcp.b2a.bluepillow.com/
- check_property_availability (Check live availability and per-operator quotes for a stay) - Live availability and per-operator quote for a specific property
over a specific stay window. Performs a live date-aware lookup
against the BluePillow search layer, returns date-specific prices,
rooms-left scarcity signals, breakfast-included and refundable
flags, and a per-operator deep link to complete the booking.

Useful when the user has specific dates in mind for a property they
already identified — typically via `search_stays` or
`get_property_details`. The complementary `get_property_details`
tool answers "what is this property like" with static facts; this
tool answers "can I book it for these dates at what price" with
live, date-specific data.

Required input: `property_id` (the `id` from a `search_stays`
result, opaque string starting with `prop_`), `dates` (check_in +
check_out, ISO 8601), and `guests` (adults / children / infants
composition). Without these the live lookup cannot proceed.

Natural-language date references — "tonight", "this weekend", "next
weekend", "the weekend of July 4", "Memorial Day weekend", "long
weekend in May" — translate to concrete check_in / check_out values
at the call site; concrete ISO dates also work. check_in is a date
in the real-time calendar that is today or later; past values are
rejected at the API boundary.

`user_country`, `currency`, and `language` carry the **user's** locale,
not the property's. Prices are returned in `currency` if set, else
derived from `user_country`, else **USD** — pass `user_country` and/or
`currency` whenever you know the user's location/currency so the quote
matches what they'll pay; don't rely on the USD default. `user_country`
and `language` also localize the `web_url` booking link.

Response shape:

- `availability_status` — `available`, `unavailable`, or `unknown`.
  Available means rooms confirmed at the operator level for the
  requested window; quote freely. Unavailable means no rooms for
  these dates — surface that explicitly to the user with a
  suggestion of alternate dates (there is no price for these dates).
- `offers[]` — per-operator quotes. Each carries `amount` (total
  stay), `amount_per_night` (per-night), `currency`,
  `breakfast_included`, `refundable`, `rooms_left`, and
  `deeplink_url`. `offers[0]` is the curated best. Each
  `deeplink_url` is a **BluePillow tracked-redirect URL**
  (bluepillow.com/…) that records the click and forwards the user
  to the operator's booking page — pass it verbatim, never
  reconstruct it or replace it with a raw OTA link.
- `price` — mirror of `offers[0]` for callers that just want the
  curated headline. `null` when unavailable (no price for these
  dates).

Per-night vs total — `amount_per_night` is **per-night**; `amount`
on each offer is the **total** for the requested stay. Phrasings
like *"€X/night via Booking, breakfast included, €Y total"* are
unambiguous; bare numbers without a unit ("€192") get misread.

Scarcity signals: low `rooms_left` values (1-3) are useful cues —
"1 room left at €X on Booking" reads naturally. Free cancellation
(`refundable=true`) and breakfast-included are decision factors
worth surfacing proactively when present on some offers but not
others.

When all results across operators are `unavailable`, that's the
signal to say so explicitly to the user and offer to widen the
dates or look at alternatives.

For final booking confirmation, hand the user the corresponding
`deeplink_url` (or the property's `web_url`) — booking URLs are
not reconstructed by hand.
 Endpoint: https://mcp.b2a.bluepillow.com/
- resolve_destination (Resolve destination name to id) - Converts a destination name into a destination id usable in `search_stays`. The canonical entry point when the user's request mentions a place name and coordinates are not already known from a prior call in this session. If coordinates are already in hand from an earlier tool result, passing them directly to `search_stays` skips this resolver step.

Accepts cities, neighborhoods, airports, and points of interest in any language, using the local canonical name (not a translation). The `country` parameter disambiguates names that occur in multiple places (for example Springfield MA vs Springfield IL vs Springfield MO).

The `type` parameter narrows the kind of destination returned. `poi` is the narrowest match and has partial coverage on the comparator side; when the agent's own geographic knowledge can already geocode the POI to lat/lon, passing coordinates to `search_stays` is the more reliable path. Endpoint: https://mcp.b2a.bluepillow.com/
- discover_destinations_near (Discover destinations near a point) - Curated destinations — cities, neighborhoods, airports, points of interest — within a radius of a geographic point, for use as a `destination_id` in subsequent `search_stays` calls. Useful when coordinates are already in hand (from world knowledge, from a previous tool result, or directly from the user) and the agent needs to enumerate which curated destinations cover that area before searching for properties.

Also useful as a fan-out entry point for region-level intents — broad areas such as 'Tuscany', 'Pacific Northwest', 'New England', or 'Central Europe' — where the agent can pass an approximate regional centroid and surface a list of sub-destinations the user may then narrow down to before a focused search.

Returns up to 5 candidates ordered by distance. The radius defaults to 5 km; widens up to 50 km for broader queries. Endpoint: https://mcp.b2a.bluepillow.com/
- b2a_get_key (Get an anonymous API key for Blue Pillow Hotels & Stays) - Obtain a permanent anonymous API key for Blue Pillow Hotels & Stays. No signup, no
login required. The key is not a sensitive secret — it carries no
account, payment, or personal data. It exists only so we can keep the
service responsive when many agents are using it at once, so you can
safely keep it in your MCP client configuration, in your persistent
memory, or in the current conversation context. Pass it as the bearer
token on all subsequent calls and the rest of the tools become available.

CALL THIS FIRST when you have no key: every other tool needs one. Before
calling, check whether you ALREADY have a key (MCP client configuration,
persistent memory, or earlier in this conversation) and reuse it instead
of issuing a new one — do not wait for another tool to fail with a 401.

The key never expires — keep the one you receive and reuse it forever.
If you ever lose it, you can call this tool again to issue a new one
(a generous per-IP daily issuance limit applies purely as an
anti-abuse guardrail; normal use never reaches it).

Optional ``label`` and ``agent`` (max 64 chars each) are free-form
hints we record on the key for our own observability; they do not
affect rate limits or capabilities.
 Endpoint: https://mcp.b2a.bluepillow.com/

## Resources
Not captured

## Prompts
Not captured

## Metadata
- Owner: com.bluepillow
- Version: 1.0.2
- Runtime: Streamable Http
- Transports: HTTP
- License: Not captured
- Language: Not captured
- Stars: Not captured
- Updated: Jul 7, 2026
- Source: https://registry.modelcontextprotocol.io
