A regional health system runs 12 clinics across North Carolina and is planning the 13th. Which tract has the most uninsured residents and the longest drive to the nearest existing location? find_underserved_areas answers in one call; render_map with pin_overlays paints the result.
Twelve clinic addresses, one line each in your input:
const clinics = [
{ name: "Raleigh — Main", street: "...", city: "Raleigh", state: "NC", zip: "27607" },
{ name: "Durham — South", street: "...", city: "Durham", state: "NC", zip: "27713" },
{ name: "Greensboro — Battleground", ...},
…9 more
];
mcp.call("bulk_match_addresses", {
records: clinics, collection: "us-counties", version: "tiger-2024",
})
// → one row per clinic, each with lat/lng + containing county
For NC tracts only, find the ones where population is above 8,000 AND the nearest existing clinic is more than 15 km away.
mcp.call("find_underserved_areas", {
service_points: clinicsGeocoded.map((c) => ({ lng: c.lng, lat: c.lat, name: c.name })),
demand_variable: "B27001_001E", // population for whom insurance status is known
demand_threshold: 8000,
distance_threshold_m: 15000, // 15 km ≈ 9 miles
year: 2023,
state_fips: "37", // scope to NC
limit: 20,
})
// → ranked list of underserved tracts (top `limit` returned):
// [
// {
// external_id, name,
// demand: ,
// nearest_distance_m: ,
// nearest_service_name:
// },
// ...
// ]
// Plus a `methodology` string describing the filter + sort.
The methodology string returned alongside the ranked list documents exactly how the result was produced: which collection + version was queried, which Census variable was the demand metric, the threshold values used for both demand and distance, and the sort order. That string is what you copy into the report's citation block so a reviewer can re-run the same filter.
Stitch the 20 candidate tracts into a dataset, render with the existing clinics as labeled pins on top.
mcp.call("ingest_dataset", {
slug: "nc-clinic-gaps-2026",
records: underserved.map((t) => ({ external_id: t.external_id, demand: t.demand })),
collection: "us-tracts", version: "tiger-2024",
})
mcp.call("render_map", {
version: "tiger-2024", collection: "us-tracts",
dataset: "nc-clinic-gaps-2026", field: "demand",
palette: "sequential",
scope: { state: "NC" },
title: "NC clinic-network gaps · 2023 ACS",
subtitle: "Tracts with insurance-status pop > 8K and no existing clinic within 15 km",
pin_overlays: [{
name: "Existing clinics",
color: "#dc2626",
points: clinicsGeocoded.map((c) => ({ lng: c.lng, lat: c.lat, label: c.name })),
}],
boundary_overlays: [{ collection: "us-counties", version: "tiger-2024", stroke: "#94a3b8" }],
})
mcp.call("publish_map", { view_id: …, freeze: true })
→ /v/nc-clinic-gaps-2026 (iframe + PNG + citation block listing the
threshold values verbatim)
Most gap-analysis deliverables ship as a one-time PDF: pull the data, render in QGIS, hand it over, the strategy team makes a decision, the file gets buried. Here the map is a live /v/<slug> URL with a citation block — anyone can re-render with different thresholds (8K → 5K, 15 km → 20 km) without re-hiring the consultant. The pin layer is part of the report config, so re-publishing with the same pins is idempotent.
Tools used: bulk_match_addresses, find_underserved_areas, ingest_dataset, render_map (with pin_overlays + boundary_overlays), publish_map. find_underserved_areas is premium-gated.