viajante
Viajante is a no-API-key travel search server that finds flights, hotels, and trip options from live providers (Google Flights/Hotels, Booking.com, Skiplagged) and returns evidence-backed results for agents and users.
Flights:
search_flightsfor named routes/dates (one-way, round-trip, multi-city) with filters for cabin, stops, bags, layovers, times, airlines, and price caps.Date flexibility:
search_datesfor the cheapest day across a window (up to 31 days) andsearch_flexfor ±N days around a target date.Destination discovery:
search_explorefinds priced destinations from an origin.Hidden-city fares:
search_hidden_city(opt-in) checks Skiplagged for a named route/date.Hotels:
search_hotelsreturns total-stay quotes with cancellation terms, location, ratings, and optional distance filtering; supports batching multiple stays in one call.Hotel rooms:
search_hotel_roomsreturns Skiplagged room-level rates and occupancy limits for 1–3 named finalists.Combined trips:
search_tripsearches flights and a hotel stay together in one request.Awards & points:
compare_awardscomputes cents-per-point for a named award offer;lookup_transfersreturns a card-to-program transfer table.Itinerary validation:
validate_itinerarychecks selected offers against their original query locally.Stay planning math:
plan_stay_blocksgroups consecutive nights by people;split_stay_costssplits stay totals and fees among sleepers.Answer verification:
verify_answerflags amounts, codes, dates, or links in a draft that no recorded search returned.Airport lookup:
lookup_airportsresolves airport/metro codes offline.Runtime info:
get_runtime_inforeports installed package, Python, and hotel schema versions offline.
Searches Google Flights and Google Hotels for flight and hotel availability, prices, itinerary details, and links, including date comparison, flexible date, explore, and combined flight-plus-hotel searches.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@viajante Find cheap hotels in Tokyo for two adults next week."
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Flight and hotel search for your terminal, Python, and AI assistants. Runs on your machine, no API keys.
Website · Usage guide · Architecture · Changelog
It reads Google Flights, Google Hotels, and Booking.com directly, and Skiplagged when you opt in. It returns prices, itinerary details, and links. It reports what the provider returned and says so when something is unknown; it never invents a fare.
Quick start
Requires Python 3.10+.
pip install viajante
viajante flights JFK-LHR:2026-11-15 --fetch sweep
viajante hotels London 2026-11-15 2026-11-20 --currency GBP --source googleUse future dates. Not sure of a code? viajante airports london.
Prefer not to install? uvx --from viajante viajante airports JFK, or
npx -y -p @viajante/mcp viajante airports JFK (both need uv).
These searches use plain HTTP. Booking.com and --fetch detail need the
browser extra.
Related MCP server: TravelGenie
Connect an AI assistant
Add this to your assistant's MCP configuration (needs
uv and Python 3.10+):
{
"mcpServers": {
"viajante": {
"command": "npx",
"args": ["-y", "@viajante/mcp"]
}
}
}Without Node, use "command": "uvx" with
"args": ["--from", "viajante[mcp]", "viajante-mcp"]. That covers Google
Flights and Hotels with no Chromium. The npm entry also includes the browser
extra, which search_explore needs.
Then ask in plain language:
Find a seven-night round trip from BOS to LHR, departing any day in November 2026. Compare the dates.
Hotels in Tokyo, November 12 to 16, two adults, free cancellation, in JPY.
Every tool result (except lookup_airports) carries one envelope: status,
completeness, empty_reason, retry_after, observed_at. Only
empty_reason: provider_empty means the provider found nothing. A client that
only takes a URL can use viajante-mcp --transport streamable-http; it has no
auth, so keep it on loopback (details).
Tool | What it does |
| One-way, round-trip, or multi-city flights |
| Cheapest fare per departure date in a window |
| Dates around a target, then flights for the cheapest day |
| Destinations from an origin, with a priced shortlist |
| Stays with total-stay prices and cancellation terms |
| Skiplagged room rates for one named hotel |
| A hotel offer from this session, plus an optional room quote |
| Flights and hotel together, summed when compatible |
| Opt-in separately ticketed itineraries (connections not protected) |
| Opt-in Skiplagged hidden-city fares |
| One fresh search to re-check an earlier offer |
| Check a proposed itinerary against search evidence |
| Cents-per-point math for a named award offer |
| Points transfer-partner table |
| Airport and metro codes, offline |
| Group consecutive nights with the same people |
| Split stay totals among who slept there |
| Flag amounts, codes, dates, or links no search returned |
| Prices this machine recorded for a query (opt-in) |
| Re-run a saved search and report the change |
| Installed version, offline |
| Long operational guide |
Command line
Run viajante <command> --help for options. Add --save FILE to write a JSON report.
Command | Purpose |
| Specific routes and dates ( |
| Cheapest departure day across a window of up to 31 days |
| A few days either side of a target date |
| Destinations from an origin airport |
| Google Hotels, Booking.com, or Skiplagged |
| Flights and a hotel stay in one request |
| Re-check a saved offer against a fresh search |
| Find airport codes |
| Skiplagged fares, award value, transfer partners |
| Recorded prices and saved re-runs (opt-in) |
| Open a prefilled GitHub issue (viajante sends nothing itself) |
viajante dates BOS-LHR --from 2026-11-01 --to 2026-11-30 --nights 7Python
from viajante import get_flights
report = get_flights("JFK-LHR:2026-11-15", fetch="sweep", top=5)
for result in report.queries:
if result.status == "ok":
for offer in result.offers:
print(offer.price, report.currency, offer.airline, offer.duration)
else:
print(result.error)FlightQuery, HotelQuery, and the search_* functions are exported too;
see the Python examples.
Optional browser support
pip install 'viajante[browser]'
playwright install chromiumFor uvx, use viajante[mcp,browser] in --from and install Chromium from
that same environment.
Good to know
Currency. Taken from
--currency, or from a named origin airport's country. Hotels always need one. Viajante never converts between currencies.Baggage.
--carry-onasks Google to price one carry-on. Checked bags (--bags) are refused, because no transport can verify them.--baggage-bufferonly affects ranking; it is not a quoted fee.Hotels. Prices are for the whole stay. Free cancellation is required unless you opt out. Google rates 0–5, Booking.com 0–10.
Recommendation. Flight results may include a short
recommendationblock. It adds to the offer list and never replaces it.Price level. Flight results may include Google's own price level (
low,typical, orhigh) from the results page already fetched. It is Google's label for that price against its typical range.--price-historyadds the page's price history. No extra request.Unknown stays unknown. Missing fields are left out or marked unknown.
Travel sites change their pages, block requests, and update prices at any time. Confirm the final fare, baggage, hotel total, and cancellation terms with the provider before you book.
Contributing
Bug reports and focused pull requests are welcome; see CONTRIBUTING.md.
License
MIT. Independent project, not affiliated with Google, Booking.com, or any airline. Use of those services is subject to their terms: Google, Booking.com.
Available Tools
22 toolscompare_awardsCompare award to cashARead-onlyIdempotent
Compare a named award offer to cash locally. Does not invent seats.
offer must include origin, destination, departure_date, program, points, and evidence. confirmed evidence needs a named source. Unnamed cash_price omits cpp_cents. Do not treat estimated or user_supplied as live inventory.
Inputs: offer (required), balances, currency, cash_price. Returns: status, empty_note, error_code, observed_at, +5 more
get_guideOperational guideARead-onlyIdempotent
The long operational guide (markdown); the same text as the viajante://guide resource.
For clients that do not read MCP resources. Returns {"guide": markdown}. Local; may run during a search.
Inputs: no named parameters. Returns: guide, status, empty_note, error_code, +6 more
get_hotel_detailsHotel finalist detailsARead-onlyIdempotent
Read a hotel offer this process returned. room_rates must be a boolean.
False returns the stored quote, does not search, and may run during another search. True asks Skiplagged for a separate USD room quote and takes the one-search lock. That quote is not the original stay when the city matches more than one place, the returned coordinates do not match the hotel, or the provider echoes different adults, rooms, or dates (occupancy_mismatch, dates_mismatch, property_mismatch). A missing echo is echo unknown. When the provider does not echo adults, rooms, and dates, a returned quote is partial. A read does not evict stored searches. Unknown or evicted ids send nothing.
Inputs: selection_id (required), room_rates. Returns: status, empty_note, error_code, observed_at, +5 more
get_runtime_infoRuntime infoARead-onlyIdempotent
Offline executing package, Python and hotel schema versions.
Check before searches; an npm MCP does not upgrade a separate uv tool installation. May run during a search. No network or personal paths.
Inputs: no named parameters. Returns: status, empty_note, error_code, observed_at, +5 more
lookup_airportsLook up airportsDRead-onlyIdempotent
Inputs: query (required), limit.
lookup_transfersLook up point transfersCRead-onlyIdempotent
Local card-to-program transfer table. Not live award availability.
Inputs: points (required), program (required), balances. Returns: status, empty_note, error_code, observed_at, +5 more
plan_stay_blocksPlan stay blocksARead-onlyIdempotent
Local: group consecutive nights with the same people into blocks.
roster maps each night (YYYY-MM-DD, the night that starts that day) to the names sleeping. Nights must be consecutive. Each block gives check_in, check_out, nights, headcount and people, so one search_hotels stay per block can use headcount as adults. Never decides where anyone sleeps.
Inputs: roster (required). Returns: status, empty_note, error_code, observed_at, +5 more
price_historyPrice historyARead-onlyIdempotent
Local read of prices this machine observed (opt-in: VIAJANTE_PRICE_HISTORY=1 in the server environment). One exact query in one currency is a series: first_seen, last_seen, lowest, highest, and the change since the previous observation. One observation reports no trend. Currencies are never merged or converted, and nothing is forecast. route is ORIGIN-DEST; date matches a departure, return, check-in or check-out; location names a hotel stay. An unreadable log gives read_error and series null: unknown, not empty. May run during a search.
Inputs: date, kind, limit, route, +3 more. Returns: status, empty_note, error_code, observed_at, +5 more
recheck_offerRe-check offerARead-onlyIdempotent
Re-check an earlier flight offer with one fresh Google Flights search (never the replay cache). offer is a prior search_flights offer, or a {query, offer} row, or enough of one: price plus every segment's flight_number, origin, destination and departure clock. query defaults to the offer's evidence query and is replayed (cabin, stops, bags, airline filters). A price_cap is not sent; a break is reported in filter_violations. Read the outcome: check_failed means the check did not run, never that the offer is gone. Confirm the price on the provider's page; this is not a booking guarantee. Full rules: get_guide.
Inputs: offer (required), fetch, proxy, query, +4 more. Returns: status, empty_note, error_code, observed_at, +5 more
search_datesCheapest-dates calendarARead-onlyIdempotent
Cheapest-per-day calendar for one named route, up to 31 days. route is ORIGIN-DEST; start and end are ISO dates. Use search_flex for +/-N around one date. This calendar is HTTP only and has no fetch mode. If it returns blocked, stop: do not switch to a browser or follow with search_flex or search_flights. Currency and country as in search_flights. Viajante does not convert.
deadline_seconds stops starting queries after that many seconds; unfinished
queries are error code deadline (not loaded, not empty). See get_guide.
Inputs: end (required), route (required), start (required), via, +33 more. Returns: status, empty_note, error_code, observed_at, +5 more
search_exploreExplore destinationsBRead-onlyIdempotent
Destinations from one origin, then a priced shortlist. Give start (ISO date) or month, not both. Needs the browser extra; one adult in economy only. Currency and country as in search_flights. Viajante does not convert. A metro origin (MIL, NYC, and the rest of the owned table) runs one explore per member airport, sequentially, and merges destinations: the cheapest fare is kept and origin_airport names which airport it came from. A rate limit stops the remaining airports. top above 30 is clamped to 30 and named in warnings. A destination without a price is not_loaded, not a usable row. A month window still shops one date; an empty shop that day is not_loaded for that shopped_date, not no_results and not provider_empty for the month.
deadline_seconds stops starting queries after that many seconds; unfinished
queries are error code deadline (not loaded, not empty). See get_guide.
Inputs: origin (required), top, via, bags, +34 more. Returns: status, empty_note, error_code, observed_at, +5 more
search_flexFlexible-date flight searchARead-onlyIdempotent
Flex window of +/-N days around one date, then one shopping search on the cheapest day. route is ORIGIN-DEST; around is an ISO date. Use search_dates for a cheapest-week calendar; do not brute-force a date matrix. Currency and country as in search_flights. A markup_drift day is not no_results: do not invent a cheapest week. A named-date search_flights is allowed.
deadline_seconds stops starting queries after that many seconds; unfinished
queries are error code deadline (not loaded, not empty). See get_guide.
Inputs: flex (required), route (required), around (required), top, +34 more. Returns: status, empty_note, error_code, observed_at, +5 more
search_flightsSearch flightsARead-onlyIdempotent
Google Flights search for named routes and dates. Each routes entry is ORIGIN-DEST:YYYY-MM-DD. Use search_dates for the cheapest week and search_flex for +/-N days. fetch=detail needs the browser extra and Chromium. Currency is currency, or inferred from a named origin's owned country; if unproven, ask. Viajante does not convert. Do not invent a bag fee. max_stops is 0, 1 or 2. Metro codes (LON, NYC) expand on one-way and rt routes. One-way searches send clock bounds, max_duration, max_layover, price_cap, less_emissions and hide_separate_tickets to the page, and check each one locally. exclude_basic_economy is refused. nearby_days (default 0, off) adds a ±N cheapest-fare grid on that search; round trips shift both dates and keep the trip length. The grid reuses the date calendar and does not change offers. A successful query carries price_insight when the page states a level (low, typical, or high) that matches its heading. That level is Google's label for the current price against its typical range, not a forecast. include_price_history adds the page's own price history and is off by default. A successful query may carry recommendation: evidence, not a verdict. Check relaxed_requirements before calling it a match. After a named hub or leisure trunk, search_hidden_city may run once with the same route and date, never when bags were named. Rules and wording: get_guide.
deadline_seconds stops starting queries after that many seconds; unfinished
queries are error code deadline (not loaded, not empty). See get_guide.
Inputs: routes (required), top, via, bags, +39 more. Returns: status, empty_note, error_code, observed_at, +5 more
search_hotel_roomsHotel room ratesARead-onlyIdempotent
Room rates for one Skiplagged hotel, for 1-3 finalists. Name it by hotel_id (the provider_id of a skiplagged search_hotels offer) or by hotel_name plus city. A name must match exactly (case, accents and punctuation ignored); no match or several matches is no_results, never a guess. Use the name form for a Google finalist: a Google hostel price can be a dorm bed, and these rates say what the room is. Rates are USD, not converted, in provider order. total_price is the whole stay for the party and rooms searched. occupancy_limit is per room type and is not proof that a party fits across rooms. Skiplagged only: do not mix these rows with Google or Booking prices.
Inputs: check_in (required), check_out (required), city, rooms, +3 more. Returns: status, empty_note, error_code, observed_at, +5 more
search_hotelsSearch hotelsARead-onlyIdempotent
Hotel search. Quotes are total-stay prices, not per person. Currency is required (no origin airport), except source skiplagged, whose quotes are USD. Quotes come back in the requested currency as the provider returned them. Viajante does not convert and never invents ISO 4217. location is one named place: ask when it is a typo, a region, or several candidate towns. The request carries adults and rooms only; do not state a room split. Google offers are in price order, not advice: weigh location, latitude and longitude, and review_count yourself. stays batches up to 8 location/check_in/check_out objects in one call. property_matrix lists each property's total per stay, null where it was not returned (not proof of unavailability), sorted by name. near is a point you name, {lat, lng}; max_distance_km needs it. entire_home=true asks for a house or villa; those offers carry sleeps, bedrooms and beds. lodging_evidence_conflict marks contradictory room or unit labels. Full rules: get_guide.
deadline_seconds stops starting queries after that many seconds; unfinished
queries are error code deadline (not loaded, not empty). See get_guide.
Inputs: top, near, rooms, stays, +11 more. Returns: status, empty_note, error_code, observed_at, +5 more
search_split_ticketsSplit-ticket itinerariesARead-onlyIdempotent
Opt-in separately ticketed itineraries built from real one-way quotes. route is ORIGIN-DEST:DATE (one-way: self-transfer via a hub) or, with trip="rt", ORIGIN-DEST:OUT:BACK (cheapest outbound plus cheapest return one-way). It runs the packaged search first (packaged_report), then up to max_hubs hubs (default 3, at most 5): 2 queries per hub, 3 with allow_overnight. via lists up to 5 connection airports; unnamed, hubs are the packaged layover airports. It stops at a recorded rate limit: do not retry. Currency as in search_flights. Every itinerary says split_ticket true and connection_protected false. A missed connection between tickets is not rebooked, and bags may need re-checking. Each ticket is confirmed on its own google_flights_url. Clock, duration, layover, via, overnight, and airport filters apply to the itinerary: via and exclude_via name hubs. min_connection_hours (default 3) is a planning default, not provider evidence. total is null unless every part shares one currency. Not for multi-city. Full rules: get_guide.
Inputs: route (required), top, via, bags, +33 more. Returns: status, empty_note, error_code, observed_at, +5 more
search_tripSearch flights and hotelBRead-onlyIdempotent
Flights then one hotel stay, under one lock. Currency follows the flight origin or an explicit code; if unproven, ask. Viajante does not convert. The same currency goes to the hotel. Stay dates default to the flights' window. Unnamed baggage_buffer is 0. A positive carry_on is verified on the page; named bags are refused. Flight queries carry the same recommendation block as search_flights. trip_total is omitted if either side missed.
deadline_seconds stops starting queries after that many seconds; unfinished
queries are error code deadline (not loaded, not empty). See get_guide.
Inputs: routes (required), location (required), top, via, +35 more. Returns: status, empty_note, error_code, observed_at, +5 more
split_stay_costsSplit stay costsARead-onlyIdempotent
Local: split each stay's total among the people who sleep there.
stays are {name, check_in, check_out, total}; roster is as for plan_stay_blocks. A stay's rate is its total over the person-nights the roster puts inside it, so someone who never sleeps there pays nothing toward it. currency is named by the caller and never converted. fee_per_person_night is a per-night charge the caller names (a city tax) added per person. Cents are allocated so each stay sums exactly. Nights no stay covers come back as unallocated_nights. Arithmetic only: it does not price or recommend a stay.
Inputs: stays (required), roster (required), currency (required), fee_per_person_night. Returns: status, empty_note, error_code, observed_at, +5 more
validate_itineraryValidate itineraryARead-onlyIdempotent
Validate selected v2 flight offers locally, without fetching. Pass legs in travel order, each with its exact query and one selected offer. The result is tri-state: unknown evidence never becomes pass. arrival_deadline is an ISO date and time: an explicit offset is compared in UTC, a naive time is local at the arrival airport. chronological checks owned segment instants. min_stay_days and max_stay_days use owned journey dates.
Inputs: legs (required), constraints (required), currency. Returns: status, empty_note, error_code, observed_at, +5 more
verify_answerVerify draft answerARead-onlyIdempotent
Check a draft reply against this process's recent search payloads.
Call before sending a reply that quotes fares, stays, dates, airport codes, or links. Lists each amount, currency, IATA code, ISO date, and URL in the draft that no recorded search returned. Drop or re-search every unowned row; do not quote it as found. Sums you computed are unowned unless a payload carries them (e.g. trip_total). Local; may run during a search.
Inputs: answer (required). Returns: status, empty_note, error_code, observed_at, +5 more
watch_priceWatch a priceA
Re-run a saved flight or hotel search and report the change. With no arguments, list saved watches (watches null and status failed when the file cannot be read). With name, kind (flight or hotel) and params (the search_flights or search_hotels arguments): validate, save, then run. Saving under an existing name replaces that watch. With only name, run the saved search. The run records an observation even when the history opt-in is off. Each run bypasses the replay cache for a fresh search, under the one-search lock and provider cooldown. A rate-limited, failed or empty run records nothing. No scheduler and no notification: do not call it in a loop.
Inputs: kind, name, params. Returns: status, empty_note, error_code, observed_at, +5 more
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 22 tools
The flight and hotel tools are mostly separable, but the agent must distinguish many adjacent modes: search_flights vs search_dates vs search_flex vs search_explore, and search_hotels vs search_hotel_rooms vs get_hotel_details. Descriptions clarify boundaries, but the overlap and long prose make misselection plausible.
Nearly all tools use snake_case verb_noun naming (search_flights, get_hotel_details, validate_itinerary, split_stay_costs). The only clear deviation is price_history, which is noun-only rather than verb-led.
At 22 tools this is a heavy surface for one server, with nine search variants plus local planning and verification helpers. The complexity of travel search justifies many of them, but the total sits in the borderline 16–25 range.
The surface covers flight and hotel search variants, price history, watches, award comparison, transfer lookup, itinerary validation, offer rechecks, stay-cost planning, and answer verification. Remaining gaps are minor or intentional, such as no booking and limited live award availability.
Maintenance
Related MCP Connectors
Flight search MCP server providing search, pagination, and itinerary details for AI assistants.
Search and compare flight offers through a cache-aware Streamable HTTP MCP server for AI agents.
AI marketplace — flights, tours, activities, transport & more via MCP. No auth required.
Give AI assistants access to real-time data. Search the web, compare flights, find hotels, and more.
Related MCP Servers
- AlicenseBqualityDmaintenanceEnables AI assistants to query flight routes, real-time flight tracking, weather, and transfer flights via standardized MCP tools.10MIT
- AlicenseNot gradedqualityDmaintenanceCoordinates flights, hotels, events, weather, currency, and traffic data through a single MCP server, enabling comprehensive trip planning via natural language prompts.MIT
- FlicenseNot gradedqualityBmaintenanceProvides real travel search for AI assistants, enabling flight, hotel, car rental, and ground transportation searches through a single MCP tool, with no API keys required.94-
- AlicenseNot gradedqualityCmaintenanceProvides live flight prices, booking links, and airport lookup via a hosted MCP server. Enables search for flights and direct booking URL retrieval.2MIT