duffel_search_flights
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
| Name | Required | Description | Default |
|---|---|---|---|
| params | Yes |
Implementation Reference
- duffel_mcp/tools/flights.py:12-116 (handler)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"
- duffel_mcp/models/flights.py:85-115 (schema)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" )
- duffel_mcp/tools/flights.py:12-21 (registration)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 } )