flight-search-mcp
<div align="center">
# ✈️ flight-search-mcp
**A Model Context Protocol server for live flight tracking — built with the official MCP Python SDK, powered by a genuinely free API.**
[](pyproject.toml)
[](https://modelcontextprotocol.io)
Give Claude (or any MCP client) the ability to look up live flights, check real-time status, and resolve airport/airline codes — no paid API, no credit card.
</div>
---
## What this does
Ask your MCP client things like:
> *"Is flight BA100 delayed?"*
> *"What flights are currently in the air from JFK to LHR?"*
> *"What's the IATA code for Heathrow?"*
...and it answers with real, live data pulled straight from [AviationStack](https://aviationstack.com).
## Why AviationStack
AviationStack is the one that's still genuinely free for indie developers: **~100
requests/month, no credit card, API key in seconds.**
The trade-off: the free plan covers **current & near-term live flights only**
— no future-dated trip search, no historical data, no fares. This is a
flight *tracker*, not a trip planner. If you outgrow that, see
[Scope & limitations](#-scope--limitations) below.
## ✨ Features
- 🔍 **`search_flights`** — live flights by route, airline, or status
- 📡 **`get_flight_status`** — real-time status, gate, delay, live position for one flight
- 🛫 **`resolve_airport`** — city/airport name → IATA code
- 🏢 **`resolve_airline`** — airline name → IATA code
- ⚡ Smart caching — protects your monthly quota automatically
- 🛡️ Input validation with actionable error messages, not raw API errors
- ✅ Tested, typed, and small enough to actually read
## 🚀 Quickstart
```bash
git clone https://github.com/PythonicMind2/flight-search-mcp.git
cd flight-search-mcp
pip install -e .
cp .env.example .env
# paste your free AviationStack key into .env — get one at
# https://aviationstack.com/signup/free (instant, no card)
python -m flight_search_mcp.server
```
### Connect to Claude Desktop
Add to `claude_desktop_config.json`:
```json
{
"mcpServers": {
"flight-search": {
"command": "python",
"args": ["-m", "flight_search_mcp.server"],
"cwd": "/absolute/path/to/flight-search-mcp",
"env": {
"AVIATIONSTACK_API_KEY": "your_api_key"
}
}
}
}
```
Restart Claude Desktop and ask it about a flight.
## 🧰 Tools
| Tool | Args | Returns |
|---|---|---|
| `search_flights` | `origin?`, `destination?`, `airline_name?`, `flight_status?`, `max_results?` | Live flights matching at least one filter |
| `get_flight_status` | `flight_iata` (e.g. `"BA100"`) | Status, gate, delay, live position |
| `resolve_airport` | `keyword` (city or airport name) | Matching IATA codes |
| `resolve_airline` | `keyword` (airline name) | Matching IATA codes |
`search_flights` requires at least one of `origin`, `destination`, or
`airline_name` — an unfiltered call would burn quota on an arbitrary global
sample.
## 🏗️ Project structure
```
flight-search-mcp/
├── src/flight_search_mcp/
│ ├── server.py # FastMCP server + tool definitions
│ ├── aviationstack_client.py # Async API client, caching, error handling
│ ├── models.py # Pydantic response models
│ ├── cache.py # Minimal in-memory TTL cache
│ └── config.py # Env-based settings
├── tests/test_server.py # Parsing + validation tests (no network)
├── .github/workflows/tests.yml # CI
├── .env.example
└── pyproject.toml
```
**Design choices worth knowing about:**
- **Auth** is a plain `access_key` query param — no OAuth token juggling.
- **Caching** is tiered: reference data (airports/airlines) is cached for an
hour since it barely changes; live flight data for 30 seconds, mainly to
absorb accidental duplicate calls in the same turn. Every cache hit is a
request you don't spend against your ~100/month quota.
- **Errors**: AviationStack often returns HTTP `200` with an `"error"`
object in the body instead of a 4xx. The client catches this and raises a
real exception with a useful hint (quota exhausted, wrong key, paid-only
feature) instead of silently returning nothing.
## 🧪 Testing
```bash
pip install pytest pytest-asyncio
pytest tests/ -v
```
All tests run against sample payloads — no API key or network access needed.
## ⚠️ Scope & limitations
AviationStack's free plan does **not** include:
- Future-dated trip search (`/flightsFuture` is paid-only)
- Historical flights beyond the current window
- `/routes` (date-independent schedule lookups — paid-only)
- Any pricing/fare data (AviationStack doesn't do fares on any plan)
- The `search` autocomplete param on `/airports`/`/airlines` (this repo
works around it by filtering one reference page client-side)
If you need real future-dated search with prices, look at
[FlightAPI.io](https://www.flightapi.io) (small free trial, then paid) or
[Duffel](https://duffel.com) (free sandbox data forever, real data is
pay-per-use). PRs adding either as an optional second backend are welcome.
## 🤝 Contributing
Issues and PRs welcome. Keep it small, keep it tested.
---
<div align="center">
Built with the <a href="https://github.com/modelcontextprotocol/python-sdk">official MCP Python SDK</a>.
</div>
TDQS
Scored across 4 tools
Each tool has a distinct purpose: get_flight_status for a specific flight, search_flights for multi-flight search, resolve_airport for IATA code lookup by city/airport name, and resolve_airline for airline code lookup. No overlap or ambiguity.
All tool names follow a consistent snake_case verb_noun pattern: get_flight_status, search_flights, resolve_airport, resolve_airline. No mixing of styles or conventions.
With 4 tools, the server is well-scoped for a flight search MCP: two lookups (airport/airline codes) and two flight operations (search and specific status). Each tool earns its place without being too few or too many.
The tool surface covers the core domain: resolving identifiers, searching live flights, and retrieving detailed status. Gaps like historical or future scheduled flights are explicitly out of scope, so no missing functionality for its stated real-time purpose.