
Wraps the FCC's broadband deployment and coverage APIs for address-level availability lookups, census block geocoding, and digital divide analysis across US geographies. The nine tools cover block-to-FIPS conversion, provider search by coordinates or holding company, coverage summaries at state/county/congressional district/tribal land levels, and comparative ranking by unserved population. Speed thresholds go up to gigabit, with filters for technology type and urban/rural/tribal segments. Includes BDC bulk download manifests for researchers who need raw extracts. Built for BEAD program eligibility work, broadband equity research, and ISP competitive analysis where you need FCC Form 477 or BDC data without writing your own API client.
Access FCC broadband availability, coverage analysis, and digital divide data for US geographies and census blocks via MCP. STDIO or Streamable HTTP.
Public Hosted Server: https://fcc-broadband.caseyjhand.com/mcp
FCC broadband data from Form 477 (June 2015 – June 2021) and the Broadband Data Collection (June 2022 onward). Geocode coordinates to census blocks, look up the ISPs and advertised speeds at a block, and summarize, compare, or rank geographies by unserved population for BEAD and equity research. Runs as a stdio process, a local Streamable HTTP server, or the public hosted endpoint above.
| Tool | Description |
|---|---|
fcc_geocode_block | Convert a lat/lon coordinate to a 2010-vintage census block FIPS code, with county and state identifiers |
fcc_search_availability | List the providers and advertised speeds at a census block, filtered by technology and speed |
fcc_get_coverage_summary | Population by provider count for one geography, split by urban/rural and tribal/non-tribal |
fcc_compare_areas | Rank coverage across several geographies of the same type, worst first |
fcc_find_underserved | Rank the most underserved areas in a state or nationwide by unserved population |
fcc_search_providers | Search holding companies by name, state, and technology; returns hoconum identifiers |
fcc_get_provider | National coverage profile for one holding company: technologies and population per speed tier |
fcc_list_filing_periods | List data vintages: Form 477 filing periods and BDC as-of dates |
fcc_list_downloads | List BDC bulk data files for one as-of date |
| Resource | Description |
|---|---|
fcc-broadband://geography/{type}/{id}/summary | Coverage summary for one geography at 25 Mbps |
fcc-broadband://providers/list/{offset} | One page of the Form 477 holding-company directory, 25 entries per page |
Resource data is also reachable through tools: fcc_get_coverage_summary for geographies, fcc_search_providers for holding companies.
| Prompt | Description |
|---|---|
broadband_equity_analysis | Structure a digital divide analysis that chains FCC coverage with Census and BLS data |
fcc_geocode_block toollatitude and longitude in decimal degrees, resolved against 2010 census boundaries through the FCC Geo APIblockFips (15 digits, ready for fcc_search_availability), censusVintage, countyFips, countyName, stateFips, stateCode, and stateName; coordinates over water or outside US coverage fail as block_not_foundfcc_search_availability toolblock_fips (15 digits, 2010 vintage) is required; tech_filter (Form 477 technology codes), min_speed_down (Mbps), and consumer (true consumer, false business) narrow the rowshoconum, techCode / techLabel, maxDownloadMbps, maxUploadMbps, consumer, and business, plus totalProviders counting distinct holding companies; an unfiltered block with no providers fails as block_not_found, while a filtered query that matches nothing returns an empty list with a noticefcc_get_coverage_summary toolgeography_type is one of nation, state, county, cd, place, cbsa, tribal; geography_id is required for every type but nation; urban_rural_filter (all / R / U) and tribal_filter (all / T / N) narrow the populationpopulation by provider count (noCoverage, oneProvider, twoProviders, threeOrMore, total), coveragePct, unservedPct, competitivePct, and a per-segment breakdown; fails as geography_not_found, invalid_geography_combo, or invalid_geography_id_shapefcc_compare_areas toolgeography_ids of one geography_type (every type but nation), or compare_all_states: true for the 50 states plus DC; sort_by is unserved_pct (default), unserved_pop, coverage_pct, or competitive_pctrank 1 is the area most in need; rows carry id, name when resolved, the provider-count populations, and all three percentages. Fails as missing_geography_ids, invalid_all_states_combo, invalid_geography_id_shape, or no_data_foundfcc_find_underserved toolstate (USPS state or territory code) scopes the search, otherwise it runs nationwide; geography_type is county (default), cd, place, or cbsa; urban_rural_filter defaults to R, min_unserved_pop to 1, and limit to 20 (max 100)noCoverage, each with rank, id, name, oneProvider, total, unservedPct, and coveragePct; totalFound counts matches before the limit. An unrecognized state fails as unknown_state, and filters that match nothing return an empty ranking with a noticefcc_search_providers toolname_search (case-insensitive partial name), state, and tech_filter are all optional and must hold on the same filing; limit defaults to 50 (max 200)hoconum, holdingCompanyName, and national statesServed / techCodes that the filters never narrow. No match returns an empty list with a notice; a live search over its 30-second budget fails as live_search_timeoutscanTruncated: true marks the list as a sample and totalCount is omitted; a full-corpus mirror (FCC_MIRROR_ENABLED=true) returns every matchfcc_get_provider toolhoconum (digits only) from fcc_search_providers or the providers directory resourcetechCodes / techLabels and speedTierPopulation, which counts each person once and omits empty tiers; business-only carriers come back with both lists empty. Fails as provider_not_found, or live_provider_timeout past the 30-second budgetfcc_list_filing_periods toolinclude_bdc: true adds BDC as-of dates when credentials are configuredperiods newest first, each with asOfDate and source (form477 or bdc), plus form477Count, bdcCount, and hasBdcCredentials; missing credentials leave bdcCount at 0 rather than raising an errorfcc_list_downloads toolas_of_date (YYYY-MM-DD) is required; filter by data_type (availability default, or challenge), category (Summary, State, Provider), technology_type (Fixed Broadband, Mobile Broadband, Mobile Voice), state, and provider_name; limit defaults to 50 (max 200), paged with offsetdownloadUrl per file, not file contents; totalFiles counts every match and nextOffset is omitted on the last page. Fails as credentials_required or invalid_as_of_datefcc-broadband://geography/{type}/{id}/summary resourcetype takes the seven fcc_get_coverage_summary geography types; id is a FIPS GEOID checked for digit count, 0 for nationacfosw; returns population, the three percentages, and segments as application/json. Use fcc_get_coverage_summary for other thresholds or filtersfcc-broadband://providers/list/{offset} resourceoffset pages the directory 25 entries at a time in hoconum order; start at 0 and follow nextOffsethoconum and, when resolved, holdingCompanyName; each page reports total and count. To find one company by name, use fcc_search_providersbroadband_equity_analysis promptregion (free text) and focus (underserved, rural, tribal, or all), both requiredBuilt 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.
FCC-specific:
fcc_list_downloads needs FCC_BDC_USERNAME and FCC_BDC_HASH_VALUE, failing as credentials_required without themtech_filter letters (acfosw for every technology, the default; a DSL, c cable, f fiber, o other, s satellite, w fixed wireless) and speed_down tiers (0.2, 4, 10, 25, 100, 250, 1000 Mbps; default 25, and 100 is the BEAD threshold)xvwq-qtaj), not the block-level deployment table, so state- and county-scale queries stay fastFCC_MIRROR_ENABLED=true) serves queries from SQLite, falling back to the live API for anything not ingestedAgent-friendly output:
appliedFilters, and every Form 477 result carries dataVintagescanTruncated and scanRowCap flag a result drawn from a capped upstream scan (fcc_search_providers, fcc_find_underserved), and an empty match returns a notice instead of an errorblock_not_found, invalid_geography_id_shape, and credentials_required, each with a recovery hintA public instance is available at https://fcc-broadband.caseyjhand.com/mcp — no installation required. Point any MCP client at it via Streamable HTTP:
{
"mcpServers": {
"fcc-broadband-mcp-server": {
"type": "streamable-http",
"url": "https://fcc-broadband.caseyjhand.com/mcp"
}
}
}
Add the following to your MCP client configuration file.
{
"mcpServers": {
"fcc-broadband-mcp-server": {
"type": "stdio",
"command": "bunx",
"args": ["@cyanheads/fcc-broadband-mcp-server@latest"],
"env": {
"MCP_TRANSPORT_TYPE": "stdio",
"MCP_LOG_LEVEL": "info"
}
}
}
}
Or with npx (no Bun required):
{
"mcpServers": {
"fcc-broadband-mcp-server": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@cyanheads/fcc-broadband-mcp-server@latest"],
"env": {
"MCP_TRANSPORT_TYPE": "stdio",
"MCP_LOG_LEVEL": "info"
}
}
}
}
For Streamable HTTP, set the transport and start the server:
MCP_TRANSPORT_TYPE=http MCP_HTTP_PORT=3010 bun run start:http
# Server listens at http://localhost:3010/mcp
fcc_list_downloads and BDC filing periods. Generate a token at broadbandmap.fcc.gov under "Manage API Access".FCC_OPENDATA_APP_TOKEN) for higher Form 477 rate limits.git clone https://github.com/cyanheads/fcc-broadband-mcp-server.git
cd fcc-broadband-mcp-server
bun install
cp .env.example .env
# edit .env and set FCC_BDC_USERNAME, FCC_BDC_HASH_VALUE, and FCC_OPENDATA_APP_TOKEN as needed
| Variable | Description | Default |
|---|---|---|
FCC_BDC_USERNAME | FCC account email for the BDC API. Needed by fcc_list_downloads and for BDC dates in fcc_list_filing_periods. | none |
FCC_BDC_HASH_VALUE | API token hash from broadbandmap.fcc.gov "Manage API Access". Pairs with FCC_BDC_USERNAME. | none |
FCC_OPENDATA_APP_TOKEN | Socrata app token; raises FCC Open Data rate limits. | none |
FCC_MIRROR_ENABLED | Serve Form 477 queries from the local SQLite mirror where its coverage allows. | false |
FCC_MIRROR_PATH | Directory holding the mirror SQLite files. | data/fcc-mirror |
MCP_TRANSPORT_TYPE | Transport: stdio or http. | stdio |
MCP_HTTP_PORT | HTTP server port. | 3010 |
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). | <project-root>/logs |
STORAGE_PROVIDER_TYPE | Storage backend: in-memory, filesystem, supabase, cloudflare-kv/r2/d1. | in-memory |
OTEL_ENABLED | Enable OpenTelemetry. | false |
See .env.example for the full list of optional overrides.
Build and run:
# One-time build
bun run rebuild
# Run the built server
bun run start:stdio
# or
bun run start:http
Run checks and tests:
bun run devcheck # Lint, format, typecheck, security
bun run test # Vitest test suite
bun run lint:mcp # Validate MCP definitions against spec
docker build -t fcc-broadband-mcp-server .
docker run --rm -p 3010:3010 fcc-broadband-mcp-server
The Dockerfile defaults to HTTP transport and stateless session mode, logging to /var/log/fcc-broadband-mcp-server. OpenTelemetry peer dependencies are installed by default — build with --build-arg OTEL_ENABLED=false to omit them. The image also ships the mirror scripts and a writable data/ directory.
Form 477 is frozen at the June 2021 filing, so it can be ingested once into a local SQLite index. The mirror is off by default, and the full corpus is about 9 GB of source data and takes hours to ingest, so the bootstrap can be scoped to the states you query:
bun run mirror:init -- --states 11,53 # specific states by 2-digit FIPS (11 = DC, 53 = WA)
bun run mirror:init -- --full # the whole corpus
bun run mirror:verify # coverage, row counts, SQLite integrity
Then set FCC_MIRROR_ENABLED=true. In Docker, run the same scripts with docker exec <container> bun run mirror:init -- --states 11,53. Run them from a source checkout or the image: the scripts import through the @/ path alias, which an npm install does not map.
fcc_search_providers, fcc_get_provider, and geographies with no state in their GEOID (cbsa, tribal, nation) serve from the mirror only after a --full ingest.comc finds "Comcast") where the live API matches any substring (mcast), so a mid-word fragment can return fewer results.FCC_OPENDATA_APP_TOKEN first for a full ingest. Bun uses the built-in bun:sqlite; Node.js needs the optional better-sqlite3 peer dependency.| Directory | Purpose |
|---|---|
src/index.ts | createApp() entry point — registers tools, resources, and prompts and inits services. |
src/config | Server-specific environment variable parsing and validation with Zod. |
src/mcp-server/tools | Tool definitions (*.tool.ts). Nine tools across FCC Open Data, the BDC API, and the Geo API. |
src/mcp-server/resources | Resource definitions (*.resource.ts). Geography summary and provider directory. |
src/mcp-server/prompts | Prompt definitions (*.prompt.ts). Broadband equity analysis prompt. |
src/services/open-data | FCC Open Data (Socrata) service for Form 477 deployment, area, and provider tables; mirror/ holds the local SQLite mirror. |
src/services/bdc-api | BDC Public Data API service — authenticated filing periods and download manifests. |
src/services/geo-api | FCC Geo API service — lat/lon to census block FIPS. |
scripts/ | Build, devcheck, and packaging scripts, plus fcc-mirror-init.ts / fcc-mirror-verify.ts. |
tests/ | Unit and integration tests mirroring src/. |
See CLAUDE.md for development guidelines and architectural rules. The short version:
try/catch in tool logicctx.log for request-scoped logging, ctx.state for tenant-scoped storagesrc/mcp-server/*/definitions/index.tshas_0–has_3more, maxaddown, maxadup) explicitlyIssues are welcome. Run checks and tests before submitting:
bun run devcheck
bun run test
Apache-2.0 — see LICENSE for details.
MCP_LOG_LEVELdefault: infoSets the minimum log level for output (e.g., 'debug', 'info', 'warn').
MCP_HTTP_HOSTdefault: 127.0.0.1The hostname for the HTTP server.
MCP_HTTP_PORTdefault: 3010The port to run the HTTP server on.
MCP_HTTP_ENDPOINT_PATHdefault: /mcpThe endpoint path for the MCP server.
MCP_AUTH_MODEdefault: noneAuthentication mode to use: 'none', 'jwt', or 'oauth'.