Skip to main content
Glama
mafedelahoz

CuddlyNest Search & Listings MCP Server

CuddlyNest Search & Listings — MCP Server

npm MCP Registry

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/<ct-id> (public); <ct-id> 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/-<id> 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.


Related MCP server: Amadeus Hotel API MCP Server

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.

{
  "mcpServers": {
    "cuddlynest": {
      "command": "npx",
      "args": ["-y", "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:

node dist/index.js --http 8080      # or: MCP_TRANSPORT=http PORT=8080 node dist/index.js
#   POST  http://<host>:8080/mcp    — JSON-RPC (stateless, no sessions)
#   GET   http://<host>:8080/health — liveness

Tools

Both tools are annotated readOnlyHint: true — they never write, book, or pay.

Search a destination and the top hotels there, from public CuddlyNest APIs (autosuggestion-2-0 for the fuzzy match, discovery-pages geo pages for the broader city list). Prices are not here — pass a hotel's productId to cuddlynest_listing_details.

Parameter

Required

Description

destination

yes

City / area string, e.g. "Cartagena, Colombia"

hotelsOnly

no

Omit the places[] block (default false)

fullCityList

no

Also pull the geo-page city list (~60–250 hotels) when it can be resolved and verified against the destination — a few extra requests (default true)

checkin, checkout, adults, children, childAges, infants, rooms, currency

no

echoed back for downstream use

Returns: { query, guests, places[], city, hotelSource, hotelCount, hotels[], note }. Each hotels[] entry: productId, name, url, slug, propertyType, starRating, guestRating (/10) + guestRatingText, reviewCount, and — from the geo page — images[], distanceFromCenterKm, featuredAmenities[]. hotelSource is "autosuggest" or "autosuggest+geopage". The list is top-matches scale, not full inventory — CuddlyNest's real results page (/sr/…) is bot-blocked and Disallowed in robots.txt.

cuddlynest_listing_details

Static basics and rooms/pricing for one hotel.

Parameter

Required

Description

hotel

yes

Listing URL or numeric product_id (trailing number in the URL)

checkin, checkout

for pricing

YYYY-MM-DD — required to read rooms/prices

adults, children, childAges, infants, rooms

no

defaults 2 / 0 / – / 0 / 1

currency

no

ISO 4217, default USD

ignoreRobotsText

no

ignore robots.txt for the static fetch

Returns: { productId, hotelUrl, guests, staticListing, staticError, rooms, roomsError, notes }. rooms.units[] is the extracted room offers, each with title, partnerName, unitPrice, currency, remainingRooms, guests, cancellationPolicyType, cancellationPolicyText, priceBreakdown, roomFilters. rooms also carries fromPrice, partnersSeen, listingUrl, scrapedAt.


Development

npm install          # installs deps + Chromium (postinstall)
npm run build        # sync-version + tsc -> dist/
npm run typecheck
npm test             # offline: smoke test (stdio) + scraper tests
npm run e2e:sansiraka # ONLINE: real scrape of cuddlynest.com, structural asserts
npm run watch

Architecture

  • index.ts — MCP server, tool schemas, stdio and Streamable HTTP transports, robots.txt handling

  • cuddlynest.ts — hotel-URL parsing, static-listing ld+json parse, destination autosuggestion, result shaping

  • scrape-listing.tsresolveListingPath, buildListingUrl, scrapeListing (headless browser), extractRoomsFromDom (React-fiber walk)

  • util.ts — generic object/JSON helpers

License

MIT — see LICENSE.

Available Tools

2 tools
cuddlynest_listing_detailsGet CuddlyNest hotel rooms & pricesA
Read-only

Get details for a specific CuddlyNest hotel: static basics (name, location, description, amenities, images) from the listing page, plus room options, prices, availability and cancellation policies read from the public listing page rendered in a headless browser.

ParametersJSON Schema
NameRequiredDescriptionDefault
hotelYesCuddlyNest hotel URL or numeric product_id. The product_id is the trailing number in a listing URL, e.g. https://www.cuddlynest.com/hotel/us/le-meridien-boston-cambridge-4264955 -> 4264955.
roomsNoNumber of rooms (default: 1)
adultsNoNumber of adults (default: 2)
checkinNoCheck-in date (YYYY-MM-DD)
infantsNoNumber of infants (default: 0)
checkoutNoCheck-out date (YYYY-MM-DD)
childrenNoNumber of children (default: 0, or derived from childAges)
currencyNoISO 4217 currency code for prices (default: USD), e.g. USD, EUR, COP
childAgesNoAge of each child at check-in, e.g. [2, 7]. Sets `children` when given.
ignoreRobotsTextNoIgnore robots.txt for the listing-page fetch on this request.

TDQS

A4.2/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description goes beyond the readOnlyHint annotation by explaining that data is 'read from the public listing page rendered in a headless browser.' This usefully discloses the data source and the browser-rendering approach. It does not mention failure modes or latency, but it adds meaningful behavioral context on top of the annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is a single dense sentence with no filler. It front-loads the core purpose and packs the key output categories into a well-organized list, earning every word.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema, the description does a good job listing the main return categories: static basics, room options, prices, availability, and cancellation policies. Combined with the fully documented input schema and readOnly/openWorld annotations, it is sufficient for an agent to select and invoke the tool correctly. Minor gaps such as exact response structure or error behavior are not critical here.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the input schema already documents all parameters clearly. The description adds high-level output context but no additional parameter-level semantics, so the baseline of 3 is appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a clear verb and resource: 'Get details for a specific CuddlyNest hotel' and enumerates what is included (static basics, room options, prices, availability, cancellation policies). This makes it easy to distinguish from the sibling cuddlynest_search, which is about searching/discovering hotels rather than retrieving a specific listing.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The phrase 'for a specific CuddlyNest hotel' clearly indicates this tool is for when the agent already has a hotel URL or product_id. It does not explicitly name cuddlynest_search as the alternative or state exclusions, but the contrast with the sibling tool is apparent and the usage context is clear.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 2 tool updatesv0.2.1
    • Changedcuddlynest_listing_details1 field changed
      • removedInput schema / properties / destinationSlug
        Removed value: -{
        -  "description": "CuddlyNest internal destination slug for the hotel's city (e.g. 'SantaMartaMagdalenaColombia'). Optional — derived from the listing page's city/state/country when omitted.",
        -  "type": "string"
        -}
    • Changedcuddlynest_search2 fields changed
      • addedInput schema / properties / fullCityList
        Added value: +{
        +  "description": "Also pull the broader city hotel list (~60) from the geo-page API when it can be resolved — a couple of extra requests. Default true.",
        +  "type": "boolean"
        +}
      • addedInput schema / properties / hotelsOnly
        Added value: +{
        +  "description": "Return only the hotel list, skipping place candidates (default false).",
        +  "type": "boolean"
        +}
  2. 2 tool updatesv0.1.0
    • First observedcuddlynest_listing_details
    • First observedcuddlynest_search

TDQS

A4.3/5.0

Scored across 2 tools

Disambiguation5/5

The two tools occupy completely separated stages of the workflow: search discovers destinations and top hotels, while listing_details drills into one hotel using a product_id. There is no overlap in purpose or output.

Naming Consistency4/5

Both names share a cuddlynest_ prefix, making the server's brand clear, but one uses an imperative verb (search) while the other is a noun phrase (listing_details). This minor inconsistency prevents a perfect score.

Tool Count3/5

Two tools is borderline for a typical MCP server, though the count fits the narrow search-and-details purpose. It feels slightly thin because there is no tool for direct lookups or other listing workflows.

Completeness5/5

The described lifecycle is complete: cuddlynest_search produces both destination candidates and hotels with product IDs, and cuddlynest_listing_details consumes those IDs to return live rooms, prices, and policy details. No obvious dead end exists in the intended flow.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    D
    maintenance
    Enables AI agents to search, browse, and book hotels from a database of 2 million properties worldwide. Provides comprehensive hotel search capabilities with location lookup, filtering by amenities, detailed property information, and integrated booking functionality.
    6
    6 npm
    1
    ISC
  • A
    license
    Not graded
    quality
    D
    maintenance
    Book hotels worldwide — search, price, prebook & book across 249 countries. 65 tools for hotel search, flights, loyalty, analytics. Zero API keys needed. at best prices for hotels 3 M+ property
    5 npm
    1
    MIT
  • A
    license
    Not graded
    quality
    C
    maintenance
    Enables AI agents to search and book hotels globally with real-time pricing and inventory from over 2 million properties.
    82
    MIT