Skip to main content
Glama

Google Flights MCP Server

by HaroldLeo
serpapi_client.py10.9 kB
""" SerpAPI client for Google Flights integration. This module provides a comprehensive interface to SerpAPI's Google Flights API, offering richer data than fast-flights including: - Flight numbers - Layover details (airport, duration, overnight) - Carbon emissions with comparison to typical - Price insights (historical data, price level) - Multi-seller booking options - Delay history - Amenities (Wi-Fi, power, entertainment) """ import os import logging from typing import Optional, Dict, Any, List from serpapi import GoogleSearch logger = logging.getLogger(__name__) class SerpAPIClient: """Client for interacting with SerpAPI Google Flights API.""" def __init__(self, api_key: Optional[str] = None): """ Initialize SerpAPI client. Args: api_key: SerpAPI API key. If not provided, reads from SERPAPI_API_KEY env var. """ self.api_key = api_key or os.getenv("SERPAPI_API_KEY") if not self.api_key: raise ValueError( "SerpAPI key required. Set SERPAPI_API_KEY environment variable or pass api_key parameter." ) def search_flights( self, departure_id: str, arrival_id: str, outbound_date: str, return_date: Optional[str] = None, adults: int = 1, travel_class: str = "economy", currency: str = "USD", max_stops: Optional[int] = None, departure_token: Optional[str] = None, booking_token: Optional[str] = None, ) -> Dict[str, Any]: """ Search for flights using SerpAPI. Args: departure_id: Origin airport code (e.g., "LAX") arrival_id: Destination airport code (e.g., "AUS") outbound_date: Departure date (YYYY-MM-DD) return_date: Return date for round-trip (YYYY-MM-DD) adults: Number of adult passengers travel_class: Cabin class (economy, premium_economy, business, first) currency: Currency code (USD, EUR, etc.) max_stops: Maximum number of stops (0 for direct) departure_token: Token to get return flights (from initial search) booking_token: Token to get booking options (from flight selection) Returns: Dictionary containing SerpAPI response with flights data """ params = { "engine": "google_flights", "api_key": self.api_key, "hl": "en", "currency": currency, } # If booking_token provided, get booking options if booking_token: params["booking_token"] = booking_token # If departure_token provided, get return flights elif departure_token: params["departure_id"] = departure_id params["arrival_id"] = arrival_id params["outbound_date"] = outbound_date if return_date: params["return_date"] = return_date params["departure_token"] = departure_token # Otherwise, initial search else: params["departure_id"] = departure_id params["arrival_id"] = arrival_id params["outbound_date"] = outbound_date if return_date: params["return_date"] = return_date params["type"] = 1 # Round-trip else: params["type"] = 2 # One-way if adults > 1: params["adults"] = adults # Map travel class class_mapping = { "economy": "1", "premium_economy": "2", "business": "3", "first": "4", } params["travel_class"] = class_mapping.get(travel_class.lower(), "1") if max_stops is not None: params["stops"] = max_stops try: search = GoogleSearch(params) results = search.get_dict() return results except Exception as e: logger.error(f"SerpAPI search failed: {e}") raise def parse_flight_results(self, results: Dict[str, Any]) -> List[Dict[str, Any]]: """ Parse SerpAPI flight results into standardized format. Args: results: Raw SerpAPI response Returns: List of parsed flight dictionaries """ flights = [] # Get best_flights and other_flights best_flights = results.get("best_flights", []) other_flights = results.get("other_flights", []) all_flights = best_flights + other_flights for idx, flight in enumerate(all_flights): is_best = idx < len(best_flights) parsed_flight = { "is_best": is_best, "price": flight.get("price"), "type": flight.get("type"), "total_duration": flight.get("total_duration"), "departure_token": flight.get("departure_token"), "booking_token": flight.get("booking_token"), } # Parse carbon emissions carbon = flight.get("carbon_emissions", {}) if carbon: parsed_flight["carbon_emissions"] = { "this_flight_grams": carbon.get("this_flight"), "typical_for_route_grams": carbon.get("typical_for_this_route"), "difference_percent": carbon.get("difference_percent"), } # Parse flight segments segments = [] layovers = flight.get("layovers", []) for segment in flight.get("flights", []): dep_airport = segment.get("departure_airport", {}) arr_airport = segment.get("arrival_airport", {}) parsed_segment = { "flight_number": segment.get("flight_number"), "airline": segment.get("airline"), "airplane": segment.get("airplane"), "departure_airport": { "code": dep_airport.get("id"), "name": dep_airport.get("name"), "time": dep_airport.get("time"), }, "arrival_airport": { "code": arr_airport.get("id"), "name": arr_airport.get("name"), "time": arr_airport.get("time"), }, "duration": segment.get("duration"), "legroom": segment.get("legroom"), "travel_class": segment.get("travel_class"), "overnight": segment.get("overnight", False), "often_delayed": segment.get("often_delayed_by_over_30_min", False), "extensions": segment.get("extensions", []), } segments.append(parsed_segment) parsed_flight["segments"] = segments # Parse layovers parsed_layovers = [] for layover in layovers: parsed_layovers.append({ "airport_code": layover.get("id"), "airport_name": layover.get("name"), "duration": layover.get("duration"), "overnight": layover.get("overnight", False), }) parsed_flight["layovers"] = parsed_layovers # Determine airlines airlines = list(set(seg.get("airline") for seg in flight.get("flights", []) if seg.get("airline"))) parsed_flight["airlines"] = ", ".join(airlines) if airlines else None flights.append(parsed_flight) return flights def get_price_insights(self, results: Dict[str, Any]) -> Optional[Dict[str, Any]]: """ Extract price insights from SerpAPI results. Args: results: Raw SerpAPI response Returns: Price insights dictionary or None """ insights = results.get("price_insights") if not insights: return None return { "lowest_price": insights.get("lowest_price"), "price_level": insights.get("price_level"), "typical_price_range": insights.get("typical_price_range"), "price_history": insights.get("price_history"), } def get_booking_options(self, results: Dict[str, Any]) -> List[Dict[str, Any]]: """ Extract booking options from SerpAPI results. Args: results: Raw SerpAPI response with booking_token used Returns: List of booking options with seller comparison """ booking_options = [] for option in results.get("booking_options", []): parsed_option = { "separate_tickets": option.get("separate_tickets", False), } # Parse "together" booking (both legs from same seller) if "together" in option: together = option["together"] parsed_option["together"] = { "book_with": together.get("book_with"), "is_airline": together.get("airline", False), "price": together.get("price"), "marketed_as": together.get("marketed_as", []), "baggage_prices": together.get("baggage_prices", []), } # Parse separate departing/returning bookings if "departing" in option: departing = option["departing"] parsed_option["departing"] = { "book_with": departing.get("book_with"), "price": departing.get("price"), "baggage_prices": departing.get("baggage_prices", []), } if "returning" in option: returning = option["returning"] parsed_option["returning"] = { "book_with": returning.get("book_with"), "price": returning.get("price"), "baggage_prices": returning.get("baggage_prices", []), } booking_options.append(parsed_option) # Also get overall baggage prices baggage = results.get("baggage_prices", {}) if baggage: return { "options": booking_options, "baggage_policies": { "departing": baggage.get("departing", []), "returning": baggage.get("returning", []), "together": baggage.get("together", []), } } return {"options": booking_options} def is_serpapi_available() -> bool: """Check if SerpAPI key is configured.""" return bool(os.getenv("SERPAPI_API_KEY"))

Latest Blog Posts

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/HaroldLeo/google-flights-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server