@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"