Rihla

io.github.leojg由 GitHub 帳號 leojg 發布v0.1.0更新於 Oct 27, 2026

Flexible multi-leg, multi-airport flight search - cheapest valid combination across an itinerary.

概覽

AI 產生的概覽

Rihla 是本地 MCP 伺服器,用於彈性的多段、多機場航班搜尋,為整趟行程估價並找出最便宜的有效組合。

可以做什麼
  • 把地名解析成 IATA 機場代碼,並附上鄰近替代機場,且不消耗搜尋配額
  • 依彈性機場、出發時間窗與停留天數搜尋多段行程,回傳排序後最便宜的組合
  • 標示每段航班的價格來源,區分參考價與可預訂票價,並在部分航段無法估價時回傳部分結果
  • 為排序後的行程提供訂票連結
適用情境
適合規劃含多段航程、多個可選機場或彈性日期的旅行,想要整趟行程最便宜的組合而非逐段比價時使用。也適合離線示範,因為不需金鑰即可用確定性的模擬資料執行。
執行需求
本機 Python 3.10 以上的程序,從 PyPI 安裝(rihla-mcp,可用 uvx 執行),透過 stdio 通訊。取得即時價格需要 TRAVELPAYOUTS_TOKEN 中的 Travelpayouts Data API 權杖,並可選用 SERPAPI_KEY 中的 SerpApi 金鑰;RIHLA_PROFILE 用來選擇 local、hosted 或 mock 資料來源。未設定金鑰時會退回離線模擬資料。使用即時來源需要網路連線。
安裝前請注意
會從環境變數讀取兩組 API 憑證:TRAVELPAYOUTS_TOKEN 與 SERPAPI_KEY。SerpApi 免費額度有限(每月 250 次搜尋),search_trip 會消耗額度,因此應先確認機場。README 提醒不要從公開託管的執行個體提供 SerpApi,因為它會擷取 Google 資料。Travelpayouts 的價格為參考價而非即時票價;此伺服器為唯讀,不會訂票或付款。

安裝

在 SourceWeft 中

  1. 開啟 儀表板中的 Rihla,將其新增到工作區。
  2. 為需要使用其工具的對話啟用該服務。

Desktop only,透過 STDIO。 STDIO 服務會啟動本機處理程序,因此需要 SourceWeft 桌面主機。

其他 MCP 客戶端

參照 儲存庫 中的啟動說明。

README

Rihla

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.

1. 1,297 USD   (44 days door-to-door)     MVD -> AMS  2026-09-08  $    383  [mock, cached]  AA AA127     FRA -> NRT  2026-10-07  $    481  [mock, cached]  KL KL411     HND -> EZE  2026-10-22  $    433  [mock, cached]  AZ AZ933
2. 1,308 USD   (44 days door-to-door)     MVD -> AMS  2026-09-14  $    446  [mock, cached]  AA AA127     ...

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.

Quick start

Requires Python ≥ 3.10.

bash
pip install rihla            # CLIpip install "rihla[mcp]"     # CLI + MCP server

Runs offline out of the box (no keys needed — a deterministic mock data source):

bash
RIHLA_PROFILE=mock rihla                # the canonical demo trip aboverihla examples/queries/canonical.json   # same trip, from a query filerihla -i                                # build a query interactively

For live prices, copy .env.example to .env and add at least a Travelpayouts token (free). See Data sources.

Writing a query

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?"

json
{  "origins": ["MVD", "EZE", "AEP"],  "stops": ["EUROPE", ["NRT", "HND"], "MVD_AREA"],  "earliest": "2026-09-08",  "latest": "2026-09-22",  "stays": [[20, 30], [15, 15]],  "date_step": 3,  "top": 5}
  • 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.

Reading results

Rihla is honest about data quality rather than pretending everything is bookable:

  • Each flight is tagged with its source, and cached when the price is indicative (Travelpayouts data is aggregated search history, not a live fare) versus a real, bookable fare (SerpApi / Google Flights).
  • If some legs can't be priced (thin routes are real — cached sources have gaps), you get a partial result over the legs that were found, never a silently wrong total.
  • For unpriced legs, Rihla shows the nearest cached fares outside your departure window — a hint to shift or widen dates.

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.

Data sources & profiles

SourceRoleCostNotes
Travelpayouts / AviasalesPrimaryFreeCached, redistribution-licensed. A month of prices per call, so the call budget stays tiny. Prices indicative; coverage follows route popularity.
SerpApi (Google Flights)Fill250 free searches/mo (BYO key)Real bookable fares; fills routes Travelpayouts misses. Only spent on uncovered routes.
MockOffline—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.

MCP server

Rihla ships an MCP server (stdio) so agents like Claude can run trip searches:

bash
pip install "rihla[mcp]"claude mcp add rihla -- rihla-mcp        # Claude Code

or in any MCP client config:

json
{  "mcpServers": {    "rihla": {      "command": "rihla-mcp",      "env": {        "TRAVELPAYOUTS_TOKEN": "your-token",        "SERPAPI_KEY": "your-key"      }    }  }}

(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:

  1. 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.
  2. 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

How it works

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.

core.py            data model + the pure optimizer (no I/O)fetchers/          PriceFetcher protocol: Mock / Travelpayouts / SerpApi + mergeplaces.py          airport sets / regionsapi.py             search_trip: serializable query in, result dict outcli.py             thin CLI over search_tripmcp_server.py      thin MCP wrapper over the same seam

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.

Development

bash
git clone https://github.com/leojg/rihla && cd rihlapip install -e ".[dev,mcp]"pytest              # offline, no keys neededruff check .

License

Licensed under the Apache License 2.0 — see LICENSE.

來源:README.md,提交 24a35e4

工具

0
SourceWeft 尚未在沙箱中啟動過該軟體包,因此其工具未知。

版本歷史

1
  1. v0.1.0最新Sep 16, 2026

評分

已滿足適用檢查項的 50%

該評分衡量此收錄條目的完整程度,依據註冊中心條目、端點與公開來源計算得出。它不是對伺服器本身的評價,也不表示伺服器安全或目前可用:可連通性是帶日期的檢查結果,評分也不包含安裝量或使用者評價。

  • 近 30 天內可連通25 分

    請保持端點能夠回應 MCP 握手;SourceWeft 每 30 天探測一次。

  • 已宣告認證方式10 分

    請在註冊中心條目中宣告憑證請求標頭,或保持端點能夠回應探測,以便判定其認證方式。

  • 已列出工具15 分

    請讓伺服器在無憑證時回應 tools/list,或發布可在沙箱中無憑證啟動的軟體包。

  • 描述必需15 分
  • 圖示10 分
  • 文件15 分
  • 授權條款10 分