Google Flights MCP
Google Flights MCP — gratuito, con publicidad
Búsqueda de vuelos de ida y vuelta en tiempo real para cualquier cliente MCP. Sin clave de API, sin cuenta, sin facturación. Se financia con una tarjeta patrocinada claramente indicada adjunta a cada resultado, no cobrándote.
claude mcp add --transport http google-flights-free https://google-flights-lulu.flightpowers.com/mcpEso es todo el montaje. No hay ninguna clave que pegar ni nada que configurar.
Las dos herramientas
Herramienta | Qué hace |
| Búsqueda de ida en un rango de fechas y una lista de destinos |
| Ida y vuelta con precio por tramos emparejados, en un rango de fechas y un valor |
Ambas aceptan un rango de fechas (departure_date_from / departure_date_to) y una
lista de destinos, y los expanden en el servidor. Una intención de usuario es una
llamada a la herramienta — "vuelo más barato a Sri Lanka en cualquier fecha de octubre" es una sola
llamada, no treinta.
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_flights además acepta nights — un número o una lista
como [5, 6, 7] — en lugar de una return_date fija, de modo que "5–7 noches en Roma
en algún momento de mayo" también es una sola llamada.
Qué se devuelve
Cada resultado incluye precio, duración, aerolínea, escalas, aeropuertos de escala y duraciones de
escala, y un buy_link para reservar. También incluye el rango de precios histórico de Google
para esa ruta y período — price_insights_low,
price_insights_high, y un veredicto low / typical / high en
price_range_in_relation_to_other_periods.
Esa última parte es la razón para preferir esto sobre un simple raspado de precios: permite que un asistente diga "$209 es típico para esta ruta, no hay necesidad de apresurarse" en lugar de solo citar un número.
Una lista de resultados vacía significa "no hay vuelos en esta ruta y fecha" — es una respuesta válida, no un error.
Las tarifas son en vivo, y solo en vivo
Cada llamada es una búsqueda nueva. Las tarifas se vuelven obsoletas en minutos, por lo que los resultados nunca deben almacenarse en caché, guardarse ni volver a citarse más tarde como si fueran actuales. Si una respuesta hace referencia a una tarifa, debe indicar cuándo se obtuvo y volver a buscar en lugar de reutilizarla.
Cómo es gratuito
Cada resultado exitoso lleva una tarjeta patrocinada claramente indicada. Esa tarjeta es todo el modelo de financiación — es lo que paga las llamadas de búsqueda del backend para que no tengas que traer una clave ni una tarjeta de crédito.
Siendo precisos al respecto, porque debes saber a qué te estás suscribiendo:
Una tarjeta patrocinada por resultado, siempre etiquetada como patrocinada.
Nunca adjunta a un error, y nunca a una respuesta de cero resultados. Además de ser molesto, un anuncio junto a datos no sustanciales se lee como un intento de inyección de prompt — las propias pruebas en vivo de Lulu hicieron que un modelo lo marcara exactamente así, 3 de 3 veces.
Los anuncios no pueden tumbar la búsqueda. El SDK de anuncios es inerte sin credenciales y falla abierto en cada error de red.
El anuncio se sirve a través de Lulu. Este servidor no envía tu identidad — envía una solicitud de espacio y renderiza lo que devuelve.
Debido a que hay anuncios, este servidor no está listado en el Registro MCP oficial ni en los directorios de Anthropic y OpenAI: los tres prohíben servidores con anuncios. Esa es una consecuencia deliberada y conocida del modelo gratuito, no un descuido.
Una advertencia que vale la pena mencionar de antemano
La tarjeta patrocinada solo puede renderizarse dentro de asistentes que muestren marcos de widgets MCP.
Los llamantes se clasifican por IP de origen y, en modo de aplicación, los clientes
que no pueden renderizar la tarjeta pueden recibir un límite de fan-out reducido o ser rechazados. El
modo de despliegue predeterminado es monitor — clasificar y registrar, servir a todos — pero
si estás conectando esto a un script sin interfaz en lugar de a un asistente, el
servidor de pago de abajo es la mejor opción y no está restringido de esta manera.
Cuando superas el gratuito
Hay un servidor hermano de pago, sin anuncios, con las mismas dos herramientas y la misma búsqueda detrás: https://google-flights-mcp.flightpowers.com/mcp
Nada aquí está bloqueado detrás de él — el servidor gratuito es completamente funcional. El de pago es simplemente la elección correcta una vez que los anuncios, el límite de 15 búsquedas o la restricción de cliente empiezan a estorbar.
Gratuito — este servidor | De pago — | |
Anuncios | una tarjeta patrocinada claramente indicada por resultado | ninguno |
Clave de API | no se necesita | tu propia clave de RapidAPI |
Límite de fan-out por llamada | 15 búsquedas | 30 búsquedas (máximo duro 60; anulación |
Informe de gastos | n/a |
|
Restricciones de cliente | clasificado por nivel; los clientes que no renderizan pueden ser limitados o rechazados | ninguna — cualquier cliente, cualquier transporte |
Listado en el Registro MCP | no (los anuncios están prohibidos allí) | sí, como |
Coste | gratuito | tu propio uso de RapidAPI |
El intercambio es sencillo: el servidor de pago no tiene anuncios ni restricciones porque tú estás pagando los costes de búsqueda con tu propia clave de RapidAPI, en lugar de que un patrocinador los pague.
Obtener la clave lleva un minuto. Suscríbete a la API de Google Flights Live en
RapidAPI — hay un nivel gratuito — y copia tu x-rapidapi-key:
https://rapidapi.com/mtnrabi/api/google-flights-live-api
Luego apunta un cliente al servidor de pago:
claude mcp add --transport http google-flights https://google-flights-mcp.flightpowers.com/mcp --header "x-rapidapi-key: YOUR_RAPIDAPI_KEY"La clave también se puede pasar como parámetro de consulta ?rapidapi_key=, o en un
campo de clave de API del cliente si el cliente lo ofrece. Cada respuesta de pago incluye
api_usage con requests_used_by_this_call, plan_requests_remaining y
plan_requests_limit, de modo que el gasto es visible antes de la siguiente llamada en lugar de
al final del mes.
La truncación siempre se informa
Cuando una solicitud supera el límite, las fechas se muestrean uniformemente en todo el rango
en lugar de truncarse a las primeras N — quince fechas repartidas en octubre
responden "lo más barato en octubre"; los primeros quince días no. Cada respuesta
lleva 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..."}Una búsqueda truncada silenciosamente se lee como una completa, que es como un usuario termina confiando en una respuesta de "lo más barato" que nunca miró la mitad del mes.
sort_type no está expuesto
En el backend, sort_type selecciona qué búsqueda se ejecuta en lugar de ordenar después;
se descarta silenciosamente para ida por the upstream API, y max_price
lo anula (app.py:255). Dado que los resultados de hasta 15 búsquedas tienen que
fusionarse y reordenarse aquí de todos modos, el servidor siempre deja que el valor predeterminado del backend
se aplique y ordena el conjunto fusionado él mismo mediante sort_by (best / price /
duration). Eso es predecible; pasar sort_type no lo es.
No afiliación
Este es un servicio independiente que devuelve precios de vuelos disponibles públicamente. No está afiliado, respaldado ni patrocinado por Google. "Google Flights" se usa solo para describir la fuente de los datos de precios.
Ejecutar tu propia instancia
Todo lo siguiente es para operar este servidor tú mismo. Si solo quieres usarlo, la línea de instalación al principio es todo lo que necesitas.
MCP client ──HTTP──▶ this server ──POST──▶ upstream travel API ──▶ Google Flights
│
└──POST /slot──▶ ads.getlulu.devLlama directamente a la API de viajes upstream y se mantiene deliberadamente separado de los canales de pago, que se facturan por llamante.
Inicio rápido
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/mcpComprobación de salud en /health, contadores en vivo en /metrics.
Configuración
BASE_LAMBDA_URL y RAPID_AUTH apuntan este servidor a tu propia API de viajes
upstream y se autentican contra ella. Si copias los valores de un archivo de entorno
que los cita, ten en cuenta que el cargador elimina una capa de comillas circundantes —
un valor pegado con sus comillas intactas produce un 403 que parece un secreto
incorrecto en lugar de un error de comillas.
Consulta example.env para cada variable. Las tres que más importan:
Variable | Por qué importa |
| Debe coincidir exactamente con la URL a la que se conectan los clientes. Ver abajo. |
| El límite de coste. Predeterminado 15. |
|
|
MCP_PUBLIC_URL es un modo de fallo silencioso
Lulu deriva el _meta.ui.domain de Claude a partir de este valor mediante hash. Claude
calcula el mismo hash de forma independiente y rechaza un marco cuyo dominio no
coincida. Una URL incorrecta significa que la tarjeta patrocinada nunca se renderiza, no se genera ningún error
en ningún lugar, y el CPM se queda en cero.
El servidor registra el dominio derivado al inicio para que el valor sea comprobable:
sponsored widget domain for https://.../mcp -> <hash>.claudemcpcontent.comPor qué el fan-out es interno
El backend acepta exactamente una tupla (origin, destination, date) por llamada, por lo que
"vuelo más barato a Sri Lanka en cualquier fecha de octubre" son 31 llamadas al backend. En la
API de pago eso está bien — cada llamada es ingresos. Aquí, los ingresos son un anuncio renderizado
por llamada a la herramienta mientras que cada llamada al backend es un coste, así que dejar que el modelo
lance 31 llamadas a la herramienta sería 31× el coste por 1× los ingresos.
Entonces el modelo hace una llamada y el servidor hace fan-out bajo un límite duro.
Control de costes
Dos salvaguardas independientes:
Por llamada a la herramienta —
MAX_BACKEND_CALLS_PER_TOOL_CALL, predeterminado 15.Ventana móvil de 24 h —
DAILY_BACKEND_CALL_BUDGET(0 lo desactiva). AlBUDGET_DEGRADE_ATdel presupuesto, el límite por llamada cae a un tercio; al 100% cae a 1.
Degradar es mejor que rechazar: una parada dura al 80% parecería una interrupción para todos los usuarios durante el resto del día.
La ventana de 24 h sobrevive a un reinicio — un bucle de caídas no debe dar al proceso un
presupuesto nuevo. Cómo sobrevive depende del almacén: con Upstash la ventana
vive en Redis y se comparte entre instancias; con el almacén en proceso se
reconstruye desde el archivo LOG_PATH al inicio. Con ninguno — sin Upstash
y sin archivo de registro escribible — el presupuesto se restablece en cada inicio en frío y no es
aplicable. Ese es el predeterminado en Vercel, así que configura Upstash allí;
/metrics informa budget.enforceable para que nunca tengas que adivinar.
Aplicación: qué es y qué no es posible
El objetivo es "solo utilizable donde la tarjeta patrocinada realmente se renderiza". El enfoque obvio no funciona, así que vale la pena ser precisos.
clientInfo.name no puede bloquear nada. La revisión 2026-07-28 de la especificación MCP dice
así de manera normativa: esos campos "son autoinformados por el remitente y no están
verificados por el protocolo… NO DEBEN usarse para decisiones de seguridad." Un
script establece "claude-ai" tan fácilmente como Claude. Se registra solo para
informes.
La IP de origen sí puede. No se puede falsificar en un handshake TLS completado, y ambos hosts intermediarios de MCP remoto desde su propia nube en lugar de la máquina del usuario:
Nivel | Cómo se decide | ¿Renderiza el widget? |
| Anthropic | Sí — el único nivel que puede disparar un beacon |
| Pista del nombre del cliente (Claude Code, Cursor, …) | No — solo tarjeta de texto CLI, estructuralmente $0 CPM |
| Todo lo demás | No |
En modo enforce, llm_host recibe el tope completo, los demás niveles reciben un tercio, y
cualquier cosa en BLOCKED_TIERS es rechazada. Dos reglas de seguridad deliberadas:
monitores el valor por defecto. Bloquear en la primera semana destruiría la muestra de tráfico que el POC existe para recopilar.unknownnunca se bloquea mientras el feed de rangos de OpenAI no esté cargado, porque sin él, cada llamada real de ChatGPT pareceunknown.
Lo que este servidor no puede medir
El beacon de impresión renderizada se dispara desde dentro del marco del widget de Lulu directamente a ads.getlulu.dev. Nunca pasa por este proceso, así que este servidor no puede calcular una tasa de renderizado real por sí solo.
Lo que hace es contar los slots servidos por nivel. Concilia eso contra las impresiones
renderizadas que reporta Lulu para obtener la proporción real, y luego pon cualquier nivel
que no renderice nada en BLOCKED_TIERS. El mecanismo está construido; la entrada es una
decisión humana informada por números reales.
Contando solicitudes de Lambda
Cada llamada de herramienta emite exactamente una línea a stdout, con el prefijo MCP_CALL .
Los nombres de campo a continuación son reales; los valores son ilustrativos — nada aquí
es una cifra publicada de latencia o volumen:
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 es el recuento de solicitudes de Lambda para esa llamada. Súmalo a través de
las líneas MCP_CALL para cualquier período y tendrás el total de solicitudes de backend. stdout
es el registro porque es el único sumidero que funciona en todas partes — Vercel
lo captura automáticamente, y el sistema de archivos allí es de solo lectura.
Una línea por llamada de herramienta, nunca por llamada de backend: Vercel limita los registros de ejecución a 256 líneas y 1 MB por solicitud.
LOG_PATH añade un segundo sumidero de archivo para despliegues en contenedor. Se desactiva
a sí mismo si la ruta no es escribible en lugar de añadir al vacío.
Métricas
GET /metrics (establece METRICS_TOKEN para requerir una cabecera x-metrics-token).
Forma a continuación, con valores ilustrativos — no cifras de uso:
{"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 devuelve recuentos de llamadas por hora UTC.
backend_calls_per_tool_call es el número que decide si este
canal sobrevive más allá del POC. Cada llamada de backend es un costo; cada llamada de
herramienta es como máximo un anuncio renderizado. Multiplícalo por el costo real por llamada de Lambda + proxy
y compáralo con el CPM que la red publicitaria reporta realmente.
durable_counters: false y budget.enforceable: false significan que los totales
cubren solo un proceso. En un contenedor eso está bien. En Vercel significa que los
números reales son más altos de lo mostrado y que el guardián de gasto no está realmente
armado — ver más abajo.
Almacenamiento de contadores
Los contadores viven detrás de una interfaz pequeña porque el objetivo de despliegue decide qué implementación es la correcta:
Almacenamiento | Cuándo | Durable |
Dict en proceso | Contenedor, local | No |
Upstash Redis (REST) | Vercel, cualquier serverless | Sí |
En Vercel, Fluid compute comparte una instancia entre invocaciones concurrentes
y escala instancias libremente, así que los contadores en proceso se fragmentan y se reinician.
No dan error — solo devuelven un número más pequeño que la verdad, que es la
peor forma en que un guardián de gasto puede fallar. Así que DAILY_BACKEND_CALL_BUDGET solo es
aplicable con un almacenamiento compartido, y el servidor lo dice al inicio y en
/metrics en lugar de fingir lo contrario.
vercel install upstash # injects UPSTASH_REDIS_REST_URL / _TOKENSe accede a Upstash a través de su API REST en lugar del protocolo wire de Redis:
las invocaciones de corta duración tienen la forma equivocada para un cliente con pooling, y
mantiene redis fuera de la lista de dependencias. Cada fallo de almacenamiento se registra y se
traga — una interrupción de contadores nunca debe convertirse en una interrupción de la búsqueda de vuelos.
Cableado de anuncios
Cableado como widget + middleware por separado en lugar de mediante la línea única
enable_lulu_ads, porque solo la ruta del middleware acepta is_error_result
— que es lo que mantiene los anuncios fuera de los resultados vacíos.
Los resultados se renderizan a través del widget de resultado de Lulu, no de su tarjeta patrocinada. La tarjeta patrocinada pinta el anuncio pero no contiene beacon de impresión renderizada; el beacon vive solo en la franja fija SPONSORED del widget de resultado. Servir la tarjeta patrocinada sola genera ingresos por clic y exactamente $0 CPM, sin que nada en ningún lugar reporte un problema. La tarjeta patrocinada se mantiene como respaldo para cuando falla el registro del widget de resultado, y ese respaldo se registra en voz alta.
El beacon es un <img> de 1×1 a ads.getlulu.dev. Los hosts de MCP Apps aplican una
CSP predeterminada de img-src 'self' data:, así que el dominio tiene que declararse en el
recurso del widget — vía app= para MCP Apps y openai/widgetCSP para ChatGPT.
Sin declarar, se ve idéntico a una integración que funciona desde fuera: la tarjeta
se renderiza, la franja se muestra, sin impresión, sin error.
Regístrate una vez, luego establece 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":"..."}'La clave de API se devuelve exactamente una vez.
El campo facturable es imp_url (snake_case) en la respuesta de /slot. Las
tipificaciones de TypeScript lo llaman impUrl, pero el SDK de Python permite ciertas claves de respuesta
y lee solo imp_url (lulu_ads/client.py:210). Una vez que existan credenciales reales,
confirma que las respuestas reales de /slot realmente lo llevan — sin
él, el widget no tiene beacon que disparar y no hay impresión facturable,
por muy bien que funcione todo lo demás.
Pruebas
.venv/bin/python -m pytest tests -q # 141 testsEl backend y el servidor de anuncios están ambos simulados, así que la suite no necesita red ni
credenciales. tests/test_ads.py y tests/test_stores.py ejecutan servidores simulados reales
en lugar de burlar el SDK, así que ejercitan el formato wire real.
Desplegar en Vercel
api/index.py entrypoint, exports `app`
vercel.json maxDuration + no-store on /mcp
.python-version 3.13
requirements.txtVercel auto-descubre api/index.py y sirve toda la aplicación como una
función Fluid que recibe cada ruta, así que /mcp, /health y /metrics todos
aterrizan en el mismo proceso. No se necesitan reescrituras.
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 --prodCuatro cosas específicas de Vercel que este código ya maneja, cada una de las cuales es un fallo silencioso si te equivocas:
Lifespan. FastMCP inicializa su gestor de sesiones streamable-HTTP en un
evento de inicio de lifespan ASGI. Sin él, cada solicitud /mcp falla con
Task group is not initialized — una interrupción total del endpoint MCP.
El soporte de lifespan de Vercel (publicado el 2025-12-09) se anuncia para "aplicaciones
FastAPI"; si cubre una aplicación Starlette desnuda no se indica. Así que
api/index.py envuelve la aplicación FastMCP en FastAPI y entrega
lifespan=_mcp_app.lifespan, que también es la integración que FastMCP
documenta. No "simplifiques" esto.
Sin estado. stateless_http=True. Las invocaciones son de corta duración y no se
garantiza que lleguen a la misma instancia, así que no hay dónde mantener una sesión.
Sistema de archivos. Solo lectura fuera de un /tmp efímero. Deja LOG_PATH
vacío; las líneas MCP_CALL de stdout son el registro.
Contadores. Configura Upstash, o el guardián de gasto es decorativo — ver más arriba.
Otros límites que importan aquí: maxDuration por defecto es 300s en cada plan
bajo Fluid (las llamadas de backend alcanzan un pico de alrededor de 90s, así que esto encaja);
las respuestas se transmiten en streaming en lugar de almacenarse en búfer, porque json_response=True re-adquiriría
el límite de cuerpo de 4.5 MB que un fan-out de 15 vías puede acercarse; y el pool
compartido de 1,024 descriptores de archivo es la razón por la que MAX_HTTP_CONNECTIONS existe.
La retención de registros es 1 hora en Hobby, 1 día en Pro. Si las líneas MCP_CALL
son tu evidencia de facturación para el POC, eso aboga por Pro más un drenaje de registros,
o por enviar los contadores a Upstash y leer /metrics.
Desplegar como contenedor
Dockerfile construye un servicio HTTP siempre activo, con la misma forma que
backend/Dockerfile (que se despliega en Render). Establece LOG_PATH y monta un
volumen en /app/logs para que la ventana móvil del guardián de gasto sobreviva a los despliegues.
De cualquier manera: después de desplegar, establece MCP_PUBLIC_URL a la URL real — sin
barra final — y confirma el dominio del widget en el registro de inicio.
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