Skip to main content
Glama
nstevens

flight-finder

by nstevens
README.md
# flight-finder

> Find the genuinely best flight β€” cash *or* points β€” by ranking every option against what a mile is actually worth to *you*.

### Three ways to use this
- 🧠 **Build your own** with Claude Code β†’ [`GUIDE.md`](./GUIDE.md)
- πŸ›  **Run the code** β†’ below
- πŸš€ **Just use it** β†’ hosted version (coming soon)

## What it does
Award availability and cash fares live in a dozen places and change by the hour. flight-finder pulls both — cash fares (via Google Flights) and award space (via the [seats.aero](https://seats.aero) Pro API) — then ranks every option using *your* per-program cents-per-mile valuations and travel preferences. It also enriches each option with **seat-quality detail from [AeroLOPA](https://www.aerolopa.com)** — cabin layout, seat counts, and the actual seat map for the specific aircraft (tail-number matched) — so you're comparing the seat you'll actually sit in, not just the price. "Is this a good deal?" becomes a number, not a vibe. And it runs as an **MCP server**, so you can plan trips conversationally in Claude Code: *"find me SEA→Tokyo in business under 120k points."*

## Quickstart
```bash
uv sync
cp .env.example .env                      # add SEATS_AERO_API_KEY (+ optional AEROLOPA_API_ID / AEROLOPA_API_SECRET for seat maps)
# edit config/profile.yaml + config/programs.yaml (home airports, point balances, cpm values)
uv run ff search --from SEA --to FCO --depart 2026-11-21 --cabin BUSINESS
uv run ff awards --from SEA --to FCO --start 2026-11-18 --end 2026-11-23 --cabin business
uv run ff plan trips/example-trip.yaml --out trips/example-shortlist.md
```

## MCP (Claude Code)
```bash
claude mcp add flight-finder --scope user \
  -- uv --directory /path/to/flight-finder run python -m flight_finder.mcp_server
```
Tools: `search_cash_one_way`, `search_cash_round_trip`, `search_awards`, `find_positioning_flights`, `get_seat_map`, `plan_trip`.

## How it works
```
src/flight_finder/
  offer.py            # normalized Offer (cash or award) + Segment
  config.py           # profile + programs + env
  cash/                # Google Flights wrapper + multi-origin Γ— date fan-out
  awards/              # seats.aero Pro client
  strategy/scorer.py   # cpm-based cost + preference-aware ranking  ← the core idea
  seatmaps/            # AeroLOPA enrichment: cabin product + seat map per aircraft (tail-matched)
  planner.py           # combines it all behind a TripSpec
  cli.py Β· mcp_server.py
```
The interesting decision: a single normalized `Offer` for cash and award, so the **scorer** can compare them on one axis β€” *your* dollar value β€” instead of apples-to-oranges price vs. miles.

## What I learned building this
The hard part wasn't the model β€” it was the **valuation layer**. The same award seat is a steal or a trap depending on which points you hold, what you value, and even which seat you'll actually sit in. Encoding that judgment is what turns a search into a recommendation. It's true of most useful AI products: the model is the commodity; the context and the scoring are the moat.

## Built with Claude Code
Built nights and weekends, mostly by talking to Claude β€” scaffolded, iterated, and tested through Claude Code.

## License
MIT

TDQS

B3.2/5.0

Scored across 6 tools

Disambiguation5/5

Each tool targets a distinct aspect of flight finding: positioning flights, seat maps, trip planning, award search, and cash search (one-way and round trip). No overlaps.

Naming Consistency5/5

All tool names follow a consistent verb_noun pattern with snake_case (e.g., find_positioning_flights, get_seat_map, search_awards).

Tool Count5/5

Six tools cover the core functionality of a flight finder without excess. The scope is well-scoped and manageable.

Completeness4/5

The set covers major flight search types (award, cash) and trip planning, but lacks tools for flight status, booking management, or filtering by amenities. Minor gaps exist.

Maintenance

ActivitySlowing
ResponsivenessSyncing