Repository navigation
agents mcp hotelbeds
The HotelBeds MCP playbook lives at
automation/agents/mcp/hotelbeds.yaml.
It exposes the HotelBeds APITUDE Hotel Booking product to domain
playbooks through the NoETL tool: kind: playbook MCP interface. The
travel itinerary planner dispatches to it for hotel search and
(TEST/SANDBOX) booking. Two sibling playbooks cover the other two
APITUDE products: hotelbeds-activities.yaml and
hotelbeds-transfers.yaml.
| Tool | Purpose |
|---|---|
search_hotels |
Availability search (POST /hotel-api/1.0/hotels) for a destination + date range. Returns hotels with a bookable rateKey per rate, enriched with photo URLs from the Content API (see below). |
book_hotel |
TEST/SANDBOX only. checkRate re-prices the rateKey (POST /hotel-api/1.0/checkrates), then confirms (POST /hotel-api/1.0/bookings). Refuses to run unless the API base is a sandbox host. Required: rate_key. |
list_destinations |
Debug helper — resolve destination codes. |
tools/list |
MCP discovery — lists the tools above. |
Every HotelBeds call carries three headers:
-
Api-key— the APITUDE key. -
X-Signature—SHA256(apiKey + secret + current_unix_time_seconds)hex digest, recomputed per request (short TTL). -
Accept-Encoding: gzip— HotelBeds requires gzip.
The key + secret resolve from the GSM secret hotelbeds-hotels-test
through the worker pod's Workload Identity — they are not in the
playbook or in worker env. See
Execution model — secrets rule.
HotelBeds availability responses do not carry images, so hotel
cards were previously blank. search_hotels now makes one extra call
to the Content API after availability:
GET /hotel-content-api/1.0/hotels?fields=images&codes=<all availability codes>
One call covers every hotel code in the result set. Each returned
image path maps to the GIATA CDN:
https://photos.hotelbeds.com/giata/bigger/<path>
The URLs land on each hotel's photos array. Enrichment is fully
graceful — a Content API failure leaves photos: [] and never fails
the search. The travel planner's _hotel_card maps photos onto the
widget card; the SPA PhotoStrip renders photos[0] with an onError
placeholder (no frontend change was needed). See the
travel wiki planner page.
book_hotel is guarded: _is_sandbox(base) must be true
(api.test.hotelbeds.com or a sandbox marker) or the tool refuses
to issue a booking — no real money can move.
Rates drift between availability and booking; checkRate is the
authoritative re-price. book_hotel books the checkRate-fresh
rate within a configurable tolerance:
-
workload.book_tolerance— percent, default 15 (HotelBeds' own default is 2.0, too tight for sandbox price drift). - If the re-price stays inside tolerance, the booking confirms and
price_changedrecords that the amount moved. - If it exceeds tolerance, the tool surfaces the fresh price for the planner to re-confirm rather than silently overcharging.
_error() takes an optional user_message (mirrors the
Duffel MCP hardening).
The raw HotelBeds diagnostic stays in _meta.error for logs; the
summary / text the planner renders become a friendly one-liner so
no raw JSON blob leaks to the UI. Constants:
-
HOTELS_UNAVAILABLE_MSG— "Hotels are temporarily unavailable, please try again in a moment." -
BOOKING_UNAVAILABLE_MSG— "Hotel booking is temporarily unavailable — please try again in a moment."
| Aspect | Value |
|---|---|
| API base |
workload.hotelbeds_api_base, default https://api.test.hotelbeds.com (TEST/SANDBOX). |
| Secret | GSM hotelbeds-hotels-test, resolved via worker Workload Identity. |
| Deploy | Catalog registration (latest-wins), no image/worker redeploy — the Python runs in-worker. |
| Live catalog |
v5 (662611025673323404); rollback v4 (660409660586853242). |
The travel itinerary planner calls search_hotels on a "hotels in …"
turn and book_hotel when the user confirms a selected rate (the
rate_key flows search → book through slot state). Shipped + live on
prod 2026-07-03 via ops#233
(tracks noetl/ai-meta#175).
- Firestore MCP agent — sibling MCP playbook.
- travel wiki — itinerary planner — the caller.
- ai-meta — Execution Model — MCP-as-playbook + secrets boundary.
Ops
Manifests
Local dev
Monitoring
Automation
See also
- noetl/noetl wiki (app)
- noetl/docs (design docs)