Flexible multi-leg, multi-airport flight search - cheapest valid combination across an itinerary.
Copy the AI prompt to install this server into Claude Code, Cursor, or another agent β or use 1-click editor setup below.
We haven't yet run this listing's install command through our automated sandbox check. This isn't a red flag β we're steadily working through the catalog.
π‘ Paste the JSON block into your client's configuration file under mcpServers, then restart the application.
Rihla (Ψ±ΨΩΨ©), "the journey." Named for Ibn Battuta's 14th-century travelogue β the record of history's farthest-traveling explorer, who left Tangier for Mecca and kept going for 29 years and 75,000 miles.
Flexible multi-leg, multi-airport flight search that finds the cheapest route across an entire itinerary β available as a CLI and as an MCP server for AI agents.
Consumer flight sites price one leg at a time. Rihla treats the whole trip as a single optimization problem:
"Leave Montevideo β or Buenos Aires, it's a ferry ride away β around September 15 for Europe. Stay 20β30 days, then Japan for 15, then home. What's the cheapest combination?"
That query has three legs, flexible airports on both ends of each, a flexible departure window, and stay-duration constraints linking the legs. Rihla prices each leg's dateΓairport grid once, then finds the cheapest valid combinations in pure local compute β so even a 3β4 leg flexible query costs only a handful of API calls.
Note the airport substitution at work: it enters Europe through Amsterdam, leaves from Frankfurt, and flies home into Buenos Aires β each leg independently picks the cheapest airport pair from its sets.
Requires Python β₯ 3.10.
Runs offline out of the box (no keys needed β a deterministic mock data source):
For live prices, copy .env.example to .env and add at least a
Travelpayouts token (free). See
Data sources.
A query is a small JSON file: your origin airports, the ordered stops, a departure window for the first leg, and how long to stay at each stop. Downstream date windows are derived β you never hand-compute "if I leave Sep 15 and stay 20β30 days, when do I fly to Tokyo?"
origins β IATA codes tried as one origin set; the cheapest wins per date.stops β each stop is a region name or a list of IATA codes. Built-in regions:
EUROPE (MAD, BCN, LIS, CDG, FCO, AMS, FRA), TOKYO (NRT, HND), MVD_AREA
(MVD, EZE, AEP). Make the last stop your origin set to fly home.earliest / latest β the departure window for the first leg only.stays β [min, max] nights at each intermediate stop
(len(stays) == len(stops) - 1).date_step β sample every N days across date windows (coarser = fewer API calls).top β how many ranked combinations to return.currency β optional ISO 4217 code (default USD).rihla -i walks you through these questions and prints the resulting JSON to save for
reuse. Add --links to any run to show booking URLs.
Rihla is honest about data quality rather than pretending everything is bookable:
cached when the price is indicative
(Travelpayouts data is aggregated search history, not a live fare) versus a real,
bookable fare (SerpApi / Google Flights).Ranking is price-only in v0.1. Open-jaw within a region is allowed by default (enter Europe at one city, leave from another); the cost of repositioning inside the region is not modeled.
| Source | Role | Cost | Notes |
|---|---|---|---|
| Travelpayouts / Aviasales | Primary | Free | Cached, redistribution-licensed. A month of prices per call, so the call budget stays tiny. Prices indicative; coverage follows route popularity. |
| SerpApi (Google Flights) | Fill | 250 free searches/mo (BYO key) | Real bookable fares; fills routes Travelpayouts misses. Only spent on uncovered routes. |
| Mock | Offline | β | Deterministic fake prices for development and demos. |
Configure via .env (see .env.example) or environment variables:
TRAVELPAYOUTS_TOKEN, SERPAPI_KEY, and RIHLA_PROFILE:
local (default) β every source whose key is set, including SerpApi.hosted β redistribution-licensed sources only (SerpApi disabled: it scrapes Google,
so do not serve it from a public hosted instance).mock β force the offline fetcher, no network.With no keys set, Rihla falls back to the mock source and says so.
Rihla ships an MCP server (stdio) so agents like Claude can run trip searches:
or in any MCP client config:
(The server also loads a .env from its working directory, so env is optional if you
run it from a checkout.)
It exposes two read-only tools with an enforced etiquette:
resolve_airports β the agent proposes IATA codes for the traveler's named
places; Rihla validates and enriches them (nearby alternatives included). Cheap and
quota-free.search_trip β the priced, quota-limited search. Tool descriptions instruct the
agent to get the traveler's explicit confirmation of the airports before spending
quota here.mcp-name: io.github.leojg/rihla
Leg prices are independent β the MVDβEurope fare doesn't depend on the Tokyo dates. So Rihla fetches each leg's dateΓairport grid once, then enumerates valid date combinations (respecting the stay constraints) entirely in memory. The combinatorial explosion lives in local compute, not in API calls.
Adding a data source (Duffel, Kiwi, β¦) is one more class implementing a one-method
protocol: quote(origin, dest, day) -> Quote.
Scope (v0.1): flight search only β no lodging, no booking or payments, single adult, one cabin. Search returns booking links, never handles the transaction.
Licensed under the Apache License 2.0 β see LICENSE.
No reviews yet β be the first to share how this listing worked for you.
Showcase your server listing on GitHub or your project documentation. Embed this dynamic SVG badge to highlight official listing status and live engagement.
[](https://allmcps.com/mcp/rihla)<a href="https://allmcps.com/mcp/rihla"><img src="https://allmcps.com/api/badge/rihla?style=directory" alt="Rihla on AllMCPs" /></a>