Skip to content

MCP Server Guide

This project exposes flight search tools via a FastMCP server. You can run it over STDIO (default) or the streamable HTTP transport.

Installation

# Install with pipx (recommended)
pipx install flights

# Or with pip
pip install flights

Running the Server

Run over STDIO

Use the console script for Claude Desktop and other MCP clients:

fli-mcp

Run over HTTP (streamable)

Use the HTTP entrypoint for web-based integrations. By default it binds to 127.0.0.1:8000.

fli-mcp-http

You can override host/port by calling the function directly in Python:

from fli.mcp import run_http

run_http(host="0.0.0.0", port=8000)

Once running, the MCP endpoint is served at /mcp/, for example: http://127.0.0.1:8000/mcp/.

A liveness probe is available at /health (returns {"status": "ok"}). It is what the docker-compose.yml healthcheck calls, and it does not contact Google Flights, so an upstream outage will not cause a healthy container to be restarted.

Claude Desktop Configuration

Add this configuration to your claude_desktop_config.json:

Location: ~/Library/Application Support/Claude/claude_desktop_config.json (macOS)

{
  "mcpServers": {
    "fli": {
      "command": "fli-mcp"
    }
  }
}

Tip: Run which fli-mcp to find the full path if needed.

Available Tools

search_flights

Search for flights between two airports on a specific date.

Parameters:

Parameter Type Required Default Description
origin string Yes - Departure airport IATA code (e.g., 'JFK')
destination string Yes - Arrival airport IATA code (e.g., 'LHR')
departure_date string Yes - Travel date in YYYY-MM-DD format
return_date string No null Return date for round trips
cabin_class string No ECONOMY ECONOMY, PREMIUM_ECONOMY, BUSINESS, or FIRST
max_stops string No ANY ANY, NON_STOP, ONE_STOP, or TWO_PLUS_STOPS
departure_window string No null Time window in 'HH-HH' format (e.g., '6-20')
airlines list No null Filter by airline codes (e.g., ['BA', 'AA'])
exclude_airlines list No null Airline IATA codes to exclude from results
alliance list No null Restrict to ONEWORLD / SKYTEAM / STAR_ALLIANCE
exclude_alliance list No null Alliance(s) to exclude from results
min_layover int No null Minimum layover duration (minutes)
max_layover int No null Maximum layover duration (minutes)
top_n int No 5 Round-trip only: outbound options expanded into return-flight combinations (1-10)
currency string No null ISO 4217 code (curr=) — e.g. 'EUR', 'JPY'
language string No null BCP-47 language code (hl=) — e.g. 'en-GB'
country string No null ISO 3166-1 alpha-2 code (gl=) — e.g. 'GB'
sort_by string No CHEAPEST CHEAPEST, DURATION, DEPARTURE_TIME, or ARRIVAL_TIME
passengers int No 1 Number of adult passengers
children int No 0 Number of children (ages 2-11)
infants_in_seat int No 0 Number of infants (under 2) occupying their own seat
infants_on_lap int No 0 Number of lap infants (under 2, no seat)

Passenger limits: total travelers (passengers + children + infants_in_seat + infants_on_lap) must be between 1 and 9, and infants_on_lap cannot exceed passengers — Google Flights' own booking limits. An invalid mix returns success: false with the specific problem (e.g. "Total passengers must be between 1 and 9 (got 10: ...)"), not a generic error.

top_n cost model: a round trip costs 1 + top_n page fetches (one outbound search, plus one per outbound candidate expanded into return flights) — top_n is capped at 10 for that reason. Round-trip results all from one airline? Raise top_n, or sort_by differently: the default sort only ever expands the cheapest top_n outbounds, which are often the same carrier. top_n is ignored for one-way searches.

Example Response:

{
  "success": true,
  "flights": [
    {
      "price": 450.00,
      "currency": "USD",
      "legs": [
        {
          "departure_airport": "JFK",
          "arrival_airport": "LHR",
          "departure_time": "2026-03-15T18:00:00",
          "arrival_time": "2026-03-16T06:30:00",
          "duration": 450,
          "airline": "BA",
          "flight_number": "178"
        }
      ],
      "booking_url": "https://www.google.com/travel/flights/booking?tfs=CBwQAh..."
    }
  ],
  "count": 5,
  "trip_type": "ONE_WAY",
  "booking_url": "https://www.google.com/travel/flights?q=Flights%20from%20JFK%20to%20LHR%20on%202026-03-15"
}

