Skip to main content
Glama

duffel_search_flights

Read-only

Search for available flights between destinations with real-time pricing, schedules, and airline details. Compare flight options across 300+ airlines before booking.

Instructions

Search for available flights based on journey requirements.

This tool creates an offer request and returns available flight options with pricing,
schedules, and airline information. Each offer includes:
- Total price and currency
- Flight segments with timings
- Airline and aircraft details
- Cabin class and baggage allowance
- Offer ID for booking

Use this when users want to:
- Find flights between destinations
- Compare prices and schedules
- Check availability for specific dates
- Get flight options before booking

Important notes:
- Offers expire after 15-30 minutes (check expires_at)
- Use passenger age instead of type for better accuracy
- Round trips require 2 slices (outbound + return)
- Direct flights: set max_connections=0

Returns flight offers in specified format (JSON or Markdown summary).

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
paramsYes

Output Schema

TableJSON Schema
NameRequiredDescriptionDefault
resultYes

Implementation Reference

  • The @mcp.tool decorator registers the tool, and the search_flights function implements the core logic: constructs offer request from input params, calls Duffel API /air/offer_requests, processes offers, formats output as JSON or Markdown, handles errors.
    @mcp.tool(
        name="duffel_search_flights",
        annotations={
            "title": "Search Flights",
            "readOnlyHint": True,
            "destructiveHint": False,
            "idempotentHint": False,
            "openWorldHint": True
        }
    )
    async def search_flights(params: FlightSearchInput) -> str:
        """
        Search for available flights based on journey requirements.
        
        This tool creates an offer request and returns available flight options with pricing,
        schedules, and airline information. Each offer includes:
        - Total price and currency
        - Flight segments with timings
        - Airline and aircraft details
        - Cabin class and baggage allowance
        - Offer ID for booking
        
        Use this when users want to:
        - Find flights between destinations
        - Compare prices and schedules
        - Check availability for specific dates
        - Get flight options before booking
        
        Important notes:
        - Offers expire after 15-30 minutes (check expires_at)
        - Use passenger age instead of type for better accuracy
        - Round trips require 2 slices (outbound + return)
        - Direct flights: set max_connections=0
        
        Returns flight offers in specified format (JSON or Markdown summary).
        """
        request_data = {
            "data": {
                "slices": [
                    {
                        "origin": slice_data.origin.upper(),
                        "destination": slice_data.destination.upper(),
                        "departure_date": slice_data.departure_date
                    }
                    for slice_data in params.slices
                ],
                "passengers": []
            }
        }
        
        for passenger in params.passengers:
            if passenger.age is not None:
                request_data["data"]["passengers"].append({"age": passenger.age})
            else:
                request_data["data"]["passengers"].append({"type": passenger.type.value})
        
        if params.cabin_class:
            request_data["data"]["cabin_class"] = params.cabin_class.value
        
        if params.max_connections is not None:
            request_data["data"]["max_connections"] = params.max_connections
        
        try:
            response = await make_api_request(
                method="POST",
                endpoint="/air/offer_requests",
                data=request_data
            )
            
            offers = response["data"].get("offers", [])
            
            if not offers:
                return "No flights found for the specified criteria. Try adjusting your search parameters (dates, cabin class, or connections)."
            
            if params.response_format == ResponseFormat.JSON:
                result = {
                    "offer_request_id": response["data"]["id"],
                    "total_offers": len(offers),
                    "offers": offers[:10]  # Limit to 10 offers for readability
                }
                return truncate_text(format_json_response(result))
            
            else:  # Markdown format
                lines = [
                    f"# Flight Search Results",
                    f"**Offer Request ID**: `{response['data']['id']}`",
                    f"**Total Offers Found**: {len(offers)}",
                    "",
                    f"Showing top {min(5, len(offers))} offers:",
                    ""
                ]
                
                for offer in offers[:5]:
                    lines.append(format_markdown_flight_offer(offer))
                    lines.append("---")
                    lines.append("")
                
                if len(offers) > 5:
                    lines.append(f"\n*{len(offers) - 5} more offers available. Use duffel_get_offer with specific offer ID for details.*")
                
                return truncate_text("\n".join(lines))
                
        except Exception as e:
            return f"Error searching flights: {str(e)}\n\nTroubleshooting:\n- Verify airport/city codes are valid 3-letter IATA codes\n- Check dates are in future and in YYYY-MM-DD format\n- Ensure passenger count is reasonable (1-9)\n- Try broader search criteria if no results"
  • Pydantic BaseModel defining the input schema for duffel_search_flights, including slices, passengers, cabin_class, max_connections, and response_format. Depends on Slice, Passenger, CabinClass models defined earlier in the same file.
    class FlightSearchInput(BaseModel):
        """Input for searching flights."""
        model_config = ConfigDict(str_strip_whitespace=True, validate_assignment=True, extra='forbid')
    
        slices: list[Slice] = Field(
            ...,
            description="List of journey slices (legs). One-way = 1 slice, round-trip = 2 slices",
            min_length=1,
            max_length=4
        )
        passengers: list[Passenger] = Field(
            ...,
            description="List of passengers traveling. Include all travelers",
            min_length=1,
            max_length=9
        )
        cabin_class: CabinClass | None = Field(
            default=None,
            description="Preferred cabin class: 'economy', 'premium_economy', 'business', or 'first'"
        )
        max_connections: int | None = Field(
            default=None,
            description="Maximum number of connections/stops per slice (e.g., 0 for direct flights, 1 for one stop)",
            ge=0,
            le=3
        )
        response_format: ResponseFormat = Field(
            default=ResponseFormat.MARKDOWN,
            description="Output format: 'json' for raw data or 'markdown' for readable summary"
        )
  • MCP tool registration decorator specifying the tool name and annotations for read-only, non-destructive behavior.
    @mcp.tool(
        name="duffel_search_flights",
        annotations={
            "title": "Search Flights",
            "readOnlyHint": True,
            "destructiveHint": False,
            "idempotentHint": False,
            "openWorldHint": True
        }
    )
