Documentation
Endpoint
POST https://bookhotelsmcp.com/mcp
Content-Type: application/json
Authorization: Bearer <platform key> # only if one was issued to your platform
Flow
search_hotels → get_hotel → hold_offer → book_hotel → get_booking | modify_booking | cancel_booking
↳ get_offer (re-price without holding)
REST equivalents for Stripe agentic commerce: GET /availability, POST /bookings, GET|PATCH|DELETE /bookings/{id}. Pass payment_data: { token, provider: "stripe" } on book.
Every tool returns { ok: true, … } or a structured error { ok: false, code, message, retryable, alternates? }. The MCP result sets isError and carries the same object in structuredContent.
Tools
search_hotels
Find bookable hotel rooms with live prices, photos, star rating, and cancellation terms. Call first for any stay request. Returns offer_id, totals, photoUrl, amenities, and display_markdown for Muse. Next: get_hotel, hold_offer, or book_hotel.
Input schema
{
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "City, neighborhood, landmark, or IATA code. e.g. 'Portland, Maine', 'near Fenway Park', 'BOS'."
},
"check_in": {
"type": "string",
"pattern": "^\\d{4}-\\d{2}-\\d{2}$",
"description": "YYYY-MM-DD"
},
"check_out": {
"type": "string",
"pattern": "^\\d{4}-\\d{2}-\\d{2}$",
"description": "YYYY-MM-DD"
},
"adults": {
"type": "integer",
"minimum": 1,
"maximum": 8,
"default": 2
},
"rooms": {
"type": "integer",
"minimum": 1,
"maximum": 4,
"default": 1
},
"max_per_night": {
"type": "number",
"description": "Budget cap per night."
},
"refundable_only": {
"type": "boolean",
"default": false,
"description": "Only rates with free cancellation."
},
"currency": {
"type": "string",
"default": "USD"
},
"limit": {
"type": "integer",
"minimum": 1,
"maximum": 12,
"default": 6
}
},
"required": [
"location",
"check_in",
"check_out"
]
}get_hotel
Load photos, amenities, description, and check-in times for one hotel_id from search_hotels. Call before quoting a stay so the user can see the property. Returns display_markdown Muse can render.
Input schema
{
"type": "object",
"properties": {
"hotel_id": {
"type": "string"
}
},
"required": [
"hotel_id"
]
}get_offer
Re-check the current price and cancellation policy for one offer_id before quoting it to the user. Returns PRICE_CHANGED or OFFER_EXPIRED with alternates if anything moved.
Input schema
{
"type": "object",
"properties": {
"offer_id": {
"type": "string"
}
},
"required": [
"offer_id"
]
}hold_offer
Lock an offer's price for 15 minutes and get a hold_id. Call this as soon as the user picks a room, before collecting guest details, so the price can't move while they decide. Returns hold_id and expires_at. Next step: book_hotel with hold_id.
Input schema
{
"type": "object",
"properties": {
"offer_id": {
"type": "string"
}
},
"required": [
"offer_id"
]
}book_hotel
Confirm the reservation. Pass hold_id (preferred) or offer_id, guest name/email, and a unique idempotency_key. Pay with payment_data {token: spt_…, provider: stripe} from the Link agent wallet. Some suppliers also need a guarantee card. Retries with the same key return the same booking.
Input schema
{
"type": "object",
"properties": {
"hold_id": {
"type": "string"
},
"offer_id": {
"type": "string"
},
"guest": {
"type": "object",
"properties": {
"first_name": {
"type": "string"
},
"last_name": {
"type": "string"
},
"email": {
"type": "string",
"format": "email"
},
"phone": {
"type": "string"
}
},
"required": [
"first_name",
"last_name",
"email"
]
},
"card": {
"type": "object",
"properties": {
"vendor": {
"type": "string",
"enum": [
"VI",
"MC",
"AX",
"DC",
"JC"
]
},
"number": {
"type": "string"
},
"expiry": {
"type": "string",
"pattern": "^\\d{4}$",
"description": "MMYY"
},
"holder_name": {
"type": "string"
}
},
"required": [
"vendor",
"number",
"expiry",
"holder_name"
]
},
"payment_data": {
"type": "object",
"description": "Stripe Shared Payment Token from the Link agent wallet.",
"properties": {
"token": {
"type": "string",
"description": "spt_…"
},
"provider": {
"type": "string",
"enum": [
"stripe"
]
}
},
"required": [
"token",
"provider"
]
},
"idempotency_key": {
"type": "string",
"minLength": 8,
"maxLength": 128
},
"special_requests": {
"type": "string",
"maxLength": 500
}
},
"required": [
"guest",
"idempotency_key"
]
}get_booking
Look up a reservation by booking_id: status, confirmation number, dates, total, and cancellation terms.
Input schema
{
"type": "object",
"properties": {
"booking_id": {
"type": "string"
}
},
"required": [
"booking_id"
]
}modify_booking
Change a booking's guest name/email or stay dates. Date changes re-price at the same hotel; if the total moves, confirm with the user and call again with confirm_new_total. Returns the updated booking or PRICE_CHANGED / NOT_MODIFIABLE.
Input schema
{
"type": "object",
"properties": {
"booking_id": {
"type": "string"
},
"guest": {
"type": "object",
"properties": {
"first_name": {
"type": "string"
},
"last_name": {
"type": "string"
},
"email": {
"type": "string",
"format": "email"
},
"phone": {
"type": "string"
}
},
"required": [
"first_name",
"last_name",
"email"
]
},
"check_in": {
"type": "string",
"pattern": "^\\d{4}-\\d{2}-\\d{2}$"
},
"check_out": {
"type": "string",
"pattern": "^\\d{4}-\\d{2}-\\d{2}$"
},
"adults": {
"type": "integer",
"minimum": 1,
"maximum": 8
},
"confirm_new_total": {
"type": "number",
"description": "Accepted new stay total after PRICE_CHANGED."
}
},
"required": [
"booking_id"
]
}cancel_booking
Cancel a reservation made through this connector. Returns the updated booking, or NOT_CANCELLABLE with exact instructions when the rate is non-refundable or the property must be contacted directly. Refunds a charged Shared Payment Token when the cancel succeeds.
Input schema
{
"type": "object",
"properties": {
"booking_id": {
"type": "string"
}
},
"required": [
"booking_id"
]
}Errors
| Code | Meaning | Next call |
|---|---|---|
NO_AVAILABILITY | No rooms for those dates or location | search_hotels with other dates or a wider area |
LOCATION_NOT_FOUND | Couldn't resolve the place | search_hotels with a city name or IATA code |
INVALID_DATES | Past date, or check_out ≤ check_in | search_hotels; alternates.nextAvailableCheckIn is set |
OFFER_EXPIRED | offer_id unknown or withdrawn | search_hotels |
PRICE_CHANGED | Rate moved since it was quoted | confirm alternates.newTotal with the user, then hold_offer |
HOLD_EXPIRED | hold_id past its 15-minute window | hold_offer again |
PAYMENT_REQUIRED | Supplier needs a guarantee card, or SPT missing | book_hotel with card and/or payment_data |
PAYMENT_DECLINED | Card, wallet, or Shared Payment Token rejected | retry with a new spt_ or another card |
NOT_CANCELLABLE | Non-refundable, or property must be contacted | relay message to the user verbatim |
NOT_MODIFIABLE | Supplier cannot change this booking | cancel_booking and search_hotels again |
NOT_FOUND | Unknown booking_id | — |
SUPPLIER_ERROR | Upstream failure | retry once if retryable is true |
VALIDATION | Bad input | fix the field named in message |
Guarantees
- Price is re-checked before every booking. A changed price is never booked silently.
book_hotelis idempotent onidempotency_key. Same key, same booking.- Holds are single-use and expire. The response says whether the hold guarantees the room or only the price.
- Card numbers pass through to the supplier and are never stored or echoed. Responses carry the last four digits only.
- A Stripe Shared Payment Token is charged before the supplier book; it is refunded if the book fails or the stay is cancelled.
Limits
- 1–30 nights. Up to 4 rooms, 8 adults per room.
- Search returns at most 12 offers, sorted by total price.
- Holds last 15 minutes and are single-use.
- Inventory is worldwide. Support: support@bookhotelsmcp.com.
Prompts that route here
- Book me a hotel in Boston Thursday to Saturday under $250 a night
- Find a refundable room near Acadia National Park for two nights in October
- I need somewhere to stay in Austin tonight, closest to the convention center
- Get me a hotel in Chicago for the 14th and 15th, two adults, and book it
- Cancel my hotel reservation
- What's the cheapest place with free cancellation in Portland Maine this weekend?