Skip to main content
Glama

search_flights

Search for flights using the Aviasales API to find available options based on travel segments, passenger count, class, and currency. Retrieve search results for further filtering and booking.

Instructions

Search for flights using the Aviasales Flight Search API. This tool performs search based on the provided flight segments, number of passengers, trip class, currency, and locale. It provides search_id and description of search results and saves found options internally.After receiving the result client can use get_flight_options tool to retrieve the found options with more granular filters.IMPORTANT: All times are local to departure/arrival locations and use HH:MM 24-hour format.IMPORTANT: Call this tool as many times as needed to find the best flight options.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
requestYes

Implementation Reference

  • The main asynchronous handler function implementing the search_flights tool logic. It prepares the request body, generates the API signature, initiates the search via POST, polls the results endpoint every 5 seconds for up to 90 seconds, aggregates and parses flight proposals, caches them, reports progress, and returns a summary description of the results.
    async def search_flights(
        request: SearchRequestModel,
        ctx: Context
    ) -> Dict[str, Any]:
        """Search for flights using Travelpayouts Flight Search API."""
        
        request_body = request.model_dump()
        request_body["token"] = API_TOKEN
        request_body["marker"] = MARKER
        request_body["passengers"] = {
            "adults": request.adults,
            "children": request.children,
            "infants": request.infants
        }
        del request_body["adults"]
        del request_body["children"]
        del request_body["infants"]
    
        signature = _generate_signature(API_TOKEN, request_body)
        request_body["signature"] = signature
    
        async with httpx.AsyncClient(timeout=40) as client:
            init_resp = await client.post(SEARCH_URL, json=request_body)
            if init_resp.status_code != 200:
                raise ToolError(f"Aviasales API returned non-200 status code: {init_resp.status_code}, raw text: {init_resp.text}")
            
            init_data = init_resp.json()
            search_id = init_data["search_id"]
            set_currency_rates(init_data["currency_rates"])
    
            deadline = datetime.now() + timedelta(seconds=90)  
            batch_proposals = None
    
            while datetime.now() < deadline:
                await asyncio.sleep(5)
                res_r = await client.get(f"{RESULTS_URL}?uuid={search_id}")
                res_r.raise_for_status()
                res_json = res_r.json()
    
                # Defensive: ensure we got a *list* per the API spec.
                if not isinstance(res_json, list):
                    raise ToolError("Unexpected response format: expected a list of results")
    
                # Aggregate proposals from every object that contains them.
                for obj in res_json:
                    if isinstance(obj, dict) and obj.get("proposals"):
                        try:
                            if not batch_proposals:
                                batch_proposals = parse_proposals_batch(obj)
                            else:
                                batch_proposals = batch_proposals.combine_with(parse_proposals_batch(obj))
                        except Exception as e:
                            print(f"Error parsing proposals: \n {json.dumps(obj, indent=2)}", file=sys.stderr)
                            raise
    
                        ctx.report_progress(progress=len(batch_proposals.proposals), total=None, message=f"Found {len(batch_proposals.proposals)} options so far...")
                    if set(obj.keys()) == {"search_id"}:
                        search_results_cache[search_id] = batch_proposals
                        return batch_proposals.get_description()
    
            search_results_cache[search_id] = batch_proposals
            return batch_proposals.get_description() if batch_proposals else "No proposals found until the search timed out."
  • Pydantic models defining the input schema for the search_flights tool, including segment details and overall search parameters like passengers, class, currency, and locale.
    class SearchRequestSegmentModel(BaseModel):
        origin: str = Field(..., description='Origin IATA (this can be airport IATA or in case city has multiple airports better to use city IATA). The IATA code is shown in uppercase letters LON or MOW')
        destination: str = Field(..., description='Destination IATA (this can be airport IATA or in case city has multiple airports better to use city IATA). The IATA code is shown in uppercase letters LON or MOW')
        date: str = Field(..., description="Departure date in YYYY-MM-DD format")
    
    class SearchRequestModel(BaseModel):
        """Search request model for Travelpayouts Flight Search API."""
        segments: List[SearchRequestSegmentModel] = Field(..., description='''List of CONNECTED flight segments for the same journey. Each segment represents one leg of a multi-city trip or round trip.
            IMPORTANT: Do NOT use multiple segments for alternative dates of the same route. For flexible dates, perform separate searches.
            
            Examples:
            - One way: [{'origin': 'SFO', 'destination': 'LAX', 'date': '2023-10-01'}]
            - Round trip: [{'origin': 'SFO', 'destination': 'LAX', 'date': '2023-10-01'}, {'origin': 'LAX', 'destination': 'SFO', 'date': '2023-10-15'}]
            - Multi-city: [{'origin': 'SFO', 'destination': 'LAX', 'date': '2023-10-01'}, {'origin': 'LAX', 'destination': 'JFK', 'date': '2023-10-05'}]
            
            For alternative dates (e.g., 'July 13 OR July 14'), use separate calls of this tool.''')
        adults: int = Field(1, ge=1, le=9, description="Number of adult passengers (12 years old and older)")
        children: int = Field(0, ge=0, le=6, description="Number of children (2-11 years old)")
        infants: int = Field(0, ge=0, le=6, description="Number of infants (under 2 years old)")
        trip_class: str = Field("Y", description="Trip class - single letter: Y for economy, C for business. Default is Y (economy class)")
        currency: str = Field("USD", description="Currency code (default is USD)")
        locale: str = Field("en", description="Locale for the response (default is en). These are the supported locales: en-us, en-gb, ru, de, es, fr, pl")
  • The @mcp.tool decorator that registers the search_flights function as an MCP tool, including its detailed description.
    @mcp.tool(
        description="Search for flights using the Aviasales Flight Search API. " \
        "This tool performs search based on the provided flight segments, number of passengers, trip class, currency, and locale. " \
        "It provides search_id and description of search results and saves found options internally." \
        "After receiving the result client can use `get_flight_options` tool to retrieve the found options with more granular filters." \
        "IMPORTANT: All times are local to departure/arrival locations and use HH:MM 24-hour format." \
        "IMPORTANT: Call this tool as many times as needed to find the best flight options."
    )
  • Helper function to generate the MD5 signature required for authenticating the API request according to Travelpayouts specification. Used in search_flights.
    def _generate_signature(token: str, body_without_sig: Dict[str, Any]) -> str:
        """Compute MD5 signature per Travelpayouts Flight Search spec."""
    
        ordered_values = _collect_values_sorted(body_without_sig)
        base_string = ":".join([token] + ordered_values)
        print(f"Signature base string: {base_string}", file=sys.stderr)
        return hashlib.md5(base_string.encode()).hexdigest()
  • Helper function to parse the raw API response into a structured ProposalsBatchModel, setting the batch reference on each proposal. Called repeatedly in search_flights to aggregate results.
    def parse_proposals_batch(api_response: dict) -> ProposalsBatchModel:
        """
        Parse API response dictionary into ProposalsBatchModel.
        
        Args:
            api_response: Dictionary containing the API response
            
        Returns:
            ProposalsBatchModel instance with flattened flight data
        """
        batch = ProposalsBatchModel(**api_response)
        for i in range(len(batch.proposals)):
            batch.proposals[i].batch_ref = batch
        return batch
