Vacation Price Tracker is a full-stack web application designed for travelers to track flight and hotel prices for specific vacations. Users can monitor price fluctuations and receive notifications when total costs drop below a custom threshold.
- Flexible Tracking: Track flights only, hotels only (up to 3 per trip), or both combined.
- AI-Powered Chat: Natural language interface using Groq (GPT OSS 120B) and MCP for conversational trip management.
- Distributed Reliability: Orchestrated workflows via Temporal for reliable price fetching.
- Smart Notifications: Automated alerts via Email (MVP) and optional SMS (Future).
- Flexible Date Optimizer (Phase 4): Survey date ranges to find cheaper travel windows.
- Frontend: Next.js 14, Tailwind, shadcn/ui, assistant-ui.
- Backend: FastAPI (Python 3.12), SQLModel/PostgreSQL.
- Orchestration: Temporal SDK for Python.
- Auth: Google OAuth 2.0 (No local passwords).
- Testing: pytest (API), Jest (web), Playwright (E2E).
- Code Quality: Ruff, SonarQube (SonarCloud).
- Flight & Hotel Data: Skiplagged MCP — public, no auth required. Provides flights and hotels through a single JSON-RPC endpoint with airline names, flight numbers (parsed from the offer ID), room-level pricing, and booking links.
- Endpoint:
https://mcp.skiplagged.com/mcp - Transport: JSON-RPC 2.0 over Streamable HTTP with SSE responses
- Auth: None — public endpoint
- Tools used:
sk_flights_search— flights by route and dates, with pagination, airline names, flight numbers encoded in the offer ID, deep booking linkssk_hotels_search— hotels by city and dates, with ratings, amenities, nightly pricessk_hotel_details— room-level data for a specific hotel (room types, bed configurations, cancellation policies, taxes)
Skiplagged encodes flight numbers inside the offer id field (e.g., trip=AC744-LH6825,TS251-AC401). The SkiplaggedClient parses this into structured carrier codes and flight numbers via app.clients.skiplagged_parser.
The client exposes search_flights_all() and search_hotels_all() that follow pagination.hasMoreResults, with limit=75 and max_pages=4 (up to 300 results) for comprehensive price tracking.
In addition to Skiplagged search, the app exposes custom MCP tools to the chat LLM for trip management:
create_trip/delete_trip— create or remove a price tracking triplist_trips/get_trip_details— inspect tracked trips and price historyset_notification— update alert thresholdspause_trip/resume_trip— toggle trackingrefresh_trip_prices/refresh_all_trip_prices— force an immediate price checksearch_flights/search_hotels— conversational search powered by Skiplagged
vacation-price-tracker/
├── apps/
│ ├── web/ # Next.js frontend (+ Playwright E2E in apps/web/e2e/)
│ ├── api/ # FastAPI backend
│ └── worker/ # Temporal workflows
├── docs/ # Design specs, implementation plans, research notes
├── infra/ # Docker Compose
└── .env.example # Configuration template
The worker bootstraps two daily Temporal schedules on startup (UTC):
| Cron (env) | Default | Job |
|---|---|---|
DAILY_REFRESH_CRON |
0 6 * * * |
Refresh all active trips, then send per-user price-drop digests. |
DAILY_HEALTH_CRON |
0 7 * * * |
Run system health checks and email an ops summary to ADMIN_EMAILS. |
See apps/worker/CLAUDE.md for details.
- Python 3.12+
- Node.js 18+ (for frontend)
- pnpm 9+ (frontend package manager)
- uv (Python package manager)
- Docker & Docker Compose (for services)
-
Clone the repo:
git clone https://github.com/your-repo/vacation-price-tracker cd vacation-price-tracker -
Generate SSL certificates (for HTTPS development):
# macOS brew install mkcert mkcert -install mkcert -key-file certs/localhost-key.pem -cert-file certs/localhost-cert.pem localhost 127.0.0.1 ::1 -
Install dependencies:
uv sync --extra dev pnpm install
-
Configure
.env:cp .env.example .env # Edit .env with your credentials: # - DATABASE_URL: Postgres connection (pre-configured for Docker) # - GROQ_API_KEY: For LLM chat # - GOOGLE_CLIENT_ID/SECRET: For OAuth # - TEMPORAL_ADDRESS: Temporal server # - SKIPLAGGED_MCP_URL: defaults to https://mcp.skiplagged.com/mcp # - SEARCHAPI_KEY: (Phase 4) For date optimizer
-
Run services with Docker:
docker compose up -d db redis temporal
-
Run database migrations:
uv run alembic upgrade head
-
Start development servers:
pnpm api:dev pnpm web:dev
Fallback to HTTP (if needed):
pnpm api:dev:http # API on http://localhost:8000 pnpm web:dev:http # Web on http://localhost:3000
-
Access:
- Web App:
https://localhost:3000 - API:
https://localhost:8000 - API Docs:
https://localhost:8000/docs - Temporal UI:
http://localhost:8080
- Web App:
uv run pytest apps/api/tests/ -v uv run pytest apps/worker/tests/ -v uv run pytest apps/api/tests/ --cov=app --cov-report=html
pnpm web:test pnpm web:test:watch
End-to-end tests live in apps/web/e2e/ and run against the full Docker stack. They exercise chat flight/hotel search, trip creation + refresh, and theme validation in both light and dark mode.
# Start the full stack first docker compose up -d # Run all E2E tests pnpm web:test:e2e # Interactive UI mode pnpm web:test:e2e:ui
pnpm verify runs dependency install, build, lint, typecheck, unit test coverage, security audits, and E2E tests across all projects.
pnpm verify
uv run ruff check apps/api/app/ uv run ruff check apps/api/app/ --fix uv run ruff format apps/api/app/
uv run pip-audit pnpm --filter vacation-price-tracker-web audit --prod
pnpm sonar
You can optionally use mcp-review for AI-powered, context-aware code review against your local git history — no PR required. It's pre-configured via .mcp-review.yml.
mcp-review --staged mcp-review --last 3
apps/api/
├── app/
│ ├── core/ # Config, constants, security
│ ├── clients/ # External API clients (Skiplagged, Groq)
│ ├── db/ # Database session, deps
│ ├── models/ # SQLModel database models
│ ├── routers/ # FastAPI route handlers
│ ├── middleware/ # CSRF, rate limiting, idempotency
│ ├── services/ # Business logic services
│ ├── tools/ # MCP tool implementations
│ └── main.py # FastAPI app entry point
└── tests/
├── clients/ # API client tests
├── integration/ # End-to-end tests
├── middleware/ # Middleware tests
├── routers/ # Endpoint tests
├── services/ # Service layer tests
├── tools/ # MCP tool tests
└── *.py # Unit tests (auth, models, schemas, etc.)
apps/web/
├── src/ # Next.js app
├── e2e/ # Playwright E2E tests
└── playwright.config.ts
| Phase | Flights | Hotels | Date Optimizer | Cost |
|---|---|---|---|---|
| MVP (Phase 1-3) | Skiplagged MCP | Skiplagged MCP | N/A | 0ドル |
| Phase 4 | Skiplagged MCP | Skiplagged MCP | SearchAPI (optional) | ~40ドル/mo for optimizer |
- Single-Provider Strategy: Skiplagged MCP provides both flights and hotels via one public endpoint. No auth, no quota juggling, no carrier gaps to work around.
- Airline Filtering: MCP responses don't expose query-level airline filters, so the worker extracts carrier codes from Skiplagged flight IDs and filters post-fetch against
trip.flight_prefs.airlines. - Room Type / View Filtering: For each trip refresh the worker pulls
sk_hotel_detailsfor the top 20 hotels by price and matches room titles / amenities againstpreferred_room_typesandpreferred_views. - OAuth on Home Server: Resolved via Cloudflare Tunnel for a public OAuth callback URL without port forwarding.
- Distributed Workflow Reliability: Temporal's Saga pattern ensures partial failures don't corrupt state during multi-step price fetches.
The web app is hosted on Vercel:
- Connect your GitHub repository to Vercel.
- Select the
apps/web/directory as the project root. - Configure
NEXT_PUBLIC_API_URLin the Vercel dashboard to point at your FastAPI backend.
Host on Fly.io, Render, AWS, or any container platform. Must be reachable from the Vercel frontend.
With the frontend on Vercel and backend elsewhere, configure CORS:
from fastapi.middleware.cors import CORSMiddleware app.add_middleware( CORSMiddleware, allow_origins=["https://your-vercel-domain.vercel.app"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], )
All state-changing requests require a CSRF token. Frontend includes the token in request headers; backend middleware validates it.