@cyanheads/faa-traffic-delays-mcp-server
Track FAA ground stops, delay programs, airport delays, the operations plan, and ATCSCC advisories via MCP. STDIO or Streamable HTTP.
6 Tools
Overview
Real-time air traffic management status from the FAA Air Traffic Control System Command Center (ATCSCC), read from the NAS Status feed behind nasstatus.faa.gov and the ATCSCC advisories database. Check US airports for ground stops, Ground Delay Programs, delays, and closures; list every active event nationwide, en-route Airspace Flow Programs included; read the operations plan for later in the day; list the advisories issued on a UTC date, canceled and past programs included; and read the full advisory behind each one. Runs as a stdio process, a local Streamable HTTP server, or the public hosted endpoint above.
| Tool | Description |
|---|
faa_delays_get_airport_status | Current status of 1β25 US airports: ground stop, Ground Delay Program, delays, closures, deicing, and runway configuration with arrival rate |
faa_delays_list_active_events | Every active event across the National Airspace System, Airspace Flow Programs included, sorted by severity with per-type counts |
faa_delays_get_operations_plan | The Command Center's operations plan: programs and initiatives expected later today, with planned time and likelihood |
faa_delays_get_advisory | Full text of one ATCSCC advisory by number and UTC date: program rate, scope, comments, and the plan's constraints |
faa_delays_list_advisories | The ATCSCC advisories issued on one UTC date, newest first, with their numbers: programs as issued, proposed, revised, and canceled, reroutes, and the operations plan |
faa_delays_list_reference | Decode event types, traffic-management terms, ARTCC codes, the FAA pacing airports, and identifier formats |
Capability reference
airports: 1β25 codes, each a 3-character FAA identifier (SEA) or ICAO code (KSEA, PHNL), case-insensitive, as an array or comma-separated string; any code not in the bundled NASR directory fails the whole call as unknown_airport (with unknownCodes)
- One row per airport, in request order:
status (closed, ground_stop, ground_delay_program, delays, restrictions_only, no_active_events), listedInFeed, airportName, artcc, latitude / longitude, requestedAs for an ICAO input, and each active event: groundStop, groundDelayProgram with programStartTime and a per-15-minute delayProfile, arrivalDelay / departureDelay, closure, closureNotam, deicing
artcc comes from the NASR directory and matches the ARTCC codes in a program's includedFacilities; an airport the feed doesn't list takes its coordinates from the directory too. runwayConfiguration (runways and arrivalRatePerHour) appears only for listed airports; isPacingAirport and timezone are omitted, with a notice, when the pacing-airport list can't be read
- Optional
event_types filter over ground_stop, ground_delay_program, airspace_flow_program, arrival_delay, departure_delay, airport_closure, closure_notam, deicing (aliases gs, gdp, afp); rows are sorted by severity, with reason, delay figures, times, and an advisory reference where the FAA links one
totalActive and countsByType cover the whole feed before the filter; shown / appliedEventTypes echo what was returned. ground_delay_program rows add programStartTime beside the current revision's startTime, and airspace_flow_program rows carry afp detail (constrained area, departure and arrival filters, altitudes, delay profile)
enRouteFeed (ok, unavailable, format_changed) reports whether Airspace Flow Programs were read. An en-route failure omits them with a notice rather than failing the call, unless they are the only type requested
- No input;
terminalPlanned and enRoutePlanned items carry text, timeQualifier (after, until, by, between), timeUtc (HHMM with no date), and likelihood (possible, probable, expected)
announcements lists current ATCSCC announcements ([] when none; absent, with a notice, when the list can't be read); advisory references the full plan text for faa_delays_get_advisory
advisory_number (1β999) and date (UTC, YYYY-MM-DD; MM/DD/YYYY accepted), taken from a faa_delays_list_advisories row or an advisory reference's number and date; numbers restart at 1 each UTC day, and past advisories stay readable
- Returns
title, controlElement, subject, effectiveTime and sentAt as the advisory prints them (DDHHMM-DDHHMM, YY/MM/DD HH:MM, on operations plans and reroutes too), and the full text; a number the database doesn't hold returns found: false with guidance that points to faa_delays_list_advisories
- Text past 50,000 characters is cut and reported through
truncated and totalChars; page failures surface as advisory_service_unavailable or advisory_contract_changed
date (UTC, YYYY-MM-DD; MM/DD/YYYY accepted; no later than tomorrow; omitted β today), optional categories (ground_stop, ground_delay_program, airspace_flow_program, ctop, route, other; aliases gs, gdp, afp) and control_element (an airport by FAA or ICAO code, an ARTCC, or DCC for national advisories), limit 1β200 (default 50) and offset
- Rows newest first:
number and date for faa_delays_get_advisory, controlElement, subject, details (a reroute's or flow constrained area's name, constrained area, and valid period), and sentAt (ISO 8601 UTC); totalCount counts every match and nextOffset is present while more remain
- Without
categories the list also carries the CDM compression advisories the FAA files under no category; a date with no advisories, or a filter that matches none, returns an empty list with a notice
topic: event_types, terms, artccs, pacing_airports, or identifiers
- Only
pacing_airports calls the FAA (live, cached 6 hours); identifiers also reports the bundled NASR airport directory's cycle date and airport count
Features
Built on @cyanheads/mcp-ts-core: stdio and Streamable HTTP transports, pluggable auth (none / jwt / oauth), swappable storage (in-memory, filesystem, Supabase, Cloudflare KV/R2/D1), structured logging with optional OpenTelemetry tracing.
FAA-specific:
- Reads the NAS Status feed (
nasstatus.faa.gov/api) and the ATCSCC advisories database (www.fly.faa.gov/adv), keyless; FAA status and NASR airport data are US federal works in the public domain (17 U.S.C. Β§105)
- Airport codes are checked against a bundled snapshot of the FAA NASR airport directory, with ICAO codes mapped to FAA identifiers (
KSEA β SEA, PHNL β HNL), so a mistyped code fails instead of reading as a quiet airport; the same directory supplies each airport's ARTCC, and its coordinates when the feed doesn't list it
- Feeds are cached in process for 60 seconds (the pacing-airport list for 6 hours) and requests to each FAA host are paced; an expired snapshot is never served when a refresh fails
- Tolerant parsing of the undocumented feed: an unreadable row is skipped and counted in the
notice, and a wrong-typed field is dropped rather than coerced
Agent-friendly output:
- Typed failure reasons keep an outage (
feed_unavailable), a slow or throttled FAA (retry_deadline_exceeded, upstream_rate_limited, pacer_shed), and a format change (feed_contract_changed, not retryable) distinct; recovery hints name the tool to call next, and rate-limit errors carry retryAfter when known
- A secondary FAA list that can't be read (pacing airports, en-route events, announcements) is omitted with a flag or
notice instead of failing the call
fetchedAt dates the snapshot, and a notice flags an arrival or departure delay last updated more than 6 hours earlier, since the FAA feed can keep a delay entry after it lapses
- FAA-authored text (reasons, NOTAMs, comments, announcements, advisory text) is quoted or fenced in
content[] so it reads as data, and stays verbatim in structuredContent
Limitations:
- Informational, not operational. No substitute for an official preflight briefing or airline operations data.
- Undocumented upstream.
nasstatus.faa.gov/api/* is the dashboard's private backend, with no schema, terms, versioning, or published limits, and it can change without notice. The server fails with feed_contract_changed rather than guess.
- Airport coverage is event-driven. The feed lists only airports with an active event, so runway configuration and arrival rate are unavailable for the rest, and
no_active_events means no FAA program, not on-time flights. Per-flight EDCTs are not in the feed.
- En-route row shape is inferred, not observed. Airspace Flow Program rows follow the shape the NAS Status dashboard's own code reads; a mismatch degrades the national list with
enRouteFeed: "format_changed" rather than failing it.
- The airport directory is a snapshot. An identifier the FAA assigns after the bundled NASR cycle is rejected as unknown until the next refresh. US airports only.
Getting started
Public Hosted Instance
A public instance is available at https://faa-traffic-delays.caseyjhand.com/mcp β no installation required. Point any MCP client at it via Streamable HTTP:
{
"mcpServers": {
"faa-traffic-delays-mcp-server": {
"type": "streamable-http",
"url": "https://faa-traffic-delays.caseyjhand.com/mcp"
}
}
}
Self-Hosted / Local
Add the following to your MCP client configuration file.
{
"mcpServers": {
"faa-traffic-delays-mcp-server": {
"type": "stdio",
"command": "bunx",
"args": ["@cyanheads/faa-traffic-delays-mcp-server@latest"],
"env": {
"MCP_TRANSPORT_TYPE": "stdio",
"MCP_LOG_LEVEL": "info"
}
}
}
}
Or with npx (no Bun required):
{
"mcpServers": {
"faa-traffic-delays-mcp-server": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@cyanheads/faa-traffic-delays-mcp-server@latest"],
"env": {
"MCP_TRANSPORT_TYPE": "stdio",
"MCP_LOG_LEVEL": "info"
}
}
}
}
Or with Docker:
{
"mcpServers": {
"faa-traffic-delays-mcp-server": {
"type": "stdio",
"command": "docker",
"args": ["run", "-i", "--rm", "-e", "MCP_TRANSPORT_TYPE=stdio", "ghcr.io/cyanheads/faa-traffic-delays-mcp-server:latest"]
}
}
}
For Streamable HTTP, set the transport and start the server:
MCP_TRANSPORT_TYPE=http MCP_HTTP_PORT=3010 bun run start:http
Prerequisites
- Bun v1.4.0 or higher (or Node.js v24+).
- No API key or account: the FAA feeds are public.
Installation
- Clone the repository:
git clone https://github.com/cyanheads/faa-traffic-delays-mcp-server.git
- Navigate into the directory:
cd faa-traffic-delays-mcp-server
- Install dependencies:
- Configure environment (optional):
Configuration
The server reads no environment variables of its own: the FAA hosts, cache lifetimes, and request pacing are fixed in the services. These framework variables cover transport, logging, and telemetry.
| Variable | Description | Default |
|---|
MCP_TRANSPORT_TYPE | Transport: stdio or http. | stdio |
MCP_HTTP_PORT | HTTP server port. | 3010 |
MCP_HTTP_HOST | HTTP server host. | 127.0.0.1 |
MCP_SESSION_MODE | HTTP session mode: stateless, stateful, or auto. | stateless |
MCP_AUTH_MODE | Authentication: none, jwt, or oauth. | none |
MCP_LOG_LEVEL | Log level (debug, info, warning, error, etc.). | info |
LOGS_DIR | Directory for log files (Node.js only). | <app-root>/logs |
OTEL_ENABLED | Enable OpenTelemetry. | false |
See .env.example for the common framework overrides.
Running the server
Local development
Project structure
| Directory | Purpose |
|---|
src/mcp-server/tools | Tool definitions (*.tool.ts), plus shared output schemas, the advisory date input, notice fragments, and Markdown helpers for FAA-authored text. |
src/services/nas-status | NAS Status feed client and tolerant feed parsers. |
src/services/advisory | ATCSCC advisories database client, advisory page and index parsers, and the advisory and index URL builders. |
src/services/airport-directory | Bundled FAA NASR airport directory (name, place, ARTCC, coordinates) and ICAO β FAA crosswalk (generated module). |
src/services/upstream | Shared FAA fetch boundary (pacing, retry, status classification) and the in-process cache. |
scripts/refresh-airport-directory.ts | Regenerates the airport directory module (bun run refresh:airports). |
tests/ | Unit and integration tests, mirroring the src/ structure, with synthetic FAA fixtures. |
docs/design.md | Tool surface design, upstream API notes, design decisions, and known limitations. |
Development guide
See CLAUDE.md for development guidelines and architectural rules. The short version:
- Handlers throw, framework catches β no
try/catch in tool logic
- Use
ctx.log for logging; FAA feeds are cached in process, not in ctx.state
- Register new tools in
allToolDefinitions in src/mcp-server/tools/definitions/index.ts
- Wrap external API calls: validate raw β normalize to domain type β return output schema; never fabricate missing fields
Contributing
Issues are welcome. Run checks and tests before submitting:
bun run devcheck
bun run test
License
This project is licensed under the Apache 2.0 License. See the LICENSE file for details.