Aviasales Flight Prices

io.github.stufentlyv0.5.0更新於 Oct 9, 2026

Flight price search via Aviasales/Travelpayouts: fares by route, cheapest days, flexible dates.

概覽

AI 產生的概覽

讓助理依航線、最便宜日期、彈性日期、預算與鄰近機場查詢 Aviasales/Travelpayouts 的快取機票價格。

功能
十三個唯讀工具用來回答機票價格問題:search_flights 查詢兩個城市在某日或某月的票價,get_prices_calendar 找出最便宜的出發日,get_flexible_date_prices 比較前後幾天,另有 get_latest_prices、get_popular_directions、get_city_directions、get_alternative_directions 用於靈感搜尋與鄰近機場,search_by_price_range 用於預算查詢。參考類工具(lookup_airlines、lookup_airports、lookup_cities、lookup_countries、find_nearest_airports)會把地名解析成 IATA 代碼並依距離排序機場。回應帶有 status、price_summary 與提示訊息,價格一律為經濟艙成人單價。
適用情境
適合讓助理用自然語言回答機票價格問題:某航線某月的票價、最便宜的出發日、改一天是否更便宜,或在預算內能飛往哪裡。它面向行程規劃與靈感搜尋,而不是訂票,因為它只做搜尋並提供外部連結。
執行需求
以本機 stdio 程序執行,通常由 MCP 用戶端啟動容器映像 ghcr.io/stufently/aviasales-mcp,需要 Docker;README 提到之後會支援 PyPI/uvx。必須提供免費的 Travelpayouts API 權杖,放在 AVIASALES_API_TOKEN 中。選用設定包括 AVIASALES_MARKET、AVIASALES_PARTNER_ID、AVIASALES_DEFAULT_CURRENCY 與 AVIASALES_LOCALE。需要能連線到 Travelpayouts Data API 的網路。
安裝前請注意
必要的 AVIASALES_API_TOKEN 是機密:README 提醒不要把它貼到會提交進 git 的專案層級設定檔,建議使用使用者層級設定或將該檔案加入 .gitignore。選用的 HTTP 模式會把服務暴露在連接埠上;未設定 MCP_AUTH_TOKEN 時沒有驗證,任何能連到該連接埠的人都會消耗你的 Travelpayouts 配額,而且沒有 TLS。價格是近期搜尋的快取而非即時機位,此伺服器從不預訂或購買。

安裝

在 SourceWeft 中

  1. 開啟 儀表板中的 Aviasales Flight Prices,將其新增到工作區。
  2. 為需要使用其工具的對話啟用該服務。

Desktop only,透過 STDIO。 STDIO 服務會啟動本機處理程序,因此需要 SourceWeft 桌面主機。

其他 MCP 客戶端

參照 儲存庫 中的啟動說明。

README

aviasales-mcp

MCP server for flight price search via Aviasales / Travelpayouts Data API. Thirteen read-only tools that let Claude Code, Claude Desktop, Cursor or any other MCP client answer flight-price questions: fares by route and month, the cheapest day to fly, flexible dates, budget and inspiration search, plus airport/city/airline lookup.

Unlike Google-Flights-scraping MCP servers, place names do not have to be guessed into IATA codes by the model: lookup_cities and find_nearest_airports resolve them.

Common prompts

Ask your agent in plain language — it picks the tool.

PromptTools it reaches for
"How much is a flight from Moscow to Istanbul in March?"lookup_cities → search_flights
"What's the cheapest day to fly to Bangkok in September?"get_prices_calendar
"I'm flying Berlin→Lisbon on 12 May, back on the 19th — would shifting a day either way be cheaper?"get_flexible_date_prices
"Where can I fly from St Petersburg for under 30 000 ₽?"get_city_directions, search_by_price_range
"Which airport should I fly into for Pattaya, and what does it cost from Dubai?"find_nearest_airports → search_flights
"Evening departures only, two adults and a child, business class."search_flights with depart_after, adults, children, trip_class

Install

You need two things: Docker, and a free Travelpayouts API token from https://www.travelpayouts.com/programs/100/tools/api.

The server ships as a container image, ghcr.io/stufently/aviasales-mcp. Your MCP client starts it on demand, so there is nothing to install beyond pasting one block below. To check the image starts, send it one initialize — it should print a JSON reply naming the server, "name":"Aviasales". This does not test the token itself; a wrong one surfaces as an error on the first search:

bash
echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"check","version":"0"}}}' \  | docker run -i --rm -e AVIASALES_API_TOKEN=your-token-here ghcr.io/stufently/aviasales-mcp:latest

:latest follows releases; pin a version tag (:0.5.0) if you would rather upgrade by hand. The package is also PyPI-ready; once it is on PyPI, uvx aviasales-mcp will work wherever docker run -i --rm … image does below.

MCP client configs

