open-transfer-times
Open Transfer Times
Is this flight connection comfortable, tight, or risky? Open, cited airport transfer data, an engine that explains its verdict, and an MCP server so AI agents can check connections before recommending a flight.
Try it in 30 seconds
Ask an AI agent to compare layovers. With the MCP server connected, it gets:
1. IST 3 h BER–IST–PVG: comfortable, margin 135 min, protected itinerary, medium confidence
2. VIE 2 h BER–VIE–PVG: comfortable, margin 65 min, protected itinerary, low confidence
3. FRA 50 min BER–FRA–PVG: risky, margin -5 min, protected itinerary, medium confidenceEach result lists the transfer steps it counted, a confidence level, and the official sources behind the airport data:
BER–LHR–PVG: REASONABLE — A practical connection on one ticket.
Confidence: MEDIUM — The transfer-step durations are rule-of-thumb estimates, not sourced figures.
Layover 90 min, transfer steps 55 min, margin 35 min.
Steps counted: Move through airport 40 min (heuristic); Allow for another security check 15 min (heuristic). No other steps were counted.Use it with Claude Desktop
Add to claude_desktop_config.json, then fully restart Claude:
{
"mcpServers": {
"open-transfer-times": { "command": "npx", "args": ["-y", "open-transfer-times-mcp"] }
}
}Then ask, in a new chat: "Compare a 50-minute connection in Frankfurt, 3 hours in Istanbul and 2 hours in Doha from Berlin to Shanghai — which is safest?" Tools and options: packages/mcp.
Use it from code
npm install open-transfer-timesconst { assessConnection } = require("open-transfer-times");
const result = assessConnection(
{ segments: [
{ origin: "BER", destination: "LHR", arrivalTime: "2026-09-20T10:00" },
{ origin: "LHR", destination: "PVG", departureTime: "2026-09-20T11:30" }
] },
{ ticketStatus: "protected" }
);
result.quality; // "reasonable"
result.confidence.level; // "medium"Related MCP server: lufthaven
Why this exists
Airlines and booking systems decide whether a connection is feasible using minimum connection times (MCT) — commercial data sold by IATA and schedule providers. Open flight-search tools can find itineraries but cannot say whether a layover is realistic. This project collects what airports and airlines publish openly, cites every fact, and marks every estimate as an estimate.
The rating (comfortable → risky) and the confidence (high → low) are reported separately: an airport without a profile, or a route whose border situation is unknown, is always low confidence — missing data is never presented as "no risk".
Part | What it is |
One YAML file per airport: transfer allowances, sourced facts, citations. Contributions welcome. | |
| |
| |
Agent evaluation: 36 questions run through Claude with the MCP server attached, graded on tool use and answer accuracy. | |
Chrome extension that runs the engine on the itinerary open in Google Flights (beta). | |
| Legacy Python research code (delay simulation, EUROCONTROL benchmarks). Not published, not maintained, not used by the packages above. |
Airport data
Thirteen hub airports have their own profile; every other airport falls back to
a transparent standard baseline. Each minute value
carries a basis:
heuristic— a rule-of-thumb estimate, not yet backed by a source;sourced— derived from a cited fact.
All 13 profiles cite official airport or airline pages. Facts confirm transfer
steps — for example that Heathrow, Hong Kong, Changi, Incheon, Narita and Doha
screen transfer passengers again, or how long Munich and Narita inter-terminal
buses take — while most durations remain estimates. airports/COVERAGE.md shows how well each
airport is sourced. To add or improve an airport, see
CONTRIBUTING.md — no code required.
This is not an official MCT table, and it does not predict delays.
Next: RFC 0001 proposes rules that depend on the trip (where you arrive from, which terminals, one ticket or two) and correct time zones. Feedback welcome.
How the assessment works
The model is deterministic. Structural feasibility starts with the scheduled
layover and deducts the physical transfer steps that apply at the connection
airport: a base transfer allowance, Schengen border control when the transfer
crosses the Schengen border at a Schengen airport, terminal changes when known,
security re-screening, and explicit baggage re-check. The remaining margin maps
to comfortable (≥ 45 min), reasonable (≥ 25), tight (≥ 5) or risky.
Ticket protection is evaluated separately as disruption exposure: separate tickets change the warning and verdict, but do not consume physical transfer time by themselves.
Google Flights extension (beta)
Google Flights desktop only. Chrome Web Store listing: not yet published. See the privacy policy and Chrome Web Store submission materials.
Open and expand a one-stop itinerary on Google Flights.
Select the Connection Quality badge in the lower-right corner.
Ticket evidence is read only from explicit ticket wording in the selected itinerary. Baggage wording is recorded separately and never proves whether a booking is protected.
If route extraction needs confirmation—or ticket type is unknown—the extension shows the detected route and asks for One protected ticket, Separate tickets, or Not sure before analysis.
Results show the scheduled layover, qualitative main factors, ticket protection, and a verdict. Heuristic process allowances are not presented as measured passenger-specific times.
Current limitations
Google Flights has no stable public DOM contract. Detection can require fixture updates when its accessible markup changes.
The parser targets one expanded, one-stop desktop itinerary on
www.google.com/travel/flights.If only a generic container or page accessibility text is available, the extension may extract the route for confirmation but forces ticket type to
unknown; it never derives ticket protection from page-wide text and never auto-analyzes that fallback.If multiple viable itinerary candidates remain without a unique booking, active-dialog, selected, or expanded signal, detection fails closed and asks the user to open one itinerary.
Airport-specific rules cover 13 hubs; other airports use the standard international-transfer baseline. Most minute values are still heuristic (see coverage).
The Schengen/non-Schengen airport map in
regions/schengen.yamlcovers common routes, not every airport.Schengen passport-control logic applies only when the connection airport is itself in Schengen; it does not claim to model every jurisdiction's transit-immigration rules.
Terminal changes are used only when known; the extension does not guess missing terminal data.
Airport, terminal, and gate values are displayed only when supplied by the open itinerary. Gates are usually assigned near departure, so an absent gate is shown as not published rather than guessed.
There is no historical delay prediction, calibrated missed-connection probability, live gate feed, weather, rebooking, account, payment, or booking functionality.
Privacy and permissions
The extension processes the visible itinerary locally in the active Google Flights page. It does not call a backend, store itinerary data, read browsing history, or send arbitrary page content anywhere.
Manifest V3 requests only activeTab, used when the user selects the toolbar action to open the page panel. The content script is limited to Google Flights on www.google.com; there is no <all_urls> access, background service worker, or analytics provider. The analytics abstraction emits local in-page events only and is ready for a privacy-reviewed provider later.
Architecture
Google Flights
→ site adapter registry
→ NormalizedItinerary (packages/core)
→ deterministic Connection Quality Engine (packages/core, rules from airports/*.yaml)
→ ConnectionAssessment
→ Chrome Extension UIextension/src/adapters/googleFlights/contains all Google Flights parsing and selectors.airports/*.yamlandregions/schengen.yamlare the editable transfer dataset;npm run airports:buildvalidates them and generatespackages/core/src/data/airport-data.generated.js.packages/core/src/airport-catalog.jsis generated from the public-domain OurAirports dataset.packages/core/src/itinerary.jsowns the stable source-neutral segment model.packages/core/src/airport-rules.jsadapts the generated dataset to engine rules.packages/core/src/engine.jscontains DOM-free assessment logic.npm run buildcopies the core files intodist/core/, so the extension and the library ship identical code.extension/src/content.jsrenders the lightweight badge, confirmation flow, and detail panel.extension/src/analytics.jsdefines the provider-free event seam.
The extension contains only public structural rules. A future server-backed implementation can replace the engine behind the same normalized input and assessment output without coupling DOM selectors to commercial logic.
The Skyscanner adapter source remains in extension/src/adapters/skyscanner/ as unregistered experimental code. It is not referenced by the production manifest, copied into dist/, or supported in this alpha.
Legacy research code
The Python package under src/connection_risk/, ADRR ingestion under src/connection_risk/ingestion/, historical providers, simulation, calibration/benchmarking tests, datasets, API, and the unused v0.4 extension service worker are preserved as earlier experiments. They are outside the MVP build and runtime path.
npm run build copies only the files referenced by the current manifest into dist/. It does not package the legacy localhost API service worker or diagnostic pages. No ADRR file, historical dataset, Python service, or delay distribution is required to build or run the MVP.
Local development
Requirements: Node.js 20+ and Chrome desktop.
npm install
npm run buildNormal installation and production builds use the committed runtime catalog at packages/core/src/airport-catalog.js; they do not require data/public/ or any local dataset. Developers may optionally refresh that catalog with:
npm run airports:catalogThis maintenance command requires data/public/airports.csv. The source CSV is intentionally ignored and is not needed for npm install, npm run check, or npm run build.
Then in Chrome:
Open
chrome://extensions.Enable Developer mode.
Select Load unpacked.
Choose this repository's
dist/directory.Open or reload Google Flights and expand a one-stop itinerary.
After source changes, run npm run check, then select Reload on the extension card and reload the Google Flights tab.
Create the reproducible Chrome Web Store submission package with:
npm run package:chromeThe generated ZIP is written to release/ with manifest.json at the archive root.
Tests
npm run checkThe release gate validates the airport dataset (and that generated files are current), runs syntax checks, the core and extension tests, and the production extension build. The test runner is compatible with the declared Node.js 20+ range and does not depend on newer node --test isolation flags.
Run the preserved Python research suite separately if you change legacy code:
.\.venv\Scripts\python.exe -m pytestNode tests cover airport-dataset validation and provenance, normalized-model validation, selected-itinerary ticket scoping, deterministic structural quality bands, protection exposure, explicit baggage re-check processes, DST-safe wall-clock arithmetic, confirmation gating, unsupported-airport fallback, overnight parsing, localized time formats, booking URL extraction, and defensive parser failures. They use extracted text/DOM stubs and never call live Google Flights.
Alpha manual checks
Before an alpha session, verify these cases on Google Flights:
Open a normal protected connection, then confirm that an unrelated Separate tickets warning elsewhere on the results page does not change the selected itinerary's ticket status.
Open an explicit self-transfer. Confirm that the physical quality is based on transfer processes while the ticket-protection warning becomes severe.
Open an itinerary with no explicit ticket evidence. Confirm that the extension asks for One protected ticket, Separate tickets, or Not sure.
Open a search-result itinerary without booking-URL certainty. Confirm that the route screen appears before any quality verdict.
Run the extension tests on machines in different timezones. The included DST regression expects
01:30 → 03:30to remain 120 minutes and23:30 → 01:00the next day to remain 90 minutes.
Available Tools
4 toolscheck_connectionCheck a flight connectionARead-onlyIdempotent
Assess whether a one-stop flight connection leaves enough time to transfer. Deducts the transfer steps that apply at the connection airport (walking, Schengen passport control, terminal change, security re-screening, baggage re-check) from the scheduled layover and returns comfortable / reasonable / tight / risky, the reasons, ticket-protection exposure, and the cited airport facts behind the estimate. Give the scheduled arrival and departure as local wall-clock times at the connection airport, or only layover_minutes when that is all the user said. Explain the result using the returned steps; airport facts are context and only apply where their scope matches the trip.
| Name | Required | Description | Default |
|---|---|---|---|
| to | Yes | Destination airport of the second flight (IATA), e.g. PVG | |
| via | Yes | Connection airport (IATA), e.g. IST | |
| from | Yes | Origin airport of the first flight (IATA), e.g. BER | |
| ticket | No | protected = both flights on one ticket/booking; separate = self-transfer on separate tickets; unknown if not stated | unknown |
| arrival_time | No | Scheduled arrival of the first flight at the connection airport, local time, YYYY-MM-DDTHH:mm. Give both times, or layover_minutes instead. | |
| departure_time | No | Scheduled departure of the second flight from the connection airport, local time, YYYY-MM-DDTHH:mm. Give both times, or layover_minutes instead. | |
| baggage_recheck | No | Set true if checked bags must be collected and checked in again | |
| layover_minutes | No | Scheduled time between arrival and departure, in minutes. Use this when the user gives only a layover duration; do not invent dates or clock times. | |
| terminal_change | No | Set true if a terminal change is known but terminals are not given | |
| arrival_terminal | No | Arrival terminal at the connection airport, if known | |
| departure_terminal | No | Departure terminal at the connection airport, if known |
Output Schema
| Name | Required | Description |
|---|---|---|
| route | Yes | |
| steps | Yes | |
| ticket | Yes | |
| factors | Yes | |
| quality | Yes | |
| verdict | Yes | |
| confidence | Yes | How far the estimate can be trusted, independent of the quality rating. Low means key information is missing. |
| disclaimer | Yes | |
| buffer_minutes | Yes | |
| layover_source | Yes | |
| layover_minutes | Yes | |
| connection_airport | Yes | |
| transfer_process_minutes | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly, idempotent, non-destructive and non-open-world, so the safety profile is covered. The description adds real behavioral content beyond that: it explains the deduction logic (walking, Schengen passport control, terminal change, security re-screening, baggage re-check), the output verdict categories, ticket-protection exposure, and the cited airport facts. It stops short of noting data freshness or how missing airport data is handled.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Purpose is front-loaded in the first sentence and each subsequent sentence carries a distinct instruction (input modes, output contents, interpretation caveat). It is dense but a single unbroken paragraph, and the final sentence about airport-fact scope is a touch abstract.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With 11 parameters, full schema coverage, an output schema, and annotations all present, the description covers the remaining gaps an agent needs: how to choose between time-based and duration-based input, what the verdicts mean, and how to explain the result. Nothing material is missing for correct invocation.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100%, so the baseline is 3, but the description adds genuine value by explaining the two mutually exclusive input modes (paired local times vs. layover_minutes) and by stressing local-time semantics at the connection airport. That meaning is not fully conveyed by the per-field schema descriptions alone.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The opening sentence states a specific verb and resource: assess whether a one-stop connection leaves enough transfer time. The 'one-stop' scoping implicitly separates it from compare_connections, but no sibling is named, so an agent must infer the boundary between checking one connection and comparing several.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives clear input-selection guidance: supply local wall-clock arrival and departure times, or layover_minutes when that is all the user said, and it warns not to invent dates or clock times. It also constrains interpretation ('airport facts are context and only apply where their scope matches the trip'). What it does not do is state when to prefer this over compare_connections or get_airport_profile.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
compare_connectionsCompare and rank flight connectionsARead-onlyIdempotent
Assess several one-stop itineraries (for example the results of a flight search) and rank them from safest to riskiest connection: by transfer quality, then ticket protection (one ticket before unknown before self-transfer), then confidence, then remaining margin. Each option takes times or layover_minutes, like check_connection. Use the optional id to map results back to your own options (e.g. an offer id or price).
| Name | Required | Description | Default |
|---|---|---|---|
| options | Yes | Two to twenty itineraries to compare |
Output Schema
| Name | Required | Description |
|---|---|---|
| ranked | Yes | |
| disclaimer | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Beyond the annotations, the description discloses the ranking behavior and priority order: transfer quality, ticket protection, confidence, then remaining margin. It also explains how the optional id maps results back to the caller's options. It does not cover edge cases such as insufficient data or how confidence and margin are computed, but the core ranking logic is transparent.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is compact and front-loaded: it states the comparison task, the ranking criteria, and the key input convention in three efficient sentences. Every sentence contributes useful information without repetition.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given the rich input schema, full field descriptions, annotations, and the presence of an output schema, the description provides enough context for an agent to invoke the tool correctly. It explains the ranking purpose and the main input conventions without needing to restate return values.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100% and the nested option fields are already well documented in the schema. The description adds some meaning by noting that each option can take times or layover_minutes and by explaining the purpose of the optional id, but most parameter semantics are already carried by the schema.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource: assess several one-stop itineraries and rank them from safest to riskiest. It also distinguishes the comparison behavior from per-connection checking by saying each option is shaped like check_connection, which helps separate it from the sibling tool.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives a clear usage context: several one-stop itineraries, for example flight-search results, should be compared and ranked. However, it does not explicitly say when to use this instead of check_connection or when not to use it, so the alternative-routing guidance is only implied.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
get_airport_profileGet an airport's transfer profileARead-onlyIdempotent
Return the transfer allowances used for an airport, whether each is a heuristic estimate or sourced, and the cited facts (with official source links) about transferring there. Airports without a profile use the standard baseline.
| Name | Required | Description | Default |
|---|---|---|---|
| airport | Yes | IATA airport code, e.g. LHR |
Output Schema
| Name | Required | Description |
|---|---|---|
| iata | Yes | |
| name | No | |
| facts | Yes | |
| coverage | Yes | |
| allowances | Yes | |
| has_profile | Yes | |
| last_reviewed | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover the safety profile as read-only, idempotent, non-destructive, and closed-world. The description adds useful behavioral context beyond that, disclosing data provenance, official source links, and the standard-baseline fallback for missing profiles.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two sentences, front-loaded with the return contents and followed immediately by the fallback rule. There is no waste or redundancy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a single-parameter read-only lookup with annotations and an output schema, the description is complete enough. It covers the returned information and the missing-profile fallback behavior.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema fully documents the single airport parameter as an IATA code with an example. The description does not add syntax or format detail beyond the schema, so baseline 3 is appropriate when schema coverage is 100%.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource: returns an airport's transfer allowances, including whether each is heuristic or sourced and cited facts with links. The purpose is clear, though it does not explicitly distinguish this lookup from siblings like check_connection or compare_connections.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explains what is returned and notes the baseline fallback for airports without a profile, but gives no explicit guidance on when to use this tool versus alternatives. It leaves usage selection to inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
list_airportsList airports with transfer profilesARead-onlyIdempotent
List the airports that have their own transfer profile, with how well each is sourced.
| Name | Required | Description | Default |
|---|---|---|---|
No parameters | |||
Output Schema
| Name | Required | Description |
|---|---|---|
| airports | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare readOnly, idempotent, non-destructive, closed-world, so the safety profile is fully covered. The description still adds value by disclosing what the result set contains — only profiled airports, each annotated with how well it is sourced — which the annotations cannot convey. It says nothing about result size or ordering, which is a minor gap.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
A single front-loaded sentence with the filtering condition before the returned metadata. Every clause earns its place and nothing is redundant with the title.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a zero-param read tool with rich annotations and an output schema, this is nearly complete — the agent knows what it gets and why. Only the implicit relationship to get_airport_profile and any ordering/pagination behavior are unaddressed.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Zero parameters, so the baseline of 4 applies; there is nothing for the description to compensate for. The schema is empty and 100% covered.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Specific verb+resource ("List the airports") with a meaningful scope qualifier — only airports that have their own transfer profile — plus the nature of the returned metadata (sourcing quality). It does not explicitly distinguish itself from get_airport_profile, though the list-vs-single contrast is strongly implied by the verb.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage is implied: call this to discover which airports have profiles before drilling into one with get_airport_profile. No explicit when-to-use/when-not, and no mention of the sibling tools by name, so the agent must infer the routing.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
4 tool updates
v0.1.0- First observed
check_connection - First observed
compare_connections - First observed
get_airport_profile - First observed
list_airports
Related MCP Connectors
Air route and connection intelligence
Airport security wait times, forecasts, FAA delays, EES border queues and baggage stats.
EU261 flight delay compensation: check eligibility, compute EV, track fares. 6 tools, 35+ airports.
Estimate mobile-data needs and build a provider-neutral connectivity checklist for any trip.
Related MCP Servers
- AlicenseBqualityAmaintenanceEnables flight planning and aviation operations through intelligent airport resolution, great-circle route calculation, and aircraft performance estimation. Supports 28,000+ airports worldwide and 190+ aircraft types for comprehensive flight planning via natural language.464MIT
- AlicenseNot gradedqualityCmaintenanceProvides real-time flight status, airport weather, delays, cheap flight deals, and TSA wait times without requiring an API key.19 npmMIT

FlightQueueofficial
AlicenseNot gradedqualityBmaintenanceProvides AI assistants with real-time and forecast airport intelligence, including security wait times, FAA delays, EU border queues, lounge and terminal info, aviation weather, and baggage performance for over 8,000 airports.MIT- FlicenseNot gradedqualityBmaintenanceEnables agents to evaluate layover time budgets and generate timed sightseeing or culinary micro-tours that account for immigration buffers, round-trip metro/taxi latencies, and connection safety.7-