Skip to main content
Glama
mtnrabi

Google Flights MCP

Google Flights MCP — 無料・広告付き

あらゆる MCP クライアント向けのリアルタイム片道・往復便検索。APIキー不要、アカウント不要、課金もありません。 運営資金は、各結果に添付される1枚の明示されたスポンサーカードによってまかなわれており、あなたに請求されることはありません。

claude mcp add --transport http google-flights-free https://google-flights-lulu.flightpowers.com/mcp

以上でセットアップは完了です。貼り付けるキーも、設定する項目もありません。


2つのツール

ツール

動作

search_oneway_flights

日付範囲と行き先リストにわたって片道検索を行う

search_roundtrip_flights

往復をペアになった区間として価格化し、日付範囲とnights値で検索

どちらも日付範囲(departure_date_from / departure_date_to)と行き先リストを受け取り、それらをサーバー側で展開します。ユーザーの1つの意図は1回のツール呼び出しになります。*「10月にスリランカへ行く最安の航空券」*は30回ではなく1回の呼び出しです。

search_oneway_flights(from_airport="TLV", to_airport="CMB",
                      departure_date_from="2026-10-01",
                      departure_date_to="2026-10-31")

  → 31 combinations requested, 15 searched, results merged and re-sorted

search_roundtrip_flightsは、固定のreturn_dateの代わりに、nights(数値または[5, 6, 7]のようなリスト)も受け取ります。そのため*「5月のどこかでローマに5〜7泊」*も1回の呼び出しになります。

返ってくるもの

各結果には、価格、所要時間、航空会社、乗り継ぎ回数、乗り継ぎ空港、乗り継ぎ時間、予約可能なbuy_linkが含まれます。さらに、Google自身のその経路・期間の過去の価格帯も含まれます。それは、price_insights_low、price_insights_high、そしてprice_range_in_relation_to_other_periods内のlow / typical / highの判定です。

この最後の部分が、単なる価格スクレイピングではなくこれを選ぶ理由です。これによりアシスタントは、単に数字を引用するだけでなく、*「この経路では$209は一般的な価格で、急ぐ必要はありません」*と言えます。

空の結果リストは「この経路と日付には便がない」ことを意味し、エラーではなく有効な回答です。

運賃はライブであり、ライブだけがすべて

すべての呼び出しは新しい検索です。運賃は数分で古くなります。したがって、結果をキャッシュ、保存、または後日現在のものとして再引用してはなりません。回答が運賃に言及する場合は、いつ取得したかを示し、再利用ではなく再検索する必要があります。


Related MCP server: trvl

無料である仕組み

成功した各結果には、1枚の明確に開示されたスポンサーカードが付属します。このカードが資金調達の全体モデルです。バックエンドの検索呼び出しの費用を支払うことで、あなたがキーやクレジットカードを持ち込む必要をなくしています。

何にオプトインことになるのかを正確に知っておいてほしいので、正確に説明します。

  • 結果ごとに1枚のスポンサーカードが付き、常にスポンサー付きと表示されます。

  • エラーに付くことは決してなく、結果0件の回答に付くことも決してありません。 実質的なデータのない広告は、不快なだけでなく、プロンプトインジェクションの試みのように読まれます。Lulu自身のライブテストでは、モデルがそれを3回中3回ちょうどフラグしました。

  • 広告が検索を停止させることはできません。 広告SDKは認証情報がないと不活性で、すべてのネットワークエラーでフェイルオープンします。

  • 広告はLulu経由で配信されます。このサーバーは広告にあなたの身元を送信するのではなく、スロットリクエストを送信して、返ってきたものをレンダリングします。

広告が含まれるため、このサーバーは公式の「MCPレジストリ」やAnthropicとOpenAIのディレクトリには掲載されていません。これら3つはすべて広告を含むサーバーを禁止しているためです。これは無料プランの意図的で既知の結果であり、見逃しではありません。

最初に述べておくべき1つの注意点

