A proxy that aggregates four travel MCP servers (Kiwi.com, Navifare, Peek.com, LastMinute) plus six built-in datasets into 21+ tools behind a single HTTP endpoint. You get flight search and price comparison, 300K+ activities and experiences, airport and airline lookups from OurAirports and OpenFlights, visa requirements from Passport Index, and UK FCDO travel advisories. Static data loads lazily with no API keys required. The server passes through upstream JSON schemas without re-validation, so clients see the real tool contracts. Optional Gemini integration routes natural language queries to the right combination of tools. Useful when you're building a travel assistant and don't want to wire up half a dozen APIs yourself.
One MCP server. Every travel tool.
Search flights, compare prices, check visas, look up airports,
get travel advisories — through a single endpoint.
Kiwi.com Navifare Peek.com LastMinute
(flights) (prices) (experiences) (flights)
\ | | /
\ | | /
+--------+-----------+--------+
| |
| MoltTravel MCP Server |
| |
| Airports Airlines Visas |
| Countries FCDO Gemini AI |
| |
+-------------|---------------+
|
MCP over HTTP
|
Any MCP Client
(Claude, Cursor, your app)
Travel data is scattered across dozens of APIs, each with its own auth, format, and quirks. MoltTravel aggregates them behind a single Model Context Protocol endpoint:
git clone https://github.com/navifare/moltravel-mcp.git
cd moltravel-mcp
python -m venv venv && source venv/bin/activate
pip install -r requirements.txt
python molttravel_server.py
Server starts at http://localhost:8000/mcp. That's it.
Add to claude_desktop_config.json:
{
"mcpServers": {
"molttravel": {
"url": "http://localhost:8000/mcp"
}
}
}
Add to .claude/settings.json:
{
"mcpServers": {
"molttravel": {
"url": "http://localhost:8000/mcp"
}
}
}
from mcp import ClientSession
from mcp.client.streamable_http import streamablehttp_client
async with streamablehttp_client("http://localhost:8000/mcp") as (r, w, _):
async with ClientSession(r, w) as session:
await session.initialize()
tools = await session.list_tools()
result = await session.call_tool("airports_lookup", {"code": "ZRH"})
| Tool | Provider | What it does |
|---|---|---|
kiwi_search-flight | Kiwi.com | Search flights by route, date, passengers, cabin class |
navifare_format_flight_pricecheck_request | Navifare | Parse flight details from natural language into structured data |
navifare_flight_pricecheck | Navifare | Compare a flight's price across multiple booking sites |
| Tool | Provider | What it does |
|---|---|---|
peek_search_experiences | Peek.com | Search 300K+ verified activities worldwide |
peek_experience_details | Peek.com | Full details, reviews, and photos for an experience |
peek_experience_availability | Peek.com | Check availability and pricing for specific dates |
peek_search_regions | Peek.com | Find region IDs by name |
peek_list_tags | Peek.com | Browse activity categories and tags |
peek_render_activity_tiles | Peek.com | Render embeddable activity widgets |
| Tool | Dataset | What it does |
|---|---|---|
airports_lookup | OurAirports | Look up by IATA or ICAO code |
airports_search | OurAirports | Search by name, filter by country or type |
airports_near | OurAirports | Find airports within a radius of any coordinates |
airlines_lookup | OpenFlights | Look up by IATA or ICAO code |
airlines_search | OpenFlights | Search by name, filter by country or active status |
visa_check | Passport Index | Check visa requirement between two countries |
visa_summary | Passport Index | Full visa-free/VOA/e-visa breakdown for a passport |
restcountries_country_info | REST Countries | Capital, currencies, languages, timezones, population |
fcdo_travel_advice | UK FCDO | Safety advisories, entry requirements, health warnings |
fcdo_list_countries | UK FCDO | List all countries with travel advisories |
data_status | — | Check which datasets are loaded and record counts |
| Tool | What it does |
|---|---|
travel_agent | Ask any travel question — Gemini routes to the right tools and returns a combined answer |
Requires
GEMINI_API_KEY. Example: "Cheapest flights from Zurich to Rome next week, and do I need a visa?"
On startup, MoltTravel connects to each upstream MCP server, calls tools/list, and registers every discovered tool with a {provider}_{tool_name} prefix:
kiwi → kiwi_search-flight, kiwi_feedback-to-devs
navifare → navifare_flight_pricecheck, navifare_format_flight_pricecheck_request
peek → peek_search_experiences, peek_experience_details, ...
lastminute → (discovered at runtime)
Native tools (airports, airlines, visas, countries, FCDO) are registered directly.
Clients see the original upstream JSON Schema for each tool — enums, nested objects, $ref, everything. Internally, MoltTravel uses a permissive Pydantic model (Any for all fields) so arguments pass through without lossy validation. The upstream MCP server validates its own args.
# Client sees the real schema
parameters = input_schema # original from upstream
# Server doesn't re-validate types — just passes through
fields[prop_name] = (Any, Field(default=None))
Static datasets (airports, airlines, visas) download on first use behind async locks. No startup penalty, no wasted bandwidth if you only use flight tools.
| Variable | Default | Description |
|---|---|---|
PORT | 8000 | Server port |
GEMINI_API_KEY | — | Enables the travel_agent natural-language routing tool |
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 8000
CMD ["python", "molttravel_server.py"]
docker build -t molttravel .
docker run -p 8000:8000 molttravel
The server reads PORT from the environment and binds to 0.0.0.0 — it works out of the box on any container platform. Just point the start command at python molttravel_server.py.
moltravel-mcp/
├── molttravel_server.py # Server core — proxy logic, native tools, routing
├── requirements.txt # mcp[cli], httpx
├── test_search.py # Integration test client
└── providers/
├── __init__.py # MCP_PROVIDERS registry + exports
├── mcp_client.py # Generic HTTP client for upstream MCP servers
├── data_loader.py # CSV downloader + haversine distance
├── airports.py # 45K airports from OurAirports
├── airlines.py # 7K airlines from OpenFlights
├── visas.py # Visa requirements from Passport Index
├── restcountries.py # REST Countries API
├── fcdo.py # UK FCDO travel advisories
└── gemini.py # Gemini Flash tool router
Add one line to providers/__init__.py and restart:
MCP_PROVIDERS = {
"kiwi": McpClient("https://mcp.kiwi.com/mcp"),
"navifare": McpClient("https://mcp.navifare.com/mcp"),
"peek": McpClient("https://mcp.peek.com/mcp"),
"your_provider": McpClient("https://mcp.example.com/mcp"), # new
}
Tools are discovered and registered automatically as your_provider_{tool_name}.
@server.tool(name="my_tool")
async def my_tool(query: str) -> str:
"""Description shown to MCP clients."""
return "result"
| Dataset | Source | License |
|---|---|---|
| Airports | OurAirports | Public Domain |
| Airlines | OpenFlights | ODbL 1.0 |
| Visa Requirements | Passport Index | MIT |
| Country Info | REST Countries | MPL 2.0 |
| Travel Advisories | UK FCDO | OGL v3.0 |
git checkout -b feature/my-feature)python molttravel_server.py)