Each flight in flights[] carries a booking_url that deep-links directly to that specific flight's booking page on Google Flights (pre-loaded itinerary, no search step required). It carries the search's cabin class and passenger mix too, so it opens priced for the same travelers as the search results. The top-level booking_url is a broader search-page link (route + date pre-filled) and is a reliable fallback. To retrieve per-vendor prices and airline-direct booking links, pass the flight's flight_number (e.g. BA178) to get_booking_options.

search_dates

Find the cheapest travel dates between two airports within a date range.

Parameters:

Parameter Type Required Default Description
origin string Yes - Departure airport IATA code (e.g., 'JFK')
destination string Yes - Arrival airport IATA code (e.g., 'LHR')
start_date string Yes - Start of date range in YYYY-MM-DD format
end_date string Yes - End of date range in YYYY-MM-DD format
trip_duration int No 3 Trip duration in days (for round-trips)
is_round_trip bool No false Search for round-trip flights
cabin_class string No ECONOMY ECONOMY, PREMIUM_ECONOMY, BUSINESS, or FIRST
max_stops string No ANY ANY, NON_STOP, ONE_STOP, or TWO_PLUS_STOPS
departure_window string No null Time window in 'HH-HH' format (e.g., '6-20')
airlines list No null Filter by airline codes (e.g., ['BA', 'AA'])
exclude_airlines list No null Airline IATA codes to exclude
alliance list No null Restrict to ONEWORLD / SKYTEAM / STAR_ALLIANCE
exclude_alliance list No null Alliance(s) to exclude
min_layover int No null Minimum layover duration (minutes)
max_layover int No null Maximum layover duration (minutes)
currency string No null ISO 4217 currency code (curr=)
language string No null BCP-47 language code (hl=)
country string No null ISO 3166-1 alpha-2 country (gl=)
sort_by_price bool No false Sort results by price (lowest first)
passengers int No 1 Number of adult passengers
children int No 0 Number of children (ages 2-11)
infants_in_seat int No 0 Number of infants (under 2) occupying their own seat
infants_on_lap int No 0 Number of lap infants (under 2, no seat)

Passenger limits: same as search_flights — total travelers 1-9, and infants_on_lap cannot exceed passengers.

Example Response:

{
  "success": true,
  "dates": [
    {
      "date": "2026-03-15",
      "price": 350.00,
      "currency": "USD",
      "return_date": null,
      "booking_url": "https://www.google.com/travel/flights?q=Flights%20from%20JFK%20to%20LHR%20on%202026-03-15"
    },
    {
      "date": "2026-03-18",
      "price": 375.00,
      "currency": "USD",
      "return_date": null,
      "booking_url": "https://www.google.com/travel/flights?q=Flights%20from%20JFK%20to%20LHR%20on%202026-03-18"
    }
  ],
  "count": 30,
  "trip_type": "ONE_WAY",
  "date_range": "2026-03-01 to 2026-03-31"
}

Each date result carries a booking_url deep-linking to Google Flights for that specific date (and return date for round trips).

search_explore

Discover where you can fly cheaply when the destination is flexible — powered by Google Flights Explore (GetExploreDestinations). One call returns dozens of destinations with their cheapest fares for an origin and a broad destination like ANYWHERE or a continent.

Parameters:

