Google Flights MCP
Google Flights MCP — free, ad-supported
Real-time one-...
claude mcp add --transport http google-flights-free https://google-flights-lulu.flightpowers.com/mcpThat is the whole setup. There is no key to paste and nothing to configure.
The two tools
| Tool ... | ...
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-sortedsearch_roundtrip...
What comes back
...
Fares are live, and only live
...
How it is free
...
One caveat worth stating up front
...
When you outgrow the free one
...
| ... |
...
claude mcp add --transport http google-flights https://google-flights-mcp.flightpowers.com/mcp --header "x-rapidapi-key: YOUR_RAPIDAPI_KEY"...
Truncation is always reported
...
{"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..."}A silently truncated search...
sort_type is not exposed
...
Non-affiliation
...
Running your own instance
...
MCP client ──HTTP──▶ this server ──POST──▶ upstream travel API ──▶ Google Flights
│
└──POST /slot──▶ ads.getlulu.devIt calls the upstream...
Quick start
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/mcpHealth check at /health, live counters at /metrics.
Configuration
...
MCP_PUBLIC_URL is a silent failure mode
...
sponsored widget domain for https://.../mcp -> <hash>.claudemcpcontent.comWhy fan-out is internal
...
Cost control
...
Enforcement: what does not and can not be possible
...
Уровень | Как определяется | Рендерит виджет? |
| Префиксы Anthropic | Да — единственный уровень, который может отправить beacon |
| Подсказка имени клиента (Claude Code, Cursor, …) | Нет — только CLI-карточка, структурно $0 CPM |
| Всё остальное | Нет |
В режиме enforce уровень llm_host получает полный лимит, остальные уровни — треть, а всё, что в BLOCKED_TIERS, отклоняется. Два намеренных правила безопасности:
monitor— это режим по умолчанию. Блокировка на первой неделе уничтожила бы выборку трафика, ради сбора которой существует POC.unknownникогда не блокируется, пока не загружен фид диапазонов OpenAI, потому что без него любой реальный вызывающий ChatGPT выглядит как unknown.
Что этот сервер не может измерить
Beacon отрисованного показа запускается изнутри фрейма виджета Lulu напрямую на ads.getlulu.dev. Он никогда не касается этого процесса, поэтому этот сервер не может сам вычислить настоящий рендер-рейт.
Он подсчитывает слоты, обслуженные по каждому уровню. Сопоставьте это с указанными Lulu отрисованными показами, чтобы получить реальный коэффициент, а затем поместите уровень, который ничего не рендерит, в BLOCKED_TIERS. Механизм уже построен; входное значение — реление выработанное на основе реальных цифр.
Подсчёт запросов Lambda
Каждый вызов инструмента отправляет ровно одну строку в stdout, с префиксом MCP_CALL . Имена полей ниже реальны; значения являются иллюстративными — ничто здесь не является опубликованные характеристики задержки или объёма:
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 за любой период и получите общее количество backend-запросов. stdout — источник учёта, потому что это единственный приёмник, который работает везде: Vercel захватывает его автоматически, а файловая система там доступна только для чтения.
Одна строка на вызов инструмента, никогда на backend-вызов: Vercel ограничивает логи выполнения 256 строками и 1 МБ на запрос.
LOG_PATH добавляет второй, файловый приёмник для контейнерных развёртываний. Он отключается сам, если путь не поддерживает запись, а не пишет в пустоту.
Метрики
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. Каждый backend-вызов — это стоимость; каждый вызов инструмента — это максимум одно показанное объявление. Умножьте его на реальную стоимость Lambda + прокси за вызов и сравните с CPM, о котором фактически отчитывается рекламная сеть.
durable_counters: false и budget.enforceable: false означают, что итоги охватывают только один процесс. Для контейнере это нормально. На Vercel это означает, что реальные цифры выше показанных и spend guard на самом деле не взведён — см. ниже.
Хранение счётчиков
Счётчики спрятаны за маленьким интерфейсом, потому что целевая платформа развёртывания определяет, какая реализация правильная:
Ждём | Когда | Долговременный |
In-process dict | Контейнер, локально | Нет |
Upstash Redis (REST) | Vercel, любой serverless | Да |
На Vercel Fluid compute разделяет один инстанс между параллельными вызовами и свободно их масштабирует, поэтому внутрипроцессные счётчики фрагментируются и сбрасываются. Они не дают ошибку — они просто возвращают число, меньшее реального, а это худший способ отказа защитного механизма бюджета. Поэтому DAILY_BACKEND_CALL_BUDGET может быть обеспечен только общим хранилищем, и сервер сообщает об этом при запуске и в /metrics, а не делает вид, что это не так.
vercel install upstash # injects UPSTASH_REDIS_REST_URL / _TOKENUpstash используется через REST API, а не через wire-протокол Redis: короткоживущие вызовы не подходят для клиента с пулом соединений, и это оставляет redis в стороне от списка зависимостей. Любой сбой хранилища логируется и проглатывается — сбоёй счётчика никогда не должен стать сбоем поиска авиабилетов.
Подключение рекламы
Реклама подключена отдельно через виджет и middleware, а не через однострочный enable_lulu_ads, потому что только путь middleware принимает is_error_result — это и не даёт рекламе попадать в пустые результаты.
Результаты рендерится через виджет Lulu result, а не через его фирменную карточку. Фирменная карточка рисует объявление, но не содержитбикон отрисованного показа; beacon живёт только в фиксированной полосе SPONSORED widget result. Показ только спонсорской карточки приносит клик-выручку и ровно $0 CPM, и нигде ничего не сообщает об ошибке. Спонсорская карточка остаётся как запасной вариант на случай сбоя регистрации виджета result, и этот сбой логируется громко.
Beacon — это элемент <img> размером 1×1, указывающий на ads.getlulu.dev. Приложения MCP Apps применяют стандартный CSP img-src 'self' data:, поэтому домен нужно объявить на ресурсе виджета — через app= для MCP Apps и openai/widgetCSP для ChatGPT. Без объявления это выглядит идентично рабочей интеграции: карточка рендерится, полоса появляется, никакого импадопрошлого нет и нет ошибки.
Зарегистрируйтесь один раз, затем настройте 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-ключ возвращается ровно один раз.
Расчётным полем является imp_url (snake_case) в ответе /slot. Типизации TypeScript называют его impUrl, но в Python SDK разрешённые ключи ответа читают только imp_url (lulu_ads/client.py:210). Когда появятся настоящие учётные данные, проверьте, что реальные ответы /slot действительно несут это поле — без него виджету нечем отправить beacon, и расчётного показа нет, как бы хорошо ни работало всё остальное.
Тесты
.venv/bin/python -m pytest tests -q # 141 testsИ backend, и рекламный сервер — стабы, поэтому тестам не нужны ни сеть, ни учётные данные. 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.txtVercel автоматически находит api/index.py и обслуживает всё приложение как одну Fluid function, принимающую все маршруты, так что /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 вещи, которые этот код уже обрабатывает, и каждая превращается в тихий сбой, если не настроить правильно:
Lifespan. FastMCP инициализирует свой менеджер сессий streamable-HTTP в событие запуска lifespan в ASGI. Без него каждый запрос /mcp падает с Task group is not initialized — это полная недоступность MCP-эндпоинта. Поддержка lifespan на Vercel (выпущена 2025-12-09) заявлена для «FastAPI-приложений»; была ли она покрывает отдельное Starlette-приложение, не выставлено. Поэтому api/index.py оборачивает приложение FastMCP и передавлаёт lifespan=_mcp_app.lifespan, что также является интеграцией, документированной FastMCP. Не «упрощайте» этоу.
Stateless. stateless_http=True. Инвокации короткоживущи и не обязаны попадать на тот же инстанс, поэтому сеанс негде хранить.
Filesystem. Вне эфемерного /tmp файловая система только для чтения. Оставьте LOG_PATH пустым; строки MCP_CALL в stdout — и есть запись.
Counters. Настройте Upstash, иначе защита бюджета чисто декоративная — см. выше.
Другие важные ограничения: maxDuration по умолчанию 300 c на всех тарифах под Fluid (backend-вызовы достигают пика ~90 c, поэтому подходит); ответы передаются стримингом, а не буферизуются, потому что возврат json_response=True снова дал бы лимит 4.5 МБ на тело, к которому может приблизяться разветвление на 15 направлений; а общий пул 1,024 файловых дескрипторов — это причина существования MAX_HTTP_CONNECTIONS.
Удержка логов — 1 час на Hobby, 1 день на Pro. Если строки MCP_CALL — это доказательства нагрузки для POC, это аргументы за Pro и прогрузку логов, либо за перенос счётчиков в Upstash и чтение /metrics.
Развёртывание контейнерта
Dockerfile собирает постоянно работающий HTTP-сервис, такой же формы, как backend/Dockerfile (и который разворачивается на Render). Установите LOG_PATH и смонтируйте том на /app/logs, чтобы скользящее окно бюджета пережило развёртывание.
В любом случае: после развёртывания установите MCP_PUBLIC_URL на реальный URL — без завершающего слеша — и проверьте домен виджета в стартовом логе.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Connectors
AI marketplace — flights, tours, activities, transport & more via MCP. No auth required.
Free, no-login flight search with real-time pricing from multiple airlines.
Duffel MCP — live flight search + pricing via the Duffel Flights API (duffel.com)
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/mtnrabi/google-flights-mcp-free'
If you have feedback or need assistance with the MCP directory API, please join our Discord server