Skip to main content
Glama

search_flights

Find and compare flight options by searching Bing Flights with parameters like dates, passengers, and cabin class.

Instructions

Search for flights on Bing Flights.

Args: origin: Origin airport code (e.g., "SEA") destination: Destination airport code (e.g., "ICN") departure_date: Departure date in YYYY-MM-DD format return_date: Return date in YYYY-MM-DD format (optional) adults: Number of adult passengers (default: 1) children: Number of child passengers (default: 0) infants: Number of infant passengers (default: 0) cabin_class: 0=Economy, 1=Premium Economy, 2=Business, 3=First (default: 0) max_results: Maximum number of results to return (default: 10) headless: Run browser in headless mode (default: True)

Returns: Dictionary containing flight search results with structure: - search_params: Search parameters used - results_count: Number of results returned - flights: List of flight options with pricing, airline, times, etc. - timestamp: When the search was performed

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
originYes
destinationYes
departure_dateYes
return_dateNo
adultsNo
childrenNo
infantsNo
cabin_classNo
max_resultsNo
headlessNo

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault

No arguments

Implementation Reference

  • The primary MCP tool handler for 'search_flights'. Decorated with @mcp.tool() for automatic registration and schema generation from type hints and docstring. Delegates to BingFlightsScraper.
    @mcp.tool()
    async def search_flights(
        origin: str,
        destination: str,
        departure_date: str,
        return_date: str | None = None,
        adults: int = 1,
        children: int = 0,
        infants: int = 0,
        cabin_class: int = 0,
        max_results: int = 10,
        headless: bool = True
    ) -> dict:
        """Search for flights on Bing Flights.
    
        Args:
            origin: Origin airport code (e.g., "SEA")
            destination: Destination airport code (e.g., "ICN")
            departure_date: Departure date in YYYY-MM-DD format
            return_date: Return date in YYYY-MM-DD format (optional)
            adults: Number of adult passengers (default: 1)
            children: Number of child passengers (default: 0)
            infants: Number of infant passengers (default: 0)
            cabin_class: 0=Economy, 1=Premium Economy, 2=Business,
                3=First (default: 0)
            max_results: Maximum number of results to return (default: 10)
            headless: Run browser in headless mode (default: True)
    
        Returns:
            Dictionary containing flight search results with structure:
            - search_params: Search parameters used
            - results_count: Number of results returned
            - flights: List of flight options with pricing, airline, times, etc.
            - timestamp: When the search was performed
        """
        scraper = BingFlightsScraper(headless=headless)
        try:
            results = await scraper.search_flights(
                origin=origin,
                destination=destination,
                departure_date=departure_date,
                return_date=return_date,
                adults=adults,
                children=children,
                infants=infants,
                cabin_class=cabin_class,
                max_results=max_results
            )
            return results
        finally:
            await scraper.close()
  • Core helper method in BingFlightsScraper class implementing the actual flight search logic: validation, URL construction, browser navigation with Playwright, scraping results, and response formatting.
    async def search_flights(
        self,
        origin: str,
        destination: str,
        departure_date: str,
        return_date: Optional[str] = None,
        adults: int = 1,
        children: int = 0,
        infants: int = 0,
        cabin_class: int = 0,
        max_results: int = 10
    ) -> dict:
        """Search for flights and return results.
    
        Args:
            origin: Airport code (e.g., "SEA")
            destination: Airport code (e.g., "ICN")
            departure_date: Departure date in YYYY-MM-DD format
            return_date: Return date in YYYY-MM-DD format (None for one-way)
            adults: Number of adult passengers (default: 1)
            children: Number of child passengers (default: 0)
            infants: Number of infant passengers (default: 0)
            cabin_class: 0=Economy, 1=Premium Economy, 2=Business,
                3=First (default: 0)
            max_results: Maximum number of results to return (default: 10)
    
        Returns:
            Dictionary with flight results
    
        Raises:
            ValueError: If parameters are invalid
            PlaywrightTimeoutError: If page doesn't load within timeout
        """
        # Validate inputs
        if not origin or not destination:
            raise ValueError("Origin and destination are required")
        if not departure_date:
            raise ValueError("Departure date is required")
        if cabin_class not in self.CABIN_CLASSES:
            raise ValueError(f"Cabin class must be 0-3, got {cabin_class}")
        if adults < 1:
            raise ValueError("At least 1 adult passenger is required")
        if max_results < 1:
            raise ValueError("max_results must be at least 1")
    
        # Build URL
        url = self._build_url(
            origin=origin.upper(),
            destination=destination.upper(),
            departure_date=departure_date,
            return_date=return_date,
            adults=adults,
            children=children,
            infants=infants,
            cabin_class=cabin_class
        )
    
        # Ensure browser is started
        if not self.context:
            await self._start()
    
        # Create page and navigate
        page = await self.context.new_page()
        try:
            await page.goto(url, timeout=60000, wait_until='networkidle')
            
            # Store the URL for use in flight data
            search_url = url
    
            # Wait for flight results to load - Bing uses .itrCard class
            try:
                await page.wait_for_selector('.itrCard', timeout=30000)
            except PlaywrightTimeoutError:
                # If no results, still try to scrape
                await page.wait_for_timeout(5000)
    
            # Scrape results
            flights = await self._scrape_flight_results(
                page, max_results, search_url
            )
    
            # Build response
            trip_type = "round-trip" if return_date else "one-way"
            return {
                "search_params": {
                    "origin": origin.upper(),
                    "destination": destination.upper(),
                    "departure_date": departure_date,
                    "return_date": return_date,
                    "trip_type": trip_type,
                    "passengers": {
                        "adults": adults,
                        "children": children,
                        "infants": infants
                    },
                    "cabin_class": self.CABIN_CLASSES[cabin_class]
                },
                "results_count": len(flights),
                "flights": flights,
                "timestamp": datetime.utcnow().isoformat() + "Z"
            }
        finally:
            await page.close()
  • Helper function to construct the Bing Flights search URL from input parameters.
    def _build_url(
        self,
        origin: str,
        destination: str,
        departure_date: str,
        return_date: Optional[str],
        adults: int,
        children: int,
        infants: int,
        cabin_class: int
    ) -> str:
        """Construct Bing Flights URL from parameters.
    
        Args:
            origin: Origin airport code
            destination: Destination airport code
            departure_date: Departure date (YYYY-MM-DD)
            return_date: Return date (YYYY-MM-DD) or None
            adults: Number of adults
            children: Number of children
            infants: Number of infants
            cabin_class: Cabin class (0-3)
    
        Returns:
            Complete Bing Flights search URL
        """
        # Determine if round-trip
        is_roundtrip = return_date is not None
        isr = 1 if is_roundtrip else 0
    
        # Use return_date if provided, otherwise use departure_date
        rdate = return_date if return_date else departure_date
    
        params = {
            'q': f'flights from {origin}-{destination}',
            'src': origin,
            'des': destination,
            'ddate': departure_date,
            'isr': isr,
            'rdate': rdate,
            'cls': cabin_class,
            'adult': adults,
            'child': children,
            'infant': infants,
            'form': 'FLAFLI' if not is_roundtrip else 'UNKHUB',
            'entrypoint': 'UNKHUB'
        }
    
        base_url = 'https://www.bing.com/travel/flight-search'
        return f"{base_url}?{urlencode(params)}"
  • Helper function to parse individual flight cards from the scraped page, extracting price, airlines, times, stops, etc.
    async def _parse_flight_card(
        self,
        element,
        index: int,
        search_url: str
    ) -> Optional[dict]:
        """Parse individual flight result card.
    
        Args:
            element: Playwright element locator for flight card
            index: Result index (1-indexed)
            search_url: The search URL to use as booking link
    
        Returns:
            Dictionary with flight details or None if parsing fails
        """
        try:
            # Extract price - look for .itrPriceVal
            price_text = None
            price_elem = await element.query_selector('.itrPriceVal')
            if price_elem:
                price_text = await price_elem.inner_text()
                price_text = price_text.strip()
    
            # Extract airline - look for .airlinePair
            airlines = []
            airline_pair = await element.query_selector('.airlinePair')
            if airline_pair:
                airline_elem = await airline_pair.query_selector(
                    '.bt_focusText'
                )
                if airline_elem:
                    airline_text = await airline_elem.inner_text()
                    airline_text = airline_text.strip()
                    if airline_text:
                        airlines = [airline_text]
    
            # Extract times and duration from .durationPair
            departure_time = None
            arrival_time = None
            duration = None
            
            duration_pair = await element.query_selector('.durationPair')
            if duration_pair:
                # Get time range (e.g., "11:00 AM - 3:55 PM")
                time_elem = await duration_pair.query_selector('.bt_focusText')
                if time_elem:
                    time_text = await time_elem.inner_text()
                    time_text = time_text.strip()
                    # Parse departure and arrival times
                    if ' - ' in time_text:
                        parts = time_text.split(' - ')
                        departure_time = parts[0].strip()
                        # Remove +1D suffix if present
                        arrival_time = parts[1].replace('+1D', '').strip()
                
                # Get duration from the span (e.g., "11h 55m")
                duration_spans = await duration_pair.query_selector_all('span')
                for span in duration_spans:
                    text = await span.inner_text()
                    text = text.strip()
                    if 'h' in text and 'm' in text:
                        duration = text
                        break
    
            # Extract stops info from .stopsPair
            stops = 0
            stops_pair = await element.query_selector('.stopsPair')
            if stops_pair:
                stops_elem = await stops_pair.query_selector('.bt_focusText')
                if stops_elem:
                    stops_text = await stops_elem.inner_text()
                    stops_text = stops_text.strip().lower()
                    if 'non-stop' in stops_text or 'nonstop' in stops_text:
                        stops = 0
                    elif '1 stop' in stops_text:
                        stops = 1
                    elif '2 stop' in stops_text:
                        stops = 2
    
            # Use the search URL as the booking link
            booking_link = search_url
    
            # Parse price
            price_value = None
            currency = "USD"
            if price_text:
                # Extract numeric value from price text
                import re
                cleaned_price = price_text.replace(',', '')
                price_match = re.search(r'[\d,]+\.?\d*', cleaned_price)
                if price_match:
                    price_value = float(price_match.group())
                # Try to detect currency
                if '$' in price_text:
                    currency = "USD"
                elif '€' in price_text:
                    currency = "EUR"
                elif '£' in price_text:
                    currency = "GBP"
    
            # Build flight data
            flight_data = {
                "price": {
                    "total": price_value,
                    "currency": currency,
                    "per_person": price_value
                },
                "airlines": airlines if airlines else ["Unknown"],
                "outbound": {
                    "departure_time": departure_time,
                    "arrival_time": arrival_time,
                    "duration": duration,
                    "stops": stops,
                    "layovers": [],
                    "flight_numbers": []
                },
                "booking_link": booking_link,
                "result_index": index
            }
    
            return flight_data
    
        except Exception as e:
            print(f"Error parsing flight card: {e}")
            return None
  • mcp_server.py:8-8 (registration)
    The @mcp.tool() decorator registers the search_flights function as an MCP tool.
    @mcp.tool()
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden of behavioral disclosure. It mentions that the tool runs a browser (via 'headless' parameter) but doesn't disclose important behavioral traits like rate limits, authentication requirements, potential costs, error conditions, or whether this is a read-only operation. The description is insufficient for a mutation/read classification.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is well-structured with clear sections (Args, Returns) and efficiently explains 10 parameters and return structure. While comprehensive, it maintains appropriate density without wasted words. The only minor improvement would be front-loading the core purpose more prominently.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the complexity (10 parameters, no annotations, but has output schema), the description is reasonably complete. It thoroughly documents all parameters and return structure. However, it lacks important contextual information about behavioral constraints, error handling, and usage boundaries that would be needed for optimal agent operation.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The description provides excellent parameter semantics that fully compensate for the 0% schema description coverage. Each parameter is clearly explained with examples (e.g., 'SEA' for origin), formats ('YYYY-MM-DD'), defaults, and meaningful mappings (cabin_class codes to class names). This adds substantial value beyond the bare schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's purpose: 'Search for flights on Bing Flights.' It specifies the exact action (search) and resource (flights), and distinguishes it from the only sibling tool (get_scraper_status) by focusing on flight search rather than scraper status checking.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides no guidance on when to use this tool versus alternatives. While it mentions Bing Flights as the source, there's no context about when this search method is preferred over other flight search tools or APIs, nor any prerequisites or limitations for usage.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

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/chonseng/bing-flights-mcp'

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