Behavior4/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 effectively describes key behaviors: it performs a search, provides a search_id and description, saves options internally, requires follow-up with `get_flight_options` for detailed results, specifies time format requirements, and encourages multiple calls for optimization. It doesn't mention error handling, rate limits, or authentication needs, but covers the core operational flow well.

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 and appropriately sized. It starts with the core purpose, explains the process and parameters, provides important usage notes, and ends with behavioral guidance. Each sentence adds value, though the second sentence is somewhat long. The 'IMPORTANT' sections are effectively highlighted.

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 of flight search, no annotations, and no output schema, the description does a good job of explaining the tool's operation. It covers the search process, result handling (via another tool), time format requirements, and usage patterns. It could benefit from mentioning error cases or response structure, but it provides sufficient context for effective use.

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

Parameters4/5

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

Schema description coverage is 0%, so the description must compensate. It lists the main parameters (flight segments, passengers, trip class, currency, locale) and provides context about their use in the search process. While it doesn't detail each parameter's format or constraints, it gives enough semantic understanding to guide usage, especially with the embedded schema providing detailed parameter descriptions.

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

Purpose4/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 using the Aviasales Flight Search API.' It specifies the verb ('search') and resource ('flights'), and mentions key parameters like flight segments, passengers, trip class, currency, and locale. However, it doesn't explicitly differentiate from sibling tools beyond mentioning that results can be retrieved with `get_flight_options`.

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

Usage Guidelines5/5

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

The description provides explicit usage guidance: it tells when to use this tool ('to find the best flight options'), mentions an alternative tool for retrieving results (`get_flight_options`), and includes important behavioral instructions ('Call this tool as many times as needed' and notes about time formats). It also implicitly guides against misuse by describing the search process.

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

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