スポンサーカードは、MCPウィジェットフレームを表示するアシスタント内でのみレンダリングできます。呼び出し元は送信元IPによって分類され、実施モードでは、カードをレンダリングできないクライアントはファンアウト上限を減らされるか、拒否される場合があります。デフォルトのデプロイモードはmonitorです。つまり、分類して記録し、全員に提供します。ただし、アシスタントではなくヘッド型スクリプトにこれを組み込む場合は、下記の有料サーバーの方が適しており、この制限を受けません。


無料版では物足りなくなった場合

同じ2つのツールと、その背後にある同じ検索を持つ、有料で広告なしの兄弟版があります:https://google-flights-mcp.flightpowers.com/mcp

ここにあるものは何も有料版の後ろに隠されていません。無料サーバーは完全に機能します。広告、15検索上限、またはクライアントの制限が邪魔になり始めたら、有料版がご存じ正しい選択です。

無料 — このサーバー

有料 — google-flights-mcp

広告

結果ごとに1枚の明示されたスポンサーカード

なし

APIキー

不要

自分のRapidAPIキー

呼び出しごとの上限

15検索

30検索(ハード上限60、呼び出しの max_searches で上書き可能)

費開催の報告

対象外

すべてのレスポンスに api_usage

クライアント制限

層で分類され、描画できないクライアントは上限回避か拒否

なし — どこでもクライアント、どんな転送でも

MCPレジストリへの掲載

しない(広告は掲載禁止)

com.flightpowers/google-flights-mcp

コスト

無料

自分のRapidAPI利用量

トレードオフは明白です。有料サーバーには広広告なく、制限もありません。それは、スポンサーが支払うではなく、自分自身の自分のRapidAPIキーで検索コストを支払うからです。

キーの取得は1分。RapidAPIでGoogle Flights Live APIに登録します(無料枠があります)— がとなり、x-rapidapi-keyをコピーしてください。

https://rapidapi.com/mtnrabi/api/google-flights-live-api

次に、クライアントを有料サーバーに向けます:

claude mcp add --transport http google-flights https://google-flights-mcp.flightpowers.com/mcp --header "x-rapidapi-key: YOUR_RAPIDAPI_KEY"

キーは、?rapidapi_key=クエリパラメータとして渡すこともできます。クライアントがAPIへの接続を提供している場合は、そのクライアントのAPIキーフィールドでも。すべての有料レスポンスには、requests_used_by_this_call、plan_requests_remaining、plan_requests_limitを含むapi_usageが含まれれます。こうして、月彷彿ではなく、次回の呼び出し前に消費量がわかります。


切り詰めは常に報告されます

リクエストが上限を超えた場合、最初のN件に切り詰めるのではなく、日付範囲全体に均等にサンプリングして切り詰めます。10月全体に分散した15の日付で「10月の最安」に答えられますが、最初の15日間では答えることはできません。すべてのレスポンスはsearch_coverageを保持します:

{"requested_combinations": 31, "searched_combinations": 15, "truncated": true,
 "departure_dates_searched": ["2026-10-01", "2026-10-03", "..."],
 "note": "This request expanded to 31 searches, above the 15-search limit..."}

黙って切り詰められた検索は完全な検索として読まれ、それこそが、ユーザーが月の半分を見ていない「最安」の答えを信頼してしまう原因になります。

sort_type は公開されない

バックエンドではsort_typeは並び替え後ではなくどの検索を実行するかを選択し、上流APIによってone-wayでは暗黙的に無視され、max_priceがそれを上書きします(app.py:255)。ここでは最大15の検索結果をマージして並べ替える必要があるため、サーバーは常にバックエンドのデフォルトに適用し、マージされたセットをsort_by(best / price / duration)で自分でソートします。これは予測可能です。sort_typeをそのまま通すのは予測可能性がありません。


非提携

