Most travel agent tutorials end at "the model suggested a hotel". That is the easy 20%. The other 80% is making the suggestion real: a hotel that exists, a price that is current, and a link a user can click without wondering whether it will work.
This is the build I use now, in eight steps, from an empty folder to a working handoff. It takes an afternoon if you already have an API key, and it deliberately skips payments, because handling payments is the step where side projects go to die.
Step 1: Decide what "book" means for you
Three models exist, and they are not interchangeable:
| Model | You handle · User experience | Effort |
|---|---|---|
| Redirect | Nothing · Clicks through to the provider | Hours |
| Assisted | Order creation · Confirms in your UI, provider completes | Weeks |
| Full | Payment, cancellation, reconciliation · Everything in-app | Months |
I start with redirect every time. The agent recommends, the provider completes. It is honest, it needs no PCI conversation, and it is enough to validate whether anyone wants the product.
Step 2: Get a key and connect the server
The server speaks streamable HTTP at one endpoint. The key comes from the partner portal, and the free tier has no call cap, which matters while you are testing a loop that might call the same tool forty times.
{
"mcpServers": {
"rollinggo-hotel": {
"type": "http",
"url": "https://mcp.rollinggo.ai/mcp",
"headers": { "Authorization": "Bearer YOUR_API_KEY" }
}
}
}
Verify it before writing any agent code:
curl -X POST https://mcp.rollinggo.ai/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{"jsonrpc":"2.0","method":"tools/list","id":1}'
If that returns three tools, the rest is application code. If it returns 400, you are missing the Accept header, which is the most common first failure and the least obvious one.
Step 3: Ask for tags before asking for hotels
getHotelSearchTags is the step everyone skips, and it is why agents invent filters like "rooftop pool".
Call getHotelSearchTags and list the tags that relate to families.
Cache the result for the session, then map the user's words onto real tags. This one call removes a category of nonsense results, and it costs a few milliseconds once.
Step 4: Search with constraints, not vibes
{
"name": "searchHotels",
"arguments": {
"originQuery": "family hotel near Tokyo Station with a pool",
"place": "Tokyo Station",
"placeType": "train_station",
"checkInParam": { "checkInDate": "2026-11-14", "stayNights": 2, "adultCount": 2 },
"filterOptions": { "starRatings": [4.0] },
"hotelTags": { "maxPricePerNight": 30000 },
"localeParam": { "language": "en", "currency": "USD" },
"size": 5
}
}
Two arguments are doing the heavy lifting. placeType keeps "Tokyo Station" from being geocoded as a city, and localeParam.currency stops you from formatting yen as dollars in the UI. Both are server-side, so you are not writing a converter.
Step 5: Render results as cards, not prose
The response gives you hotelName, starRating, priceInfo, address, and bookingUrl. Render those five things. A paragraph that says "a charming option near the station" is worse than a card with a price and a button, because the card can be checked.
The Tokyo Station Hotel ★★★★★
Marunouchi, 1-9-1 · from 198 USD / night
Breakfast included · Free cancellation until 12 Nov
[Check availability]
If a field is missing, leave a gap. Do not fill it with a sentence the model made up.
Step 6: Handle "show me more" without re-asking
This is where most implementations leak quality. The user says "more options", the agent re-runs the search with slightly different parameters, and the second page is a different dataset with different prices.
The server returns a nextPageToken. Pass it back, and the next page inherits the original destination, dates, occupancy, and filters. The model does not get a chance to drift on parameters, because it is not choosing them again.
{ "name": "searchHotels", "arguments": { "nextPageToken": "NPT_20261101_TYO_TOKEN_A1" } }
That one behaviour is the difference between a demo and something a person can use for five minutes.
Step 7: Re-check the price before you promise it
Rates move. A quote at 9 a.m. is a different number at 3 p.m., and a user who clicks a stale price loses trust in the whole product.
Call getHotelDetail for the chosen hotel right before rendering the confirmation, and show the timestamp:
Rate checked at 14:32 UTC · total 396 USD for 2 nights
If the rate changed by more than a small threshold, say so and show the new number. Users forgive a price change. They do not forgive a silent one.
Step 8: Hand off, then watch what happens
The booking URL is the end of your responsibility and the start of your instrumentation. Log the click, the hotel ID, and the quoted price. If you have an affiliate or partner arrangement, the provider's dashboard tells you what converted; the click log tells you where people hesitated.
For the first week, check three numbers: clicks per session, hotels viewed before a click, and correction messages per session. If the last one is above one, the data layer is fine and the prompting or rendering is not.
The failure states you must design
This is the part that separates a working product from a happy-path demo.
No results. Say why: dates, budget, or filters. Offer one specific relaxation, not five.
Sold out between search and detail. Re-query once, then show the next closest option.
Missing dates. Ask, do not assume. "Tomorrow" from a user in another timezone is a bug report.
Ambiguous place. "Springfield" needs a country. Ask before searching, or restrict by a country code you already have.
Past dates. Return an error state, not an empty list. An empty hotel list reads as "no availability", which is a different and wrong message.
What this build deliberately does not do
No payment flow. No cancellation handling. No order status screen. Those are the OAuth tier and a business onboarding conversation, and they are the right next step only after redirect mode proves people click.
That is not a limitation to apologise for. It is the smallest product that tells you whether the idea works, and it ships in an afternoon instead of a quarter.
The five-minute path
If you want the exact setup I used, the redirect mode guide covers the key, the config block, the three tools, and the response shape. Read the response example before the prose; it is the clearest description of what the product actually does.
Then build the failure states first. The happy path takes an hour. The failure states are what users remember.
Top comments (0)