Every block runs the same command. -e AVIASALES_API_TOKEN with no value hands the container the variable the client sets in env, so the token is written in exactly one place. AVIASALES_MARKET is optional but worth setting — see Configuration; delete it and its -e pair if you do not need it.

Keep the token out of anything you commit. A project-level .mcp.json, .cursor/mcp.json or .zed/settings.json is a normal thing to check into git, and a token pasted there goes with it. Prefer the user-level config file, or .gitignore the project one.

Claude Code

bash
claude mcp add --scope user aviasales -e AVIASALES_API_TOKEN=your-token-here -- \  docker run -i --rm -e AVIASALES_API_TOKEN ghcr.io/stufently/aviasales-mcp:latest

--scope user makes it available in every project and keeps the token in ~/.claude.json, out of the repository. Check with claude mcp list.

Claude Desktop, Cursor, Windsurf

All three take the same mcpServers JSON; only the file differs:

ClientConfig file
Claude Desktop~/Library/Application Support/Claude/claude_desktop_config.json (macOS), %APPDATA%\Claude\claude_desktop_config.json (Windows)
Cursor~/.cursor/mcp.json (global) or .cursor/mcp.json (project)
Windsurf (Devin Desktop)~/.codeium/windsurf/mcp_config.json; newer builds read ~/.config/devin/mcp_config.json — the MCP settings page opens the right one
json
{  "mcpServers": {    "aviasales": {      "command": "docker",      "args": [        "run", "-i", "--rm",        "-e", "AVIASALES_API_TOKEN",        "-e", "AVIASALES_MARKET",        "ghcr.io/stufently/aviasales-mcp:latest"      ],      "env": {        "AVIASALES_API_TOKEN": "your-token-here",        "AVIASALES_MARKET": "ru"      }    }  }}

Claude Desktop starts with a trimmed PATH and may not find docker by name: if the server fails to start, replace "docker" with its absolute path (which docker).

Zed

~/.config/zed/settings.json (or zed: open settings):

json
{  "context_servers": {    "aviasales": {      "command": "docker",      "args": [        "run", "-i", "--rm",        "-e", "AVIASALES_API_TOKEN",        "-e", "AVIASALES_MARKET",        "ghcr.io/stufently/aviasales-mcp:latest"      ],      "env": {        "AVIASALES_API_TOKEN": "your-token-here",        "AVIASALES_MARKET": "ru"      }    }  }}

Codex

bash
codex mcp add aviasales --env AVIASALES_API_TOKEN=your-token-here -- \  docker run -i --rm -e AVIASALES_API_TOKEN ghcr.io/stufently/aviasales-mcp:latest

Or by hand in ~/.codex/config.toml:

toml
[mcp_servers.aviasales]command = "docker"args = ["run", "-i", "--rm", "-e", "AVIASALES_API_TOKEN", "-e", "AVIASALES_MARKET",        "ghcr.io/stufently/aviasales-mcp:latest"]
[mcp_servers.aviasales.env]AVIASALES_API_TOKEN = "your-token-here"AVIASALES_MARKET = "ru"

Any other stdio client

Command docker, arguments run -i --rm -e AVIASALES_API_TOKEN ghcr.io/stufently/aviasales-mcp:latest, and AVIASALES_API_TOKEN in the environment the client gives the process. The server is also listed in the official MCP registry as io.github.stufently/aviasales-mcp, which clients with a registry browser can install from directly.

Tools

Flight prices

ToolDescription
search_flightsPrices between two cities on a date or across a month (v3/prices_for_dates)
get_prices_calendarPrices grouped by day or month — the cheapest day to fly
get_flexible_date_pricesPrices for the days around your dates — "would shifting a day be cheaper?"
get_latest_pricesMost recently found fares, optionally filtered by route
get_popular_directionsWhere travellers reach a destination from
get_city_directionsCheapest destinations reachable from a city — inspiration search
get_alternative_directionsPrices for nearby airports/cities
search_by_price_rangeFlights inside a budget; omit the destination to search anywhere (no date filter — the endpoint ignores one)

search_flights, get_prices_calendar, get_flexible_date_prices and get_latest_prices accept adults (1–9), children (0–8), infants (0–8) and trip_class (economy/comfort/business/first).

The Data API serves a cache of recent searches and takes no passenger parameters, so the party is encoded into each ticket's booking_link instead — the link opens Aviasales with the full party and class pre-filled and shows the real total. The prices themselves are always per adult in economy, which is what the price_note field on every price response spells out for the model.

search_flights also takes depart_after / depart_before (HH:MM, 24-hour) to keep only departures in a time window; set depart_after later than depart_before for a window that wraps midnight (red-eyes).

Tickets carry duration_total (door-to-door minutes), duration_to / duration_back (flight time per direction) and layover_minutes (combined ground time between connections). Every price response also carries a price_summary (min/median/max) so the model can tell a good fare from a bad one without a second search.

Reference data