独立したサービスで、公的に入手して旅行の価格設定を返します。Googleとは提携しておらず、承認されてなく、スポンサーにもありません。「Google備品として説明にのみ使用されています。Google Flights」は、価格データの出典を説明するためにのみ使用されています。


自分でインスタンスを実行する

以下はすべて、このサーバーを自分で操作する方法です。使用だけが必要なら、冒頭のインストール行だけで十分です。

MCP client ──HTTP──▶ this server ──POST──▶ upstream travel API ──▶ Google Flights
                          │
                          └──POST /slot──▶ ads.getlulu.dev

これは上流の旅行APIを直接呼び出すものであり、呼び出し元ごとに課金される有料チャネルとは意図的に分離されています。

クイックスタート

cd mcp_server
python3 -m venv .venv && .venv/bin/pip install -r requirements.txt
cp example.env .env          # fill it in
set -a && . .env && set +a
.venv/bin/python -m src      # serves on :8000/mcp

ヘルスチェックは/health、ライブカウンタは/metricsです。

設定

BASE_LAMBDA_URLとRAPID_AUTHは、このサーバーを独自の上流の旅行APIに接続し、認証します。もし引用符を囲む環境ファイルから値をコピーする場合、ローダーが前後の引用符を1層取り除くことに注意してください。引用符をそのまま貼り付けた値は、引用符の間違いではなく間違ったシークレットに見える403になります。

すべての変数はexample.envを参照してください。最も重要なものは3つです:

変数

重要な理由

MCP_PUBLIC_URL

クライアントが接続するURLと正確一致する必要があります。 下記を参照。

MAX_BACKEND_CALLS_PER_TOOL_CALL

コスト上限。デフォルト15。

ENFORCEMENT_MODE

off / monitor / enforce。デフォルトはmonitor。

MCP_PUBLIC_URLは沈黙の失敗モード

Luluはこの値をハッシュしてClaudeの_meta.ui.domainを導き出します。Claude同じ仕組みで同じハッシュを計算し、ドメインが一致しないフレームを拒否します。URLの誤りでもスポンサーカードは描画され続け、どこにもエラーも発生せず、CPMはゼロになります。

サーバーは起動時に派生ドメインをログに出力するため、値を確認できます:

sponsored widget domain for https://.../mcp -> <hash>.claudemcpcontent.com

ファンアウトが内部である理由

バックエンドは1回の呼び出しで、正確に1つの(origin, destination, date)のタプルを受け取ります。そのため「10月のどこでもスリランで行く最安便」はバックエンド呼び出しは31回です。有料APIでは気になりません。各呼び出しが収益になるからです。しかしここでは、収益はツール呼び出しごとに1つのレンダリングされます広告であり、バックエンド呼び出しごとにコストになります。したがって、モデルに31のツール呼び出しを発火させると、収益が1倍のままでコストが31倍になります。

そこで、モデルが1回の呼び出しを行い、サーバーはハードキャップのためにファンアウトします。

コスト管理

2つの独立したガードです:

  1. ツール呼び出しごと — MAX_BACKEND_CALLS_PER_TOOL_CALL、デフォルト15。

  2. 直近の時間安定 — DAILY_BACKEND_CALL_BUDGET(0で無効)。予算がBUDGET_DEGRADE_ATに達すると、呼び出しごとの上限はN分の1に下がります。100%で上限は1になります。

制限を下げる方が拒否するより勝ります。80%で停止すると、残り一日の間すべてのユーザーに停止が見えるからです。

24時間のウィンドウは再起動しても存続します。クラッシュループでプロセスが新しい予算を手に入れないようにしない簡単に。存続方法はストアに依存します。Upstashの場合はRedis上にウィンドウが存在し、インスタンス間で共有されます。in-processストアの場合はLOG_PATH ファイルから起動時に再構築されます。Upstashも書き込み可能なログファイルもない場合は、コールドスタートごとに予算がリセットされ、適用できません。 これがVercelのデフォルトなので、そこではUpstashを設定してください。/metrics はbudget.enforceableを報告するので、推測する必要はありません。

