Skip to main content
Glama
joelynli0110

open-transfer-times

Open Transfer Times

CI npm

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 confidence

Each 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-times
const { 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

airports/

One YAML file per airport: transfer allowances, sourced facts, citations. Contributions welcome.

packages/core

open-transfer-times: dependency-free engine that turns an itinerary into an explained assessment.

packages/mcp

open-transfer-times-mcp: MCP server so AI agents can check and rank connections, with cited sources.

eval/

Agent evaluation: 36 questions run through Claude with the MCP server attached, graded on tool use and answer accuracy.

extension/

Chrome extension that runs the engine on the itinerary open in Google Flights (beta).

src/, pyproject.toml

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.

  1. Open and expand a one-stop itinerary on Google Flights.

  2. Select the Connection Quality badge in the lower-right corner.

  3. 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.

  4. 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.

  5. 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.yaml covers 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 UI
  • extension/src/adapters/googleFlights/ contains all Google Flights parsing and selectors.

  • airports/*.yaml and regions/schengen.yaml are the editable transfer dataset; npm run airports:build validates them and generates packages/core/src/data/airport-data.generated.js.

  • packages/core/src/airport-catalog.js is generated from the public-domain OurAirports dataset.

  • packages/core/src/itinerary.js owns the stable source-neutral segment model.

  • packages/core/src/airport-rules.js adapts the generated dataset to engine rules.

  • packages/core/src/engine.js contains DOM-free assessment logic. npm run build copies the core files into dist/core/, so the extension and the library ship identical code.

  • extension/src/content.js renders the lightweight badge, confirmation flow, and detail panel.

  • extension/src/analytics.js defines 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 build

Normal 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:catalog

This 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:

  1. Open chrome://extensions.

  2. Enable Developer mode.

  3. Select Load unpacked.

  4. Choose this repository's dist/ directory.

  5. 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:chrome

The generated ZIP is written to release/ with manifest.json at the archive root.

Tests

npm run check

The 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 pytest

Node 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:

  1. 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.

  2. Open an explicit self-transfer. Confirm that the physical quality is based on transfer processes while the ticket-protection warning becomes severe.

  3. Open an itinerary with no explicit ticket evidence. Confirm that the extension asks for One protected ticket, Separate tickets, or Not sure.

  4. Open a search-result itinerary without booking-URL certainty. Confirm that the route screen appears before any quality verdict.

  5. Run the extension tests on machines in different timezones. The included DST regression expects 01:30 → 03:30 to remain 120 minutes and 23:30 → 01:00 the next day to remain 90 minutes.

Available Tools

4 tools
check_connectionCheck a flight connectionA
Read-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.

NameRequiredDescriptionDefault
toYesDestination airport of the second flight (IATA), e.g. PVG
viaYesConnection airport (IATA), e.g. IST
fromYesOrigin airport of the first flight (IATA), e.g. BER
ticketNoprotected = both flights on one ticket/booking; separate = self-transfer on separate tickets; unknown if not statedunknown
arrival_timeNoScheduled arrival of the first flight at the connection airport, local time, YYYY-MM-DDTHH:mm. Give both times, or layover_minutes instead.
departure_timeNoScheduled departure of the second flight from the connection airport, local time, YYYY-MM-DDTHH:mm. Give both times, or layover_minutes instead.
baggage_recheckNoSet true if checked bags must be collected and checked in again
layover_minutesNoScheduled 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_changeNoSet true if a terminal change is known but terminals are not given
arrival_terminalNoArrival terminal at the connection airport, if known
departure_terminalNoDeparture terminal at the connection airport, if known

Output Schema

NameRequiredDescription
routeYes
stepsYes
ticketYes
factorsYes
qualityYes
verdictYes
confidenceYesHow far the estimate can be trusted, independent of the quality rating. Low means key information is missing.
disclaimerYes
buffer_minutesYes
layover_sourceYes
layover_minutesYes
connection_airportYes
transfer_process_minutesYes

TDQS

A4.1/5.0
Behavior4/5

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.

Conciseness4/5

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.

Completeness5/5

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.

Parameters4/5

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.

Purpose4/5

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.

Usage Guidelines4/5

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 connectionsA
Read-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).

NameRequiredDescriptionDefault
optionsYesTwo to twenty itineraries to compare

Output Schema

NameRequiredDescription
rankedYes
disclaimerYes

TDQS

A4.3/5.0
Behavior4/5

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.

Conciseness5/5

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.

Completeness5/5

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.

Parameters3/5

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.

Purpose5/5

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.

Usage Guidelines4/5

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 profileA
Read-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.

NameRequiredDescriptionDefault
airportYesIATA airport code, e.g. LHR

Output Schema

NameRequiredDescription
iataYes
nameNo
factsYes
coverageYes
allowancesYes
has_profileYes
last_reviewedNo

TDQS

A3.7/5.0
Behavior4/5

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.

Conciseness5/5

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.

Completeness5/5

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.

Parameters3/5

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.

Purpose4/5

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.

Usage Guidelines2/5

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 profilesA
Read-onlyIdempotent

List the airports that have their own transfer profile, with how well each is sourced.

NameRequiredDescriptionDefault

No parameters

Output Schema

NameRequiredDescription
airportsYes

TDQS

A3.9/5.0
Behavior4/5

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.

Conciseness5/5

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.

Completeness4/5

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.

Parameters4/5

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.

Purpose4/5

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.

Usage Guidelines3/5

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.

  1. 4 tool updatesv0.1.0
    • First observedcheck_connection
    • First observedcompare_connections
    • First observedget_airport_profile
    • First observedlist_airports

Related MCP Connectors

Related MCP Servers

  • A
    license
    B
    quality
    A
    maintenance
    Enables 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.
    46
    4
    MIT
  • A
    license
    Not graded
    quality
    B
    maintenance
    Provides 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