Skip to content

Commit 7f4829d

Browse files
author
BusinessHotels
authored
Update README.md
1 parent 6476835 commit 7f4829d

1 file changed

Lines changed: 41 additions & 28 deletions

File tree

src/business-hotels-mcp/README.md

Lines changed: 41 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -448,57 +448,70 @@ print(final.text)
448448
449449
---
450450
451-
## 🔁 Multi-Hotel Comparison Pattern
451+
## 🔁 Multi-Hotel Comparison Pattern (High Performance)
452+
This API uses a **one-hotel-per-request** architecture. To maintain sub-second response times for comparisons, agents must use **asynchronous parallelism** rather than sequential loops.
452453
453-
This API uses a **one-hotel-per-request** architecture — there is no batch endpoint. To compare multiple properties, loop through each hotel individually, collect all results, then present a unified ranked response to the user.
454-
455-
## ✅ Correct Pattern — Parallel Fetching (Under 2 Seconds)
456-
457-
To make the Multi-Hotel Comparison Pattern truly demonstrate our sub-second speed, this implementation moves away from sequential loops in favor of **asynchronous parallelism**.
458-
459-
In a sequential version, 5 hotels taking 800ms each would take 4 seconds. With the parallel code below, all requests fire at once, finishing the entire comparison in **~800ms to 1 second**.
460-
461-
This example uses Python's `concurrent.futures` to fire all requests simultaneously, ensuring your agent gets a full comparison in the time it takes for a single request.
454+
## ✅ Correct Pattern — Parallel Fetching (Under 1 Second)
455+
In a sequential loop, 5 hotels would take ~4 seconds. With the parallel code below using `concurrent.futures`, all requests fire simultaneously, finishing the entire comparison in the time it takes for a single request.
462456
463457
```python
464-
import requests
458+
import requests
459+
from concurrent.futures import ThreadPoolExecutor
465460
466461
URL = "https://www.businesshotels.com/mcp-server.php?route=tools/get_live_hotel_rates"
467462
HEADERS = {"Content-Type": "application/json", "X-API-KEY": "test-live-hotel-rates2025"}
468463
469464
hotels_to_check = [
470465
"Fairmont San Francisco, San Francisco, CA, US",
471-
"Four Seasons San Francisco at Embarcadero San Francisco, CA, US",
466+
"Four Seasons San Francisco at Embarcadero, San Francisco, CA, US",
472467
"Ritz-Carlton San Francisco, San Francisco, CA, US",
473468
"St. Regis San Francisco, San Francisco, CA, US",
474-
"Palace Hotel a Luxury Collection Hotel, San Francisco, CA, US"
469+
"Palace Hotel a Luxury Collection Hotel, San Francisco, CA, US"
475470
]
476471
477-
params = {"checkinDate": "2026-07-12", "checkoutDate": "2026-07-14", "adults": 2, "currency": "USD"}
478-
results = []
472+
params = {"checkinDate": "2026-07-12", "checkoutDate": "2026-07-14", "adults": 2, "currency": "USD"}
473+
474+
def fetch_hotel_data(hotel_name):
475+
"""Worker function to fetch a single hotel rate."""
476+
try:
477+
response = requests.post(URL, headers=HEADERS, json={**params, "hotelName": hotel_name}, timeout=10)
478+
return response.json()
479+
except Exception:
480+
return None
479481
480-
for hotel in hotels_to_check:
481-
data = requests.post(URL, headers=HEADERS, json={**params, "hotelName": hotel}, timeout=10).json()
482-
rates = data.get("rates")
483-
if rates and rates.get("display_all_in_total"):
484-
price = float(str(rates["display_all_in_total"]).replace(",", ""))
482+
# EXECUTE IN PARALLEL: This is what makes it fast.
483+
with ThreadPoolExecutor(max_workers=5) as executor:
484+
raw_results = list(executor.map(fetch_hotel_data, hotels_to_check))
485+
486+
# Process and sort results
487+
results = []
488+
for data in raw_results:
489+
if data and data.get("rates") and data["rates"].get("display_all_in_total"):
490+
# Strip commas for math: "1,250.00" -> 1250.0
491+
price = float(str(data["rates"]["display_all_in_total"]).replace(",", ""))
485492
results.append({
486-
"name": data["hotel_name"],
487-
"price": price,
488-
"url": data["booking_page_live_rates"],
489-
"hotel_id": data.get("hotel_id")
493+
"name": data["hotel_name"],
494+
"price": price,
495+
"url": data["booking_page_live_rates"]
490496
})
491497
492-
# Sort and present ALL results together — never respond mid-loop
493498
results.sort(key=lambda x: x["price"])
499+
494500
for i, h in enumerate(results, 1):
495501
print(f"{i}. {h['name']}: ${h['price']:.2f}")
496502
497-
cheapest = results
498-
print(f"\n🏆 Best Value: {cheapest['name']} at ${cheapest['price']:.2f}")
499-
print(f"👉 Book Now: {cheapest['url']}")
503+
if results:
504+
print(f"\n🏆 Best Value: {results[0]['name']} at ${results[0]['price']:.2f}")
505+
print(f"👉 Book Now: {results[0]['url']}")
500506
```
501507
508+
### ⚠️ Critical Implementation Guardrails
509+
* **Confidence Scoring:** Always check the `best_match_score`. If it is below **0.85**, the agent must verify the hotel identity (City/State) before providing a booking link.
510+
* **Price Lock Expiry:** The `ppn_bundle` and price are valid for **~20 minutes**. Agents should re-fetch the rate if the user returns to an old session.
511+
* **Discovery Specs:** For non-MCP integrations, use our [OpenAPI 3.0 Spec](https://www.businesshotels.com/openapi.json) or [AI Plugin Manifest](https://www.businesshotels.com/.well-known/ai-plugin.json).
512+
513+
514+
502515
### Architectural Rules
503516
504517
> 📌 **Complete all requests before responding.** Always finish the full loop before presenting any results. Responding mid-loop creates a fragmented, incomplete user experience.

0 commit comments

Comments
 (0)