Skip to main content
Glama
mafedelahoz

CuddlyNest Search & Listings MCP Server

by mafedelahoz

CuddlyNest Search & Listings — MCP Server

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.

Seeded from the MCP-server scaffolding of openbnb-org/mcp-server-airbnb (MIT). The MCP transport/registration layer is reused; the data-fetching layer is a full rewrite. The original copyright notice is retained in LICENSE per the MIT terms.


How it gets the data

CuddlyNest does not serve room prices in its static HTML. Pricing renders client-side on the public listing page, backed by the site's own infrastructure and third-party wholesale suppliers (dida travels, hotelplanner, ratehawk, hxpro, rakuten, …).

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.

Scope — public content only. This is deliberately aligned with what the tech team recommended: read the same public page a visitor sees, using the same transport already used for amenities/images/location. The server does not call CuddlyNest's internal WebSocket, does not touch their auth/token endpoints, and carries no access token. An earlier draft that minted an anonymous token and spoke to the private WebSocket directly was dropped — nothing established that as authorized.

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 → city/state/country/coords/slug

autosuggestion-2-0.cuddlynest.com (public, no auth)

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

{
  "mcpServers": {
    "cuddlynest": {
      "command": "npx",
      "args": ["-y", "@cuddlynest/mcp-server-cuddlynest"]
    }
  }
}

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.


Tools

Resolve a destination to its CuddlyNest candidates via the public autosuggestion API — name, city/state/country, coordinates, property count.

Parameter

Required

Description

destination

yes

City / area string, e.g. "Santa Marta, Colombia"

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

no

echoed back for downstream use

Returns: { query, guests, candidates[], note }. This tool does not enumerate a destination's hotels — call cuddlynest_listing_details for a specific hotel.

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
  • test-scrape.jsbuildListingUrl (pure) + extractRoomsFromDom replayed against fixtures/hotel-sansiraka-2026-10-05.json (a real capture from cuddlynest.com on 2026-09-01: Hotel Sansiraka 4395541, 2026-10-05→08, 2 adults + 1 child age 2, COP — 9 rooms across dida travels / hxpro / ratehawk / rakuten). A local headless Chromium rebuilds the page's DOM+fiber shape from that fixture and checks the extractor reconstructs it — no network.

  • test-extension.js — MCP handshake, tool listing, cuddlynest_search (hits the autosuggestion API), cuddlynest_listing_details product_id parsing.

  • scripts/e2e-hotel-sansiraka.mjs — runs a real scrape of the Sansiraka listing and asserts the live result matches the fixture's structure (room object shape/keys, non-empty, partner variety). Live prices and the exact partner set drift from the fixture — that's expected.

Last real e2e run (2026-09-01): 7 rooms from dida travels / hxpro / ratehawk / hotelplanner; dida travels prices matched the fixture exactly; rakuten had no availability that run. Structure ✅.

Architecture

  • index.ts — MCP server, tool schemas, routing, 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

  • Not affiliated with CuddlyNest. Reads publicly available listing information.

  • Respects robots.txt by default for the static listing fetch (override for testing). Be mindful of request frequency — each cuddlynest_listing_details call with dates launches a browser and loads one page.

License

MIT — see LICENSE.

Available Tools

2 tools
cuddlynest_listing_detailsA

Get details for a specific CuddlyNest hotel: static basics (name, location, description, amenities, images) from the listing page, plus live room options, prices, availability and cancellation policies streamed from CuddlyNest's wholesale-supplier WebSocket (accumulated across partial messages).

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.
destinationSlugNoCuddlyNest internal destination slug for the hotel's city (e.g. 'SantaMartaMagdalenaColombia'). Optional — derived from the listing page's city/state/country when omitted.
ignoreRobotsTextNoIgnore robots.txt for the listing-page fetch on this request.

TDQS

A4.1/5.0
Behavior4/5

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

With no annotations provided, the description carries the full behavioral disclosure burden. It does well by revealing the two data sources — a listing page and a wholesale-supplier WebSocket — and by noting that live data is accumulated across partial messages. It does not mention reliability, rate limits, or exact asynchronous behavior of the WebSocket, but the disclosed behavior is materially useful.

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

Conciseness4/5

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

The description is a single dense sentence that front-loads the core action and then layers static vs. live data sources, ending with the partial-message accumulation caveat. It is efficient and readable, though the parentheticals make it somewhat long; no sentence is wasted.

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?

Given 11 parameters, no annotations, and no output schema, the description covers the essential context: what data is returned, where it comes from, and what the live parameters are for. It does not describe the return shape or partial-failure behavior in detail, but the listed result categories are enough for an agent to invoke the tool correctly.

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 baseline is 3. The description adds useful overall context by clarifying that parameters like checkin/checkout/adults drive the live pricing and availability stream, while the hotel parameter identifies the listing. It does not add per-parameter detail beyond what the schema already provides.

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 opens with a specific verb and resource ('Get details for a specific CuddlyNest hotel') and lists concrete result categories: static basics, live room options, prices, availability, and cancellation policies. This clearly distinguishes it from the sibling cuddlynest_search, which by name handles search rather than retrieval of 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 intended use is clear: call this when you already have a specific CuddlyNest hotel and want both static and live details. However, it does not explicitly name cuddlynest_search or state when not to use this tool, so it falls just short of fully explicit usage guidance.

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

TDQS

A3.9/5.0
Disambiguation5/5

The two tools are clearly distinct: one resolves destinations via autosuggestion, while the other retrieves details for a specific known hotel. There is no meaningful overlap or ambiguity between them.

Naming Consistency4/5

Both tools share the cuddlynest_ prefix and use snake_case, but one uses a verb phrase (search) while the other uses a noun phrase (listing_details). The pattern is readable and predictable, with only minor stylistic inconsistency.

Tool Count3/5

With only two tools, the server feels thin for a 'Search & Listings' offering. The tools cover two discrete actions, but the count is borderline and likely limits what agents can accomplish.

Completeness2/5

The server can resolve a destination and fetch details for a known hotel, but it lacks any tool to enumerate or search hotels within a destination. This creates a significant workflow gap: an agent cannot go from destination selection to a list of available properties.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

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
    22
    1
    ISC
  • A
    license
    Not graded
    quality
    B
    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
    22
    1
    MIT
  • A
    license
    Not graded
    quality
    B
    maintenance
    Enables AI agents to search and book hotels globally with real-time pricing and inventory from over 2 million properties.
    81
    MIT

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/mafedelahoz/mcp-cuddlynest'

If you have feedback or need assistance with the MCP directory API, please join our Discord server