Parameter Type Required Default Description
origin string Yes - Airport IATA code (e.g., 'JFK') or a city knowledge-graph mid (e.g., '/m/04jpl' for London)
departure_date string Yes - Departure date in YYYY-MM-DD format
destination string No ANYWHERE ANYWHERE, EUROPE, SOUTHERN_EUROPE, ASIA, AFRICA, NORTH_AMERICA, SOUTH_AMERICA, OCEANIA, or a region or country knowledge-graph mid (e.g., '/m/07ssc' for the UK). Airports and cities are not supported
round_trip bool No false Price round trips instead of one-ways. Needs a fixed trip length
trip_min_nights int No null Trip length in nights (round trips: 1-23, one fixed value)
trip_max_nights int No null Must equal trip_min_nights for round trips
max_price int No null Maximum fare cap
cabin_class string No ECONOMY ECONOMY, PREMIUM_ECONOMY, BUSINESS, or FIRST
max_stops string No ANY ANY, NON_STOP, ONE_STOP, or TWO_PLUS_STOPS
airlines list No null Filter by airline codes (e.g., ['BA', 'AA'])
exclude_airlines list No null Airline IATA codes to exclude
alliance list No null Restrict to ONEWORLD / SKYTEAM / STAR_ALLIANCE
exclude_alliance list No null Alliance(s) to exclude
max_flight_duration int No null Maximum flight duration in minutes
passengers int No 1 Number of adult passengers
currency string No null ISO 4217 currency code (curr=)
language string No null BCP-47 language code (hl=)
country string No null ISO 3166-1 alpha-2 country (gl=)
sort_by_price bool No true Sort destinations by price (lowest first)
limit int No null Maximum number of destinations to return

Example Response:

{
  "success": true,
  "origin": "LHR",
  "origin_name": "London",
  "destination": "EUROPE",
  "region_name": "Europe",
  "departure_date": "2026-09-10",
  "trip_type": "ONE_WAY",
  "count": 77,
  "priced_count": 55,
  "destinations": [
    {
      "name": "Edinburgh",
      "country": "United Kingdom",
      "mid": "/m/02m77",
      "price": 21.0,
      "currency": "GBP",
      "departure_date": "2026-09-10",
      "arrival_date": "2026-09-10",
      "airline": "RK",
      "airline_name": "Ryanair UK",
      "stops": 0,
      "duration_minutes": 80,
      "destination_airport": "EDI",
      "latitude": 55.953252,
      "longitude": -3.188267,
      "image_url": "https://encrypted-tbn3.gstatic.com/images?q=...",
      "flights_url": "https://www.google.com/travel/flights?q=Flights%20from%20LHR%20to%20EDI%20on%202026-09-10"
    }
  ]
}

Some destinations come back without a price (price: null) — Google found no itinerary matching the filters for them. Follow up with search_flights using a result's destination_airport and departure_date for bookable itineraries; each priced destination also carries a ready-made flights_url deep link.

Explore still uses the GetExploreDestinations RPC. Google refuses a share of these requests with error 13 (about 40% in live checks on 2026-11-11, in clusters); the search retries it a few times with a short backoff, and if every attempt is refused it returns error_type: "rejected_error" with retryable: true, so one more try after a few seconds is worthwhile. The public /travel/explore page currently embeds configuration, but no destination results to use as a fallback. Other failures return success: false with error_type and retryable, following the other search tools. Destinations without a fare remain successful results with price: null.

Round trips need a fixed trip length (trip_min_nights, and trip_max_nights if given, set to the same value). For a range of nights Google quotes one-way prices for many destinations, so the tool rejects it. With the length fixed, flights_url includes the return date.

Destinations must be a region, continent or country. Google rejects an airport (error 3) and returns nothing for a city, so the tool refuses an IATA code and points to search_flights instead.

get_booking_options

Get bookable fares — vendor names, prices, and direct booking URLs — for a single itinerary. The tool runs a fresh search, selects the flight identified by flight_numbers (or the top result when omitted), and returns the airline-direct and online-travel-agency options Google surfaces for it.

Use search_flights first to discover flight numbers, then call this tool to find out where (and at what price) a specific flight can be booked.

Parameters:

