CuddlyNest Search & Listings — MCP Server
以下摘录自该仓库的 README(片段) · 在 GitHub 读完整 README →
A Model Context Protocol (MCP) server for searching CuddlyNest
hotels and retrieving listing details, including **room options, prices,
availability and cancellation policies**.
Read-only by design: search and listing details only. No booking, no payment.
How it gets the data
This server reads that 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 the rooms, waits for them to render, and reads the
result out of the DOM.
| Data | Source |
| --- | --- |
| Name, description, address, coordinates, star rating, amenities, images | Listing page schema.org ld+json + Open Graph tags (cuddlynest.ts) |
| Room title, partner, unit_price, remaining_rooms, price_breakdown, cancellation_policy (incl. .text), room_filters | Rendered listing page DOM, via a React-fiber walk (scrape-listing.ts) |
| Destination → place candidates + top hotels | autosuggestion-2-0.cuddlynest.com (public, no auth) |
| Destination → broader city hotel list (~60–250) | discovery-pages.cuddlynest.com/fetch_geopage/ (public); recovered from a hotel's product-detail breadcrumbs |
| product_id → name / city / breadcrumbs | ldp-2-0-product-details.cuddlynest.com/api/v1/productDetail (public) |
| product_id → canonical listing path | /hotel/- server redirect |
The DOM extraction, and how it breaks
extractRoomsFromDom() walks every price-shaped text node (COL$742,637), then
walks up its **React fiber tree** to the nearest ancestor component whose props
carry both unit_price and roomGroups. Those props are the room offer the
page already rendered.
This is coupled to CuddlyNest's current frontend internals (a React prop shape,
not a stable contract). If they ship a frontend change it can start returning
zero rooms even though the public page still shows prices. The single place to
update is the detector condition 'unit_price' in p && 'roomGroups' in p in
scrape-listing.ts. npm run e2e:sansiraka is meant to
catch that early (non-zero exit, not a silent empty result).
The fromPriceText ("From COL$…") field uses a looser heuristic and can come
back null even on a healthy scrape; rooms.fromPrice (cheapest extracted
unit) is the reliable figure.
---
Requirements
- Node.js 18+
- A Chromium build for Playwright. npm install runs playwright install
chromium automatically (postinstall); if that is blocked in your
environment, run npx playwright install chromium once by hand.
Installation
Published as cuddlynest-mcp on
npm and listed in the official MCP registry
as io.github.mafedelahoz/cuddlynest-mcp.
Add "--ignore-robots-txt" to args to bypass robots.txt for the
listing-page fetches. CUDDLYNEST_SCRAPE_TIMEOUT_MS (default 35000) caps how
long the browser waits for prices to render.
An MCPB bundle (.mcpb) for Claude Desktop is attached to each
GitHub release — note it
does **not** bundle Chromium, so run npx playwright install chromium once after
installing it that way.
Remote / Streamable HTTP
Default transport is stdio. For a hosted deployment, run it over Streamable HTTP:
---
Tools
Both tools are annotated readOnlyHint: true — they never write, book, or pay.