Skip to main content
Glama

India Hotels MCP

CI License: Apache-2.0 Data: ODbL

Contributing · Disclaimer · Security · Changelog

An MCP server for finding, comparing and planning hotel stays across India around the places a trip actually revolves around: railway stations, airports, bus terminals, landmarks, localities and raw coordinates. It merges live prices from free meta-search sources, and adds road distance and drive time to arrival and departure points plus multi-stop stay planning.

It is standalone. Other servers (for example a train-timetable server) work with it through plain values Claude passes between them: lat/lng, Indian Railways station codes, IATA airport codes and ISO-8601 datetimes.

Tools

Full input and output schemas for every tool: docs/tools.md (generated from the server's tools/list; npm run docs:tools regenerates it and CI fails if it is out of date).

Tool

What it does

resolve_place

Name or code → coordinates (stations, airports, bus stations, landmarks, localities); or coordinates → nearest stations, airports and bus stations

search_hotels

Hotels in or near a place for given dates, merged across sources, with a section per source (its own name, rating, amenities, link and offers) and the cheapest live price in INR for the party (adults, children_ages); sort by price, rating or distance; optional drive-time limit

get_hotel_details

One hotel in full from every source, grouped by source: all offers per booking site (Booking.com, Agoda, Trip.com, MakeMyTrip, …) in INR with tax status, room, meals and links, plus each source's details (description, address, photos, amenities, review highlights, nearby places, house rules)

compare_hotels

Up to 10 hotels × up to 6 labelled places: drive or walk times, totals, ranking

travel_times

Origin × destination matrix of road km, free-flow and traffic-adjusted minutes

plan_stays

Per-stop stay planning from arrival/departure times: dates, candidates ranked by price + transfer time, leave-by times, warnings, retiring rooms

find_retiring_rooms

IRCTC railway retiring rooms at or near a station, with booking rules and portal link

get_data_sources

Status, limitations and quotas of every source; bundled dataset dates and licences

All tools are read-only; nothing books, pays or cancels.

Related MCP server: DIDA Hotel MCP

Data sources

Need

Source

Notes

Live prices

trivago official MCP

Free, no key. Cheapest advertiser per hotel (Booking.com, Agoda, MakeMyTrip, …) in INR

Live prices + availability

HotelsCasa MCP

Free, no key, true radius search. EUR, converted to INR

Live prices (unofficial)

SerpApi Google Hotels (scrapes Google; not a Google API)

Off by default; needs SERPAPI_KEY and ENABLE_UNOFFICIAL_SOURCES=true; 250 free searches/month

Live prices (unofficial)

Xotelo (derived from TripAdvisor meta-search)

Off by default; enabled only with ENABLE_UNOFFICIAL_SOURCES=true. Adds ~8 s per uncached search

FX

Frankfurter (ECB rates)

Daily reference rates

Hotel locations

OpenStreetMap snapshot (bundled)

~24k hotels/guest houses/hostels; no prices (include_unpriced)

Stations, bus stations

OpenStreetMap snapshot (bundled)

~9.2k stations with codes, ~5.3k bus stations

Airports

OurAirports (bundled)

151 Indian airports

Retiring rooms

IRCTC public station list (bundled, refreshed by hand)

356 stations; live availability needs a PNR on the IRCTC portal

Geocoding

Photon, Nominatim

Public instances, cached; Nominatim limited to 1 req/s

Routing

OSRM

FOSSGIS public instance by default, ~1 req/s, cached; self-hostable

Third-party prices are meta-search prices: they can differ at checkout and may exclude GST (includes_taxes: null means unknown). Every price carries its source, seller and fetched_at. OSM routing has no traffic data, so drive times are multiplied by METRO_TRAFFIC_MULTIPLIER (default 1.5) in the 8 largest metros and OTHER_TRAFFIC_MULTIPLIER (1.2) elsewhere; both raw and adjusted minutes are returned.

None of the free live-price sources has an SLA. Any source can be switched off with PROVIDERS_DISABLED, and a failing source never fails a search: results come back from the others with sources_failed filled in. Unofficial sources (Xotelo, and SerpApi's Google Hotels scraper) run only when ENABLE_UNOFFICIAL_SOURCES=true; check the terms that apply to you first (see DISCLAIMER.md).

git clone https://github.com/shubhamyadav8901/hotels-mcp.git && cd hotels-mcp
cp .env.example .env                # optional settings; see Configuration
docker compose up -d --build        # http://localhost:3001/mcp
curl -s localhost:3001/healthz      # {"status":"ok",...}

Or run the prebuilt image (amd64 and arm64, published for each release) without cloning:

docker run -d --name india-hotels-mcp --restart unless-stopped \
  -p 127.0.0.1:3001:3001 -e HTTP_USER_AGENT="india-hotels-mcp (+https://github.com/you)" \
  ghcr.io/shubhamyadav8901/hotels-mcp:latest

Add --env-file .env to pass optional settings. The bundled datasets (data/*.json.gz) are in the repository and the image; rebuilding them is optional (see "Rebuilding the bundled data"). Run docker compose up -d after changing .env. The server listens on port 3001 (so it can run next to other local MCP servers on 3000), bound to 127.0.0.1 only.

Connect Claude Code:

claude mcp add --transport http india-hotels http://localhost:3001/mcp

Claude Desktop and claude.ai connect to remote URLs only (Settings → Connectors); expose the server through a tunnel and add its hostname to ALLOWED_HOSTS. The HTTP server has no authentication, so put your own access control in front of any tunnel.

Inspect or test:

npx @modelcontextprotocol/inspector --cli http://localhost:3001/mcp --transport http --method tools/list

Without Docker

Requires Node 22+.

npm ci
npm run build
npm start -- --http                 # HTTP on http://localhost:3001/mcp

Or let Claude Code start it over stdio:

claude mcp add --transport stdio --env HTTP_USER_AGENT="india-hotels-mcp (+https://github.com/you)" \
  india-hotels -- node /path/to/hotels-mcp/dist/src/server.js

Self-hosted routing (optional)

The public OSRM instance is for light use. To self-host:

scripts/osrm-setup.sh northern-zone   # or "india": ~1.7 GB download, ~25 GB disk, 12+ GB Docker RAM
docker compose --profile osrm up -d
# then in .env: OSRM_URL=http://localhost:5001  OSRM_FOOT_URL=http://localhost:5002

Configuration

See .env.example. Main settings: HTTP_USER_AGENT, SERPAPI_KEY (optional), ENABLE_UNOFFICIAL_SOURCES (default false; true enables Xotelo), PROVIDERS_DISABLED, OSRM_URL, OSRM_FOOT_URL, NOMINATIM_URL, PHOTON_URL, METRO_TRAFFIC_MULTIPLIER, OTHER_TRAFFIC_MULTIPLIER, TRAIN_BUFFER_MIN (default 30), FLIGHT_BUFFER_MIN (default 120), DEFAULT_MIN_RATING_PCT (default 0 = off; default for the min_rating_pct filter, which the agent can override per request), PROVIDER_DEADLINE_MS (default 20000; each source's time limit per search), SERPAPI_MAX_PAGES (default 1) and STATE_DIR (default ~/.cache/india-hotels-mcp; holds the SerpApi monthly search count so it survives restarts; docker compose keeps it in the hotels-state volume).

HTTP mode only: PORT (default 3001), HOST (bind address, default 127.0.0.1; 0.0.0.0 in the Docker image) and ALLOWED_HOSTS (comma-separated extra hostnames accepted in the Host header, any port; localhost, 127.0.0.1 and [::1] are always accepted). Under docker compose, PORT and HOST are fixed by docker-compose.yml.

Searches are for the party given by adults (1–8) plus children_ages (up to 4 children, ages 0–17), at most 8 guests, in one room: every source is asked for that occupancy, and each quotes the offer it chooses (the room is the source's choice, so check it on the booking site). Xotelo cannot price children, so it counts them as adults, and trivago prices every child as age 6 whatever ages are given. With sort=price, Google Hotels and HotelsCasa return their cheapest results rather than their most relevant ones. min_stars (and, for Google, max_price_inr) is also applied by those sources on their own price basis, before results are merged. A town or city name (place: "Jaipur") searches the town, not its main station.

Coverage: no source returns every hotel. trivago returns ~25 hotels around a point (no radius control), so wider searches (over 3 km) query up to 13 points across the circle; HotelsCasa returns pages of 10 (up to 5 pages for wide searches); Google Hotels returns ~20 per page (SERPAPI_MAX_PAGES, default 1, one SerpApi search each); Xotelo prices only the 5 nearest of the hotels it lists. search_hotels returns a coverage entry per source (hotels returned, priced, furthest km, and what limited it), so results are not mistaken for every hotel in the radius.

Guest-rating floor: min_rating_pct (e.g. 60 = 6.0/10 = 3.0/5). Its default comes from the server setting DEFAULT_MIN_RATING_PCT (0, i.e. off, unless you set it) and shows in the tool schema, so an agent can override it per request ("at least 80%" → 80, "include unrated hotels" → 0). While it is on, hotels no source rates are left out, and the notes say how many.

Each hotel comes with a section per source, holding that source's own listing and offers:

Source

What its section adds

trivago

the cheapest booking site's price, its rating and reviews, top amenities, area, hotel page

HotelsCasa

a live-checked price with the room name, meals, refundability and availability; property type; rating

Google Hotels (SerpApi)

the lowest listed price, rating and reviews, property type, amenities, check-in and check-out times

Xotelo (TripAdvisor)

a price per booking site (Agoda, Booking.com, Trip.com…) with tax, property type, usual price range (USD)

OpenStreetMap

location and type only (no prices)

search_hotels shows up to 3 offers per source; get_hotel_details shows them all, with each source's details (description, address, phone, photos, distance to the centre, location rating, review scores, pros and cons, review topics, nearby places, important info, missing amenities, badges). For HotelsCasa it calls its free hotel-details tool; with google_prices: true it also fetches Google's page for the hotel (1 SerpApi search) for a price per booking site, address and phone. When trivago's area search leaves a hotel out, get_hotel_details asks trivago for it by name (accepted only if trivago returns the same hotel id); if the hotel still can't be found live, the prices the search returned for the same stay and party are used.

Beds: no source can filter by bed type. Where the source names the room (HotelsCasa), each price carries it in room (e.g. "Family Room", "Comfort Quadruple Room", or "Deluxe Double Room" with extra beds), so check it before booking for four adults.

Example: the cheapest rooms in Jaipur for two adults and two children:

{
  "place": "Jaipur",
  "radius_km": 6,
  "check_in": "2026-11-10",
  "check_out": "2026-11-11",
  "adults": 2,
  "children_ages": [6, 9],
  "sort": "price"
}

Development

npm run typecheck && npm test && npm run format:check   # gate before committing
npm run smoke                                          # live acceptance checks (network, a few minutes)
npm run dev                                            # watch mode, stdio

Unit tests use synthetic fixtures only and never touch the network.

Rebuilding the bundled data

Run about once a month:

HTTP_USER_AGENT="india-hotels-mcp/0.1 (you@example.com)" npm run build:data
# add -- --with-irctc to refresh the IRCTC retiring-room list (a single manual request;
# IRCTC's terms forbid automated access, so never schedule it)
# the Xotelo location-key table is rebuilt separately with: npx tsx scripts/build-xotelo-keys.ts

Limitations

  • Coverage of live prices is strong in metros and tourist towns and thin in small towns; use include_unpriced to see OpenStreetMap-only listings there.

  • Indian OTAs (MakeMyTrip, Goibibo) appear only when trivago lists them as the cheapest advertiser.

  • No public-transport routing; no live traffic.

  • IRCTC retiring-room availability and exact prices are not available without a PNR.

  • The SerpApi monthly quota counter is kept in memory and resets when the server restarts, and SerpApi's 50 searches/hour limit is not enforced locally.

Contributing

Bug reports, data corrections and new sources are welcome. Read CONTRIBUTING.md for the ground rules and checks, and the Code of Conduct. Report security issues privately as described in SECURITY.md.

Disclaimer

This is an independent project, not affiliated with trivago, HotelsCasa, TripAdvisor, Xotelo, Booking.com, Agoda, MakeMyTrip, IRCTC, Indian Railways, Google, SerpApi, OpenStreetMap or any other source. Prices are meta-search prices that may exclude GST; check with the seller before booking. Nothing is booked. See DISCLAIMER.md.

Licences

Code: Apache-2.0 (LICENSE). Bundled OpenStreetMap-derived data: ODbL 1.0, © OpenStreetMap contributors (data/LICENSE, data/NOTICE). Tool responses that use OSM data carry the attribution. Other bundled and run-time data sources are listed in NOTICE.

Available Tools

8 tools
compare_hotelsCompare hotels by travel timeA
Read-onlyIdempotent

Compares up to 10 hotels by travel time to a set of labelled places, such as tonight's arrival station, tomorrow's departure airport and attractions. Hotels are hotel_ids from search_hotels (which also supplies their cheapest price) or name + lat/lng. Places are lat+lng, station code, IATA code or place name. Returns, per hotel, the time and distance to every place, total minutes, the cheapest price from the search that found it (with its source, fetch time and dates) and a rank by total minutes. Does not fetch new prices; get_hotel_details does.

ParametersJSON Schema
NameRequiredDescriptionDefault
modeNodrive (car/taxi, with traffic allowance) or walk.drive
hotelsYesHotels to compare.
placesYesPlaces to measure from each hotel; set label, e.g. 'arrive NDLS 20:10'.

Output Schema

ParametersJSON Schema
NameRequiredDescription
notesYesHow times are computed, caveats and attribution.
hotelsYesHotels sorted by rank.

TDQS

A4.4/5.0
Behavior4/5

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

Annotations already declare readOnly/idempotent/non-destructive, so the bar is lower, and the description still adds real behavioral context: it characterises the returned data (per-place time and distance, total minutes, rank by total minutes), and flags that prices are stale 'from the search that found it' with source, fetch time and dates. No contradiction with 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.

Conciseness4/5

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

One dense but front-loaded block: purpose first, then input sourcing, then return shape, then the 'does not fetch prices' caveat. Every sentence carries information, though the output enumeration is long enough to push against the value of the output schema.

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

Completeness5/5

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

An output schema exists so return values need not be explained, yet the description still orients the agent on what comes back, where inputs originate, and the key limitation (no fresh pricing). For a 3-parameter read-only comparison tool, nothing an agent needs to invoke it correctly is missing.

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

Parameters4/5

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

Schema coverage is 100%, so the baseline is 3, but the description adds meaning the schema does not: which values count as a hotel (hotel_id OR name + lat/lng), the mutually exclusive place forms (lat+lng, station code, IATA, place name), and a label convention example ('arrive NDLS 20:10'). This helps an agent build correct payloads rather than merely restating fields.

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?

Opens with a specific verb and resource plus its scope limit ('Compares up to 10 hotels by travel time to a set of labelled places'). It distinguishes itself from siblings by naming where hotel_ids come from (search_hotels) and which tool handles the thing it does not do (get_hotel_details).

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?

Gives clear context for inputs (hotel_ids come from search_hotels, which also supplies price) and an explicit exclusion with the alternative named: 'Does not fetch new prices; get_hotel_details does.' It does not, however, say when to prefer this over travel_times or plan_stays.

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

find_retiring_roomsFind IRCTC railway retiring roomsA
Read-onlyIdempotent

Lists Indian Railways stations that have IRCTC retiring rooms (rooms and dormitories inside the station for ticketed passengers), at a station code or within a radius of a place. Returns station code, name, operator and distance, the booking rules, indicative price ranges and the IRCTC booking portal link. Live availability and exact prices are not available here: they need a Confirmed or RAC PNR on the IRCTC portal.

ParametersJSON Schema
NameRequiredDescriptionDefault
latNoLatitude (India). Use with lng.
lngNoLongitude (India). Use with lat.
iataNoIndian airport IATA code, e.g. DEL.
placeNoPlace name, e.g. 'Taj Mahal' or 'Paharganj, Delhi'; resolved to its best match.
radius_kmNoSearch radius when not giving a station_code.
station_codeNoIndian Railways station code, e.g. NDLS.

Output Schema

ParametersJSON Schema
NameRequiredDescription
notesYesData source and search caveats.
rulesYesIRCTC booking rules for retiring rooms.
stationsYesStations with IRCTC retiring rooms, nearest first.
booking_urlYesIRCTC retiring-room booking portal.
indicative_pricesYesTypical price ranges from secondary sources; not live.

TDQS

A4.2/5.0
Behavior4/5

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

Annotations already declare readOnlyHint, idempotentHint and openWorldHint, so the safety profile is covered. The description adds genuine behavioral context beyond that: what the records contain (booking rules, indicative price ranges, portal link) and the important limitation that availability/prices are not live and depend on a Confirmed or RAC PNR.

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?

Three sentences, front-loaded with the core purpose, then inputs, then the binding caveat. Very little waste, though the parenthetical definition of retiring rooms is slightly verbose for an agent audience.

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

Completeness5/5

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

An output schema exists, so return values need not be enumerated, yet the description still summarizes them usefully. With annotations covering the read-only profile and the schema covering all params, nothing needed to invoke the tool correctly is missing; the PNR/portal caveat closes the main expectation gap.

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 all six parameters (lat/lng, iata, place, radius_km, station_code) are already documented with examples and constraints. The description only restates the station-code and radius-of-a-place modes without adding format or precedence guidance (e.g. what happens if both place and station_code are supplied). Baseline 3.

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 specific verb (Lists) and a specific resource (Indian Railways stations with IRCTC retiring rooms), and even defines the domain term in parentheses. It is clearly distinct from siblings such as search_hotels and get_hotel_details, which cover commercial lodging.

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?

It spells out the two accepted lookup modes (a station code, or a radius around a place) and sets an explicit boundary: live availability and exact prices are 'not available here' and require a PNR on the IRCTC portal. That is clear when-not guidance, though it does not name a sibling tool as the alternative.

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

get_data_sourcesData sources and statusA
Read-onlyIdempotent

Lists every data source this server uses: hotel price sources, hotel location data, geocoding, routing and bundled datasets. For each it gives whether it is enabled, whether it is official or an unofficial third-party endpoint, recent success/failure, remaining quota and known limitations. Bundled datasets include their build date and licence. Does not fetch any hotel data.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

Output Schema

ParametersJSON Schema
NameRequiredDescription
providersYesEvery data source and its current status.
snapshotsYesBundled datasets, with build date and licence.

TDQS

A4.2/5.0
Behavior4/5

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

Annotations already declare readOnlyHint=true, idempotentHint=true and destructiveHint=false, so safety is covered; the description goes further by disclosing what each entry contains (enabled flag, official vs unofficial third-party, recent success/failure, remaining quota, known limitations, build date and licence) and explicitly denies fetching hotel data. It stops short of describing ordering or freshness of the reported status, but adds real context beyond 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.

Conciseness4/5

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

Three sentences, front-loaded with the core action, and the final boundary sentence earns its place by preventing an agent from expecting hotel payloads. The middle sentence is a dense comma list of returned fields, which is slightly heavy but still scannable.

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

Completeness5/5

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

With no parameters, rich annotations and an output schema present, the description only needs to convey purpose, scope and the negative boundary — all of which it does. Nothing an agent needs in order to call it correctly is missing.

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

Parameters4/5

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

The tool takes zero parameters, so the baseline of 4 applies and there is nothing for the description to disambiguate. It correctly avoids inventing parameter-like filtering language.

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?

States a specific verb and resource ('Lists every data source this server uses') and enumerates the scope: price sources, location data, geocoding, routing and bundled datasets. None of the siblings (search_hotels, resolve_place, travel_times, etc.) do anything similar, so the agent can separate this metadata/status tool from the hotel-lookup tools at a glance.

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

Usage Guidelines3/5

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

Usage is implied rather than stated: the enumeration of status/quota/limitation fields suggests it is the diagnostic tool to call before or after a hotel query fails. It closes one boundary explicitly ('Does not fetch any hotel data'), but never says when to prefer it over the sibling tools or what condition should trigger it.

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

get_hotel_detailsOne hotel's prices and details from every sourceA
Read-onlyIdempotent

Everything the sources say about one hotel for the given dates, grouped by source: each source's own name, rating, property type, amenities and link; all its offers (booking site such as Booking.com, Agoda or MakeMyTrip; price in INR and the original currency; taxes; refundability; availability; room and meals as the source names them; link), cheapest first; and its details (description, address, phone, website, photos, distance to the centre, location rating, review scores, pros and cons, review topics, nearby places, important info such as ID rules, amenities it lacks, badges). HotelsCasa's hotel page is fetched for every call; google_prices also fetches Google's page (1 SerpApi search from a monthly quota). The hotel is a hotel_id from search_hotels or plan_stays, or a name with lat/lng. Does not book.

ParametersJSON Schema
NameRequiredDescriptionDefault
latNoHotel latitude, when not giving hotel_id.
lngNoHotel longitude, when not giving hotel_id.
nameNoHotel name, when not giving hotel_id.
adultsNoAdults (18+) sharing the one room searched for.
check_inYesCheck-in date, YYYY-MM-DD (IST).
hotel_idNohotel_id (or any of its also_ids) from search_hotels or plan_stays.
check_outYesCheck-out date, YYYY-MM-DD.
children_agesNoAges (0–17) of children sharing that room, one entry per child, e.g. [6, 9]; empty for adults only.
google_pricesNoAlso fetch Google's page for the hotel (1 SerpApi search from a monthly quota, when enabled): a price per booking site, address, phone and website.

Output Schema

ParametersJSON Schema
NameRequiredDescription
hotelYesThe hotel the prices are for, as the sources list it.
notesYesCaveats about the prices.
sourcesYesEach source's own listing of the hotel (name, rating, details, link) with all its offers, cheapest source first.
check_inYesCheck-in date, YYYY-MM-DD.
check_outYesCheck-out date, YYYY-MM-DD.
sources_okYesIds of the sources that answered the re-search near the hotel.
cheapest_inrYesLowest bookable per_night_inr across all sources (null if none in INR).
priciest_inrYesHighest per_night_inr across all sources (null if none in INR).
sources_failedYesSources or lookups that did not answer, and why.

TDQS

A4/5.0
Behavior4/5

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

Annotations already cover the safety profile (readOnlyHint, idempotentHint, destructiveHint=false), so the bar is lower, and the description still adds real cost/side-effect context: HotelsCasa's page is fetched on every call, and google_prices spends 1 SerpApi search from a monthly quota. The 'Does not book' line is redundant with readOnlyHint but harmless.

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

Conciseness3/5

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

The scoping facts (dates, source grouping, quota, identifier source, no booking) are front-loaded, but the long mid-sentence enumeration of every offer and detail field is a run-on list that duplicates the output schema. Much of that bulk does not earn its place.

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 a 9-parameter tool with an output schema, the definition covers inputs, identifier provenance, quota cost, and the read-only non-booking nature, so an agent has what it needs to call correctly. The return-field enumeration is redundant with the output schema rather than filling a genuine gap.

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 coverage is 100%, so all nine parameters including the hotel_id/name+lat/lng alternative and the quota note on google_prices are already documented in the schema. The description corroborates but adds no syntax, format, or default detail beyond it, so baseline 3 applies.

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 names a specific verb and resource — everything the sources say about one hotel for given dates, grouped by source with offers and details — and the 'one hotel' scope cleanly separates it from search_hotels and compare_hotels. An agent can identify the tool without opening the schema.

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?

It states where the identifier comes from ('a hotel_id from search_hotels or plan_stays, or a name with lat/lng') and sets an explicit exclusion ('Does not book'), which is clear context. It stops short of naming compare_hotels as the alternative for multi-hotel comparison, so it is clear but not fully routing.

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

plan_staysPlan hotel stays for an itineraryA
Read-onlyIdempotent

Plans where to stay for each stop of a trip in India. Each stay is an arrival (place + time) and the next departure (place + time); places are lat+lng, station code, IATA code or place name, so train and flight times from any source can be passed in. For every stay it works out check-in/check-out dates (early-morning arrivals book the previous night), searches hotels near the arrival point and, if elsewhere, the departure point, and ranks them by nightly price plus drive time from arrival and to departure (valued at value_of_time_inr_per_hour). Returns candidates with leave-by times that include a check-in buffer for trains or flights, IRCTC retiring-room options for 3–48 h stays at stations, and warnings for late arrivals, early departures and same-day stays. Does not book.

ParametersJSON Schema
NameRequiredDescriptionDefault
staysYesOne entry per stop, in trip order.
adultsNoAdults (18+) sharing the one room searched for.
min_starsNoMinimum hotel class (official stars).
radius_kmNoHotel search radius around each point.
candidatesNoHotels to return per stay.
children_agesNoAges (0–17) of children sharing that room, one entry per child, e.g. [6, 9]; empty for adults only.
max_price_inrNoMaximum nightly price in INR.
min_rating_pctNoMinimum guest rating in percent, e.g. 60 = 6.0/10 = 3.0/5. Default 0 (server setting); 0 turns the filter off. While on, hotels with no guest rating are left out.
value_of_time_inr_per_hourNoHow much an hour of transfer time is worth, to trade travel time against price (0 = price only).

Output Schema

ParametersJSON Schema
NameRequiredDescription
notesYesBuffers used, price and routing caveats, and attribution.
staysYesOne plan per requested stay, in trip order.

TDQS

A4.5/5.0
Behavior5/5

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

Annotations already cover the safety profile (readOnly, idempotent, non-destructive, openWorld), and the description still adds substantial non-obvious behavior: early-morning arrivals book the previous night, hotels are searched near arrival and if elsewhere also the departure point, ranking blends nightly price with drive time valued at value_of_time_inr_per_hour, leave-by times include a check-in buffer, retiring rooms are offered for 3–48 h stays, and late/early/same-day cases emit warnings.

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?

Purpose is front-loaded in the first sentence and every sentence carries information about distinct behavior; nothing is redundant filler. The third sentence is a long multi-clause run-on bundling check-in logic, search scope, ranking and outputs, which makes it harder to scan.

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?

For a 9-parameter orchestration tool with an output schema, the description covers scope, computation logic, ranking, warnings and the non-booking boundary, so an agent can invoke it correctly. The only real omission is routing guidance against the six sibling tools that overlap it partially (search_hotels, find_retiring_rooms, travel_times).

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

Parameters4/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 and the schema already documents the point formats, adults/children, radius, candidates and filters. The description adds value by explaining how value_of_time_inr_per_hour is actually applied (drive time from arrival and to departure traded against nightly price), which the schema only states abstractly.

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?

States a specific verb and resource ('Plans where to stay for each stop of a trip in India') and immediately defines the unit of work: one stay = arrival point/time + next departure point/time. The itinerary-level, multi-stop scope makes it distinguishable from the single-search siblings like search_hotels.

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?

Clear context for when the tool applies (planning lodging across all stops of a trip, with train/flight times passed in from any source) and an explicit exclusion ('Does not book'). It never names an alternative, though — nothing tells the agent to fall back to search_hotels for a single stay or find_retiring_rooms for a lone retiring-room query.

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

resolve_placeResolve a place in IndiaA
Read-onlyIdempotent

Turns a name or code into coordinates, or coordinates into nearby transport points. With query: matches railway station codes and names, airport IATA codes and names, bus stations, and landmarks or localities via OpenStreetMap geocoding, returning ranked matches with kind, code and lat/lng. With lat/lng: returns the nearest railway stations (within 10 km), airports with IATA codes (within 60 km) and bus stations (within 5 km), with straight-line distances. Does not search hotels.

ParametersJSON Schema
NameRequiredDescriptionDefault
latNoLatitude, for nearby transport points.
lngNoLongitude, for nearby transport points.
kindNoOnly return this kind of place.
limitNoMaximum matches for a query.
queryNoName or code, e.g. 'Howrah', 'BLR', 'Hawa Mahal'.

Output Schema

ParametersJSON Schema
NameRequiredDescription
notesYesCaveats, unavailable geocoders and attribution.
nearbyYesTransport points near the given lat/lng, nearest first (null when query was given).
matchesYesPlaces matching query, best first (empty when lat/lng was given).

TDQS

A4.7/5.0
Behavior5/5

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

Annotations already declare readOnly, openWorld, idempotent, and non-destructive behavior. Beyond that, the description adds concrete behavioral details: ranked matches with kind/code/lat/lng, radius limits of 10 km for stations, 60 km for airports, 5 km for bus stations, straight-line distances, and the explicit exclusion of hotels.

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 front-loaded, dense, and free of filler. It covers the dual-mode behavior and the key exclusion in three efficient sentences.

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

Completeness5/5

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

For a five-parameter, dual-mode geospatial resolution tool with annotations and an output schema already present, the description provides enough context to call the tool correctly. It explains both input modes, the kinds of results, distance limits, and a notable exclusion.

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

Parameters4/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 meaningful semantics by explaining what `query` matches (railway station codes and names, airport IATA codes and names, bus stations, landmarks/localities via OpenStreetMap) and what lat/lng returns, going beyond the schema's brief parameter descriptions.

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 gives a specific verb+resource and clearly describes the two operating modes: name/code into coordinates, or coordinates into nearby transport points. It also explicitly distinguishes itself from hotel search with 'Does not search hotels,' which helps against siblings like search_hotels.

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?

It clearly states when to use each mode: `query` for station/airport/bus/landmark lookup and lat/lng for nearby transport discovery, with specific distance thresholds. It does not name a specific sibling alternative for non-hotel place resolution, but the mode selection guidance is explicit and useful.

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

search_hotelsSearch hotels near a placeA
Read-onlyIdempotent

Finds hotels around a place in India for given dates, with live prices from several sources (trivago, HotelsCasa, Google Hotels via SerpApi, Xotelo/TripAdvisor) merged per hotel. The place is lat+lng, an Indian Railways station code, an airport IATA code, or a place name. Each hotel has its distance, stars, best-supported guest rating and cheapest price in INR, and a section per source with that source's own name, rating, property type, amenities, link and offers (booking site, price, taxes, refundability, room and meals as the source names them). Every source is asked for one room for the party, and each quotes the offer it chooses. With max_drive_minutes, keeps only hotels within that drive time (OpenStreetMap routing with a traffic allowance) and adds drive_minutes. Results are paginated. Every offer per source is in get_hotel_details; times to other places are in compare_hotels. coverage reports what each source returned and what limited it (sources return limited pages, so the list is not exhaustive). Does not book.

ParametersJSON Schema
NameRequiredDescriptionDefault
latNoLatitude (India). Use with lng.
lngNoLongitude (India). Use with lat.
iataNoIndian airport IATA code, e.g. DEL.
sortNoSort order; drive_time needs max_drive_minutes.distance
limitNoHotels per page.
placeNoPlace name, e.g. 'Taj Mahal' or 'Paharganj, Delhi'; resolved to its best match.
adultsNoAdults (18+) sharing the one room searched for.
offsetNoNumber of hotels to skip, for paging.
check_inYesCheck-in date, YYYY-MM-DD (IST).
check_outYesCheck-out date, YYYY-MM-DD, after check_in.
min_starsNoMinimum hotel class (official stars).
radius_kmNoSearch radius in km.
station_codeNoIndian Railways station code, e.g. NDLS.
children_agesNoAges (0–17) of children sharing that room, one entry per child, e.g. [6, 9]; empty for adults only.
max_price_inrNoOnly hotels with a known nightly price at or below this, in INR.
min_rating_pctNoMinimum guest rating in percent, e.g. 60 = 6.0/10 = 3.0/5. Default 0 (server setting); 0 turns the filter off. While on, hotels with no guest rating are left out.
include_unpricedNoAlso list hotels with no live price (OpenStreetMap listings), for places with thin price coverage.
max_drive_minutesNoOnly hotels within this many minutes' drive of the place (traffic-adjusted).

Output Schema

ParametersJSON Schema
NameRequiredDescription
notesYesCaveats about the results, sources and attribution.
queryYesThe search as run.
totalYesNumber of hotels matching all filters, across all pages.
anchorYesThe resolved search place.
hotelsYesThis page of hotels, in the requested sort order.
showingYesWhich slice of the results this page holds, e.g. 'Showing 1–10 of 42'.
coverageYesWhat each source returned within the radius and what limited it; sources return limited pages.
sources_okYesIds of the sources that answered.
sources_failedYesSources that did not answer, and why.

TDQS

A4.3/5.0
Behavior4/5

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

Annotations cover safety (readOnly/idempotent/openWorld/non-destructive), and the description adds genuinely non-obvious behavior: per-hotel merging across sources, one room searched for the whole party, drive-time filtering via OpenStreetMap routing with a traffic allowance, pagination, and the key caveat that results are not exhaustive because sources return limited pages. It omits auth/rate-limit or latency expectations, so it stops short of a 5.

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?

Front-loaded with what the tool does before constraints and routing, and every clause carries information. It is long (roughly eight sentences) and the sentence enumerating per-hotel fields largely restates the output schema, which is the only real waste.

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?

For an 18-parameter, multi-source, paginated search with an output schema present, the description covers the aggregation model, sorting/filtering caveats, and non-exhaustiveness. It need not repeat return fields, and its doing so is harmless rather than required; nothing an agent needs to call it correctly appears missing.

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

Parameters4/5

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

Schema coverage is 100%, so baseline is 3. The description adds interpretation the schema cannot: that adults/children_ages describe a single shared room, that each source quotes the offer it chooses, that max_drive_minutes injects a drive_minutes field, and that coverage explains which source limited the result. That is meaningful semantic value beyond per-field docs.

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?

Starts with a specific verb+resource+scope ('Finds hotels around a place in India for given dates') and immediately states the value proposition: live prices merged per hotel from trivago, HotelsCasa, Google Hotels/SerpApi, Xotelo/TripAdvisor. It also distinguishes itself from siblings by naming where the detail lives (get_hotel_details) and what it does not do ('Does not book').

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?

Explicitly routes the agent: full per-source offers go to get_hotel_details, travel times to compare_hotels, and it states the tool never books. However, it gives no positioning against other siblings such as plan_stays, find_retiring_rooms, or resolve_place, and offers no explicit 'when not to use this' beyond the booking exclusion.

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

travel_timesTravel times between placesA
Read-onlyIdempotent

Computes road distance and travel time from each origin to each destination in India (a matrix), by car or on foot. Each place is lat+lng, a railway station code, an airport IATA code or a place name. Returns straight-line km, road km, free-flow minutes and traffic-adjusted minutes per pair, with a warning when a point is far from any road. Does not cover public transport or flights.

ParametersJSON Schema
NameRequiredDescriptionDefault
modeNodrive (car/taxi, with traffic allowance) or walk.drive
originsYesStarting places.
destinationsYesDestination places.

Output Schema

ParametersJSON Schema
NameRequiredDescription
legsYesOne leg per origin–destination pair, origin by origin.
notesYesHow times are computed, and attribution.

TDQS

A4.5/5.0
Behavior4/5

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

Annotations already declare readOnly, idempotent, non-destructive and open-world behavior, so the safety profile is covered. The description adds genuinely useful behavior beyond that: the exact per-pair outputs (straight-line km, road km, free-flow and traffic-adjusted minutes) and the warning emitted when a point is far from any road, plus the fact that place names are resolved to a 'best match'.

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?

Three tight sentences: purpose and scope first, accepted input identifiers second, returned values plus failure warning third. No filler, no repetition of the title, and the most decision-relevant constraint (India, road/walk only) is front-loaded.

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

Completeness5/5

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

An output schema exists, so return values did not need to be spelled out, yet the description helpfully names the key per-pair metrics and the far-from-road warning. Combined with the schema's bounds (max 10 origins, 25 destinations, India lat/lng limits) and the annotations' read-only profile, an agent has everything needed to call this correctly.

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

Parameters4/5

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

Schema coverage is 100%, so the baseline is 3 and the schema already documents every parameter including the four point identifier kinds and the drive/walk enum. The description adds meaning on top by summarizing that each place may be lat+lng, a station code, an IATA code or a place name, and by noting that names are resolved to their best match rather than exact-matched.

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?

States a specific verb and resource ('Computes road distance and travel time from each origin to each destination in India (a matrix)') plus the supported modes, so the agent knows exactly what operation this is. It also delineates scope by naming what it does not cover (public transport, flights), which no sibling does, cleanly separating it from the hotel/place siblings.

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 description establishes clear context for use: India-only, drive-vs-walk mode, and the matrix (each-to-each) semantics, plus an explicit exclusion of public transport and flights. It stops short of naming an alternative tool (e.g. resolve_place for ambiguous place resolution), so it is strong but not fully routing-oriented.

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. 9 tool updatesv0.3.0
    • Changedcompare_hotels19 fields changed
      • addedOutput schema / properties / hotels / description
        Added value: +"Hotels sorted by rank."
      • changedOutput schema / properties / hotels / items / properties / cheapest / anyOf
        Previous value: -[
        -  {
        -    "additionalProperties": false,
        -    "properties": {
        -      "check_in": {
        -        "type": "string"
        -      },
        -      "check_out": {
        -        "type": "string"
        -      },
        -      "fetched_at": {
        -        "type": "string"
        -      },
        -      "per_night_inr": {
        -        "type": [
        -          "number",
        -          "null"
        -        ]
        -      },
        -      "seller": {
        -        "type": "string"
        -      },
        -      "source": {
        -        "type": "string"
        -      }
        -    },
        -    "required": [
        -      "per_night_inr",
        -      "seller",
        -      "source",
        -      "fetched_at",
        -      "check_in",
        -      "check_out"
        -    ],
        -    "type": "object"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]New value: +[
        +  {
        +    "additionalProperties": false,
        +    "properties": {
        +      "adults": {
        +        "description": "Adults in that search.",
        +        "type": "number"
        +      },
        +      "check_in": {
        +        "description": "Check-in date of that search, YYYY-MM-DD.",
        +        "type": "string"
        +      },
        +      "check_out": {
        +        "description": "Check-out date of that search, YYYY-MM-DD.",
        +        "type": "string"
        +      },
        +      "children_ages": {
        +        "description": "Ages of children in that search.",
        +        "items": {
        +          "type": "number"
        +        },
        +        "type": "array"
        +      },
        +      "fetched_at": {
        +        "description": "ISO time the source returned this price.",
        +        "type": "string"
        +      },
        +      "per_night_inr": {
        +        "description": "Per-night price converted to INR (null if no exchange rate).",
        +        "type": [
        +          "number",
        +          "null"
        +        ]
        +      },
        +      "seller": {
        +        "description": "Booking site the price is from; the source id when the source names no seller.",
        +        "type": "string"
        +      },
        +      "source": {
        +        "description": "Id of the data source that returned the price.",
        +        "type": "string"
        +      }
        +    },
        +    "required": [
        +      "per_night_inr",
        +      "seller",
        +      "source",
        +      "fetched_at",
        +      "check_in",
        +      "check_out",
        +      "adults",
        +      "children_ages"
        +    ],
        +    "type": "object"
        +  },
        +  {
        +    "type": "null"
        +  }
        +]
      • changedOutput schema / properties / hotels / items / properties / cheapest / description
        Previous value: -"Cheapest price from the search that returned this hotel, for that search's dates."New value: +"Lowest per-night INR price across sources from that search, for its dates and party."
      • addedOutput schema / properties / hotels / items / properties / hotel_id / description
        Added value: +"hotel_id as given (null when the hotel was given by lat/lng)."
      • addedOutput schema / properties / hotels / items / properties / lat / description
        Added value: +"Hotel latitude."
      • addedOutput schema / properties / hotels / items / properties / legs / description
        Added value: +"Travel from this hotel to each place, in the order given."
      • addedOutput schema / properties / hotels / items / properties / legs / items / properties / from / description
        Added value: +"Label of the origin."
      • addedOutput schema / properties / hotels / items / properties / legs / items / properties / minutes / description
        Added value: +"Travel time after the traffic multiplier, minutes; equals minutes_raw for walking (null when no route)."
      • addedOutput schema / properties / hotels / items / properties / legs / items / properties / minutes_raw / description
        Added value: +"Free-flow travel time from the router, minutes (null when no route)."
      • addedOutput schema / properties / hotels / items / properties / legs / items / properties / road_km / description
        Added value: +"Road distance along the route, km (null when no route was found)."
      • addedOutput schema / properties / hotels / items / properties / legs / items / properties / straight_km / description
        Added value: +"Straight-line distance, km."
      • addedOutput schema / properties / hotels / items / properties / legs / items / properties / to / description
        Added value: +"Label of the destination."
      • addedOutput schema / properties / hotels / items / properties / legs / items / properties / traffic_multiplier / description
        Added value: +"Factor applied to free-flow drive time for traffic, higher in metro areas; 1 for walking."
      • addedOutput schema / properties / hotels / items / properties / legs / items / properties / warning / description
        Added value: +"Set when no route was found or a point is over 500 m from the nearest road."
      • addedOutput schema / properties / hotels / items / properties / lng / description
        Added value: +"Hotel longitude."
      • addedOutput schema / properties / hotels / items / properties / name / description
        Added value: +"Hotel name (the coordinates when no name was given)."
      • addedOutput schema / properties / hotels / items / properties / rank / description
        Added value: +"Position by total_minutes, then per_night_inr; 1 is best."
      • addedOutput schema / properties / hotels / items / properties / total_minutes / description
        Added value: +"Sum of minutes over all legs (null when any leg has no route)."
      • addedOutput schema / properties / notes / description
        Added value: +"How times are computed, caveats and attribution."
    • Changedfind_retiring_rooms11 fields changed
      • addedOutput schema / properties / booking_url / description
        Added value: +"IRCTC retiring-room booking portal."
      • addedOutput schema / properties / indicative_prices / description
        Added value: +"Typical price ranges from secondary sources; not live."
      • addedOutput schema / properties / notes / description
        Added value: +"Data source and search caveats."
      • addedOutput schema / properties / rules / description
        Added value: +"IRCTC booking rules for retiring rooms."
      • addedOutput schema / properties / stations / description
        Added value: +"Stations with IRCTC retiring rooms, nearest first."
      • addedOutput schema / properties / stations / items / properties / distance_km / description
        Added value: +"Straight-line distance from the search place, km; 0 for the station itself."
      • addedOutput schema / properties / stations / items / properties / lat / description
        Added value: +"Station latitude (null if unknown)."
      • addedOutput schema / properties / stations / items / properties / lng / description
        Added value: +"Station longitude (null if unknown)."
      • addedOutput schema / properties / stations / items / properties / managed_by / description
        Added value: +"Who runs the retiring rooms, as IRCTC lists it."
      • addedOutput schema / properties / stations / items / properties / station_code / description
        Added value: +"Station code."
      • addedOutput schema / properties / stations / items / properties / station_name / description
        Added value: +"Station name."
    • Changedget_data_sources20 fields changed
      • addedOutput schema / properties / providers / description
        Added value: +"Every data source and its current status."
      • addedOutput schema / properties / providers / items / properties / disabled_reason / description
        Added value: +"Why the source is disabled (null when enabled)."
      • addedOutput schema / properties / providers / items / properties / enabled / description
        Added value: +"Whether the source is in use."
      • addedOutput schema / properties / providers / items / properties / id / description
        Added value: +"Source id, as used in source fields elsewhere."
      • addedOutput schema / properties / providers / items / properties / kind / description
        Added value: +"What the source provides: hotel-prices, hotel-locations, geocoding, routing, fx or dataset."
      • changedOutput schema / properties / providers / items / properties / last_error / anyOf
        Previous value: -[
        -  {
        -    "additionalProperties": false,
        -    "properties": {
        -      "at": {
        -        "type": "string"
        -      },
        -      "code": {
        -        "type": "string"
        -      },
        -      "message": {
        -        "type": "string"
        -      }
        -    },
        -    "required": [
        -      "at",
        -      "code",
        -      "message"
        -    ],
        -    "type": "object"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]New value: +[
        +  {
        +    "additionalProperties": false,
        +    "properties": {
        +      "at": {
        +        "description": "ISO time of the error.",
        +        "type": "string"
        +      },
        +      "code": {
        +        "description": "Error code, e.g. UPSTREAM_UNAVAILABLE.",
        +        "type": "string"
        +      },
        +      "message": {
        +        "description": "What went wrong.",
        +        "type": "string"
        +      }
        +    },
        +    "required": [
        +      "at",
        +      "code",
        +      "message"
        +    ],
        +    "type": "object"
        +  },
        +  {
        +    "type": "null"
        +  }
        +]
      • addedOutput schema / properties / providers / items / properties / last_error / description
        Added value: +"The most recent failed call since the server started (null if none)."
      • addedOutput schema / properties / providers / items / properties / last_success_at / description
        Added value: +"ISO time of the last successful call since the server started (null if none)."
      • addedOutput schema / properties / providers / items / properties / limitations / description
        Added value: +"Known limitations of the source."
      • addedOutput schema / properties / providers / items / properties / name / description
        Added value: +"Human-readable source name."
      • addedOutput schema / properties / providers / items / properties / needsKey / description
        Added value: +"Whether the source needs an API key."
      • addedOutput schema / properties / providers / items / properties / official / description
        Added value: +"true for an official API, false for an unofficial third-party endpoint with no SLA."
      • addedOutput schema / properties / providers / items / properties / quota_remaining / description
        Added value: +"Calls left in the source's current quota window (null when the source has no tracked quota)."
      • addedOutput schema / properties / snapshots / description
        Added value: +"Bundled datasets, with build date and licence."
      • addedOutput schema / properties / snapshots / items / properties / built_at / description
        Added value: +"When the dataset was built (null if unknown)."
      • addedOutput schema / properties / snapshots / items / properties / description / description
        Added value: +"What the dataset contains."
      • addedOutput schema / properties / snapshots / items / properties / id / description
        Added value: +"Dataset id."
      • addedOutput schema / properties / snapshots / items / properties / licence / description
        Added value: +"Licence of the data."
      • addedOutput schema / properties / snapshots / items / properties / rows / description
        Added value: +"Number of records in the dataset."
      • addedOutput schema / properties / snapshots / items / properties / source / description
        Added value: +"Where the data comes from."
    • Addedget_hotel_details
    • Removedget_hotel_rates
    • Changedplan_stays49 fields changed
      • changedInput schema / properties / adults / description
        Previous value: -"Guests in the room; searches are for one room."New value: +"Adults (18+) sharing the one room searched for."
      • addedInput schema / properties / children_ages
        Added value: +{
        +  "default": [],
        +  "description": "Ages (0–17) of children sharing that room, one entry per child, e.g. [6, 9]; empty for adults only.",
        +  "items": {
        +    "maximum": 17,
        +    "minimum": 0,
        +    "type": "integer"
        +  },
        +  "maxItems": 4,
        +  "type": "array"
        +}
      • addedInput schema / properties / min_rating_pct
        Added value: +{
        +  "default": 0,
        +  "description": "Minimum guest rating in percent, e.g. 60 = 6.0/10 = 3.0/5. Default 0 (server setting); 0 turns the filter off. While on, hotels with no guest rating are left out.",
        +  "maximum": 100,
        +  "minimum": 0,
        +  "type": "number"
        +}
      • addedInput schema / properties / min_stars / description
        Added value: +"Minimum hotel class (official stars)."
      • addedOutput schema / properties / notes / description
        Added value: +"Buffers used, price and routing caveats, and attribution."
      • addedOutput schema / properties / stays / description
        Added value: +"One plan per requested stay, in trip order."
      • addedOutput schema / properties / stays / items / properties / arrive / description
        Added value: +"Where and when the traveller arrives."
      • addedOutput schema / properties / stays / items / properties / arrive / properties / at / description
        Added value: +"Arrival or departure time in IST, ISO-8601 with +05:30."
      • addedOutput schema / properties / stays / items / properties / arrive / properties / code / description
        Added value: +"Railway station code or airport IATA code (null when the place has none)."
      • addedOutput schema / properties / stays / items / properties / arrive / properties / kind / description
        Added value: +"Kind of place: station, airport, bus_station, landmark, locality or point (raw coordinates)."
      • addedOutput schema / properties / stays / items / properties / arrive / properties / label / description
        Added value: +"Label of the point: as given, else its code or name."
      • addedOutput schema / properties / stays / items / properties / arrive / properties / name / description
        Added value: +"Resolved place name."
      • addedOutput schema / properties / stays / items / properties / candidates / description
        Added value: +"Hotels for this stay, lowest score_inr first, then cheapest."
      • addedOutput schema / properties / stays / items / properties / candidates / items / properties / hotel_id / description
        Added value: +"Hotel id as source:source_id of the first source that listed it."
      • addedOutput schema / properties / stays / items / properties / candidates / items / properties / lat / description
        Added value: +"Hotel latitude."
      • addedOutput schema / properties / stays / items / properties / candidates / items / properties / leave_by / description
        Added value: +"Latest time to leave the hotel, IST ISO-8601: departure minus drive time and the train or flight buffer."
      • addedOutput schema / properties / stays / items / properties / candidates / items / properties / lng / description
        Added value: +"Hotel longitude."
      • addedOutput schema / properties / stays / items / properties / candidates / items / properties / minutes_from_arrival / description
        Added value: +"Traffic-adjusted drive from the arrival point, minutes (null when not routed)."
      • addedOutput schema / properties / stays / items / properties / candidates / items / properties / minutes_to_departure / description
        Added value: +"Traffic-adjusted drive to the departure point, minutes (null when not routed)."
      • addedOutput schema / properties / stays / items / properties / candidates / items / properties / name / description
        Added value: +"Hotel name."
      • addedOutput schema / properties / stays / items / properties / candidates / items / properties / per_night_inr / description
        Added value: +"Lowest per-night INR price across sources."
      • addedOutput schema / properties / stays / items / properties / candidates / items / properties / rating_10 / description
        Added value: +"Guest review score on a 0–10 scale (null if unrated)."
      • addedOutput schema / properties / stays / items / properties / candidates / items / properties / score_inr / description
        Added value: +"per_night_inr × nights plus both drives valued at value_of_time_inr_per_hour, INR; lower is better."
      • addedOutput schema / properties / stays / items / properties / candidates / items / properties / seller / description
        Added value: +"Booking site of per_night_inr; the source id when the source names no seller."
      • addedOutput schema / properties / stays / items / properties / candidates / items / properties / stars / description
        Added value: +"Official hotel class, 1–5 stars (null if unknown)."
      • addedOutput schema / properties / stays / items / properties / check_in / description
        Added value: +"Hotel check-in date, YYYY-MM-DD; the previous day for arrivals before 06:00 IST."
      • addedOutput schema / properties / stays / items / properties / check_out / description
        Added value: +"Hotel check-out date, YYYY-MM-DD."
      • addedOutput schema / properties / stays / items / properties / depart / description
        Added value: +"Where and when the traveller leaves."
      • addedOutput schema / properties / stays / items / properties / depart / properties / at / description
        Added value: +"Arrival or departure time in IST, ISO-8601 with +05:30."
      • addedOutput schema / properties / stays / items / properties / depart / properties / code / description
        Added value: +"Railway station code or airport IATA code (null when the place has none)."
      • addedOutput schema / properties / stays / items / properties / depart / properties / kind / description
        Added value: +"Kind of place: station, airport, bus_station, landmark, locality or point (raw coordinates)."
      • addedOutput schema / properties / stays / items / properties / depart / properties / label / description
        Added value: +"Label of the point: as given, else its code or name."
      • addedOutput schema / properties / stays / items / properties / depart / properties / name / description
        Added value: +"Resolved place name."
      • addedOutput schema / properties / stays / items / properties / nights / description
        Added value: +"Nights between check_in and check_out."
      • addedOutput schema / properties / stays / items / properties / retiring_rooms / description
        Added value: +"Arrival or departure stations with IRCTC retiring rooms, listed only for 3–48 h stays."
      • addedOutput schema / properties / stays / items / properties / retiring_rooms / items / properties / distance_km / description
        Added value: +"Straight-line distance from the search place, km; 0 for the station itself."
      • addedOutput schema / properties / stays / items / properties / retiring_rooms / items / properties / lat / description
        Added value: +"Station latitude (null if unknown)."
      • addedOutput schema / properties / stays / items / properties / retiring_rooms / items / properties / lng / description
        Added value: +"Station longitude (null if unknown)."
      • addedOutput schema / properties / stays / items / properties / retiring_rooms / items / properties / managed_by / description
        Added value: +"Who runs the retiring rooms, as IRCTC lists it."
      • addedOutput schema / properties / stays / items / properties / retiring_rooms / items / properties / station_code / description
        Added value: +"Station code."
      • addedOutput schema / properties / stays / items / properties / retiring_rooms / items / properties / station_name / description
        Added value: +"Station name."
      • addedOutput schema / properties / stays / items / properties / searched_around / description
        Added value: +"Labels of the points hotels were searched around: the arrival, and the departure when elsewhere."
      • addedOutput schema / properties / stays / items / properties / sources_failed / description
        Added value: +"Sources that did not answer for this stay, and why."
      • addedOutput schema / properties / stays / items / properties / sources_failed / items / properties / code / description
        Added value: +"Error code, e.g. UPSTREAM_UNAVAILABLE or RATE_LIMITED."
      • addedOutput schema / properties / stays / items / properties / sources_failed / items / properties / message / description
        Added value: +"What went wrong."
      • addedOutput schema / properties / stays / items / properties / sources_failed / items / properties / source / description
        Added value: +"Id of the source that failed."
      • addedOutput schema / properties / stays / items / properties / stay / description
        Added value: +"Stay number, from 1, in trip order."
      • addedOutput schema / properties / stays / items / properties / stay_hours / description
        Added value: +"Hours from arrival to departure, 1 decimal."
      • addedOutput schema / properties / stays / items / properties / warnings / description
        Added value: +"Late arrivals, early departures, short or same-day stays and empty results."
    • Changedresolve_place11 fields changed
      • addedOutput schema / properties / matches / description
        Added value: +"Places matching query, best first (empty when lat/lng was given)."
      • addedOutput schema / properties / matches / items / properties / code / description
        Added value: +"Railway station code or airport IATA code (null when the place has none)."
      • addedOutput schema / properties / matches / items / properties / context / description
        Added value: +"Extra detail, e.g. the airport's city, the bus operator, a halt marker or the geocoder's locality."
      • addedOutput schema / properties / matches / items / properties / kind / description
        Added value: +"Kind of place: station, airport, bus_station, landmark, locality or point (raw coordinates)."
      • addedOutput schema / properties / matches / items / properties / lat / description
        Added value: +"Latitude."
      • addedOutput schema / properties / matches / items / properties / lng / description
        Added value: +"Longitude."
      • addedOutput schema / properties / matches / items / properties / name / description
        Added value: +"Place name."
      • addedOutput schema / properties / matches / items / properties / source / description
        Added value: +"Dataset or geocoder the place came from, e.g. openstreetmap, ourairports, or input for given coordinates."
      • changedOutput schema / properties / nearby / anyOf
        Previous value: -[
        -  {
        -    "additionalProperties": false,
        -    "properties": {
        -      "airports": {
        -        "items": {
        -          "additionalProperties": false,
        -          "properties": {
        -            "code": {
        -              "type": [
        -                "string",
        -                "null"
        -              ]
        -            },
        -            "context": {
        -              "type": [
        -                "string",
        -                "null"
        -              ]
        -            },
        -            "distance_km": {
        -              "type": "number"
        -            },
        -            "kind": {
        -              "type": "string"
        -            },
        -            "lat": {
        -              "type": "number"
        -            },
        -            "lng": {
        -              "type": "number"
        -            },
        -            "name": {
        -              "type": "string"
        -            },
        -            "source": {
        -              "type": "string"
        -            }
        -          },
        -          "required": [
        -            "kind",
        -            "name",
        -            "code",
        -            "context",
        -            "lat",
        -            "lng",
        -            "source",
        -            "distance_km"
        -          ],
        -          "type": "object"
        -        },
        -        "type": "array"
        -      },
        -      "bus_stations": {
        -        "items": {
        -          "additionalProperties": false,
        -          "properties": {
        -            "code": {
        -              "type": [
        -                "string",
        -                "null"
        -              ]
        -            },
        -            "context": {
        -              "type": [
        -                "string",
        -                "null"
        -              ]
        -            },
        -            "distance_km": {
        -              "type": "number"
        -            },
        -            "kind": {
        -              "type": "string"
        -            },
        -            "lat": {
        -              "type": "number"
        -            },
        -            "lng": {
        -              "type": "number"
        -            },
        -            "name": {
        -              "type": "string"
        -            },
        -            "source": {
        -              "type": "string"
        -            }
        -          },
        -          "required": [
        -            "kind",
        -            "name",
        -            "code",
        -            "context",
        -            "lat",
        -            "lng",
        -            "source",
        -            "distance_km"
        -          ],
        -          "type": "object"
        -        },
        -        "type": "array"
        -      },
        -      "stations": {
        -        "items": {
        -          "additionalProperties": false,
        -          "properties": {
        -            "code": {
        -              "type": [
        -                "string",
        -                "null"
        -              ]
        -            },
        -            "context": {
        -              "type": [
        -                "string",
        -                "null"
        -              ]
        -            },
        -            "distance_km": {
        -              "type": "number"
        -            },
        -            "kind": {
        -              "type": "string"
        -            },
        -            "lat": {
        -              "type": "number"
        -            },
        -            "lng": {
        -              "type": "number"
        -            },
        -            "name": {
        -              "type": "string"
        -            },
        -            "source": {
        -              "type": "string"
        -            }
        -          },
        -          "required": [
        -            "kind",
        -            "name",
        -            "code",
        -            "context",
        -            "lat",
        -            "lng",
        -            "source",
        -            "distance_km"
        -          ],
        -          "type": "object"
        -        },
        -        "type": "array"
        -      }
        -    },
        -    "required": [
        -      "stations",
        -      "airports",
        -      "bus_stations"
        -    ],
        -    "type": "object"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]New value: +[
        +  {
        +    "additionalProperties": false,
        +    "properties": {
        +      "airports": {
        +        "description": "Up to 2 nearest airports with IATA codes within 60 km.",
        +        "items": {
        +          "additionalProperties": false,
        +          "properties": {
        +            "code": {
        +              "description": "Railway station code or airport IATA code (null when the place has none).",
        +              "type": [
        +                "string",
        +                "null"
        +              ]
        +            },
        +            "context": {
        +              "description": "Extra detail, e.g. the airport's city, the bus operator, a halt marker or the geocoder's locality.",
        +              "type": [
        +                "string",
        +                "null"
        +              ]
        +            },
        +            "distance_km": {
        +              "description": "Straight-line distance from the given lat/lng, km.",
        +              "type": "number"
        +            },
        +            "kind": {
        +              "description": "Kind of place: station, airport, bus_station, landmark, locality or point (raw coordinates).",
        +              "type": "string"
        +            },
        +            "lat": {
        +              "description": "Latitude.",
        +              "type": "number"
        +            },
        +            "lng": {
        +              "description": "Longitude.",
        +              "type": "number"
        +            },
        +            "name": {
        +              "description": "Place name.",
        +              "type": "string"
        +            },
        +            "source": {
        +              "description": "Dataset or geocoder the place came from, e.g. openstreetmap, ourairports, or input for given coordinates.",
        +              "type": "string"
        +            }
        +          },
        +          "required": [
        +            "kind",
        +            "name",
        +            "code",
        +            "context",
        +            "lat",
        +            "lng",
        +            "source",
        +            "distance_km"
        +          ],
        +          "type": "object"
        +        },
        +        "type": "array"
        +      },
        +      "bus_stations": {
        +        "description": "Up to 2 nearest bus stations within 5 km.",
        +        "items": {
        +          "additionalProperties": false,
        +          "properties": {
        +            "code": {
        +              "description": "Railway station code or airport IATA code (null when the place has none).",
        +              "type": [
        +                "string",
        +                "null"
        +              ]
        +            },
        +            "context": {
        +              "description": "Extra detail, e.g. the airport's city, the bus operator, a halt marker or the geocoder's locality.",
        +              "type": [
        +                "string",
        +                "null"
        +              ]
        +            },
        +            "distance_km": {
        +              "description": "Straight-line distance from the given lat/lng, km.",
        +              "type": "number"
        +            },
        +            "kind": {
        +              "description": "Kind of place: station, airport, bus_station, landmark, locality or point (raw coordinates).",
        +              "type": "string"
        +            },
        +            "lat": {
        +              "description": "Latitude.",
        +              "type": "number"
        +            },
        +            "lng": {
        +              "description": "Longitude.",
        +              "type": "number"
        +            },
        +            "name": {
        +              "description": "Place name.",
        +              "type": "string"
        +            },
        +            "source": {
        +              "description": "Dataset or geocoder the place came from, e.g. openstreetmap, ourairports, or input for given coordinates.",
        +              "type": "string"
        +            }
        +          },
        +          "required": [
        +            "kind",
        +            "name",
        +            "code",
        +            "context",
        +            "lat",
        +            "lng",
        +            "source",
        +            "distance_km"
        +          ],
        +          "type": "object"
        +        },
        +        "type": "array"
        +      },
        +      "stations": {
        +        "description": "Up to 3 nearest railway stations within 10 km.",
        +        "items": {
        +          "additionalProperties": false,
        +          "properties": {
        +            "code": {
        +              "description": "Railway station code or airport IATA code (null when the place has none).",
        +              "type": [
        +                "string",
        +                "null"
        +              ]
        +            },
        +            "context": {
        +              "description": "Extra detail, e.g. the airport's city, the bus operator, a halt marker or the geocoder's locality.",
        +              "type": [
        +                "string",
        +                "null"
        +              ]
        +            },
        +            "distance_km": {
        +              "description": "Straight-line distance from the given lat/lng, km.",
        +              "type": "number"
        +            },
        +            "kind": {
        +              "description": "Kind of place: station, airport, bus_station, landmark, locality or point (raw coordinates).",
        +              "type": "string"
        +            },
        +            "lat": {
        +              "description": "Latitude.",
        +              "type": "number"
        +            },
        +            "lng": {
        +              "description": "Longitude.",
        +              "type": "number"
        +            },
        +            "name": {
        +              "description": "Place name.",
        +              "type": "string"
        +            },
        +            "source": {
        +              "description": "Dataset or geocoder the place came from, e.g. openstreetmap, ourairports, or input for given coordinates.",
        +              "type": "string"
        +            }
        +          },
        +          "required": [
        +            "kind",
        +            "name",
        +            "code",
        +            "context",
        +            "lat",
        +            "lng",
        +            "source",
        +            "distance_km"
        +          ],
        +          "type": "object"
        +        },
        +        "type": "array"
        +      }
        +    },
        +    "required": [
        +      "stations",
        +      "airports",
        +      "bus_stations"
        +    ],
        +    "type": "object"
        +  },
        +  {
        +    "type": "null"
        +  }
        +]
      • addedOutput schema / properties / nearby / description
        Added value: +"Transport points near the given lat/lng, nearest first (null when query was given)."
      • addedOutput schema / properties / notes / description
        Added value: +"Caveats, unavailable geocoders and attribution."
    • Changedsearch_hotels49 fields changed
      • changedInput schema / properties / adults / description
        Previous value: -"Guests in the room; searches are for one room."New value: +"Adults (18+) sharing the one room searched for."
      • addedInput schema / properties / children_ages
        Added value: +{
        +  "default": [],
        +  "description": "Ages (0–17) of children sharing that room, one entry per child, e.g. [6, 9]; empty for adults only.",
        +  "items": {
        +    "maximum": 17,
        +    "minimum": 0,
        +    "type": "integer"
        +  },
        +  "maxItems": 4,
        +  "type": "array"
        +}
      • addedInput schema / properties / min_rating_pct
        Added value: +{
        +  "default": 0,
        +  "description": "Minimum guest rating in percent, e.g. 60 = 6.0/10 = 3.0/5. Default 0 (server setting); 0 turns the filter off. While on, hotels with no guest rating are left out.",
        +  "maximum": 100,
        +  "minimum": 0,
        +  "type": "number"
        +}
      • changedInput schema / properties / min_stars / description
        Previous value: -"Minimum star rating."New value: +"Minimum hotel class (official stars)."
      • addedOutput schema / properties / anchor / description
        Added value: +"The resolved search place."
      • addedOutput schema / properties / anchor / properties / code / description
        Added value: +"Railway station code or airport IATA code (null when the place has none)."
      • addedOutput schema / properties / anchor / properties / context / description
        Added value: +"Extra detail, e.g. the airport's city, the bus operator, a halt marker or the geocoder's locality."
      • addedOutput schema / properties / anchor / properties / kind / description
        Added value: +"Kind of place: station, airport, bus_station, landmark, locality or point (raw coordinates)."
      • addedOutput schema / properties / anchor / properties / lat / description
        Added value: +"Latitude."
      • addedOutput schema / properties / anchor / properties / lng / description
        Added value: +"Longitude."
      • addedOutput schema / properties / anchor / properties / name / description
        Added value: +"Place name."
      • addedOutput schema / properties / anchor / properties / source / description
        Added value: +"Dataset or geocoder the place came from, e.g. openstreetmap, ourairports, or input for given coordinates."
      • addedOutput schema / properties / coverage
        Added value: +{
        +  "description": "What each source returned within the radius and what limited it; sources return limited pages.",
        +  "items": {
        +    "additionalProperties": false,
        +    "properties": {
        +      "hotels": {
        +        "description": "Hotels the source returned within the radius.",
        +        "type": "number"
        +      },
        +      "max_km": {
        +        "description": "Distance of the source's furthest hotel from the search place, km (null if it returned none).",
        +        "type": [
        +          "number",
        +          "null"
        +        ]
        +      },
        +      "note": {
        +        "description": "What limited the source's results, e.g. pages fetched.",
        +        "type": [
        +          "string",
        +          "null"
        +        ]
        +      },
        +      "priced": {
        +        "description": "How many of those hotels the source priced.",
        +        "type": "number"
        +      },
        +      "source": {
        +        "description": "Id of the source.",
        +        "type": "string"
        +      }
        +    },
        +    "required": [
        +      "source",
        +      "hotels",
        +      "priced",
        +      "max_km",
        +      "note"
        +    ],
        +    "type": "object"
        +  },
        +  "type": "array"
        +}
      • addedOutput schema / properties / hotels / description
        Added value: +"This page of hotels, in the requested sort order."
      • addedOutput schema / properties / hotels / items / properties / also_ids / description
        Added value: +"Ids (source:source_id) of the same hotel at other sources; each resolves to this hotel."
      • changedOutput schema / properties / hotels / items / properties / cheapest / anyOf
        Previous value: -[
        -  {
        -    "additionalProperties": false,
        -    "properties": {
        -      "available": {
        -        "type": [
        -          "boolean",
        -          "null"
        -        ]
        -      },
        -      "currency": {
        -        "type": "string"
        -      },
        -      "fetched_at": {
        -        "type": "string"
        -      },
        -      "includes_taxes": {
        -        "type": [
        -          "boolean",
        -          "null"
        -        ]
        -      },
        -      "per_night": {
        -        "type": "number"
        -      },
        -      "per_night_inr": {
        -        "type": [
        -          "number",
        -          "null"
        -        ]
        -      },
        -      "refundable": {
        -        "type": [
        -          "boolean",
        -          "null"
        -        ]
        -      },
        -      "seller": {
        -        "type": [
        -          "string",
        -          "null"
        -        ]
        -      },
        -      "source": {
        -        "type": "string"
        -      },
        -      "url": {
        -        "type": [
        -          "string",
        -          "null"
        -        ]
        -      }
        -    },
        -    "required": [
        -      "per_night_inr",
        -      "per_night",
        -      "currency",
        -      "seller",
        -      "source",
        -      "includes_taxes",
        -      "available",
        -      "refundable",
        -      "url",
        -      "fetched_at"
        -    ],
        -    "type": "object"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]New value: +[
        +  {
        +    "additionalProperties": false,
        +    "properties": {
        +      "per_night_inr": {
        +        "description": "Per-night price in INR (null if no exchange rate).",
        +        "type": [
        +          "number",
        +          "null"
        +        ]
        +      },
        +      "seller": {
        +        "description": "Booking site of the price (null when the source names none).",
        +        "type": [
        +          "string",
        +          "null"
        +        ]
        +      },
        +      "source": {
        +        "description": "Source that quoted it; its section in sources has the details.",
        +        "type": "string"
        +      },
        +      "url": {
        +        "description": "Link to the offer, when given.",
        +        "type": [
        +          "string",
        +          "null"
        +        ]
        +      }
        +    },
        +    "required": [
        +      "per_night_inr",
        +      "source",
        +      "seller",
        +      "url"
        +    ],
        +    "type": "object"
        +  },
        +  {
        +    "type": "null"
        +  }
        +]
      • addedOutput schema / properties / hotels / items / properties / cheapest / description
        Added value: +"Lowest bookable per-night INR price across sources; null when no source priced the hotel."
      • addedOutput schema / properties / hotels / items / properties / distance_km / description
        Added value: +"Straight-line distance from the search place, km."
      • addedOutput schema / properties / hotels / items / properties / drive_minutes / description
        Added value: +"Traffic-adjusted drive time from the search place, minutes (null without max_drive_minutes)."
      • addedOutput schema / properties / hotels / items / properties / hotel_id / description
        Added value: +"Hotel id as source:source_id of the first source that listed it."
      • addedOutput schema / properties / hotels / items / properties / lat / description
        Added value: +"Hotel latitude."
      • addedOutput schema / properties / hotels / items / properties / lng / description
        Added value: +"Hotel longitude."
      • addedOutput schema / properties / hotels / items / properties / name / description
        Added value: +"Hotel name as the first source gives it."
      • removedOutput schema / properties / hotels / items / properties / price_count
        Removed value: -{
        -  "type": "number"
        -}
      • addedOutput schema / properties / hotels / items / properties / rating_10 / description
        Added value: +"Guest review score on a 0–10 scale, from the source with the most reviews (null if unrated)."
      • addedOutput schema / properties / hotels / items / properties / review_count / description
        Added value: +"Number of guest reviews behind rating_10 (null if unknown)."
      • addedOutput schema / properties / hotels / items / properties / sources / description
        Added value: +"Each source's own listing of this hotel (name, rating, details, link) with its offers, cheapest source first; up to 3 offers per source here (get_hotel_details lists all)."
      • addedOutput schema / properties / hotels / items / properties / sources / items / additionalProperties
        Added value: +false
      • addedOutput schema / properties / hotels / items / properties / sources / items / properties
        Added value: +{
        +  "amenities": {
        +    "description": "Amenities the source lists, in its own words.",
        +    "items": {
        +      "type": "string"
        +    },
        +    "type": "array"
        +  },
        +  "area": {
        +    "description": "Area or locality the source places it in.",
        +    "type": "string"
        +  },
        +  "cheapest_inr": {
        +    "description": "Lowest per_night_inr among this source's bookable offers (null if none).",
        +    "type": [
        +      "number",
        +      "null"
        +    ]
        +  },
        +  "check_in_time": {
        +    "description": "Check-in time the source states.",
        +    "type": "string"
        +  },
        +  "check_out_time": {
        +    "description": "Check-out time the source states.",
        +    "type": "string"
        +  },
        +  "details": {
        +    "additionalProperties": false,
        +    "description": "Further details from this source; get_hotel_details only.",
        +    "properties": {
        +      "address": {
        +        "description": "Street address.",
        +        "type": "string"
        +      },
        +      "category_scores": {
        +        "description": "Review scores by category.",
        +        "items": {
        +          "additionalProperties": false,
        +          "properties": {
        +            "name": {
        +              "description": "Category, e.g. Cleanliness.",
        +              "type": "string"
        +            },
        +            "score": {
        +              "description": "Score on the source's own scale.",
        +              "type": "number"
        +            }
        +          },
        +          "required": [
        +            "name",
        +            "score"
        +          ],
        +          "type": "object"
        +        },
        +        "type": "array"
        +      },
        +      "cons": {
        +        "description": "What reviews criticise, in the source's words.",
        +        "items": {
        +          "type": "string"
        +        },
        +        "type": "array"
        +      },
        +      "description": {
        +        "description": "The source's description of the property.",
        +        "type": "string"
        +      },
        +      "distance_to_centre": {
        +        "description": "Distance to the city centre as the source words it, e.g. \"6.0 km to City centre\".",
        +        "type": "string"
        +      },
        +      "excluded_amenities": {
        +        "description": "Amenities the source says the property does not have.",
        +        "items": {
        +          "type": "string"
        +        },
        +        "type": "array"
        +      },
        +      "images": {
        +        "description": "A few photo URLs.",
        +        "items": {
        +          "type": "string"
        +        },
        +        "type": "array"
        +      },
        +      "important_info": {
        +        "description": "Conditions guests must know, e.g. ID required.",
        +        "items": {
        +          "type": "string"
        +        },
        +        "type": "array"
        +      },
        +      "labels": {
        +        "description": "Badges the source shows.",
        +        "items": {
        +          "type": "string"
        +        },
        +        "type": "array"
        +      },
        +      "location_rating": {
        +        "description": "Location score on a 0–5 scale (Google).",
        +        "type": "number"
        +      },
        +      "nearby_places": {
        +        "description": "Nearby places the source lists.",
        +        "items": {
        +          "additionalProperties": false,
        +          "properties": {
        +            "name": {
        +              "description": "Place.",
        +              "type": "string"
        +            },
        +            "travel": {
        +              "description": "How to get there, e.g. \"Walking 1 min\".",
        +              "type": "string"
        +            }
        +          },
        +          "required": [
        +            "name"
        +          ],
        +          "type": "object"
        +        },
        +        "type": "array"
        +      },
        +      "phone": {
        +        "description": "Phone number.",
        +        "type": "string"
        +      },
        +      "pros": {
        +        "description": "What reviews praise, in the source's words.",
        +        "items": {
        +          "type": "string"
        +        },
        +        "type": "array"
        +      },
        +      "review_topics": {
        +        "description": "Review topics with how often and how they are mentioned.",
        +        "items": {
        +          "additionalProperties": false,
        +          "properties": {
        +            "mentions": {
        +              "description": "How many reviews mention it.",
        +              "type": "number"
        +            },
        +            "name": {
        +              "description": "Topic, e.g. Location or Public transit.",
        +              "type": "string"
        +            },
        +            "negative": {
        +              "description": "How many of those are negative.",
        +              "type": "number"
        +            },
        +            "positive": {
        +              "description": "How many of those are positive.",
        +              "type": "number"
        +            }
        +          },
        +          "required": [
        +            "name",
        +            "mentions"
        +          ],
        +          "type": "object"
        +        },
        +        "type": "array"
        +      },
        +      "website": {
        +        "description": "The property's own website.",
        +        "type": "string"
        +      }
        +    },
        +    "type": "object"
        +  },
        +  "fetched_at": {
        +    "description": "ISO time this source answered.",
        +    "type": "string"
        +  },
        +  "name": {
        +    "description": "Hotel name as this source gives it.",
        +    "type": "string"
        +  },
        +  "offers": {
        +    "description": "This source's offers for the stay and party, cheapest first.",
        +    "items": {
        +      "additionalProperties": false,
        +      "properties": {
        +        "available": {
        +          "description": "Whether the source reports it bookable for these dates (null when not stated).",
        +          "type": [
        +            "boolean",
        +            "null"
        +          ]
        +        },
        +        "currency": {
        +          "description": "ISO currency code of per_night and total, e.g. INR or EUR.",
        +          "type": "string"
        +        },
        +        "includes_taxes": {
        +          "description": "Whether the price includes taxes such as GST (null when the source does not say).",
        +          "type": [
        +            "boolean",
        +            "null"
        +          ]
        +        },
        +        "meal_plan": {
        +          "description": "Meals included as the source states them, e.g. Room Only.",
        +          "type": "string"
        +        },
        +        "per_night": {
        +          "description": "Per-night price in the source's currency.",
        +          "type": "number"
        +        },
        +        "per_night_inr": {
        +          "description": "Per-night price converted to INR (null if no exchange rate).",
        +          "type": [
        +            "number",
        +            "null"
        +          ]
        +        },
        +        "refundable": {
        +          "description": "Whether the rate is refundable (null when not stated).",
        +          "type": [
        +            "boolean",
        +            "null"
        +          ]
        +        },
        +        "room": {
        +          "description": "Room as the source names it (null when it does not say).",
        +          "type": [
        +            "string",
        +            "null"
        +          ]
        +        },
        +        "seller": {
        +          "description": "Booking site of the offer, e.g. Agoda (null when the source names none; then it sells the room itself).",
        +          "type": [
        +            "string",
        +            "null"
        +          ]
        +        },
        +        "total": {
        +          "description": "Price for the whole stay in that currency (null when not given).",
        +          "type": [
        +            "number",
        +            "null"
        +          ]
        +        },
        +        "url": {
        +          "description": "Link to the offer at the source, when given.",
        +          "type": [
        +            "string",
        +            "null"
        +          ]
        +        }
        +      },
        +      "required": [
        +        "seller",
        +        "per_night_inr",
        +        "per_night",
        +        "currency",
        +        "total",
        +        "includes_taxes",
        +        "refundable",
        +        "available",
        +        "room",
        +        "url"
        +      ],
        +      "type": "object"
        +    },
        +    "type": "array"
        +  },
        +  "offers_total": {
        +    "description": "How many offers the source returned (offers may list fewer).",
        +    "type": "number"
        +  },
        +  "property_type": {
        +    "description": "Kind of property as the source labels it, e.g. Hotel, Homestay.",
        +    "type": "string"
        +  },
        +  "rating_10": {
        +    "description": "This source's guest rating on a 0–10 scale (5-point scales doubled); null if unrated.",
        +    "type": [
        +      "number",
        +      "null"
        +    ]
        +  },
        +  "review_count": {
        +    "description": "Number of reviews behind rating_10 at this source.",
        +    "type": [
        +      "number",
        +      "null"
        +    ]
        +  },
        +  "source": {
        +    "description": "Data source id: trivago, hotelscasa, xotelo, serpapi (Google Hotels) or osm_lodging.",
        +    "type": "string"
        +  },
        +  "source_id": {
        +    "description": "The hotel's id at this source; source:source_id is one of the hotel's ids.",
        +    "type": "string"
        +  },
        +  "stars": {
        +    "description": "Hotel class this source gives, 1–5 stars (null if none).",
        +    "type": [
        +      "number",
        +      "null"
        +    ]
        +  },
        +  "typical_price": {
        +    "additionalProperties": false,
        +    "description": "The source's usual nightly price range for the property, independent of these dates.",
        +    "properties": {
        +      "currency": {
        +        "description": "Currency of min and max.",
        +        "type": "string"
        +      },
        +      "max": {
        +        "description": "High end of the usual nightly price.",
        +        "type": "number"
        +      },
        +      "min": {
        +        "description": "Low end of the usual nightly price.",
        +        "type": "number"
        +      }
        +    },
        +    "required": [
        +      "min",
        +      "max",
        +      "currency"
        +    ],
        +    "type": "object"
        +  },
        +  "url": {
        +    "description": "The hotel's page at this source, when given.",
        +    "type": [
        +      "string",
        +      "null"
        +    ]
        +  }
        +}
      • addedOutput schema / properties / hotels / items / properties / sources / items / required
        Added value: +[
        +  "source",
        +  "source_id",
        +  "name",
        +  "rating_10",
        +  "review_count",
        +  "stars",
        +  "url",
        +  "cheapest_inr",
        +  "offers",
        +  "offers_total",
        +  "fetched_at"
        +]
      • changedOutput schema / properties / hotels / items / properties / sources / items / type
        Previous value: -"string"New value: +"object"
      • addedOutput schema / properties / hotels / items / properties / stars / description
        Added value: +"Official hotel class, 1–5 stars (null if unknown)."
      • changedOutput schema / properties / hotels / items / required
        Previous value: -[
        -  "hotel_id",
        -  "also_ids",
        -  "name",
        -  "lat",
        -  "lng",
        -  "distance_km",
        -  "stars",
        -  "rating_10",
        -  "review_count",
        -  "cheapest",
        -  "price_count",
        -  "sources",
        -  "drive_minutes"
        -]New value: +[
        +  "hotel_id",
        +  "also_ids",
        +  "name",
        +  "lat",
        +  "lng",
        +  "distance_km",
        +  "stars",
        +  "rating_10",
        +  "review_count",
        +  "cheapest",
        +  "sources",
        +  "drive_minutes"
        +]
      • addedOutput schema / properties / notes / description
        Added value: +"Caveats about the results, sources and attribution."
      • addedOutput schema / properties / query / description
        Added value: +"The search as run."
      • addedOutput schema / properties / query / properties / adults / description
        Added value: +"Adults in the one room searched for."
      • addedOutput schema / properties / query / properties / check_in / description
        Added value: +"Check-in date, YYYY-MM-DD."
      • addedOutput schema / properties / query / properties / check_out / description
        Added value: +"Check-out date, YYYY-MM-DD."
      • addedOutput schema / properties / query / properties / children_ages
        Added value: +{
        +  "description": "Ages of children sharing that room.",
        +  "items": {
        +    "type": "number"
        +  },
        +  "type": "array"
        +}
      • addedOutput schema / properties / query / properties / radius_km / description
        Added value: +"Radius searched, km; widened when max_drive_minutes needs it."
      • changedOutput schema / properties / query / required
        Previous value: -[
        -  "radius_km",
        -  "check_in",
        -  "check_out",
        -  "adults"
        -]New value: +[
        +  "radius_km",
        +  "check_in",
        +  "check_out",
        +  "adults",
        +  "children_ages"
        +]
      • addedOutput schema / properties / showing / description
        Added value: +"Which slice of the results this page holds, e.g. 'Showing 1–10 of 42'."
      • addedOutput schema / properties / sources_failed / description
        Added value: +"Sources that did not answer, and why."
      • addedOutput schema / properties / sources_failed / items / properties / code / description
        Added value: +"Error code, e.g. UPSTREAM_UNAVAILABLE or RATE_LIMITED."
      • addedOutput schema / properties / sources_failed / items / properties / message / description
        Added value: +"What went wrong."
      • addedOutput schema / properties / sources_failed / items / properties / source / description
        Added value: +"Id of the source that failed."
      • addedOutput schema / properties / sources_ok / description
        Added value: +"Ids of the sources that answered."
      • addedOutput schema / properties / total / description
        Added value: +"Number of hotels matching all filters, across all pages."
      • changedOutput schema / required
        Previous value: -[
        -  "anchor",
        -  "query",
        -  "showing",
        -  "total",
        -  "hotels",
        -  "sources_ok",
        -  "sources_failed",
        -  "notes"
        -]New value: +[
        +  "anchor",
        +  "query",
        +  "showing",
        +  "total",
        +  "hotels",
        +  "sources_ok",
        +  "sources_failed",
        +  "coverage",
        +  "notes"
        +]
    • Changedtravel_times10 fields changed
      • addedOutput schema / properties / legs / description
        Added value: +"One leg per origin–destination pair, origin by origin."
      • addedOutput schema / properties / legs / items / properties / from / description
        Added value: +"Label of the origin."
      • addedOutput schema / properties / legs / items / properties / minutes / description
        Added value: +"Travel time after the traffic multiplier, minutes; equals minutes_raw for walking (null when no route)."
      • addedOutput schema / properties / legs / items / properties / minutes_raw / description
        Added value: +"Free-flow travel time from the router, minutes (null when no route)."
      • addedOutput schema / properties / legs / items / properties / road_km / description
        Added value: +"Road distance along the route, km (null when no route was found)."
      • addedOutput schema / properties / legs / items / properties / straight_km / description
        Added value: +"Straight-line distance, km."
      • addedOutput schema / properties / legs / items / properties / to / description
        Added value: +"Label of the destination."
      • addedOutput schema / properties / legs / items / properties / traffic_multiplier / description
        Added value: +"Factor applied to free-flow drive time for traffic, higher in metro areas; 1 for walking."
      • addedOutput schema / properties / legs / items / properties / warning / description
        Added value: +"Set when no route was found or a point is over 500 m from the nearest road."
      • addedOutput schema / properties / notes / description
        Added value: +"How times are computed, and attribution."
  2. 8 tool updatesv0.1.0
    • First observedcompare_hotels
    • First observedfind_retiring_rooms
    • First observedget_data_sources
    • First observedget_hotel_rates
    • First observedplan_stays
    • First observedresolve_place
    • First observedsearch_hotels
    • First observedtravel_times

TDQS

A4.2/5.0

Scored across 8 tools

Disambiguation4/5

Tools have distinct primary purposes (search, details, compare, plan, travel, retiring rooms, place resolution, data sources), but some overlap exists: travel_times and compare_hotels both compute travel times, and plan_stays also surfaces IRCTC retiring-room options. Descriptions explicitly clarify boundaries with 'Does not...' statements, reducing confusion.

Naming Consistency4/5

Names consistently use snake_case and mostly follow a verb_noun pattern (get_data_sources, resolve_place, search_hotels, get_hotel_details, compare_hotels, plan_stays, find_retiring_rooms). travel_times breaks the verb_noun pattern as a noun phrase, but the style remains uniform and readable.

Tool Count5/5

8 tools is well within the ideal 3–15 range for a domain covering hotel search, details, comparison, trip planning, travel times, retiring rooms, place resolution, and data source introspection. Each tool earns its place with a clear, non-redundant function.

Completeness4/5

The surface covers core research and planning workflows: search, details, compare, plan, travel times, retiring rooms, place resolution, and data source introspection. Booking is explicitly out of scope, and no tool provides train/flight schedules (users must supply times), but no dead ends exist for the stated purpose.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    D
    maintenance
    Enables multi-modal travel planning for India, aggregating flights, trains, buses, cabs, and personal vehicles across 117 cities with multi-leg routing and weighted scoring.
    2 npm
    Apache 2.0
  • A
    license
    Not graded
    quality
    B
    maintenance
    Enables AI agents to search, compare, and book hotels with real-time pricing and availability, supporting multiple location types, star ratings, and price filters.
    11
    MIT
  • A
    license
    B
    quality
    A
    maintenance
    Official MCP server for MAQAMI, a hotel and flight booking platform with 3M+ hotels. Search live hotel rates and flights, look up places, airports and hotel details, then prebook and book. Remote Streamable HTTP endpoint, no API key required.
    12
    41
    428 npm
    2
    MIT