Purpose
mcp-cuddlynest is a Model Context Protocol (MCP) server for searching CuddlyNest hotels and retrieving listing details, including room options, prices, availability, and cancellation policies. It is explicitly designed to be read-only: it only performs searches and reads listing details; it does not involve booking, nor does it involve payment.
Main Capabilities
The server provides two tools, both marked with readOnlyHint: true, meaning they never write, book, or pay.
-
cuddlynest_search: searches popular local hotels from a destination, using data from the public CuddlyNest API (autosuggestion-2-0 for fuzzy matching, discovery-pages geo pages for broader city lists). Prices are not returned here; you need to pass the hotel's productId to cuddlynest_listing_details. Return structure: { query, guests, places[], city, hotelSource, hotelCount, hotels[], note }. Each hotels[] entry contains productId, name, url, slug, propertyType, starRating, guestRating (/10) and guestRatingText, reviewCount, as well as images[], distanceFromCenterKm, featuredAmenities[] from the geo page. hotelSource takes the value "autosuggest" or "autosuggest+geopage". Note that this list is top-match scale, not full inventory.
-
cuddlynest_listing_details: returns a single hotel's static basic information and rooms/prices. Return structure: { productId, hotelUrl, guests, staticListing, staticError, rooms, roomsError, notes }. rooms.units[] are the extracted room offers, each containing title, partnerName, unitPrice, currency, remainingRooms, guests, cancellationPolicyType, cancellationPolicyText, priceBreakdown, roomFilters. rooms also contains fromPrice, partnersSeen, listingUrl, scrapedAt.
Usage and Data Retrieval Mechanism
The server reads data the same way a visitor does: it opens the real, public listing page in a headless browser (Playwright/Chromium), lets the page's own JavaScript load rooms, waits for it to render, and then reads the results from the DOM.
The DOM extraction logic is in extractRoomsFromDom(): it iterates over every price-pattern text node (e.g. COL$742,637), then walks up the React fiber tree to find the nearest ancestor component whose props carry both unit_price and roomGroups. These props are the room offers the page has already rendered.
Installation and run key points:
- Requires a Chromium build for Playwright. npm install automatically runs playwright install chromium (postinstall); if your environment blocks that operation, you can manually run npx playwright install chromium once.
- Published on npm as cuddlynest-mcp, and registered in the official MCP registry as io.github.mafedelahoz/cuddlynest-mcp.
- You can add "--ignore-robots-txt" in args to bypass robots.txt for listing page scraping. CUDDLYNEST_SCRAPE_TIMEOUT_MS (default 35000) limits how long the browser waits for prices to render.
- Each GitHub release includes an MCPB package file (.mcpb) for Claude Desktop; note that it does not bundle Chromium, so after installing this way you need to manually run npx playwright install chromium.
- The default transport is stdio. For hosted deployment, you can switch to running it over Streamable HTTP.
Repository file descriptions:
- index.ts — MCP server, tool schemas, stdio and Streamable HTTP transports, robots.txt handling
- cuddlynest.ts — hotel URL parsing, static listing ld+json parsing, destination autosuggest, result shaping
- scrape-listing.ts — resolveListingPath, buildListingUrl, scrapeListing (headless browser), extractRoomsFromDom (React fiber traversal)
- util.ts — general-purpose object/JSON helper functions
The license is MIT; see LICENSE for details.
Use cases
Suitable for searching CuddlyNest hotels by destination through an MCP client (such as Claude Desktop or other MCP-capable environments), and reading a given hotel's live room types, prices, availability, and cancellation policies. Suitable for scenarios that require read-only queries of publicly available hotel information without carrying out any transaction flow.
Limitations and known risks
- Read-only by design: search and listing details only, with no booking and no payment.
- DOM extraction is coupled to CuddlyNest's current front-end internals (a React prop shape, not a stable contract). If its front end changes, it may start returning zero rooms even if the public pages still show prices. The only place that needs updating is the detection condition in scrape-listing.ts, 'unit_price' in p && 'roomGroups' in p. npm run e2e:sansiraka is intended to catch this early (with a non-zero exit code, rather than silently returning empty results).
- The fromPriceText ("From COL$…") field uses looser heuristics and may return null even under normal scraping; rooms.fromPrice (the cheapest extracted unit) is more reliable data.
- The search returns the top match scale, not the full inventory; CuddlyNest's actual results page (/sr/…) has bot blocking and is Disallowxt.
- Scraping depends on a headless browser and Chromium build; if the environment blocks automatic installation, you need to install it manually.