edge-utah-gis-mcp
v1.0.2
Published
MCP server for Utah public GIS: parcels, land use, hazards, utilities, schools, traffic.
Readme
Utah GIS MCP
An MCP server that lets Claude (or any MCP client) answer land, site and market questions for a home builder from Utah's public GIS data: county parcels and assessor records, future land use, flood and geologic hazards, water and power providers, schools, traffic and transit.
It covers the whole Utah open GIS data catalog, about 560 datasets from UGRC, UDOT, Utah Geological Survey, Water Rights, UTA and others. No API key needed. Read-only.
Use it for:
- Land acquisition: vacant and agricultural parcels by size, value, owner and future land use.
- Site due diligence: flood zones, liquefaction, landslides, faults, wildfire risk, and water, power and city service for a parcel.
- Market and competition: subdivisions, build years, home sizes and values by city.
- Buyer-facing info: school districts and nearby schools, commute and transit access.
Sample questions
Land acquisition
- "Find vacant parcels of 10 acres or more in Eagle Mountain and Saratoga Springs. Sort them by land value per acre and show the 15 cheapest per acre, with parcel IDs and owner names."
- "Which agricultural or greenbelt parcels over 20 acres in Davis and Weber counties fall inside areas marked residential on the MAG/WFRC Generalized Future Land Use map?"
- "List government-owned parcels over 5 acres in Lehi, American Fork and Herriman, and who owns each."
Site due diligence
- "For Utah County parcel 690941252, check whether it's in a FEMA flood hazard area, what the liquefaction potential is, whether any landslide areas or Quaternary faults are within 500 m, and whether it's a high-risk wildfire property."
- "For that same parcel: which culinary water supplier and secondary water system serve it, which power company, and which city it's in (or whether it's unincorporated)?"
- "What's the traffic count (AADT) on the nearest state road to 40.39, -111.93?"
Market and competition
- "In Vineyard, group residential parcels by subdivision name. Which subdivisions have the most homes built since 2018, and what's their median market value and average building square footage?"
- "Compare Lehi, Spanish Fork and Syracuse: median residential market value, median lot size in acres, and the share of homes built since 2015."
- "In Salt Lake and Utah counties, which cities had the most new residential parcels with a 2020 or later build year?"
Buyer-facing
- "What school district is 40.5, -111.9 in, what's its enrollment, and which elementary, junior high and high schools are nearest?"
Screening a list of sites
- "Here are 5 addresses: [paste them]. For each, give the parcel ID, acreage, assessed value, flood zone, liquefaction risk, school district and water provider as a table."
Useful datasets
The agent can find any of these with search_datasets, but it helps to name them in your question.
| Topic | Dataset (search title) |
|---|---|
| Parcels, owner, value, acreage | Utah <County> County Parcels, Utah <County> County Parcels LIR (see County parcels) |
| Public land | Utah Government Owned Parcels |
| Future development | Generalized Future Land Use (MAG and WFRC 2025) |
| City limits | Utah Municipal Boundaries |
| Flood | Utah Flood Hazard Areas, Utah Base Flood Elevations, Utah Levees (NFHL) |
| Geologic hazards | Utah Liquefaction Potential, Utah Landslide Inventory Polygons, Utah Quaternary Faults |
| Wildfire | Utah High Risk WUI Properties, Historic Utah Wildfire Perimeters |
| Water | CulinaryWaterServiceAreas, Utah Wholesale Culinary Water Suppliers, Secondary Water Systems, Irrigation Company Service Areas |
| Power | Utah Electrical Service |
| Schools | Utah School District Boundaries, Utah Schools PreK to 12 |
| Roads and traffic | AADT Rounded (UDOT traffic counts) |
| Tax districts | Local Taxing Districts, Utah Tax Entities 2025 |
| ZIP codes and demographics | Utah ZIP Code Areas, Utah Census Tracts |
Caveats
- Assessor values are not sale prices.
TOTAL_MKT_VALUEandLAND_MKT_VALUEcome from county assessors and can lag the market. - Some parcels have no value.
LAND_MKT_VALUEis empty on some parcels, and empty values sort first. Filter withLAND_MKT_VALUE > 0when ranking by value. - Property class is inconsistent across counties. Utah County marks about 117k parcels as
Unknown, so land searches there should include them. Check a county's values first withgroupBy: "PROP_CLASS". - Owner names aren't available everywhere. Utah County publishes
OWNERNAME; Salt Lake County doesn't. - There's no parcel-level zoning data. The closest dataset is the regional Future Land Use map. Confirm zoning with the city.
- Hazard layers are for screening. They don't replace a geotechnical report, a title search or an official FEMA flood determination.
- Data may be out of date. Check the
modifieddate in search results.
Install
Requires Node 18+. No clone.
Claude Code
claude mcp add utah-gis -- npx -y edge-utah-gis-mcpCursor, Claude Desktop, and other MCP clients
{
"mcpServers": {
"utah-gis": {
"command": "npx",
"args": ["-y", "edge-utah-gis-mcp"]
}
}
}From a checkout of this repo, use node index.js in place of npx -y edge-utah-gis-mcp.
How it works
Instead of one tool per dataset, the server has three generic tools that work on any queryable layer in the catalog:
search_datasets → describe_layer → query_layer
find a dataset list layers/fields filter, locate, count, groupAll tools are marked read-only (readOnlyHint). Errors, such as a bad WHERE clause or an unknown field, come back as tool errors with the ArcGIS message, so the agent can correct itself and retry.
search_datasets
| Param | Notes |
|---|---|
| q | Free text: future land use, liquefaction, culinary water |
| tag | Exact tag: Cadastre, Boundaries, Transportation, Water, Geoscience, Society, Energy, UDOT, … |
| type | Feature Service (queryable), Map Service, Web Map, WMTS, Vector Tile Service |
| limit, offset | Paging (max 100 per page) |
Returns total plus items with title, type, url, snippet, tags, modified and the dataset's page link.
describe_layer
- Service URL (
…/FeatureServer): lists its layers and tables with their URLs. - Layer URL (
…/FeatureServer/0): returns geometry type, record count,maxRecordCountand fields. Fields with coded values include a code → label map.
query_layer
| Param | Default | Notes |
|---|---|---|
| url | – | Layer URL ending in /FeatureServer/<n> or /MapServer/<n> |
| where | 1=1 | SQL WHERE clause |
| outFields | * | Comma-separated field names |
| latitude, longitude | – | Features intersecting this point (WGS84) |
| radiusMeters | 0 | Buffer around the point (max 50 km) |
| limit, offset | 25, 0 | Paging (limit max 2000) |
| orderBy | – | e.g. LAND_MKT_VALUE ASC |
| returnGeometry | false | Geometry in WGS84 lon/lat, 6 decimal places |
| countOnly | false | Returns { "count": n } |
| groupBy | – | Returns [{ <field>: value, count }], largest first |
When a page is truncated, the response has exceededTransferLimit: true and a hint: increase offset by limit to get the next page.
County parcels
Every county's parcels use the same URL pattern, so the search and describe steps can be skipped:
https://services1.arcgis.com/99lidPhWCzftIe9K/arcgis/rest/services/Parcels_<County>/FeatureServer/0
https://services1.arcgis.com/99lidPhWCzftIe9K/arcgis/rest/services/Parcels_<County>_LIR/FeatureServer/0<County> is CamelCase with no spaces: SaltLake, Utah, Davis, Weber, Tooele, Wasatch, Summit, Cache, BoxElder, Washington, Iron, …
Basic parcels (Parcels_<County>): PARCEL_ID, PARCEL_ADD (uppercase address), PARCEL_CITY, PARCEL_ZIP, OWN_TYPE (Federal/Private/State/Tribal), ACCOUNT_NUM, CoParcel_URL, plus date and metadata fields. Some counties, such as Utah County, add OWNERNAME.
Assessor detail (Parcels_<County>_LIR): adds TOTAL_MKT_VALUE, LAND_MKT_VALUE, PARCEL_ACRES, PROP_CLASS (Residential, Vacant, Commercial, Agricultural, Greenbelt, …), SUBDIV_NAME, BUILT_YR, EFFBUILT_YR, BLDG_SQFT, FLOORS_CNT, CONST_MATERIAL and TAX_DISTRICT.
Use PARCEL_ACRES for lot size. Shape__Area is in Web Mercator square meters, which overstates true area by about 1.7× at Utah's latitudes.
Example tool calls
// Vacant land of 10+ acres in Eagle Mountain, cheapest land value first
{ "name": "query_layer", "arguments": {
"url": "https://services1.arcgis.com/99lidPhWCzftIe9K/arcgis/rest/services/Parcels_Utah_LIR/FeatureServer/0",
"where": "PROP_CLASS='Vacant' AND PARCEL_ACRES>=10 AND PARCEL_CITY='Eagle Mountain' AND LAND_MKT_VALUE > 0",
"outFields": "PARCEL_ID,PARCEL_ADD,PARCEL_ACRES,LAND_MKT_VALUE",
"orderBy": "LAND_MKT_VALUE ASC" } }
// Owner name for those parcels (Utah County basic layer)
{ "name": "query_layer", "arguments": {
"url": "https://services1.arcgis.com/99lidPhWCzftIe9K/arcgis/rest/services/Parcels_Utah/FeatureServer/0",
"where": "PARCEL_ID IN ('690941252')", "outFields": "PARCEL_ID,OWNERNAME" } }
// Homes built since 2018 in Vineyard, per subdivision
{ "name": "query_layer", "arguments": {
"url": "https://services1.arcgis.com/99lidPhWCzftIe9K/arcgis/rest/services/Parcels_Utah_LIR/FeatureServer/0",
"where": "PARCEL_CITY='Vineyard' AND BUILT_YR>=2018", "groupBy": "SUBDIV_NAME" } }
// Find a hazard dataset, list its layers, then test a point
{ "name": "search_datasets", "arguments": { "q": "liquefaction", "type": "Feature Service" } }
{ "name": "describe_layer", "arguments": { "url": "<service url from search>" } }
{ "name": "query_layer", "arguments": { "url": "<layer url>", "latitude": 40.39, "longitude": -111.93 } }
// Which school district is this point in?
{ "name": "query_layer", "arguments": {
"url": "https://services1.arcgis.com/99lidPhWCzftIe9K/arcgis/rest/services/UtahSchoolDistrictBoundaries/FeatureServer/0",
"latitude": 40.5, "longitude": -111.9 } }Limits
- Only Feature Services and Map Services can be queried. Web Maps, WMTS, vector tiles and the CSV item show up in search, but only their URLs are returned.
urlmust be an https ArcGIS REST service URL. This is a pattern check, not a host allowlist.- Some catalog services require a login token and will return an error.
Test
Live smoke test covering search → describe → query across several counties, the assessor (LIR) layer and a non-parcel dataset:
npm test