Parameter Type Required Default Description
origin string Yes - Departure airport IATA code (e.g., 'JFK')
destination string Yes - Arrival airport IATA code (e.g., 'LHR')
departure_date string Yes - Travel date in YYYY-MM-DD format
flight_numbers list No null Ordered flight numbers identifying the itinerary (e.g. ['BA178'], or ['AA100', 'AA200'] round-trip). Bare ('178') or airline-prefixed ('BA178'). Omit to price the top result.
return_date string No null Return date for round trips
cabin_class string No ECONOMY ECONOMY, PREMIUM_ECONOMY, BUSINESS, or FIRST
max_stops string No ANY ANY, NON_STOP, ONE_STOP, or TWO_PLUS_STOPS
passengers int No 1 Number of adult passengers
children int No 0 Number of children (ages 2-11)
infants_in_seat int No 0 Number of infants (under 2) occupying their own seat
infants_on_lap int No 0 Number of lap infants (under 2, no seat) — cannot exceed passengers
airlines list No null Filter by airline codes (e.g., ['BA', 'AA'])
exclude_basic_economy bool No false Exclude basic economy fares. Currently ignored by the search transport (logged as a warning).
departure_window string No null Time window in 'HH-HH' format (e.g., '6-20')
sort_by string No CHEAPEST Sort order — matters when flight_numbers is omitted
exclude_airlines list No null Airline IATA codes to exclude
alliance / exclude_alliance list No null Restrict / exclude ONEWORLD, SKYTEAM, STAR_ALLIANCE
min_layover / max_layover int No null Layover duration bounds (minutes)
top_n int No 5 Round-trip only: outbound options the re-run search expands into return-flight combinations (1-10)
emissions string No ALL ALL or LESS. Currently ignored by the search transport (logged as a warning).
checked_bags int No 0 Checked bags included in price (0–2). Currently ignored by the search transport (logged as a warning).
carry_on bool No false Include carry-on bag fee in price. Currently ignored by the search transport (logged as a warning).
currency string No null ISO 4217 currency code (curr=)
language string No null BCP-47 language code (hl=)
country string No null ISO 3166-1 alpha-2 country (gl=)

Tip: Pass the same filters you used for search_flights — including top_n — so the re-run search reproduces the same result set. Otherwise — especially when flight_numbers is omitted — the priced "top result" may differ from the one the user saw, and a flight only visible at a higher top_n may not be found at all.

Example Response:

{
  "success": true,
  "selected_flight": {
    "price": 450.00,
    "currency": "USD",
    "legs": [{ "airline": "BA", "flight_number": "178", "...": "..." }],
    "booking_url": "https://www.google.com/travel/flights/booking?tfs=CBwQAh..."
  },
  "options": [
    {
      "vendor_name": "British Airways",
      "vendor_code": "BA",
      "is_airline_direct": true,
      "price": 450.00,
      "currency": "USD",
      "booking_url": "https://www.britishairways.com/...",
      "google_click_url": "https://www.google.com/..."
    }
  ],
  "count": 1,
  "booking_url": "https://www.google.com/travel/flights?q=Flights%20from%20JFK%20to%20LHR%20on%202026-03-15"
}

selected_flight.booking_url is a deep link that opens the specific itinerary's booking page directly on Google Flights (the tfs protobuf URL, no search step required). The top-level booking_url is a broader search-page link.

When no flight matches flight_numbers, the response has success: false and an available_flights list of the flight-number sequences that were found, so you can retry with a valid identifier.

Vendor fares may be empty

Google's booking endpoint often returns no per-vendor fares without a browser-minted session token. When that happens options is [] and the response carries a note — use selected_flight.booking_url to open the specific flight's booking page directly, or fall back to the top-level booking_url for the search page.

Error Responses

Every tool's failure response (success: false) also carries error_type (a stable string) and retryable (a bool), alongside the existing free-text error message — so a caller can decide what to do next without string-matching error. http_error responses additionally carry http_status when the upstream status code is known. Classification is by exception type, never by message text, so error_type will not change out from under you even if wording on error does.

This vocabulary is shared with the CLI: fli flights ... --format json and fli dates ... --format json errors use the exact same error_type strings from the exact same classifier (fli.core.errors.classify_error), so an agent or script that handles both surfaces only needs to learn the vocabulary once.

