@mcp.tool(
name="duffel_create_order",
annotations={
"title": "Create Flight Order (Book Flight)",
"readOnlyHint": False,
"destructiveHint": False,
"idempotentHint": False,
"openWorldHint": True
}
)
async def create_order(params: CreateOrderInput) -> str:
"""
Create a flight booking (order) for the specified offer and passengers.
⚠️ IMPORTANT: This creates a real booking and may involve payment. This action:
- Creates a confirmed airline reservation
- May charge the payment method (if not using test mode)
- Issues a booking reference from the airline
- Is typically non-refundable or has cancellation fees
Before calling this tool:
1. Verify the offer is current using duffel_get_offer
2. Confirm all passenger details are accurate (names match IDs)
3. Check payment amount matches offer total
4. Ensure user understands booking terms
Required data:
- Offer ID from search results
- Complete passenger details (names as on ID, date of birth, contact info)
- Payment information matching offer total
The tool returns:
- Order ID for reference
- Airline booking reference
- Confirmation details
- Ticket information
Response codes:
- 201: Order created successfully (immediate confirmation)
- 200: Booking confirmed, details arriving soon (webhook notification will follow)
- 202: Booking processing (check webhooks or email for confirmation)
Use test mode tokens for testing to avoid actual charges.
Returns order details in specified format (JSON or Markdown).
"""
order_data = {
"data": {
"selected_offers": [params.offer_id],
"passengers": [
{
"id": pax.id,
"given_name": pax.given_name,
"family_name": pax.family_name,
"born_on": pax.born_on,
"email": pax.email,
"phone_number": pax.phone_number,
"gender": pax.gender,
**({"title": pax.title} if pax.title else {})
}
for pax in params.passengers
],
"payments": [
{
"type": payment.type.value,
"amount": payment.amount,
"currency": payment.currency.upper()
}
for payment in params.payments
]
}
}
try:
response = await make_api_request(
method="POST",
endpoint="/air/orders",
data=order_data
)
status_code = response.get("meta", {}).get("status", 201)
if status_code == 200:
message = response.get("data", {}).get("message", "")
return (
f"✅ Booking Confirmed!\n\n"
f"{message}\n\n"
f"The complete booking details will be available shortly. "
f"You will receive:\n"
f"- A webhook notification when the order is created\n"
f"- An email to your support contact address\n\n"
f"**Offer ID**: `{params.offer_id}`\n"
f"Please check your webhooks or email for the order ID and booking reference."
)
elif status_code == 202:
return (
f"⏳ Booking Processing\n\n"
f"Your booking request has been accepted and is being processed. "
f"You will receive confirmation via:\n"
f"- Webhook notification (order.created or order.creation_failed)\n"
f"- Email to your support contact address\n\n"
f"**Offer ID**: `{params.offer_id}`\n"
f"This typically takes a few minutes. Please do not retry this request."
)
else:
order = response["data"]
if params.response_format == ResponseFormat.JSON:
return truncate_text(format_json_response(order))
else: # Markdown format
lines = [
"# ✅ Booking Confirmed!",
"",
f"**Order ID**: `{order['id']}`",
f"**Booking Reference**: {order.get('booking_reference', 'N/A')}",
f"**Total**: {format_currency(order['total_amount'], order['total_currency'])}",
"",
"## Passengers"
]
for i, pax in enumerate(order.get("passengers", []), 1):
lines.append(f"{i}. {pax.get('given_name')} {pax.get('family_name')}")
lines.append("")
lines.append("## Flight Details")
for i, slice_info in enumerate(order.get("slices", []), 1):
origin = slice_info["origin"]["iata_code"]
destination = slice_info["destination"]["iata_code"]
lines.append(f"**Slice {i}**: {origin} → {destination}")
lines.append(f"- Departure: {slice_info['departure_at']}")
lines.append(f"- Arrival: {slice_info['arrival_at']}")
lines.append("")
lines.append("---")
lines.append("*Save the Order ID and Booking Reference for future reference.*")
return truncate_text("\n".join(lines))
except Exception as e:
error_msg = str(e)
troubleshooting = "\n\nTroubleshooting:\n"
if "offer_expired" in error_msg:
troubleshooting += "- The offer has expired. Perform a new flight search to get current offers.\n"
elif "offer_no_longer_available" in error_msg:
troubleshooting += "- The offer is no longer available. Search again or select a different offer.\n"
elif "price_changed" in error_msg:
troubleshooting += "- The price has changed. Retrieve the offer again to get updated pricing.\n"
elif "validation" in error_msg.lower():
troubleshooting += "- Check passenger details are complete and accurate\n"
troubleshooting += "- Verify names match government ID exactly\n"
troubleshooting += "- Ensure date of birth is in YYYY-MM-DD format\n"
troubleshooting += "- Confirm email and phone number are valid\n"
elif "payment" in error_msg.lower():
troubleshooting += "- Verify payment amount matches offer total_amount exactly\n"
troubleshooting += "- Confirm currency matches offer total_currency\n"
troubleshooting += "- Check payment type is appropriate for your account\n"
else:
troubleshooting += "- Verify all passenger details are accurate\n"
troubleshooting += "- Ensure payment amount and currency match the offer\n"
troubleshooting += "- Check if using test mode token for testing\n"
return f"Error creating order: {error_msg}{troubleshooting}"