執行:可能なことと可能でないこと

clientInfo.nameが何もゲートしないのであり、何もできない。MCP仕様2026-07-28は規範的に述べています。このフィールドは「送信者によって自己報告され、プロトコルによって検証されない…セキュリティ判断のために依存するべきではない」と。スクリプトは、Claudeがそうするのと同じ簡単に"claude-ai"という設定できる。記録は単なる報告のみ。

送信IP is can

送信元IPにゲートされている。送信元は完成したTLSハンドシェイクで偽造できず、両ホストはユーザーのマシンではなく、そのクラウドとゾーンからリモートMCPをバッカするので:

Tier

どう決まるか

ウィジェットをレンダリングしますか?

llm_host

Anthropic の 160.79.104.0/21、または OpenAI が公開している chatgpt-connectors.json のプレフィックス

はい — ビーコンを発火できる唯一の Tier

local_client

クライアント名のヒント(Claude Code、Cursor、…)

いいえ — CLI テキストカードのみ。構造上 CPM は $0

unknown

それ以外のすべて

いいえ

enforce モードでは、llm_host には全額の上限が適用され、他の Tier は 3 分の 1 となり、BLOCKED_TIERS に含まれるものは拒否されます。意図的な安全規則が 2 つあります:

  • monitor がデフォルトです。最初の週にブロックすると、POC が収集するために存在するトラフィックサンプルが台無しになります。

  • OpenAI のレンジフィードが読み込まれていない間、unknown がブロックされることはありません。なぜなら、それがなければ実際の ChatGPT 呼び出し元がすべて unknown に見えるからです。

このサーバーが測定できないこと

レンダリング済みインプレッションのビーコンは、Lulu ウィジェットフレームの内部から ads.getlulu.dev へ直接発火します。このプロセスには一切触れないため、このサーバーは単独で真のレンダー率を計算できません。

このサーバーが行うのは、Tier ごとに 配信された スロット数を数えることです。それを Lulu が報告するレンダリング済みインプレッションと照合して実際の比率を求め、何もレンダリングしない Tier を BLOCKED_TIERS に入れます。その仕組みは構築済みです。入力となる判断は、実際の数値に基づく人間の判断です。

Lambda リクエストの数え方

すべてのツール呼び出しは、MCP_CALL というプレフィックスを付けて stdout へ正確に 1 行出力します。下記のフィールド名は実在します。値は例示です — ここにあるものは公開されたレイテンシーやボリュームの数値ではありません:

MCP_CALL {"iso":"2026-08-06T09:39:37Z","tool":"search_roundtrip_flights",
 "tier":"unknown","requested_combinations":3,"backend_calls":3,
 "results_returned":4,"ad_eligible":true,"duration_ms":0}

backend_calls はその呼び出しにおける Lambda リクエスト数です。 任意の期間の MCP_CALL 行を合計すると、バックエンドリクエストの総数になります。stdout が記録なのは、どこでも機能する唯一のシンクだからです — Vercel は自動的に取得します。そこでファイルシステムは読み取り専用です。

1 行は ツール呼び出し ごとであり、バックエンド呼び出しごとでは決してありません。Vercel はランタイムログをリクエストあたり 256 行・1 MB に制限しています。

LOG_PATH はコンテナデプロイ用に 2 つ目のファイルシンクを追加します。パスが書き込み可能でない場合は、無に追記するのではなく自身を無効化します。

メトリクス

GET /metrics(METRICS_TOKEN を設定すると x-metrics-token ヘッダーを要求します)。形式は下記の通りで、値は例示です。利用実績の数値ではありません:

{"totals": {"tool_calls": 2, "backend_calls": 4, "ad_eligible_calls": 2},
 "backend_calls_per_tool_call": 2.0,
 "by_tier": {"llm_host": {"tool_calls": 2, "backend_calls": 4}},
 "budget": {"used_24h": 4, "budget": 1000, "remaining": 996,
            "enforceable": false},
 "durable_counters": false,
 "notes": ["..."]}

