README.md•16.4 kB
# Duffel MCP Server
A Model Context Protocol (MCP) server that enables LLMs to interact with the [Duffel API](https://duffel.com) for searching and booking flights, accommodations, and managing travel bookings.
## Features
### ✈️ Flight Operations
- **Search Flights** - Find available flights with pricing, schedules, and airline information
- **Get Offer Details** - Retrieve up-to-date pricing and availability for specific offers
- **Create Orders** - Book flights with passenger details and payment
- **Manage Orders** - View and manage existing bookings
### 🏨 Accommodation (Stays) Operations
- **Search Accommodation** - Find available hotels and properties by location and dates
- **Get Accommodation Details** - Retrieve detailed property information, amenities, and ratings
- **Get Accommodation Rates** - View available rooms, pricing, and rate options
- **Create Stays Quote** - Generate a quote to lock in pricing before booking
- **Create Stays Booking** - Book accommodations with guest details
- **Get Stays Booking** - View and manage accommodation bookings
### 🌍 Supporting Resources
- **Search Airports** - Find airports by name, city, or IATA code
- **List Airports** - Browse airports with country filtering
### 🎯 Key Capabilities
- **300+ Airlines** - Access to major airlines via NDC, GDS, and LCC
- **Millions of Properties** - Hotels, resorts, vacation rentals worldwide
- **Real-time Data** - Live pricing, availability, and seat selection
- **Flexible Search** - One-way, round-trip, multi-city flights; location-based accommodation search
- **Smart Responses** - JSON or Markdown formatted output
- **Error Handling** - Clear, actionable error messages
- **Complete Booking Flow** - From search to confirmation for both flights and accommodations
## Installation
### Prerequisites
- Python 3.12 or higher
- Duffel API account ([sign up here](https://app.duffel.com/join))
- Duffel API access token
- [uv](https://docs.astral.sh/uv/) (fast Python package manager)
### Install uv
On macOS with Homebrew:
```bash
brew install uv
```
Or via the official installer (macOS/Linux):
```bash
curl -LsSf https://astral.sh/uv/install.sh | sh
# then restart your shell or source the profile output by the installer
```
### Project setup
1. Clone or download the server file/repo
2. Create the virtual environment and install dependencies with uv:
```bash
uv sync
```
This uses `pyproject.toml` to create `.venv` and install exact versions.
3. Set up environment variable:
```bash
export DUFFEL_ACCESS_TOKEN="your_duffel_token_here"
```
Get your token from the [Duffel Dashboard](https://app.duffel.com):
- Navigate to: More → Developers → Access Tokens
- Create a test token for testing (free, unlimited balance)
- Create a live token for production bookings
## Usage
### Running the Server
Run with uv (uses the synced virtualenv automatically):
```bash
uv run python duffel_mcp.py
```
### Configuration for Claude Desktop
Add to your Claude Desktop config file (`claude_desktop_config.json`):
```json
{
"mcpServers": {
"duffel": {
"command": "uv",
"args": ["run", "python", "/path/to/duffel_mcp.py"],
"env": {
"DUFFEL_ACCESS_TOKEN": "your_token_here"
}
}
}
}
```
### Configuration for Other MCP Clients
For other MCP clients, follow their specific configuration format, ensuring:
- The server is launched with Python 3.12+
- `DUFFEL_ACCESS_TOKEN` environment variable is set
- Server communicates via stdio
## Available Tools
### 1. `duffel_search_flights`
Search for available flights based on journey requirements.
**Parameters**:
- `slices` - Journey legs (origin, destination, date)
- `passengers` - List of travelers (use `age` for best accuracy)
- `cabin_class` - Optional: economy, premium_economy, business, first
- `max_connections` - Optional: limit stops (0 = direct flights)
- `response_format` - json or markdown
**Example**:
```python
{
"slices": [
{
"origin": "JFK",
"destination": "LAX",
"departure_date": "2025-12-15"
},
{
"origin": "LAX",
"destination": "JFK",
"departure_date": "2025-12-22"
}
],
"passengers": [
{"age": 35},
{"age": 32},
{"age": 8}
],
"cabin_class": "economy",
"max_connections": 1
}
```
### 2. `duffel_get_offer`
Retrieve current pricing and details for a specific offer.
**Parameters**:
- `offer_id` - Offer ID from search results
- `response_format` - json or markdown
**Important**: Always call this before booking to ensure offer is still valid and get current pricing.
### 3. `duffel_create_order`
Create a flight booking with passenger details and payment.
**Parameters**:
- `offer_id` - Offer ID to book
- `passengers` - Complete passenger details (name, DOB, contact)
- `payments` - Payment information
- `response_format` - json or markdown
**⚠️ Warning**: This creates real bookings! Use test tokens for development.
**Example**:
```python
{
"offer_id": "off_00009htYpSCXrwaB9DnUm0",
"passengers": [
{
"id": "pas_00009hj8USM7Ncg31cBCL",
"given_name": "John",
"family_name": "Smith",
"born_on": "1985-03-15",
"email": "john.smith@example.com",
"phone_number": "+14155551234",
"gender": "m",
"title": "mr"
}
],
"payments": [
{
"type": "balance",
"amount": "520.00",
"currency": "USD"
}
]
}
```
### 4. `duffel_get_order`
Retrieve details for an existing order.
**Parameters**:
- `order_id` - Order ID from booking
- `response_format` - json or markdown
### 5. `duffel_search_airports`
Search for airports by name, city, or code.
**Parameters**:
- `query` - Search term (e.g., "London", "Heathrow", "LHR")
- `limit` - Max results (1-100, default: 20)
- `response_format` - json or markdown
### 6. `duffel_search_accommodation`
Search for available accommodations based on location, dates, and guest requirements.
**Parameters**:
- `check_in_date` - Check-in date in YYYY-MM-DD format
- `check_out_date` - Check-out date in YYYY-MM-DD format
- `guests` - Guest information (adult_count, child_count)
- `rooms` - Number of rooms needed (default: 1)
- `location` - Geographic location (latitude, longitude, radius in km) OR
- `accommodation_ids` - Specific accommodation IDs to search
- `response_format` - json or markdown
**Example**:
```python
{
"check_in_date": "2025-12-15",
"check_out_date": "2025-12-18",
"guests": {
"adult_count": 2,
"child_count": 1
},
"rooms": 1,
"location": {
"latitude": 51.5074,
"longitude": -0.1278,
"radius": 10
}
}
```
### 7. `duffel_get_accommodation`
Get detailed information for a specific accommodation property.
**Parameters**:
- `accommodation_id` - Accommodation ID (e.g., "acc_00009htYpSCXrwaB9DnUm0")
- `response_format` - json or markdown
### 8. `duffel_get_accommodation_rates`
Retrieve available rooms and rates for a search result.
**Parameters**:
- `search_result_id` - Search result ID from accommodation search
- `response_format` - json or markdown
**Note**: Use the search_result_id from `duffel_search_accommodation` results.
### 9. `duffel_create_stays_quote`
Create a quote to lock in pricing before booking.
**Parameters**:
- `rate_id` - Rate ID from accommodation rates
- `response_format` - json or markdown
**Important**: Always create a quote before booking to confirm current pricing.
### 10. `duffel_create_stays_booking`
Create an accommodation booking from a quote.
**Parameters**:
- `quote_id` - Quote ID from stays quote
- `guests` - List of guest details (given_name, family_name, email, phone_number)
- `special_requests` - Optional: Special requests for the property
- `response_format` - json or markdown
**⚠️ Warning**: This creates real bookings! Use test tokens for development.
**Example**:
```python
{
"quote_id": "quo_00009htYpSCXrwaB9DnUm0",
"guests": [
{
"given_name": "John",
"family_name": "Smith",
"email": "john.smith@example.com",
"phone_number": "+14155551234"
}
],
"special_requests": "Late check-in after 10 PM"
}
```
### 11. `duffel_get_stays_booking`
Retrieve details for an existing accommodation booking.
**Parameters**:
- `booking_id` - Stays booking ID
- `response_format` - json or markdown
### 12. `duffel_search_airports`
Search for airports by name, city, or code.
**Parameters**:
- `query` - Search term (e.g., "London", "Heathrow", "LHR")
- `limit` - Max results (1-100, default: 20)
- `response_format` - json or markdown
### 13. `duffel_list_airports`
List airports with optional country filter.
**Parameters**:
- `country_code` - Optional: ISO country code (e.g., "US", "GB")
- `limit` - Results per page (1-200, default: 50)
- `response_format` - json or markdown
## Typical Workflows
### Booking a Flight
1. **Search for flights**:
```
Search for round-trip flights from New York to London, departing Dec 15, returning Dec 22,
2 adult passengers, economy class, direct flights only
```
2. **Get offer details**:
```
Get the latest pricing for offer off_00009htYpSCXrwaB9DnUm0
```
3. **Create booking**:
```
Book this offer with passengers: John Smith (john@example.com, +14155551234, DOB 1985-03-15)
and Jane Smith (jane@example.com, +14155551235, DOB 1987-07-20). Use balance payment for $1,450.00 USD.
```
### Booking Accommodation
1. **Search for accommodations**:
```
Find hotels in central London for 2 adults, checking in Dec 15, checking out Dec 18,
within 10km of coordinates 51.5074, -0.1278
```
2. **Get rates for a property**:
```
Show me the available rooms and rates for search result srs_00009htYpSCXrwaB9DnUm0
```
3. **Create a quote**:
```
Create a quote for rate rat_00009htYpSCXrwaB9DnUm0
```
4. **Complete booking**:
```
Book this quote quo_00009htYpSCXrwaB9DnUm0 for guest John Smith
(john.smith@example.com, +14155551234) with special request: "Late check-in after 10 PM"
```
### Finding Airports
```
What airports are in the London area?
```
```
What's the airport code for San Francisco International?
```
## Best Practices
### ✅ Do's
#### For Flights:
1. **Use `age` over `type`** - Provides better accuracy across airlines
2. **Check offer expiry** - Flight offers expire in 15-30 minutes
3. **Verify before booking** - Always retrieve offer for current price
4. **Test mode first** - Use test tokens during development
5. **Handle async responses** - Some bookings return 200/202 with webhook notifications
#### For Stays:
1. **Create quote before booking** - Always generate a quote to lock in pricing
2. **Provide accurate coordinates** - Use precise latitude/longitude for location searches
3. **Specify check-in/out dates** - Rates vary significantly by date
4. **Include all guest details** - Complete information ensures smooth check-in
5. **Use search_result_id** - Required to fetch rates for a specific property
### ❌ Don'ts
1. **Don't retry failed bookings** - If booking creation fails, don't retry the same request
2. **Don't cache offers or quotes** - Always fetch fresh data before booking
3. **Don't ignore validation errors** - They guide toward correct usage
4. **Don't use expired offers or quotes** - Check expiry timestamps
5. **Don't skip the quote step** - For stays, always create a quote before booking
## Error Handling
The server provides clear, actionable error messages:
- **offer_expired** - Perform new search
- **offer_no_longer_available** - Select different offer
- **price_changed** - Retrieve offer again for updated price
- **validation_error** - Check parameter formats and requirements
- **payment_declined** - Verify payment details
## Testing
### Test Mode
Duffel provides unlimited test balance:
1. Create a **test** access token in dashboard
2. Use `"type": "balance"` for payments
3. Search and book without actual charges
4. Use test airline: "Duffel Airways"
### Manual Testing
```bash
# Set test token
export DUFFEL_ACCESS_TOKEN="duffel_test_xxx"
# Run server with uv
uv run python duffel_mcp.py
```
Then interact through your MCP client to test various workflows.
## API Reference
For complete Duffel API documentation:
- [Getting Started with Flights](https://duffel.com/docs/guides/getting-started-with-flights)
- [Getting Started with Stays](https://duffel.com/docs/guides/getting-started-with-stays)
- [API Reference](https://duffel.com/docs/api/overview/welcome)
- [Response Handling](https://duffel.com/docs/api/overview/response-handling)
## Troubleshooting
### General Issues
#### "DUFFEL_ACCESS_TOKEN environment variable not set"
Set the environment variable with your Duffel API token.
### Flight-Specific Issues
#### "offer_expired" errors
Flight offers have short expiry times (15-30 min). Perform a new search.
#### "validation_error" on flight booking
- Check passenger names match government ID exactly
- Verify date format is YYYY-MM-DD
- Ensure payment amount matches offer total
- Confirm all required fields are provided
#### "No flights found"
- Verify airport codes are valid IATA codes (use `duffel_search_airports`)
- Check dates are in future
- Try broader search criteria (more connections, different cabin class)
### Stays-Specific Issues
#### "No accommodations found"
- Verify latitude/longitude coordinates are valid
- Ensure radius is between 1-100 km
- Check dates are in future and in YYYY-MM-DD format
- Try expanding search radius or different dates
#### "No rates available"
- The property may be fully booked for your dates
- The search_result_id may have expired - perform a new search
- Try different check-in/check-out dates
#### "quote_expired" or quote errors
- Quotes expire after some time - create a new quote
- Verify the rate is still available
- The rate_id must be from a recent search
#### "validation_error" on stays booking
- Ensure at least one guest is provided
- Verify all guest emails are valid
- Check phone numbers include country code (e.g., +1)
- Confirm quote_id is correct and not expired
### Webhooks not received
- Configure webhook URLs in Duffel Dashboard
- Check your email for booking confirmations
- Use `duffel_get_order` or `duffel_get_stays_booking` to manually check status
## License
This MCP server is provided as-is for integration with Duffel API. See Duffel's terms of service for API usage terms.
## Support
- **Duffel Documentation**: https://duffel.com/docs
- **Duffel Support**: https://support.duffel.com
- **MCP Documentation**: https://modelcontextprotocol.io
## Contributing
Contributions welcome! Potential enhancements:
- Order modifications and cancellations (flights and stays)
- Seat selection tools for flights
- Baggage management for flights
- Loyalty program integration (flights and stays)
- Advanced filtering options (amenities, price ranges)
- Accommodation suggestions/autocomplete
- Multi-property booking support
- Cancellation management for stays
## Code Structure
The codebase has been refactored into a modular structure for better maintainability:
```
duffel_mcp/
├── __init__.py # Package initialization
├── config.py # Configuration and constants
├── server.py # MCP server instance
├── models/ # Pydantic models (flights, stays, airports)
├── api/ # API client and authentication
├── formatters/ # Response formatting utilities
└── tools/ # MCP tools (flights, stays, airports)
```
For detailed structure documentation, see [STRUCTURE.md](STRUCTURE.md).
**Benefits**:
- Clean separation of concerns (models, API, formatters, tools)
- Easy to navigate and maintain
- Modular testing and extension
- Simple to add new features
## Version History
- **v0.2.1** - Code Refactoring
- Modular code structure for better maintainability
- Separated models, API client, formatters, and tools into logical modules
- Added STRUCTURE.md architecture documentation
- Cleaner imports and organization
- **v0.2.0** - Stays API Integration
- Accommodation search by location
- Get accommodation details and rates
- Quote creation for stays
- Stays booking creation and management
- Complete stays workflow support
- Enhanced documentation with stays examples
- **v0.1.0** - Initial release
- Flight search and booking
- Order management
- Airport search
- Markdown and JSON output formats