Behavior4/5

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

The description adds valuable behavioral context beyond annotations: it notes that offers expire after 15-30 minutes (important for timing), recommends using passenger age for accuracy, explains how to handle round trips and direct flights, and mentions the return format options. While annotations cover read-only and non-destructive aspects, the description enriches this with operational details without contradicting annotations.

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

Conciseness5/5

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

The description is well-structured and front-loaded with the core purpose, followed by bullet points for offer details, usage scenarios, and important notes. Every sentence adds value without redundancy, making it efficient and easy to scan for key information.

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 tool's complexity, the description is largely complete: it covers purpose, usage, behavioral nuances, and output format. With an output schema present, it appropriately omits detailed return value explanations. However, the low schema description coverage (0%) means parameter semantics are not fully addressed, leaving a minor gap in overall context.

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

Parameters3/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 for parameter documentation. It implicitly references parameters like destinations, dates, max_connections, and passenger age through usage examples and notes, but does not explicitly list or define all parameters. This provides some semantic context but falls short of fully documenting the input schema, aligning with the baseline expectation when schema coverage is low.

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 specific action ('Search for available flights'), identifies the resource ('based on journey requirements'), and distinguishes it from siblings like duffel_create_order (booking) and duffel_get_offer (retrieving specific offers). It explicitly mentions creating an offer request and returning flight options with detailed components.

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 guidance on when to use this tool ('when users want to: Find flights between destinations, Compare prices and schedules, Check availability for specific dates, Get flight options before booking'), which clearly differentiates it from sibling tools like duffel_create_order for booking or duffel_get_offer for retrieving specific offers. It also includes practical tips like using passenger age and handling round trips.

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/FortripEngineering/duffel-mcp'

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