ToolDescription
lookup_airlinesAirlines by name or IATA code
lookup_airportsAirports by name, IATA code, or city code
lookup_citiesCities by name or IATA code — turn a city name into a code
lookup_countriesCountries by name or code
find_nearest_airportsAirports closest to a place or to lat/lon, by distance

The lookup_* tools take search, limit (default 50, max 500) and locale, and return {status, total, returned, truncated, data}. Always pass search: the underlying datasets are ~10k airports and ~9.6k cities, which is far more than any model can hold in context. Matches are ranked (exact code, then exact city code, then name), and airports with no scheduled service sort last. Each dataset is downloaded once per process and cached for 24 hours.

find_nearest_airports answers the question lookup_airports cannot: the closest airport is rarely named after the town. It resolves near="Pattaya" against the cached city and airport datasets and ranks by great-circle distance — no third-party geocoder involved.

Errors and empty results

Every response carries status ("ok" or "error"), so an empty data list is never confused with a failure. Bad input is refused before the API call, with the expected format spelled out ("departure_at must be \"YYYY-MM-DD\" or \"YYYY-MM\"…"), and both error and empty responses carry a hint naming what to try next. On a rejected argument the hint says what to substitute and which tool resolves it — enough for the model to fix the call itself on the second attempt.

Setup

Building the image yourself from a checkout, instead of pulling it:

  1. Get an API token at https://www.travelpayouts.com/programs/100/tools/api
  2. Copy .env.example to .env and fill in your token
  3. Build and run with Docker:
bash
docker build -t aviasales-mcp .docker run --env-file .env aviasales-mcp

Configuration

VariableRequiredDescription
AVIASALES_API_TOKENYesTravelpayouts API token
AVIASALES_PARTNER_IDNoPartner ID for booking links
AVIASALES_DEFAULT_CURRENCYNoDefault price currency (default: rub)
AVIASALES_MARKETNo2-letter market whose price cache to read (unset → ru)
AVIASALES_LOCALENoLanguage of reference data names (default: en)
LOG_LEVELNoLogging level (default: INFO)
MCP_PORTNoServe streamable-HTTP on this port instead of stdio (PORT also accepted)
MCP_HOSTNoBind address for HTTP mode (default: 127.0.0.1; use 0.0.0.0 in Docker)
MCP_AUTH_TOKENNoShared secret required on every HTTP request
MCP_AUTH_ALLOW_QUERY_TOKENNoAlso accept the token as ?token= (default: false)
MCP_ALLOW_INSECURE_HTTPNoPermit a non-loopback bind with no token (default: false)

AVIASALES_MARKET is worth setting: the price cache is per market, and the same route in the same currency comes back at a different price for ru and us.

HTTP transport

By default the server speaks stdio, which is what local MCP clients expect. Setting MCP_PORT switches it to streamable-HTTP so it can be reached remotely:

bash
docker run --env-file .env \  -e MCP_PORT=8080 -e MCP_HOST=0.0.0.0 -e MCP_AUTH_TOKEN=<your-secret> \  -p 8080:8080 aviasales-mcp

The endpoint is then http://<host>:8080/mcp, and every request must present the token as Authorization: Bearer <token>; anything else gets a 401.

Some MCP clients cannot set headers. MCP_AUTH_ALLOW_QUERY_TOKEN=true also accepts ?token=<token>, but note that uvicorn — and any proxy in front of it — writes the full URL to its access log, so the secret ends up in logs. Prefer the header.

There is no TLS here: terminate it at a reverse proxy if the port is reachable from anywhere untrusted.

Without MCP_AUTH_TOKEN the port is unauthenticated and anyone who reaches it can spend your Travelpayouts quota. Loopback binds are allowed (with a warning); binding anything else refuses to start unless you also set MCP_ALLOW_INSECURE_HTTP=true.

Limitations

  • Prices are a cache of recent searches, not live availability. A fare can be gone by the time the link opens; expires_at says when the quote lapses.
  • Prices are always per adult in economy. Passenger count and cabin change the booking link, never the quoted number.
  • No booking. This server searches and links out; it never holds or buys.
  • Cache coverage is uneven. An empty result means nobody searched that route recently, not that the route does not exist.
  • get_latest_prices, the matrices and nearby airports come from the older v2 response shape: they name the selling agency rather than the airline and carry no flight number.
  • Rate limits are per endpoint (600/min for most, 60/min for the week and nearby matrices). The server retries 429s and warns when the published quota runs low.

Development

bash
docker build --target dev -t aviasales-mcp-dev .docker run --rm aviasales-mcp-dev pytest -qdocker run --rm -v "$(pwd)":/app -w /app aviasales-mcp-dev ruff check src/ tests/

CI runs the suite on Python 3.12, 3.13 and 3.14 plus a Docker image build — see .github/workflows/ci.yml.

License

GPL-3.0-or-later — see LICENSE.

來源:README.md,提交 917b5b9

工具

0
工具後設資料尚未被收錄。

版本歷史

1
  1. v0.5.0最新Oct 9, 2026