hotelzero 1.3.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 CHANGED
@@ -23,7 +23,6 @@ export interface HotelSearchParams {
23
23
  currency?: string;
24
24
  sortBy?: "popularity" | "price_lowest" | "price_highest" | "rating" | "distance";
25
25
  limit?: number;
26
- offset?: number;
27
26
  }
28
27
  export interface HotelFilters {
29
28
  minRating?: number;
package/dist/browser.js CHANGED
@@ -251,7 +251,7 @@ export class HotelBrowser {
251
251
  }
252
252
  }
253
253
  buildBookingUrl(params, filters) {
254
- const { destination, checkIn, checkOut, guests, rooms, currency, sortBy, offset } = params;
254
+ const { destination, checkIn, checkOut, guests, rooms, currency, sortBy } = params;
255
255
  const url = new URL("https://www.booking.com/searchresults.html");
256
256
  url.searchParams.set("ss", destination);
257
257
  url.searchParams.set("checkin", checkIn);
@@ -259,10 +259,6 @@ export class HotelBrowser {
259
259
  url.searchParams.set("group_adults", guests.toString());
260
260
  url.searchParams.set("no_rooms", rooms.toString());
261
261
  url.searchParams.set("selected_currency", currency || "USD");
262
- // Pagination offset
263
- if (offset && offset > 0) {
264
- url.searchParams.set("offset", offset.toString());
265
- }
266
262
  // Sort order
267
263
  if (sortBy) {
268
264
  const sortMap = {
@@ -625,33 +621,42 @@ export class HotelBrowser {
625
621
  async scrollToLoadMore(targetResults = 25) {
626
622
  if (!this.page)
627
623
  return;
628
- // Calculate how many scroll iterations needed
629
- // Each scroll typically loads ~15-25 more results
630
- // We start with ~25, so to get to targetResults we need (targetResults - 25) / 20 more scrolls
631
- const scrollsNeeded = Math.max(1, Math.ceil((targetResults - 25) / 20));
632
- const maxScrolls = Math.min(scrollsNeeded, 5); // Cap at 5 scrolls to avoid excessive loading
633
- // Scroll down to load more results
634
- for (let i = 0; i < maxScrolls; i++) {
635
- await this.page.evaluate(() => {
636
- window.scrollBy(0, window.innerHeight);
637
- });
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));
638
628
  await this.page.waitForTimeout(1000);
639
- // Check if "Load more" button exists and click it
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
640
641
  try {
641
642
  const loadMoreBtn = await this.page.$('button[data-testid="load-more-results"]');
642
643
  if (loadMoreBtn) {
643
644
  await loadMoreBtn.click();
644
- await this.page.waitForTimeout(1500);
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;
645
650
  }
646
651
  }
647
652
  catch {
648
- // Ignore if button not found
653
+ // Button not found or click failed
654
+ break;
649
655
  }
650
656
  }
651
657
  // Scroll back to top
652
- await this.page.evaluate(() => {
653
- window.scrollTo(0, 0);
654
- });
658
+ await this.page.evaluate(() => window.scrollTo(0, 0));
659
+ await this.page.waitForTimeout(500);
655
660
  }
656
661
  async extractHotelDetails() {
657
662
  if (!this.page)
package/dist/index.js CHANGED
@@ -42,7 +42,6 @@ const FindHotelsSchema = z.object({
42
42
  sortBy: z.enum(["popularity", "price_lowest", "price_highest", "rating", "distance"]).optional().describe("Sort results by"),
43
43
  // Pagination
44
44
  limit: z.number().min(1).max(100).optional().describe("Maximum number of results to return (default: 25, max: 100)"),
45
- offset: z.number().min(0).optional().describe("Number of results to skip for pagination (default: 0)"),
46
45
  // Rating & Price
47
46
  minRating: z.number().optional().describe("Minimum rating (6=Pleasant, 7=Good, 8=Very Good, 9=Wonderful)"),
48
47
  minPrice: z.number().optional().describe("Minimum price per night"),
@@ -168,7 +167,7 @@ function formatHotelResult(hotel, index) {
168
167
  // Create MCP server
169
168
  const server = new Server({
170
169
  name: "hotelzero",
171
- version: "1.3.0",
170
+ version: "1.3.1",
172
171
  }, {
173
172
  capabilities: {
174
173
  tools: {},
@@ -197,7 +196,6 @@ const findHotelsInputSchema = {
197
196
  },
198
197
  // Pagination
199
198
  limit: { type: "number", description: "Maximum results to return (default: 25, max: 100)", default: 25 },
200
- offset: { type: "number", description: "Number of results to skip for pagination", default: 0 },
201
199
  // Property Type
202
200
  propertyType: {
203
201
  type: "string",
@@ -360,12 +358,11 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
360
358
  currency: parsed.currency,
361
359
  sortBy: parsed.sortBy,
362
360
  limit: parsed.limit,
363
- offset: parsed.offset,
364
361
  };
365
362
  // Build filters object from all parsed parameters
366
363
  const filters = {};
367
364
  // Copy all filter properties (exclude search params)
368
- const filterKeys = Object.keys(parsed).filter(k => !['destination', 'checkIn', 'checkOut', 'guests', 'rooms', 'currency', 'sortBy', 'limit', 'offset'].includes(k));
365
+ const filterKeys = Object.keys(parsed).filter(k => !['destination', 'checkIn', 'checkOut', 'guests', 'rooms', 'currency', 'sortBy', 'limit'].includes(k));
369
366
  for (const key of filterKeys) {
370
367
  const value = parsed[key];
371
368
  if (value !== undefined && value !== null) {
@@ -436,20 +433,14 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
436
433
  const filtersLine = activeFilters.length > 0
437
434
  ? `Filters: ${activeFilters.join(", ")}\n\n`
438
435
  : "\n";
439
- // Pagination info
440
- const offset = parsed.offset || 0;
441
- const displayLimit = parsed.limit || 25;
442
- const paginationLine = offset > 0
443
- ? `Showing results ${offset + 1}-${offset + results.length} of available hotels\n\n`
444
- : "";
445
436
  const hotelList = results
446
- .map((h, i) => formatHotelResult(h, i + offset))
437
+ .map((h, i) => formatHotelResult(h, i))
447
438
  .join("\n\n");
448
439
  return {
449
440
  content: [
450
441
  {
451
442
  type: "text",
452
- text: header + filtersLine + paginationLine + hotelList,
443
+ text: header + filtersLine + hotelList,
453
444
  },
454
445
  ],
455
446
  };
@@ -557,7 +548,7 @@ process.on("SIGTERM", async () => {
557
548
  async function main() {
558
549
  const transport = new StdioServerTransport();
559
550
  await server.connect(transport);
560
- console.error("HotelZero v1.3.0 running on stdio");
551
+ console.error("HotelZero v1.3.1 running on stdio");
561
552
  }
562
553
  main().catch((error) => {
563
554
  console.error("Fatal error:", error);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hotelzero",
3
- "version": "1.3.0",
3
+ "version": "1.3.1",
4
4
  "description": "MCP server for searching hotels on Booking.com with 80+ filters",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",