io.github.cyanheads/faa-airmen-registry-mcp-server
Search FAA airmen certificates, ratings, and informational medical records.
Que pouvez-vous faire avec io.github.cyanheads/faa-airmen-registry-mcp-server ?
- Offline SQLite lookups over the four monthly source files (
PILOT_BASIC,NONPILOT_BASIC,PILOT_CERT,NONPILOT_CERT), joined by FAA Unique ID, keeping every distinct certificate and differing identity variant - Street addresses are discarded before anything is stored. City and ZIP import, output, and filtering need
FAA_AIRMEN_INCLUDE_LOCATION=true; state is always available - Serving-time redaction is on by default (
FAA_AIRMEN_REDACT_PII, onlyfalseturns it off): name searches need first and last name,prefixmatching and city/ZIP filters are refused, city and ZIP are omitted from output, and `faa_airmen_check_medical_sta... - The
faa-airmen-registry-mirrorCLI downloads and validates each snapshot into an immutable generation; a failed refresh leaves the last complete one serving - The FAA omits airmen who withhold address release and publishes no certificate numbers or recurrent-training dates. A miss is not evidence that someone is uncertificated, and a match is not a current-privilege determination; use the [FAA's official services...
- Provenance on every snapshot-backed response: source name, snapshot month, source age with a
staleflag, generation ID, and the generation's location import policy, plus the normalizedappliedFilters;faa_airmen_list_referencetopicmirror_statusre... - Whole-record paging: list tools return complete people within a ~24 KiB response budget, so
limit(default 10, at most 50; a larger limit returns 50 with a notice) is an upper bound.totalCount,returnedCount, andnextOffsetdescribe the actual page... - Typed failures that name the next call:
mirror_missing,location_access_disabled,pii_redacted,invalid_filter, and the other declared reasons each carry a recovery step. A validation failure names the field and its expected form (`state must be a t...
Installation
Ajoutez ceci à la configuration MCP de votre client (Claude Desktop : claude_desktop_config.json ; Cursor : .cursor/mcp.json ; VS Code et les autres utilisent la même forme « mcpServers »).
claude mcp add --transport http faa-airmen-registry-mcp-server 'https://faa-airmen-registry.caseyjhand.com/mcp'
{
"mcpServers": {
"faa-airmen-registry-mcp-server": {
"type": "http",
"url": "https://faa-airmen-registry.caseyjhand.com/mcp"
}
}
}
@cyanheads/faa-airmen-registry-mcp-serverclaude mcp add faa-airmen-registry-mcp-server -e 'FAA_AIRMEN_MIRROR_DIR=<FAA_AIRMEN_MIRROR_DIR>' -e 'FAA_AIRMEN_INCLUDE_LOCATION=false' -e 'FAA_AIRMEN_REDACT_PII=true' -e 'FAA_AIRMEN_STALE_AFTER_DAYS=45' -e 'MCP_LOG_LEVEL=info' -- npx -y @cyanheads/faa-airmen-registry-mcp-server@0.2.1
{
"mcpServers": {
"faa-airmen-registry-mcp-server": {
"command": "npx",
"args": [
"-y",
"@cyanheads/faa-airmen-registry-mcp-server@0.2.1"
],
"env": {
"FAA_AIRMEN_MIRROR_DIR": "<FAA_AIRMEN_MIRROR_DIR>",
"FAA_AIRMEN_INCLUDE_LOCATION": "false",
"FAA_AIRMEN_REDACT_PII": "true",
"FAA_AIRMEN_STALE_AFTER_DAYS": "45",
"MCP_LOG_LEVEL": "info"
}
}
}
}
Variables d'environnement
FAA_AIRMEN_MIRROR_DIRPersistent local mirror directory; defaults to the platform user-data directory.FAA_AIRMEN_INCLUDE_LOCATIONOpt in to city and ZIP import; display and filtering also need FAA_AIRMEN_REDACT_PII=false.FAA_AIRMEN_REDACT_PIIServing-time personal-data redaction, on by default; only 'false' turns it off.FAA_AIRMEN_STALE_AFTER_DAYSSource-age warning threshold in days, integer 1–366.MCP_LOG_LEVELSets the minimum log level for output (e.g., 'debug', 'info', 'warn').
@cyanheads/faa-airmen-registry-mcp-serverclaude mcp add faa-airmen-registry-mcp-server -e 'FAA_AIRMEN_MIRROR_DIR=<FAA_AIRMEN_MIRROR_DIR>' -e 'FAA_AIRMEN_INCLUDE_LOCATION=false' -e 'FAA_AIRMEN_REDACT_PII=true' -e 'FAA_AIRMEN_STALE_AFTER_DAYS=45' -e 'MCP_TRANSPORT_TYPE=<MCP_TRANSPORT_TYPE>' -e 'MCP_HTTP_HOST=127.0.0.1' -e 'MCP_HTTP_PORT=3010' -e 'MCP_HTTP_ENDPOINT_PATH=/mcp' -e 'MCP_AUTH_MODE=none' -e 'MCP_LOG_LEVEL=info' -- npx -y @cyanheads/faa-airmen-registry-mcp-server@0.2.1
{
"mcpServers": {
"faa-airmen-registry-mcp-server": {
"command": "npx",
"args": [
"-y",
"@cyanheads/faa-airmen-registry-mcp-server@0.2.1"
],
"env": {
"FAA_AIRMEN_MIRROR_DIR": "<FAA_AIRMEN_MIRROR_DIR>",
"FAA_AIRMEN_INCLUDE_LOCATION": "false",
"FAA_AIRMEN_REDACT_PII": "true",
"FAA_AIRMEN_STALE_AFTER_DAYS": "45",
"MCP_TRANSPORT_TYPE": "<MCP_TRANSPORT_TYPE>",
"MCP_HTTP_HOST": "127.0.0.1",
"MCP_HTTP_PORT": "3010",
"MCP_HTTP_ENDPOINT_PATH": "/mcp",
"MCP_AUTH_MODE": "none",
"MCP_LOG_LEVEL": "info"
}
}
}
}
Variables d'environnement
FAA_AIRMEN_MIRROR_DIRPersistent local mirror directory; defaults to the platform user-data directory.FAA_AIRMEN_INCLUDE_LOCATIONOpt in to city and ZIP import; display and filtering also need FAA_AIRMEN_REDACT_PII=false.FAA_AIRMEN_REDACT_PIIServing-time personal-data redaction, on by default; only 'false' turns it off.FAA_AIRMEN_STALE_AFTER_DAYSSource-age warning threshold in days, integer 1–366.MCP_TRANSPORT_TYPESelects the HTTP transport.MCP_HTTP_HOSTThe hostname for the HTTP server.MCP_HTTP_PORTThe port to run the HTTP server on.MCP_HTTP_ENDPOINT_PATHThe endpoint path for the MCP server.MCP_AUTH_MODEAuthentication mode to use: 'none', 'jwt', or 'oauth'.MCP_LOG_LEVELSets the minimum log level for output (e.g., 'debug', 'info', 'warn').
Documentation
Tirée du README du projet sur GitHub, actualisée chaque semaine. Les liens ouvrent GitHub.
Sommaire
@cyanheads/faa-airmen-registry-mcp-server
Search FAA airmen certificates, ratings, and informational medical records via MCP. STDIO or Streamable HTTP.
6 Tools
Overview
FAA airmen certification records from the Releasable Airmen Database, served from a local snapshot. Resolve names to separate identity candidates, read pilot and non-pilot certificates with their ordered ratings, find flight and ground instructors and remote pilots, and read informational medical fields.
Runs as a stdio process or a loopback Streamable HTTP server, directly or in Docker. The operator builds the snapshot with a separate CLI; the server never downloads data, and no API key is needed.
Tools
| Tool | Description |
|---|---|
faa_airmen_verify_certificate | Find identity candidates by name or FAA Unique ID, optionally narrowed by certificate type, level, and ratings, and read their complete certificate records |
faa_airmen_get_type_ratings | Read all pilot-family certificates and their aircraft type, category/class, and instrument ratings |
faa_airmen_check_medical_status | Read informational medical and BasicMed fields without assessing fitness or eligibility |
faa_airmen_search_instructors | Find flight and ground instructor records by name, state, and ratings |
faa_airmen_verify_remote_pilot | Find releasable remote pilot certificate records by name or Unique ID |
faa_airmen_list_reference | Read codes, interpretation guidance, observed type designators, and mirror status |
Capability reference
faa_airmen_verify_certificate tool
mode: "name"requireslastName;firstName,state,city, andzipCodenarrow it.nameMatchapplies to each supplied name field:words(default) matches whole leading words, soJOHNfinds the FIRST NAMEJOHN ROBERT(FAA first names usually include middle names) andSMITHfinds the LAST NAMESMITH JR; only a space separates words, soSMITHdoes not findSMITH-JONES.exactcompares the whole field, andprefixmatches from its beginning, mid-word includedmode: "id"takes oneuniqueId, an opaque A or C plus seven digits that is not a certificate number, and rejects name and location fieldscertificateType(a type letter, its reference topiccertificate_typesdescription such asremote pilotorflight_instructorin any case, orCFI) selects people but returns all their certificates; people sharing a name stay separate candidatescertificateLevel(one letter from reference topiccertificate_levels; requirescertificateType),ratingCodes(1–11 codes from topicratings), andtypeRatings(1–11 designators from topictype_ratings, such asB-737) narrow candidates further. Every requested credential must sit on one certificate, and matches still return all their certificates. Repeats collapse. A bare code or designator matches any level, andlevel/codeonly that slot's level:A/B-737is a type rating held at ATP level, whilecertificateLevel: "A"withB-737asks for an ATP certificate carrying it at any level,C/B-737included- A zero result with credential filters says how many people the name search matches without them, so a name miss reads apart from a credential miss
- With
FAA_AIRMEN_REDACT_PIIon (the default), name mode needs bothlastNameandfirstNameand refusesnameMatch: "prefix",city, andzipCodewithpii_redacted; city and ZIP are omitted from results
faa_airmen_get_type_ratings tool
- One
uniqueIdfromfaa_airmen_verify_certificate; returns every certificate from the FAA pilot files with orderedratingsandtypeRatingsslots, each keeping itsrawvalue,ordinal, andcode found: falsefor an unknown ID; a known person with only non-pilot certificates gets an emptycertificatesarray
faa_airmen_check_medical_status tool
- One
uniqueId; returns each distinct reported medical record:medicalClass,medicalDateRawandmedicalExpireDateRaw(normally MMYYYY), and the BasicMed course and checklist dates (normally YYYYMMDD) foundseparates an unknown ID from a known person, andhasReportedMedicalFields: falsemeans nothing was reported, never an adverse finding. Every result carries adisclaimer: the fields are informational, not evidence of fitness or eligibility- With
FAA_AIRMEN_REDACT_PIIon (the default), every call is refused withpii_redacted
faa_airmen_search_instructors tool
- Needs at least one of
lastName,firstName,state,city,zipCode, orratingCodes;instructorTypeisflight(F),ground(G), orboth(default) ratingCodestakes 1–11 codes from reference topicratings, all required on the same certificate; repeats collapse. A bare code matches any source level, andlevel/code(therawform results return, such asF/INSTA; the level is one letter) only that level. CFII isINSTA,INSTH,INSTI, orINSTPon F and MEI isAMEorASME;INST,ADV, andBASICoccur only on G- With
FAA_AIRMEN_REDACT_PIIon (the default), a name search needs bothlastNameandfirstName, andnameMatch: "prefix",city, andzipCodeare refused withpii_redacted; city and ZIP are omitted from results - Matches carry their complete qualifying
instructorCertificates. Legacy expiry fields don't establish currency, and CFI certificates no longer expire under the December 2024 rule
faa_airmen_verify_remote_pilot tool
- Same
mode,uniqueId, and name selectors asfaa_airmen_verify_certificate; returns only people holding U certificates, with all of them inremotePilotCertificates - With
FAA_AIRMEN_REDACT_PIIon (the default), the same name restrictions apply: both names required, andnameMatch: "prefix",city, andzipCoderefused withpii_redacted - Certificate presence doesn't establish Part 107 recurrent-training currency or operating authority
faa_airmen_list_reference tool
topiciscertificate_types,certificate_levels,ratings,type_ratings,identifiers,medical_guidance, ormirror_status; the four vocabulary topics take a case-insensitivefilterof up to 80 characters- Static topics and
mirror_statusanswer before a snapshot exists;type_ratingslists the designators observed in the imported snapshot mirror_statusreturnsmirrorStatus.state(missing,ready,unavailable),refreshState(idle,running,failed),lastRefreshFailure(the stage of a refresh that didn't complete, such asacquisition,validation,disk,cancelled,timeout, orinterruptedfor one whose process ended without recording a result), and the latest generation's provenance, without filesystem paths
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:
- Offline SQLite lookups over the four monthly source files (
PILOT_BASIC,NONPILOT_BASIC,PILOT_CERT,NONPILOT_CERT), joined by FAA Unique ID, keeping every distinct certificate and differing identity variant - Street addresses are discarded before anything is stored. City and ZIP import, output, and filtering need
FAA_AIRMEN_INCLUDE_LOCATION=true; state is always available - Serving-time redaction is on by default (
FAA_AIRMEN_REDACT_PII, onlyfalseturns it off): name searches need first and last name,prefixmatching and city/ZIP filters are refused, city and ZIP are omitted from output, andfaa_airmen_check_medical_statusrefuses every call. Refusals carry the reasonpii_redacted, every successful response carriespiiRedacted, and the mirror import is unaffected - The
faa-airmen-registry-mirrorCLI downloads and validates each snapshot into an immutable generation; a failed refresh leaves the last complete one serving - The FAA omits airmen who withhold address release and publishes no certificate numbers or recurrent-training dates. A miss is not evidence that someone is uncertificated, and a match is not a current-privilege determination; use the FAA's official services for authoritative answers
Agent-friendly output:
- Provenance on every snapshot-backed response: source name, snapshot month, source age with a
staleflag, generation ID, and the generation's location import policy, plus the normalizedappliedFilters;faa_airmen_list_referencetopicmirror_statusreturns the full record, including hashes, row counts, and source-quality warning counts - Whole-record paging: list tools return complete people within a ~24 KiB response budget, so
limit(default 10, at most 50; a larger limit returns 50 with a notice) is an upper bound.totalCount,returnedCount, andnextOffsetdescribe the actual page; offsets stop at 10,000, whereatPagingCapasks for narrower filters - Typed failures that name the next call:
mirror_missing,location_access_disabled,pii_redacted,invalid_filter, and the other declared reasons each carry a recovery step. A validation failure names the field and its expected form (state must be a two-letter state or territory code.), never the value, and empty pages still return totals and guidance
Getting started
Initialize the snapshot
Build the local snapshot before connecting a client:
export FAA_AIRMEN_MIRROR_DIR="$HOME/.local/share/faa-airmen-registry-mcp-server"
npx -y --package @cyanheads/faa-airmen-registry-mcp-server faa-airmen-registry-mirror init
init downloads the current FAA archive (about 57 MB) and builds a SQLite generation of about 1.75 GB; it took about 12 minutes in testing. The importer also keeps a 1 GiB free-space reserve, so allow about 3 GB. The same CLI runs verify, status, refresh, and prune. refresh builds the next generation beside the current one (another ~1.75 GB) and never deletes old ones, since a running server may still be reading one. status reports how many superseded generations there are and their size. Stop every server process that uses the mirror, then run prune: it takes the writer lock, refuses unless the published snapshot is ready, deletes every generation CURRENT doesn't name, and prints what it removed and the bytes freed. Deleting a generation a server still has open can fail that server's lookups (Bun on macOS) or the deletion itself (Windows).
Point the MCP client at the same FAA_AIRMEN_MIRROR_DIR; left unset, both use the platform user-data directory. To keep city and ZIP, set FAA_AIRMEN_INCLUDE_LOCATION=true for both import and serving; a snapshot imported without it needs a refresh after enabling. Output redaction is on by default and applies at serving time only; a personal local install that wants the full surface (medical lookups, partial-name and city/ZIP searches, city and ZIP in output) sets FAA_AIRMEN_REDACT_PII=false on the server.
MCP client configuration
Add the following to your MCP client configuration file.
{
"mcpServers": {
"faa-airmen-registry-mcp-server": {
"type": "stdio",
"command": "bunx",
"args": ["@cyanheads/faa-airmen-registry-mcp-server@latest"],
"env": {
"MCP_TRANSPORT_TYPE": "stdio",
"FAA_AIRMEN_MIRROR_DIR": "/absolute/path/to/airmen-mirror"
}
}
}
}
Or with npx (no Bun required):
{
"mcpServers": {
"faa-airmen-registry-mcp-server": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@cyanheads/faa-airmen-registry-mcp-server@latest"],
"env": {
"MCP_TRANSPORT_TYPE": "stdio",
"FAA_AIRMEN_MIRROR_DIR": "/absolute/path/to/airmen-mirror"
}
}
}
}
Or with Docker. The image keeps its snapshot in /data/airmen, so initialize a named volume first; run the same command with refresh, verify, or status for maintenance, and with prune once every server container using the volume is stopped:
docker volume create faa-airmen-data
docker run --rm -v faa-airmen-data:/data/airmen ghcr.io/cyanheads/faa-airmen-registry-mcp-server:latest bun dist/cli/mirror.js init
{
"mcpServers": {
"faa-airmen-registry-mcp-server": {
"type": "stdio",
"command": "docker",
"args": ["run", "-i", "--rm", "-v", "faa-airmen-data:/data/airmen", "-e", "MCP_TRANSPORT_TYPE=stdio", "ghcr.io/cyanheads/faa-airmen-registry-mcp-server:latest"]
}
}
}
For Streamable HTTP, start the server on loopback:
MCP_TRANSPORT_TYPE=http MCP_HTTP_PORT=3010 bun run start:http
# Or from the image, published on loopback only:
docker run --rm -p 127.0.0.1:3010:3010 -v faa-airmen-data:/data/airmen ghcr.io/cyanheads/faa-airmen-registry-mcp-server:latest
# Server listens at http://127.0.0.1:3010/mcp
Prerequisites
- Bun v1.4.0 or higher (or Node.js v24+).
- About 3 GB of persistent local storage for the snapshot; network access is needed only for
initandrefresh. - Under Node,
better-sqlite3loads prebuilt bindings for macOS, Linux, and Windows on x64 and arm64 with no compiler step. Execution is tested on macOS under Node and Bun.
Installation
- Clone the repository:
git clone https://github.com/cyanheads/faa-airmen-registry-mcp-server.git
- Navigate into the directory:
cd faa-airmen-registry-mcp-server
- Install dependencies:
bun install
- Configure environment:
cp .env.example .env
# set FAA_AIRMEN_MIRROR_DIR, and FAA_AIRMEN_INCLUDE_LOCATION if you want city and ZIP
# FAA_AIRMEN_REDACT_PII=false lifts serving-time redaction on a personal local install
- Build and initialize the snapshot:
bun run rebuild
node --env-file=.env dist/cli/mirror.js init
The CLI doesn't load .env itself; pass --env-file=.env or export the variables.
Configuration
| Variable | Description | Default |
|---|---|---|
FAA_AIRMEN_MIRROR_DIR | Persistent snapshot directory, shared by the CLI and the server. | Platform user-data directory plus faa-airmen-registry-mcp-server |
FAA_AIRMEN_INCLUDE_LOCATION | true opts in to city and ZIP import, and to output and filtering when FAA_AIRMEN_REDACT_PII=false; false disables them. | false |
FAA_AIRMEN_REDACT_PII | Serving-time redaction, on by default; only false (any case) turns it off, and any other value leaves it on. Never changes what the mirror imports. | true |
FAA_AIRMEN_STALE_AFTER_DAYS | Source-age warning threshold, integer 1–366; never a credential verdict. | 45 |
MCP_TRANSPORT_TYPE | Transport: stdio or http. | stdio |
MCP_HTTP_HOST | HTTP bind address. | 127.0.0.1 |
MCP_HTTP_PORT | HTTP server port. | 3010 |
MCP_SESSION_MODE | HTTP session mode: stateless, stateful, or auto; .env.example and the Docker image set stateless. | auto |
MCP_AUTH_MODE | Authentication: none, jwt, or oauth. | none |
MCP_LOG_LEVEL | Log level (debug, info, warning, error, etc.). | info |
OTEL_ENABLED | Enable OpenTelemetry. | false |
See .env.example for the common framework overrides. Framework storage settings don't relocate the FAA snapshot.
Running the server
Local development
- Build and run the production version:
# One-time build and snapshot bun run rebuild node --env-file=.env dist/cli/mirror.js init # Run the built server bun run start:stdio # or bun run start:http - Run checks and tests:
bun run devcheck # Lints, formats, type-checks, and more bun run test # Runs the test suite
Project structure
| Directory | Purpose |
|---|---|
src/mcp-server/tools | Tool definitions (*.tool.ts), shared schemas, and lossless rendering. Six read-only tools. |
src/services/airmen | FAA acquisition, streaming import, immutable snapshot lifecycle, queries, and reference tables. |
src/cli | The faa-airmen-registry-mirror operator CLI. |
src/config | Server-specific environment variable parsing and validation with Zod. |
tests/ | Synthetic-source lifecycle, query, and MCP contract tests. |
Development guide
See CLAUDE.md for development guidelines and architectural rules. The short version:
- Handlers throw, framework catches — no
try/catchin tool logic - The snapshot is operator-owned and read-only; keep it in the service layer and use
ctx.stateonly for tenant state - Register new tools in the
createApp()arrays insrc/index.ts - Validate source rows → normalize to domain types → return the output schema; never fabricate missing fields, never persist street addresses, and use synthetic identities in tests and examples
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.
Questions fréquentes
Qu'est-ce que io.github.cyanheads/faa-airmen-registry-mcp-server ?
Search FAA airmen certificates, ratings, and informational medical records.
Comment installer io.github.cyanheads/faa-airmen-registry-mcp-server ?
Il fonctionne comme serveur distant. Dans Claude Code : claude mcp add --transport http faa-airmen-registry-mcp-server 'https://faa-airmen-registry.caseyjhand.com/mcp'. Pour les autres clients, collez la configuration JSON de cette page dans leurs réglages MCP.
Faut-il une clé API pour io.github.cyanheads/faa-airmen-registry-mcp-server ?
Son entrée au registre n'indique aucune clé ni secret obligatoire. Certaines fonctions peuvent tout de même demander un compte auprès du service auquel il se connecte.
io.github.cyanheads/faa-airmen-registry-mcp-server est-il open source ?
Oui. Son code est sur GitHub sous licence Apache-2.0.
Qui maintient io.github.cyanheads/faa-airmen-registry-mcp-server ?
Il est publié par cyanheads. AI.tn le référence sans le maintenir ni le recommander.
Issu du registre MCP officiel. Être listé n'est pas une recommandation : vérifiez le code et les permissions d'un serveur avant de lui donner accès à vos données.