GET /metrics/calls?hours=24 は UTC 時間ごとの呼び出し回数を返します。

backend_calls_per_tool_call は、このチャネルが POC を超えて生き残るかを決定する数値です。 すべてのバックエンド呼び出しはコストであり、すべてのツール呼び出しは最大でも 1 つのレンダリング済み広告です。それに実際の呼び出しあたりの Lambda + プロキシコストを掛け、広告ネットワークが実際に報告する CPM と比較してください。

durable_counters: false と budget.enforceable: false は、合計が 1 つのプロセスだけを対象にしていることを意味します。コンテナではそれで問題ありません。Vercel では、実値が表示よりも高くなり、支出ガードが実際には有効化されていないことを意味します — 下記参照。

カウンターの保存

デプロイ先が正しい実装を決定するため、カウンターは小さなインターフェースの背後にあります:

ストア

使う場面

永続性

In-process dict

コンテナ、ローカル

いいえ

Upstash Redis( REST API経由 )

Vercel、あらゆるサーバーレス

はい

Vercel では、Fluid compute が並行呼び出し間で 1 つのインスタンスを共有し、インスタンスを自由にスケールするため、インプロセスのカウンターは断片化してごとにリセットされます。エラーにはならず、実際より小さい数を返すだけです。これは支出ガードが失敗する最悪の形です。このため、DAILY_BACKEND_CALL_BUDGET は共有ストアがある場合にのみ強制可能であり、サーバーは起動時と /metrics でその旨を伝えて、そうでないふりはしません。

vercel install upstash   # injects UPSTASH_REDIS_REST_URL / _TOKEN

Upstash へは Redis ワイヤプロトコルではなく REST API 経由でアクセスします。短命の呼び出しはプーリングクライアントには不向きであり、redis を依存関係リストから除外できます。ストアの障害はすべてログに記録され、飲み込まれます — カウンターの障害がフライト検索の障害になることは絶対にあってはなりません。

広告の配線

enable_lulu_ads を 1 行入れるのではなく、ウィジェットとミドルウェアを別々に配線しています。is_error_result を受け入れるのはミドルウェア経路だけであり、それが空の結果から広告を除外する理由だからです。

結果は Lulu のresult ウィジェットを経由してレンダリングされ、スポンサードカードでは表示されません。スポンサードカードは広告を描画しますが、レンダリングインプレッションビーコンは含まれません。ビーコンは result ウィジェットの固定 SPONSORED ストリップにのみ存在します。スポンサードカードだけを配信すると、クリック収益と CPM $0 が得られ、どこも問題を報告しません。スポンサードカードは、result ウィジェットの登録が失敗した場合のフォールバックとして保持され、そのフォールバックはログに明確に記録されます。

ビーコンは ads.getlulu.dev への 1×1 の <img> です。MCP Apps ホストはデフォルトの CSP として img-src 'self' data: を適用するため、そのドメインはウィジェットリソース上で宣言する必要があります — MCP Apps では app= 経由、ChatGPT では openai/widgetCSP 経由です。宣言されていない場合、外部から見ると正常に機能する統合と同一に見えます。カードは描画され、ストリップは表示され、インプレッションもエラーもありません。

一度前で登録してから、LULU_ADS_PUBLISHER_ID / LULU_ADS_API_KEY を設定します:

curl -X POST https://ads.getlulu.dev/publishers \
  -H 'content-type: application/json' \
  -d '{"name":"...","contact_email":"...","server_url":"..."}'

API キーはちょうど 1 回だけ返却されます。

