Repository navigation
Releases: felipebasurto/viajante
Release list
Viajante 1.5.2
Added
- Hermes Agent:
integrations/hermes/is a Portable Agent Plugins v1 package (stdio MCP server pinned to this release, plus a short skill). The landing page and the MCP skill carry thehermes mcp addline. flightsand MCPsearch_flightsinclude a nearby-date price grid.
--nearby-days/nearby_daysis opt-in (default0, off). Each one-way
or round-trip query gainsnearby_dates: the cheapest owned fare per day for
±N around the requested date,deltaagainst that date, andcheapest: true
on the lowest priced day. Round trips shift the outbound and return together
and keep the trip length. The grid reuses the public date calendar (one batched
window per query). Multi-city queries omit it. An unpriced day is not "no
flights". The grid does not changeoffers.- Flight results include
price_insightfrom the public Google Flights page
already fetched (level,lowest,typical_range,page_label).level
is Google's label (low,typical, orhigh) for the current price against
its typical range. Viajante quotes that label and does not say the price will
drop. A missing insight is null. When the heading and the page data disagree,
price_insightis null andprice_insight_errorismarkup_drift; the
search still succeeds.--price-history(MCPinclude_price_history, default
off) adds the page's price history.watch_priceincludes the current run's
price_insight.
Viajante 1.5.1
Added
viajante feedbackopens a prefilled GitHub issue with the Viajante version,
Python version, and platform. Viajante sends nothing itself;--no-browser
prints the link only.- One-way
flightstakes--depart-before HH:MM,--arrive-after HH:MM,
--less-emissions, and--hide-separate-tickets(MCPsearch_flights:
depart_before,arrive_after,less_emissions,hide_separate_tickets).
Fetchautoandsweepsend the clock bounds,max_duration,max_layover,
the price cap, and the two options to Google's page. The page must echo each
one; a missing or tighter echo ismarkup_drift. The local filter still runs on
every row. Each query's JSON carriesgoogle_filterswith the bounds sent. A
price cap below Google's cheapest fare ends asno_resultsfor the query as
sent, because Google then lists the flights unpriced. --exclude-basic-economyis refused on every path: the public page echoes no
basic-economy filter, so viajante cannot prove it applied.
Changed
- Detail searches click Reject only on Google's consent banner. The Accept
selectors are gone. - A results page that finishes loading with no results grid and no empty state is
markup_driftafter a 20 s probe, not a 60 s timeout retried three times. - The Google cooldown after a 429 is a fixed 2 minutes, or the named
Retry-After
up to 30 minutes. It no longer doubles on each repeat limit. - Public Google Flights results pages are parsed into one HTML tree per fetch,
not two or three, and the ds:1 data is decoded once. On a real 5.3 MB results
page, 31 parses took 1.5 s of CPU before and 0.6 s now (median of 5 runs, the
same 113 cards). A live search spends most of its time on the network, so the end-to-end
gain is much smaller. The block check runs once per response, not once per caller.
Fixed
verify_answerno longer passes an invented amount near zero. A zero money field
(an unnamed baggage buffer) owned any claim up to 1, and recommendation score maps
(breakdown,weights) were read as amounts in the quote currency.--helpexamples use future dates and no longer show--bags, which the
public page refuses.- The README, usage guide, and
llms.txtno longer say--bagsasks Google to
price checked bags. Only--carry-onrides the request. - Four CLI tests used a departure date that is now in the past.
- The MCP
initializeresponse reports the viajante version; it was blank.
Viajante 1.5.0
Changed
- Breaking (flight
recommendation, schema 2): shortlist entries point to
their offer byevidence_idandgoogle_flights_url; the fullofferis
embedded only when it is not inoffers(a relaxed pick).relaxation_order
andscoringare gone, andweightsappears only when prices could not be
compared. Labels are role names:top_score,lowest_price,shortest
(plus_distinctforms),alternative. Highlights restate returned fields
and no longer compare ("Lowest fare among N compared" is removed). A one-way
result with eight offers is about 23% smaller. - Round-trip packages:
stops_count,stops,durationand
duration_hoursnow describe the whole trip (worst leg's stops, summed leg
time),nullwhen a leg is unknown. They used to describe the outbound only.
layover_cityandlayover_hoursarenull; per-journey layovers are in
legs[].layovers. - Speed: packaged round trips read their return pages in one concurrent
batch (about twice as fast); public-pageds:1extraction is about 6x faster
on a large page; MCPtools/listis about 29% smaller. - Consent: a declined Google consent is kept between CLI calls (only the
SOCScookie, 30 days, never an accepted consent), so a new process skips
the consent round trip (3 requests to 1 in one live check on 2026-10-08). - MCP SDK 2.x: the server runs on
MCPServerand needsmcp>=2.3.0.
Transport settings go torun(). A failed tool is a result withis_error. - Split tickets take the same clock, duration, layover, via, overnight, and
airport filters as one-way search, judged per ticket and hub connection. - Skiplagged: the parser reads the shape the provider actually returns,
checked against captured responses intests/fixtures/skiplagged/.
hidden_cityisnullwhen Skiplagged sends noattributes. - Cooldown records name the viajante version and endpoint that wrote them.
- MCP responses carry one-hour freshness hints (
ttl_ms) on the catalog,
resources, andserver/discover. Progress is reported when the request has a
progressToken. - Locked dependencies upgraded (
mcp2.3.0,playwright1.63.0,pydantic
2.14.0,curl-cffi0.16.3, and others). flights.pyandmodels.pyare split into smaller modules;viajante.models
still exports every name and JSON is unchanged. MCP parameters are declared
once inmcp_handlers.py.
Fixed
- Empty vs failed: a nonstop search that returns a results page with no
itineraries isno_results/provider_empty, notmarkup_drift. A search
deadline is never retried as a fetch failure. - Hotels: Booking's free-cancellation filter sends
fc=2(it sentoos=1).
A failed widening page is recorded inpage_errors. A price in another ISO
code is not relabelled; when every card does so the query is
currency_mismatch.get_hotel_detailsanswers inconclusively when a room
city cannot be proven. A negated free-cancellation phrase after the words
("free cancellation not available") is unknown; 1.4.7 read it as free. - Filters:
min_layoverchecks every connection.no_overnight/
require_overnightanyapplies to hub splits. Mixed round-trip splits
filter before choosing the cheapest pair. Round-trip packages keep both legs'
carriers, so an excluded return carrier drops the package. - Prices and parsing:
parse_pricereads a trailing ISO code (KWD and BHD
were 1000x too high). Non-finite stay totals and award inputs are rejected.
Award cents-per-point refuses taxes above the cash price. Curaçao, Sint
Maarten, and Panama origins no longer infer a currency. - Dates and flex:
search_datesand flextypical,vs_typical, and the
summary use every day's cheapest eligible fare before any buffer.
sort="price"shows each day's cheapest fare.explore --monthfor the
current month starts today. - Providers:
Retry-Afteris finite and capped at 30 minutes. Google's
consent page is dismissed every time it appears. A timed-out batch keeps the
pages that arrived. The detail path stops after a block. Skiplagged errors
are rejected once, not retried; hidden-city refuses more than 9 adults. - MCP: handler argument errors carry the
Error executing tool <name>:
prefix.watch_pricevalidation no longer replays a cached search. Progress
into a phase with a smaller total is no longer dropped.
Removed
- The unreachable unsigned RPC transport, the legacy RPC calendar and automatic
typical lookup, unused carrier helpers, and the never-set offer
cheapest_date/cheapestfields. - The sweep-to-detail fallback for empty or failed flight queries.
Viajante 1.4.7
[1.4.7] - 2026-10-08
Changed
- Round-trip date sweeps (
search_dates, andsearch_flexwith a stay) are
much faster on long windows. Days are read in groups the size of the sweep
concurrency (eight by default, two withVIAJANTE_SWEEP_MODE=conservative):
each group's outbound pages go out together, then those days' return pages.
With the default mode a 31-day window takes about 35 rounds of requests
instead of 279 one after another. - A page that fails with a transport error or HTTP 5xx during a round-trip date
sweep is retried once instead of stopping the whole window. A 429 from the
provider still stops the remaining requests and applies the cooldown; groups
that already finished keep their prices. - MCP progress for date sweeps now reports a day only after it has finished,
so clients no longer see31/31before the first result arrives. A day cut
off by the deadline is not reported as done. - The npm wrapper
@viajante/mcpnow starts the MCP server with the browser
extra (uvx --from viajante[mcp,browser]==<version> viajante-mcp), so
search_explorehas Playwright available. Install Chromium once with
uvx --from 'viajante[mcp,browser]==<version>' playwright install chromium.
TheviajanteCLI bin is unchanged.
Added
- The MCP guide tells assistants to pass
deadline_secondson long round-trip
date or flex sweeps, so finished days come back early and unfinished days are
marked with thedeadlinecode instead of looking empty. - The missing-Chromium hint names the exact
uvx ... playwright install chromiumcommand for the npm/uvx setup.
Install
npx -y @viajante/mcp@1.4.7uvx --from 'viajante[mcp,browser]==1.4.7' viajante-mcp
Viajante 1.4.6
See the release record for transport behavior,
compatibility changes, validation, and publication verification.
Changed
- Flight
autoandsweepread Google's public results page.autono longer
selects Playwright based on query count or installation. Browser detail is
explicit; a public-page failure does not fall back to detail. - Packaged round trips inspect at most eight outbound candidates on selected
return pages and retain the provider's package total. Dates/flex use public
page GETs for their explicit windows; ordinary flight searches do not launch
a hidden 31-day typical lookup.
Added
-
VIAJANTE_SWEEP_MODE=standard|conservativecontrols public GET concurrency
(8 or 2); invalid values fail before provider access. -
Provider-block diagnostics expose endpoint host and path, HTTP/RPC status, sent state,
attempt count, and cooldown basis. Status 13 alone does not identify its cause. -
Public-page sweep sends
--carry-on(one bag for the whole party),
--airlines,--exclude-airlines, and--alliance, and fails the page read
unless the page echoes each filter. Airline exclusion is also enforced
locally because Google does not apply it; cards with no carrier evidence are
dropped under an exclusion. -
--trip multi --fetch detailsearches multi-city packages in the browser.
Not yet verified against the live provider. -
explorereads destinations from the catalog request Google's public Explore
page issues in Chromium, checking its origin and date echo. -
Provider-block diagnostics distinguish queued requests from dispatched ones.
Fixed
- Explore preserves HTTP 429 and
Retry-After, detects status 13 in every RPC
row/chunk, and stops pending searches after a block, including proxied calls. - Explore checks cancellation and deadlines during browser waits; cancelled
responses do not write cooldowns, and a deadline never becomes provider-empty. - Airline filters match owned codes exactly, including returned codeshares;
name aliases are a fallback only when codes are absent, with word boundaries. - Multi-city reselection rejects ambiguous or incomplete identities and uses
the current selected row's evidence with the provider's final package total. - Empty flight pages still require the requested alliance's catalog echo.
- Runtime capability flags retain boolean types, with additive transport and
scope limits. Agent guidance matches the supported public-page filters.
Known limitations
- Public-page sweep refuses checked bags, a zero carry-on, alliance exclusion,
and multi-city before networking. Browser detail refuses every bag and
carrier filter. Airline exclusion cannot prove the operating carrier of each
segment. Explore needs Chromium, accepts only one adult in economy, and its
catalog request often returned status 13 during validation. Round-trip results are scope-bound to at most eight outbound
candidates. See the release record for validation boundaries.
Install the release with uvx --from 'viajante[mcp]==1.4.6' viajante-mcp or npx -y @viajante/mcp@1.4.6.
Flight-provider validation and package installation are separate evidence; multi-city detail remains unverified live.
Viajante 1.4.5
Viajante 1.4.5 adds flight offer re-checks, deterministic recommendation and opt-in split-ticket searches, a typed MCP result envelope, progress/cancellation/deadlines, local HTTP transport, and opt-in price history with on-demand watches.
Hotel finalist checks preserve property identity and keep fresh room quotes separate from the original shortlist price. Additional review fixes cover Unicode names, city aliases, query/offer route and date binding, provider city identity, cancelled-response cooldowns, final-fetch cancellation and three-decimal price changes.
The release exposes 22 MCP tools. Existing inputs remain compatible; the MCP SDK floor is now mcp>=1.14.1,<2.
Validation: 1,634 offline tests (two explicit macOS skips), Ruff, benchmark gate, Python 3.10–3.14 CI on both the release head and merge commit, minimum-SDK stdio smoke, and built distributions. These checks do not establish live-provider availability.
See the complete release record and changelog.
Publication completed successfully in the release workflow: PyPI, npm and both MCP Registry stdio entries are at 1.4.5. Independent public uvx and npx sessions each exposed 22 tools and reported runtime 1.4.5. The attached wheel and sdist match the public PyPI artifacts byte-for-byte.
Viajante 1.4.1
Added
- Hotel schema 2 adds
lodging_evidence_conflict, exposing explicit room/entire-unit contradictions while retaining the provider title and raw text. The CLI prints the conflicting labels as evidence.
Fixed
- A room title that conflicts with an entire-home unit chip no longer proves an entire home. Both lodging kind and property type stay unknown; Google property descriptions still cannot prove the priced unit. Entire-house chips are recognized alongside cottage and villa chips.
- Itinerary validation includes return and multi-city dates and leaves missing packaged journeys, segment counts, and layover evidence unknown.
- Packaged flight filters are checked after attaching the next journey and before final top selection. Unknown connection locations cannot prove airport exclusions.
- Trip totals preserve separate dated journeys and full multi-city leg identities. Answer verification binds each amount to its owned currency and cached searches refresh the evidence ledger.
- New detail searches respect Google's machine-wide cooldown. Skiplagged 429s preserve rate-limit flags and record cooldowns after session recovery.
- Explore reports expose per-query pricing failures separately from empty results, retain accurate coverage, avoid caching failed pricing, and print failures with the appropriate CLI exit status.
- MCP cancellation keeps the process busy until its worker finishes. Atomic writes use unique temporary files, and nested hotel stays reject non-integer occupancy instead of coercing it.
- The locked PyJWT dependency is updated to 2.15.1 to address the dependency audit findings.
Changed
- Reduce all 15 async MCP adapters to parameter forwarding while preserving tool signatures, defaults, return shapes and search/lookup workers. The direct runtime tool is unchanged.
- Remove unused internal carrier and hotel parser helpers and migrate parser tests to
parse_hotels_page(...).cards. - Remove the redundant internal urllib/opener injection path. HTTP HTML fetches use the shared Chrome TLS client or an injected
SweepHttpClient; the default path respects provider cooldown. The removedopenerconstructor keyword and parser wrapper were outside the supported library exports.
Validation
- 1,074 offline tests, Ruff lint/format and offline benchmark gate passed.
- Wheel/sdist and npm tarball builds passed; strict Twine metadata checks passed.
- Release PR CI passed Python 3.10–3.14, lint and installed-wheel checks.
Live flight fares, browser UI and Booking navigation remain unverified. Final prices, party capacity, room allocation and cancellation terms still require provider evidence.
Publication verified
- Main CI passed all seven jobs. Tag
v1.4.1points toe5091a1999001fb0e634e4e76e0cd2d95fc27008; main and develop are synchronized. - PyPI, npm and MCP Registry publication completed successfully. Independent public Registry readback confirmed version 1.4.1 with both PyPI and npm transports.
- Public
uvxandnpx -y @viajante/mcp@1.4.1installations passed real stdio checks: all 16 tools, runtime 1.4.1/schema 2, airport lookup, changed-roster grouping, exact-cent splits and preserved unallocated nights. - GitHub wheel/sdist assets match the public PyPI SHA-256 hashes. The public npm package matches the packed tarball's SHA-1 and SHA-512 hashes and carries the required
mcpName. - npm propagation took several minutes. The first fresh uvx resolution preceded package-index propagation; the subsequent refreshed installation passed. No release tag or published package was replaced.
Viajante 1.4.0
Google Hotels navigation now uses provider entity IDs instead of click trackers that can return empty pages. Navigation links preserve requested dates, adults, rooms, currency and filters.
- Add optional
max_distance_km/--max-distance-km, requiring an explicitnearpoint; exclude outside or unlocated properties before ranking. - Separate priced-unit evidence from general property descriptions. Exclude known party-size and single-unit capacity contradictions.
- Add CLI
--version, MCPget_runtime_infoand hotel resultviajante_version. The MCP server exposes 16 tools. - Strengthen agent contracts for changing nightly rosters, cancellation deadlines, dormitory exclusivity, replacement quotes and sequential searches.
Navigation context does not prove a current price, private room, room allocation or cancellation terms. Finalists still require provider verification.
Validation and publication
- 1,043 offline tests, Ruff lint/format and offline benchmark gate passed.
- Main CI passed all seven jobs, including Python 3.10–3.14 and installed-wheel checks.
- Public PyPI and npm installations passed actual stdio checks for all 16 tools, runtime version, radius input and local stay arithmetic.
- Published wheel and sdist hashes match PyPI metadata.
- MCP Registry publisher confirmed
io.github.felipebasurto/viajanteversion 1.4.0; independent public Registry API readback timed out. - Live HTTP checks validated six generated Google navigation URLs. Browser UI and Booking live navigation were not exercised.
Viajante 1.3.1
Fixed
- npm package metadata includes
mcpName, required to register the npm transport with MCP Registry. Python and npm versions remain aligned; runtime tools and schemas are unchanged. - Release publishing can resume npm and MCP Registry from an existing tag without republishing PyPI. MCP Registry waits for the npm version to become publicly available, and token-free npm publishing removes setup-node's empty auth entry before using OIDC.
Full Changelog: v1.3.0...v1.3.1
Viajante 1.3.0
Added
- MCP
search_hotelsaccepts up to 8stays, each with location, dates, and optional adults and rooms. Multi-stay reports carryproperty_matrix: each property's total per stay, sorted by name, never by price. A null means it was not among that stay's returned offers, not that it is unavailable. - Hotel
near({lat, lng}in MCP,--near LAT,LNGin CLI): offers with owned coordinates carry straight-linedistance_kmto the named point. No point is assumed. - Google Hotels evidence: offer
sleeps,place_types,class_label, andpriced_adults; query-resultresolved_placeandplace_bounds. Vacation-rental chips feeddetails, allowing bedrooms, beds, and lodging kind to be parsed. - Opt-in hotel source
skiplagged(MCPsource, CLI--source): USD only, at most 10 adults and 9 rooms, noentire_home, never mixed with Google or Booking. Offers carryprovider_id;resolved_placeidentifies the city actually searched when Skiplagged loosely resolves a place name. - MCP
search_hotel_rooms/ CLIviajante hotel-rooms: Skiplagged room rates with provideroccupancy_limit,refundable,free_cancellation, andtaxes_and_fees, in provider order. Select by hotel id or exact normalized name plus city; no match or several matches returnsno_resultswith returned candidates when available, never a guessed match. Room-rate requests support up to 10 adults and 5 rooms. - Offline MCP
plan_stay_blocksgroups consecutive nights with the same people into stay blocks.split_stay_costsshares each stay only among its occupants by their nights, using a named currency and optional per-person nightly fee. Allocated cents sum exactly; uncovered nights areunallocated_nights. Neither tool fetches prices or converts currency. - Additive hotel JSON field
applied.not_appliednames filters a source cannot apply, including Skiplagged's unsupported free-cancellation search filter. Other additive hotel fields areprovider_id,distance_km, reportnear/property_matrix, and the Google evidence fields above; no hotel JSON keys were renamed. - MCP
verify_answer: flags amounts, currencies, IATA codes, ISO dates, and links in a draft reply that no search in this process returned. - Google rate-limit cooldown: a direct HTTP 429 pauses Google searches on the machine (2 min doubling to 30 min, or a named
Retry-After); errors carryrate_limited: true. Identical successful MCP searches within 5 minutes returncached: true. - Google Hotels offers carry owned
latitude,longitude, andreview_count. A namedmin_ratingalso fetches the relevance-sorted page on the same multiplexed round-trip. docs/architecture.md: how the request path, fetch modes, hotels, and failure taxonomy fit together.
Changed
--fetch autouses sweep for packaged round-trip and multi-city searches, since only sweep shops the return leg.viajante pointsis the transfer-table lookup only (mirrors MCPlookup_transfers). Cents-per-point math lives inviajante awards/compare_awards.- Explore prices all shortlisted destinations on one multiplexed sweep round-trip.
- Google Hotels record detection no longer special-cases
€(no user-visible change; non-euro totals already parsed). - Internal signatures no longer default
currencyto EUR; every model, source, and parser takes the owned quote currency explicitly. - Internal cleanup: one offer-filter bundle, one JSON save path, shared CLI flag helpers (−1.5k lines, JSON unchanged).
Removed
- The natural-language planner (
prompt_plan.py,plan_prompt) and the graded prompt battery (viajante bench --prompts,--holdout,--timeit-sweep,tests/prompts/, LLM judge). The calling agent plans; viajante fetches evidence.get_flightsnow takes a route spec or trips and rejects prose withValueError. - The looping-agent protocol docs (
program.md,bench-history.md,docs/archive/). The offlineviajante benchgate stays.
Fixed
- A data-less Google wrb.fr status 13 envelope is
blockedand now records the same cooldown as HTTP 429 for direct sessions only. Google Hotels reportsblockedwithrate_limited: true, rather thanrejected. Shared provider cooldown state lives inratelimit.py. - Skiplagged HTTP 429 is
blockedwithrate_limited: true, without retries. Its separateskiplagged-rate-limit.jsonpauses new calls; real calls are paced one second apart. - Detail fetch fails fast on
google.com/sorryinstead of waiting minutes for result cards. viajante hidden-cityand MCPsearch_hidden_city: Skiplagged cards are USD. A named keep that matches no owned card currency iscurrency_mismatch(owned quote stamped), not silentno_results. Viajante does not convert. Omit currency or pass USD; do not advertise EUR as a Skiplagged quote.- MCP
search_datesround-trip calendar path: whenreturn_dateis set, the calendar sweep uses owned outbound+return pairs. Historical smoke evidence is indocs/smoke-2026-09-17.md. - MCP
validate_itinerary: never reportsfeasible=truewithout owned provenance; incomplete evidence stays unknown/infeasible.
Known limitations
- Skiplagged quotes are USD and are never converted. A room type's
occupancy_limitis the provider's number, not proof that a group fits across several rooms. Its search cannot apply free cancellation; confirm individual room terms. - Google hostel prices may be dormitory beds: Google supplies no room type. Check finalists by exact name with
search_hotel_roomsand confirm the room on the provider before treating it as private. - A data-less status 13 may have another cause; its guessed cooldown can pause Google searches for 2 minutes even without a real rate limit.
- Google base/tax/fee breakdown (
record[6][2][44], unconfirmed) and caller-named exchange rates remain outside this release, pending evidence and a design decision.
Full Changelog: v1.2.1...v1.3.0