rihla について
Rihla は、日程や空港を柔軟に指定できる複数区間の旅行向けフライト検索 CLI と MCP サーバーです。各区間の日付と空港の候補を見積もり、滞在日数の条件を満たす組み合わせを価格順に提示します。オフライン用のモックデータも利用できます。
主な機能
- 01複数区間のフライト検索
- 02空港と日程の柔軟な指定
- 03区間間の滞在日数条件
- 04有効な組み合わせの価格順表示
- 05運賃の出典と参考価格の表示
- 06運賃未取得区間を含む部分結果
活用シーン
- 01複数都市の旅程で空港候補を比較
- 02出発日を調整して低価格ルートを探す
- 03モック運賃でオフラインの旅程を検討
- 04運賃情報が不完全なルートを確認
MCP 対応
stdio の MCP サーバーは読み取り専用ツールを 2 つ提供します。`resolve_airports` は空港コードを検証・補完し、`search_trip` は価格付きの旅程を検索します。利用枠のある検索の前に空港の確認を求める指示があります。
設定
ローカルサーバーの設定はどのクライアントでも共通です。コピーしてクライアントの MCP 設定ファイルに貼り付けてください。
uvx rihla-mcpTRAVELPAYOUTS_TOKENシークレットTravelpayouts Data API token (free) - the primary price source. Without any keys the server runs on offline mock data.SERPAPI_KEYシークレットSerpApi key (optional, 250 free searches/mo) - fills routes Travelpayouts misses with bookable Google Flights fares.RIHLA_PROFILESource profile: 'local' (all configured sources), 'hosted' (redistribution-licensed only), or 'mock' (offline).ローカルサーバーにはマシン上の実行環境(Node.js 18 以上など)が必要です。設定ファイルの場所はクライアントごとに異なります。
ツール
ローカルサーバーのツールはマシン上で起動して初めて取得できます。メンテナーはまだツール一覧を提供していません。
詳細
Rihla
Rihla (رحلة)、「旅」を意味します。イブン・バットゥータの14世紀の旅行記にちなんで名付けられました。 歴史上最も遠くまで旅した探検家の記録です。彼はタンジールを出発してメッカを目指し、 その後も旅を続け、29年の間に75,000マイルを移動しました。
複数区間・複数空港に柔軟に対応し、旅程全体で最も安いルートを見つけるフライト検索です。CLI と AI エージェント向け MCP サーバーとして利用できます。
一般的なフライト検索サイトでは、区間ごとに料金を検索します。Rihla では、旅行全体を単一の最適化問題として扱います。
「9月15日ごろにモンテビデオから、あるいはフェリーですぐのブエノスアイレスからヨーロッパへ出発。20~30日滞在してから日本へ15日間行き、その後帰国する場合、最も安い組み合わせは?」
このクエリには3つの区間があり、それぞれの出発地と到着地の空港に柔軟性があり、出発期間も指定でき、各区間をつなぐ滞在期間の制約もあります。Rihla は各区間の日付×空港の組み合わせを一度だけ検索し、すべてローカル環境で計算して最も安い有効な組み合わせを見つけます。そのため、3~4区間の柔軟なクエリでも、API 呼び出しはわずか数回で済みます。
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
...
空港の代替がどのように機能しているかに注目してください。ヨーロッパにはアムステルダムから入り、フランクフルトから出発して、帰りはブエノスアイレスに到着します。各区間で、指定した空港の組み合わせから最も安いものを個別に選んでいます。
クイックスタート
Python ≥ 3.10 が必要です。
pip install rihla # CLI
pip install "rihla[mcp]" # CLI + MCP server
初期設定のままオフラインで実行できます(キーは不要です。決定論的なモックデータソースを使用します)。
RIHLA_PROFILE=mock rihla # the canonical demo trip above
rihla examples/queries/canonical.json # same trip, from a query file
rihla -i # build a query interactively
実際の価格を取得するには、.env.example を .env にコピーし、少なくともTravelpayouts のトークンを追加してください(無料)。詳しくはデータソースとプロファイルをご覧ください。
クエリの作成
クエリは小さな JSON ファイルです。出発空港、順番に並んだ経由地、最初の区間の出発期間、各経由地での滞在期間を指定します。後続区間の日付範囲は自動的に算出されるため、「9月15日に出発して20~30日間滞在した場合、東京へ飛ぶのはいつか」を自分で計算する必要はありません。
{
"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 コードのセットです。日付ごとに最も安いものが選ばれます。stops— 各経由地は地域名または IATA コードのリストで指定します。組み込みの地域は、EUROPE(MAD、BCN、LIS、CDG、FCO、AMS、FRA)、TOKYO(NRT、HND)、MVD_AREA(MVD、EZE、AEP)です。帰国するには、最後の経由地を出発地の空港セットにしてください。earliest/latest— 最初の区間のみの出発期間です。stays— 各中間の経由地での滞在日数を[min, max]で指定します (len(stays) == len(stops) - 1)。date_step— 日付範囲内を N 日ごとにサンプリングします(間隔を広げると API 呼び出しが減ります)。top— 返す上位の組み合わせ数です。currency— ISO 4217 通貨コードを任意で指定します(既定値はUSD)。
rihla -i を実行すると、これらの項目を順番に入力でき、再利用用に保存する JSON が出力されます。実行時に --links を追加すると、予約 URL が表示されます。
結果の読み方
Rihla は、すべて予約可能であるかのように装うのではなく、データの品質を正直に示します。
- 各フライトにはデータソースが表示されます。価格が参考価格(Travelpayouts のデータは過去の検索履歴を集計したもので、リアルタイムの運賃ではありません)の場合は
cachedと表示され、実際に予約可能な運賃(SerpApi / Google Flights)と区別できます。 - 一部の区間を検索できない場合(路線が少ない場合は実際に起こり得ます。キャッシュ済みのデータソースには欠落があります)、検索できた区間だけの部分的な結果が返されます。不正確な合計金額が黙って表示されることはありません。
- 価格を取得できなかった区間については、出発期間の範囲外にある、最も近いキャッシュ済み運賃を表示します。日付をずらす、または範囲を広げる際の参考になります。
v0.1 では価格のみで順位を付けます。地域内でのオープンジョーは既定で許可されています(ヨーロッパにはある都市から入り、別の都市から出発できます)。地域内で移動する費用は計算に含まれません。
データソースとプロファイル
| ソース | 役割 | 費用 | 備考 |
|---|---|---|---|
| Travelpayouts / Aviasales | 主要 | 無料 | キャッシュされ、再配布ライセンスが適用されています。1 回の呼び出しで 1 か月分の価格を取得できるため、呼び出し予算を最小限に抑えられます。価格は参考値です。対象範囲は路線の人気度に応じます。 |
| SerpApi(Google Flights) | 補完 | 月 250 回まで無料検索(BYO キー) | 実際に予約可能な運賃を取得し、Travelpayouts で見つからない路線を補います。対象外の路線に限って利用します。 |
| Mock | オフライン | — | 開発やデモ用の、決定論的な架空価格です。 |
.env(.env.exampleを参照)または環境変数で設定します。
TRAVELPAYOUTS_TOKEN、SERPAPI_KEY、RIHLA_PROFILEを指定してください。
local(デフォルト) — キーが設定されているすべてのソースを使用します。SerpApi も含まれます。hosted— 再配布ライセンスが適用されたソースのみを使用します(SerpApi は Google をスクレイピングするため無効です。 公開ホストインスタンスから提供しないでください)。mock— オフラインフェッチャーを強制し、ネットワークを使用しません。
キーが設定されていない場合、Rihla はモックソースにフォールバックし、その旨を伝えます。
MCP サーバー
Rihla には MCP サーバー(stdio)が付属しており、Claude などのエージェントが旅行を検索できます。
pip install "rihla[mcp]"
claude mcp add rihla -- rihla-mcp # Claude Code
または、任意の MCP クライアント設定で指定します。
{
"mcpServers": {
"rihla": {
"command": "rihla-mcp",
"env": {
"TRAVELPAYOUTS_TOKEN": "your-token",
"SERPAPI_KEY": "your-key"
}
}
}
}
(サーバーは作業ディレクトリにある .env も読み込むため、チェックアウトから実行する場合は env は省略可能です。)
サーバーは、次の 2 つの読み取り専用ツールを、定められたエチケットに従って公開します。
resolve_airports— エージェントが旅行者の指定した場所の IATA コードを提案し、Rihla がそれを検証して情報を補います(近隣の代替空港も含みます)。低コストで、クォータを消費しません。search_trip— 価格を取得する、クォータ制限付きの検索です。ツールの説明では、ここでクォータを消費する前に、空港について旅行者から明示的な確認を得るようエージェントに指示しています。
mcp-name: io.github.leojg/rihla
仕組み
各区間の価格は独立しています。MVD→ヨーロッパの運賃は、東京の日程には左右されません。そのため Rihla は各区間の日付×空港のグリッドを一度だけ取得し、その後、滞在条件を満たす有効な日程の組み合わせをすべてメモリ上で列挙します。組み合わせの爆発的な増加はローカルの計算で処理し、API 呼び出しには影響しません。
core.py data model + the pure optimizer (no I/O)
fetchers/ PriceFetcher protocol: Mock / Travelpayouts / SerpApi + merge
places.py airport sets / regions
api.py search_trip: serializable query in, result dict out
cli.py thin CLI over search_trip
mcp_server.py thin MCP wrapper over the same seam
データソース(Duffel、Kiwi など)の追加は、1 つのメソッドを持つプロトコル quote(origin, dest, day) -> Quote を実装するクラスを 1 つ追加するだけです。
対象範囲(v0.1): 航空券検索のみです。宿泊、予約、支払いは扱いません。大人 1 名、1 キャビンクラスが対象です。検索結果には予約リンクを表示しますが、取引は処理しません。
開発
git clone https://github.com/leojg/rihla && cd rihla
pip install -e ".[dev,mcp]"
pytest # offline, no keys needed
ruff check .
ライセンス
Apache License 2.0 の下でライセンスされています。詳しくはLICENSEをご覧ください。
コメント
AI アプリケーションが外部のツールやデータソースに統一的な方法で接続するためのオープンなプロトコルです。クライアントはモデルとの対話を担い、MCP サーバーはツール・リソース・プロンプトテンプレートを提供します。標準メッセージでやり取りするため、1 つのサーバーを複数のクライアントで再利用できます。