課金対象フィールドは /slot レスポンス上の imp_url(snake_case)です。TypeScript の型定義はそれを impUrl と呼びますが、Python SDK はレスポンスキーを許可リストで制限し imp_url だけを読み取ります(lulu_ads/client.py:210)。実際の認証情報が存在する場合は、実 /slot` レスポンスが実際にそれを担っていることを確認してください。ない場合は、ウィジェットには発火するビーコンがなく、課金対象インプレッションもありません。たとえ他のすべてが正しく動いているとしてもです。

テスト

.venv/bin/python -m pytest tests -q     # 141 tests

バックエンドも広告サーバーもスタブにしているため、テストスイートはネットワークも認証情報も必要としません。tests/test_ads.py と tests/test_stores.py は SDK をモックしてはなく、実際のスタブサーバーを実行するため、実際のワイアフォーマットを実行できます。

Vercel へのデプロイ

api/index.py      entrypoint, exports `app`
vercel.json       maxDuration + no-store on /mcp
.python-version   3.13
requirements.txt

Vercel は api/index.py を自動検出し、すべてのパスを受け取る 1 つの Fluid 関数としてアプリ全体を配信するため、/mcp、/health、/metrics はすべて同じプロセスで処理されます。リライトは不要です。

vercel link          # new project
vercel install upstash

# `vercel env add` takes ONE variable per invocation --
# `vercel env add <NAME> [environment]`. Each prompts for the value.
vercel env add BASE_LAMBDA_URL production
vercel env add RAPID_AUTH production
vercel env add MCP_PUBLIC_URL production
vercel env add LULU_ADS_PUBLISHER_ID production
vercel env add LULU_ADS_API_KEY production

vercel deploy --prod

このコードがすでに処理している Vercel 固有の 4 項目です。それぞれについて、誤ると静かに失敗するものです。

Lifespan。 FastMCP は ASGI の lifespan スタートアップイベントで streamable-HTTP セッションマネージャーを初期化します。これがないとすべての /mcp リクエストが Task group is not initialized で失敗し、MCP エンドポイントが完全に停止します。Vercel の lifespan サポート(2025-12-09 リリース)は「FastAPI アプリ」向けに告知されています。「はうす not yet」の FastAPI アプリが含まれるかは明記されていません。そのため api/index.py は FastMCP アプリを FastAPI でラップし、lifespan=_mcp_app.lifespan を引き渡します。これは FastMCP がドキュメントで示す方法でもあります。この「簡略化」を取り除かないでください。

Statless。 stateless_http=True。 内ュベーションは短命で、同じインスタンスに到達する保証はないため、セッションを保存できる場所がありません。

ファイルシステム。 一時的な /tmp 以外は読み取り専用です。LOG_PATH は空のままにします。stdout の MCP_CALL 行が記録です。

カウンター。 Upstash を設定しなければ、支出ガードは decor(装飾)です。上記を参照してください。

ここで重要となるその他の制限: maxDuration は Fluid 配下のすべてのプランで 300 秒が初期値です(バックエンドの呼び出しは約 90 秒でピークになるため収まります)。レスポンスはバッファリングではなくストリーミングされます。json_response=True にすると、15 方向のファンアウトが近づくことのできる 4.5 MB のボディ上限が再適用だろうためです。また、共有される 1,024 ファイルディスクリプタプールが、MAX_HTTP_CONNECTIONS が存在する理由です。

ログ保持期間は Hobby で 1 時間、Pro で 1 日です。MCP_CALL 行が POC の課金根拠であるなら、Pro に加えてログドレインを導入するか、同じカウンターを Upstash に送って /metrics を読むことを検討すべきです。

コンテナとしてデプロイする

Dockerfile は常時稼働する HTTP サービスをビルドします。backend/Dockerfile(Render にデプロイされる形)と同じ構成です。LOG_PATH を設定し、/app/logs にボリュームをマウントすると、支出ガードのローリングウィンドウがデプロイをまたいで存続できます。

どちらの場合でも、デプロイ後に MCP_PUBLIC_URL に実 URL を設定します(末尾スラッシュなし)。起動ログでウィジェットドメインを確認してください。

Related MCP Connectors

Related MCP Servers