hotelzero 1.2.0 → 1.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser.d.ts +1 -0
- package/dist/browser.js +42 -12
- package/dist/index.js +9 -5
- package/package.json +1 -1
package/dist/browser.d.ts
CHANGED
package/dist/browser.js
CHANGED
|
@@ -576,10 +576,15 @@ export class HotelBrowser {
|
|
|
576
576
|
await this.checkForNoResults();
|
|
577
577
|
// Close any popups/modals
|
|
578
578
|
await this.dismissPopups();
|
|
579
|
-
// Scroll to load more results
|
|
580
|
-
|
|
579
|
+
// Scroll to load more results (pass limit to control how many to load)
|
|
580
|
+
const targetResults = params.limit || 25;
|
|
581
|
+
await this.scrollToLoadMore(targetResults);
|
|
581
582
|
// Extract detailed hotel info
|
|
582
|
-
|
|
583
|
+
let hotels = await this.extractHotelDetails();
|
|
584
|
+
// Apply limit to cap results
|
|
585
|
+
if (params.limit && params.limit > 0) {
|
|
586
|
+
hotels = hotels.slice(0, params.limit);
|
|
587
|
+
}
|
|
583
588
|
// Apply client-side filtering and scoring if we have preferences
|
|
584
589
|
if (filters) {
|
|
585
590
|
return this.scoreAndFilterHotels(hotels, filters);
|
|
@@ -613,20 +618,45 @@ export class HotelBrowser {
|
|
|
613
618
|
}
|
|
614
619
|
}
|
|
615
620
|
}
|
|
616
|
-
async scrollToLoadMore() {
|
|
621
|
+
async scrollToLoadMore(targetResults = 25) {
|
|
617
622
|
if (!this.page)
|
|
618
623
|
return;
|
|
619
|
-
//
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
});
|
|
624
|
+
// If we only need 25 or fewer, minimal scrolling
|
|
625
|
+
if (targetResults <= 25) {
|
|
626
|
+
// Just one scroll to ensure initial results are loaded
|
|
627
|
+
await this.page.evaluate(() => window.scrollBy(0, window.innerHeight));
|
|
624
628
|
await this.page.waitForTimeout(1000);
|
|
629
|
+
await this.page.evaluate(() => window.scrollTo(0, 0));
|
|
630
|
+
return;
|
|
631
|
+
}
|
|
632
|
+
// For larger limits, we need to scroll and click "Load more" multiple times
|
|
633
|
+
// Booking.com loads ~25 results initially, then ~25 more per "Load more" click
|
|
634
|
+
const clicksNeeded = Math.ceil((targetResults - 25) / 25);
|
|
635
|
+
const maxClicks = Math.min(clicksNeeded, 4); // Cap at 4 clicks (~125 results max)
|
|
636
|
+
for (let i = 0; i < maxClicks; i++) {
|
|
637
|
+
// Scroll to bottom to trigger lazy loading and find "Load more" button
|
|
638
|
+
await this.page.evaluate(() => window.scrollTo(0, document.body.scrollHeight));
|
|
639
|
+
await this.page.waitForTimeout(1500);
|
|
640
|
+
// Try to click "Load more" button
|
|
641
|
+
try {
|
|
642
|
+
const loadMoreBtn = await this.page.$('button[data-testid="load-more-results"]');
|
|
643
|
+
if (loadMoreBtn) {
|
|
644
|
+
await loadMoreBtn.click();
|
|
645
|
+
await this.page.waitForTimeout(2000); // Wait for results to load
|
|
646
|
+
}
|
|
647
|
+
else {
|
|
648
|
+
// No more "Load more" button, we've loaded all available results
|
|
649
|
+
break;
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
catch {
|
|
653
|
+
// Button not found or click failed
|
|
654
|
+
break;
|
|
655
|
+
}
|
|
625
656
|
}
|
|
626
657
|
// Scroll back to top
|
|
627
|
-
await this.page.evaluate(() =>
|
|
628
|
-
|
|
629
|
-
});
|
|
658
|
+
await this.page.evaluate(() => window.scrollTo(0, 0));
|
|
659
|
+
await this.page.waitForTimeout(500);
|
|
630
660
|
}
|
|
631
661
|
async extractHotelDetails() {
|
|
632
662
|
if (!this.page)
|
package/dist/index.js
CHANGED
|
@@ -40,6 +40,8 @@ const FindHotelsSchema = z.object({
|
|
|
40
40
|
// Currency & Sorting
|
|
41
41
|
currency: z.string().optional().describe("Currency code (USD, EUR, GBP, JPY, etc.)"),
|
|
42
42
|
sortBy: z.enum(["popularity", "price_lowest", "price_highest", "rating", "distance"]).optional().describe("Sort results by"),
|
|
43
|
+
// Pagination
|
|
44
|
+
limit: z.number().min(1).max(100).optional().describe("Maximum number of results to return (default: 25, max: 100)"),
|
|
43
45
|
// Rating & Price
|
|
44
46
|
minRating: z.number().optional().describe("Minimum rating (6=Pleasant, 7=Good, 8=Very Good, 9=Wonderful)"),
|
|
45
47
|
minPrice: z.number().optional().describe("Minimum price per night"),
|
|
@@ -165,7 +167,7 @@ function formatHotelResult(hotel, index) {
|
|
|
165
167
|
// Create MCP server
|
|
166
168
|
const server = new Server({
|
|
167
169
|
name: "hotelzero",
|
|
168
|
-
version: "1.
|
|
170
|
+
version: "1.3.1",
|
|
169
171
|
}, {
|
|
170
172
|
capabilities: {
|
|
171
173
|
tools: {},
|
|
@@ -192,6 +194,8 @@ const findHotelsInputSchema = {
|
|
|
192
194
|
description: "Sort results by",
|
|
193
195
|
enum: ["popularity", "price_lowest", "price_highest", "rating", "distance"]
|
|
194
196
|
},
|
|
197
|
+
// Pagination
|
|
198
|
+
limit: { type: "number", description: "Maximum results to return (default: 25, max: 100)", default: 25 },
|
|
195
199
|
// Property Type
|
|
196
200
|
propertyType: {
|
|
197
201
|
type: "string",
|
|
@@ -353,11 +357,12 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
353
357
|
rooms: parsed.rooms,
|
|
354
358
|
currency: parsed.currency,
|
|
355
359
|
sortBy: parsed.sortBy,
|
|
360
|
+
limit: parsed.limit,
|
|
356
361
|
};
|
|
357
362
|
// Build filters object from all parsed parameters
|
|
358
363
|
const filters = {};
|
|
359
|
-
// Copy all filter properties
|
|
360
|
-
const filterKeys = Object.keys(parsed).filter(k => !['destination', 'checkIn', 'checkOut', 'guests', 'rooms', 'currency', 'sortBy'].includes(k));
|
|
364
|
+
// Copy all filter properties (exclude search params)
|
|
365
|
+
const filterKeys = Object.keys(parsed).filter(k => !['destination', 'checkIn', 'checkOut', 'guests', 'rooms', 'currency', 'sortBy', 'limit'].includes(k));
|
|
361
366
|
for (const key of filterKeys) {
|
|
362
367
|
const value = parsed[key];
|
|
363
368
|
if (value !== undefined && value !== null) {
|
|
@@ -429,7 +434,6 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
429
434
|
? `Filters: ${activeFilters.join(", ")}\n\n`
|
|
430
435
|
: "\n";
|
|
431
436
|
const hotelList = results
|
|
432
|
-
.slice(0, 15) // Top 15 results
|
|
433
437
|
.map((h, i) => formatHotelResult(h, i))
|
|
434
438
|
.join("\n\n");
|
|
435
439
|
return {
|
|
@@ -544,7 +548,7 @@ process.on("SIGTERM", async () => {
|
|
|
544
548
|
async function main() {
|
|
545
549
|
const transport = new StdioServerTransport();
|
|
546
550
|
await server.connect(transport);
|
|
547
|
-
console.error("HotelZero v1.
|
|
551
|
+
console.error("HotelZero v1.3.1 running on stdio");
|
|
548
552
|
}
|
|
549
553
|
main().catch((error) => {
|
|
550
554
|
console.error("Fatal error:", error);
|