error_type retryable Meaning / what to do
validation_error false Bad input — unknown airport code, an invalid passenger mix, a date range over the 93-date cap, etc. Fix the request; retrying unchanged will fail again.
unsupported_error false The request is well-formed but this transport can't serve it (e.g. multi-city). Change the request.
parse_error false Google served a page fli couldn't read. Usually (not always) a regional consent/blocked interstitial. Not retryable as-is; set FLI_SOCS_COOKIE (EU/EEA) and retry — don't just retry the same request unchanged.
rejected_error false (true for Explore's intermittent error 13) Google refused the RPC outright (e.g. get_booking_options's GetBookingResults call today). Deterministic; retrying the same request will not help, except when retryable is true, which only Explore's error 13 sets.
timeout true The request to Google Flights timed out. See retry guidance below.
certificate_error false TLS certificate verification failed, most often behind a TLS-intercepting corporate proxy. Deterministic; set FLI_CA_BUNDLE (or CURL_CA_BUNDLE / REQUESTS_CA_BUNDLE) to a CA bundle path and try again — retrying unchanged will not help.
connection_error true A network/DNS issue prevented reaching Google Flights. See retry guidance below.
http_error true iff http_status is 429 or 5xx Non-2xx HTTP response from Google; check http_status. See retry guidance below.
search_error false Any other typed search-client failure not covered above.
unexpected_error false An unclassified exception — most likely a bug, worth reporting.

Retry guidance for retryable: true

The client has already retried internally with backoff before raising (see the tenacity-based retry in fli/search/client.py). A caller that sees retryable: true should retry at most once or twice more, with its own exponential backoff measured in seconds (not milliseconds) between attempts — not hammer the endpoint immediately. http_status: 429 specifically is Google telling you to slow down — back off more, not less.

Example error response:

{
  "success": false,
  "error": "Search failed: Timed out talking to Google Flights (www.google.com). ...",
  "flights": [],
  "error_type": "timeout",
  "retryable": true
}

Available Prompts

The MCP server also provides prompt templates to help guide searches:

search-direct-flight

Generates a tool call to find direct flights between two airports.

Arguments: - origin - Departure airport IATA code (required) - destination - Arrival airport IATA code (required) - date - Departure date in YYYY-MM-DD format (optional) - prefer_non_stop - Set to true to prefer nonstop flights (optional)

find-budget-window

Suggests the cheapest travel dates for a route within a flexible window.

Arguments: - origin - Departure airport IATA code (required) - destination - Arrival airport IATA code (required) - start_date - Start of the travel window (optional) - end_date - End of the travel window (optional) - duration - Desired trip length in days (optional)

Configuration

The MCP server can be configured via environment variables:

Variable Description Default
FLI_MCP_DEFAULT_PASSENGERS Default number of adult passengers 1
FLI_MCP_DEFAULT_CURRENCY Currency code for results USD
FLI_MCP_DEFAULT_CABIN_CLASS Default cabin class ECONOMY
FLI_MCP_DEFAULT_SORT_BY Default sorting strategy CHEAPEST
FLI_MCP_DEFAULT_DEPARTURE_WINDOW Default departure window (HH-HH) null
FLI_MCP_MAX_RESULTS Maximum results returned null (no limit)

The underlying Google Flights HTTP client (shared with the CLI, not FLI_MCP_-prefixed) also honors these variables:

Variable Description Default
FLI_TIMEOUT Request timeout in seconds 60
FLI_SOCS_COOKIE Consent cookie sent to skip Google's EU/EEA interstitial; set empty to send none a pre-accepted value
FLI_CA_BUNDLE Path to a PEM CA bundle for networks with a custom certificate authority (e.g. a TLS-intercepting corporate proxy) unset
CURL_CA_BUNDLE Fallback CA bundle path, used when FLI_CA_BUNDLE is unset unset
REQUESTS_CA_BUNDLE Fallback CA bundle path, used when the two above are unset unset

If a search fails with error_type: "certificate_error", configure one of the CA bundle variables above instead of disabling TLS verification. These are read once per worker thread, the first time that thread makes a request — for a long-running MCP server process, restart it after changing one so already-created sessions pick up the new value.

Example Conversations

Once configured with Claude Desktop, you can have natural conversations:

User: "Find me flights from New York to London next month"

Claude: Uses search_flights with origin=JFK, destination=LHR

User: "What are the cheapest dates to fly to Tokyo from San Francisco in April?"

Claude: Uses search_dates with origin=SFO, destination=NRT, start_date and end_date in April

User: "Search for business class, non-stop flights from LAX to Paris on March 15th"

Claude: Uses search_flights with cabin_class=BUSINESS, max_stops=NON_STOP