Skip to main content
Glama
hesscl

census-geocoding-mcp

by hesscl
README.md
# πŸ—ΊοΈ Census Geocoding MCP

[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![MCP](https://img.shields.io/badge/MCP-compatible-blueviolet)](https://modelcontextprotocol.io)
[![Node.js](https://img.shields.io/badge/Node.js-%3E%3D18.0.0-339933?logo=node.js&logoColor=white)](https://nodejs.org)
[![TypeScript](https://img.shields.io/badge/TypeScript-5.x-3178c6?logo=typescript&logoColor=white)](https://www.typescriptlang.org)
[![U.S. Census Bureau](https://img.shields.io/badge/Data%20Source-U.S.%20Census%20Bureau-003087)](https://geocoding.geo.census.gov/geocoder)

An **MCP (Model Context Protocol) server** that gives AI assistants full access to the [U.S. Census Bureau Geocoding Services API](https://geocoding.geo.census.gov/geocoder). Geocode addresses, reverse-geocode coordinates, and retrieve Census geographies β€” all without an API key.

---

## ✨ Features

- πŸ”“ **No API key required** β€” the Census Geocoding API is completely free and public
- πŸ“ **Single-record geocoding** β€” one-line or parsed addresses (stateside + Puerto Rico)
- πŸ—‚οΈ **Geography lookup** β€” resolve addresses or coordinates to Census tracts, blocks, counties, congressional districts, and more
- πŸ“¦ **Batch processing** β€” up to **10,000 records per request**, as CSV or structured arrays
- 🌐 **Puerto Rico support** β€” dedicated endpoints with Urbanization and Municipio fields
- πŸ›οΈ **Benchmark & vintage control** β€” target specific MAF/TIGER database versions and Census geography vintages
- 🟦 **TypeScript** β€” fully typed, ESM, Node.js β‰₯ 18

---

## 🧰 Tools (12 total)

### πŸ” Discovery

| Tool | Description |
|------|-------------|
| `list_benchmarks` | List all available MAF/TIGER locator database versions |
| `list_vintages` | List vintages (Census geography versions) for a given benchmark |

### πŸ“Œ Single-Record Geocoding (Locations)

| Tool | Description |
|------|-------------|
| `geocode_oneline_address` | Geocode a full address on one line |
| `geocode_parsed_address` | Geocode a US (stateside) address with separate fields |
| `geocode_pr_address` | Geocode a Puerto Rico address (supports Urbanization & Municipio) |

### πŸ—ΊοΈ Single-Record Geography Lookup

| Tool | Description |
|------|-------------|
| `find_geographies_oneline` | Geocode a one-line address + return Census geographies |
| `find_geographies_parsed` | Geocode a parsed address + return Census geographies |
| `find_geographies_pr` | Geocode a PR address + return Census geographies |
| `find_geographies_coordinates` | Look up Census geographies for a lon/lat coordinate pair |

### πŸ“¦ Batch Processing

| Tool | Description |
|------|-------------|
| `batch_geocode_locations` | Batch geocode up to 10,000 addresses (coordinates only) |
| `batch_geocode_geographies` | Batch geocode up to 10,000 addresses + Census geographies |
| `batch_geolookup_coordinates` | Batch geography lookup for up to 10,000 coordinate pairs |

---

## πŸš€ Quick Start

### Prerequisites

- [Node.js](https://nodejs.org) β‰₯ 18.0.0

### Install & Build

```bash
git clone https://github.com/your-username/census-geocoding-mcp.git
cd census-geocoding-mcp
npm install
npm run build
```

### Add to Claude Desktop

Edit your Claude Desktop configuration file:

- **macOS:** `~/Library/Application Support/Claude/claude_desktop_config.json`
- **Windows:** `%APPDATA%\Claude\claude_desktop_config.json`

```json
{
  "mcpServers": {
    "census-geocoding": {
      "command": "node",
      "args": ["/absolute/path/to/census-geocoding-mcp/dist/index.js"]
    }
  }
}
```

Restart Claude Desktop β€” the 12 geocoding tools will be available immediately.

---

## πŸ› οΈ Tool Reference

### `list_benchmarks`

Lists all available benchmarks (MAF/TIGER locator database versions).

```
No parameters required.
```

---

### `list_vintages`

Lists all vintages available for a given benchmark.

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `benchmark` | string | βœ… | Benchmark name or ID (e.g. `Public_AR_Current`) |

---

### `geocode_oneline_address`

Geocode a complete address provided as a single string.

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `address` | string | βœ… | Full address, e.g. `4600 Silver Hill Rd, Washington, DC, 20233` |
| `benchmark` | string | βž– | Default: `Public_AR_Current` |

**Returns:** matched address, interpolated coordinates, Tigerline ID, address range components.

---

### `geocode_parsed_address`

Geocode a US stateside address with fields parsed separately. Requires at minimum `street` + `zip` OR `street` + `city` + `state`.

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `street` | string | βœ… | House number and street name |
| `city` | string | βž– | City name |
| `state` | string | βž– | 2-letter state abbreviation |
| `zip` | string | βž– | 5-digit ZIP code |
| `benchmark` | string | βž– | Default: `Public_AR_Current` |

---

### `geocode_pr_address`

Geocode a Puerto Rico address with optional Urbanization and Municipio.

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `street` | string | βœ… | House number and street name |
| `urb` | string | βž– | Urbanization name |
| `city` | string | βž– | City name |
| `municipio` | string | βž– | Municipio name |
| `zip` | string | βž– | ZIP code (begins with 006, 007, or 009) |
| `benchmark` | string | βž– | Default: `Public_AR_Current` |

---

### `find_geographies_oneline` / `find_geographies_parsed` / `find_geographies_pr`

Same parameters as their `geocode_*` counterparts, plus:

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `vintage` | string | βž– | Default: `Current_Current` |
| `layers` | string | βž– | Comma-delimited layer names/IDs, or `all` |

**Geography layers returned include:** States, Counties, Census Tracts, Census Blocks, Congressional Districts, County Subdivisions, Incorporated Places, State Legislative Districts, Urban Areas, Combined Statistical Areas, and more.

---

### `find_geographies_coordinates`

Reverse geocode a coordinate pair to Census geographies.

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `longitude` | number | βœ… | Longitude (X) in decimal degrees, range `[-180, 180]`, e.g. `-84.39215` |
| `latitude` | number | βœ… | Latitude (Y) in decimal degrees, range `[-90, 90]`, e.g. `33.75649` |
| `benchmark` | string | βž– | Default: `Public_AR_Current` |
| `vintage` | string | βž– | Default: `Current_Current` |
| `layers` | string | βž– | Comma-delimited layer names/IDs, or `all` |

---

### `batch_geocode_locations`

Batch geocode up to 10,000 addresses. Accepts either raw CSV or a structured array.

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `csv_content` | string | βž– | Raw CSV: `UniqueID,Street,City,State,ZIP` per row |
| `addresses` | array | βž– | Array of `{id, street, city?, state?, zip?}` objects |
| `benchmark` | string | βž– | Default: `Public_AR_Current` |

> `csv_content` takes precedence if both are provided. At least one must be supplied.

**Returns CSV columns:** Record ID, Input Address, Match Indicator, Match Type, Output Address, Coordinates, Tigerline ID, Tigerline Side.

---

### `batch_geocode_geographies`

Like `batch_geocode_locations` but also returns Census geography codes.

Additional parameter:

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `vintage` | string | βž– | Default: `Current_Current` |

**Returns CSV columns:** all location columns + State Code, County Code, Tract Code, Block Code.

---

### `batch_geolookup_coordinates`

Batch geography lookup for coordinate pairs.

| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `csv_content` | string | βž– | Raw CSV: `UniqueID,Longitude,Latitude` per row |
| `coordinates` | array | βž– | Array of `{id, x, y}` objects; `x` (longitude) must be in `[-180, 180]`, `y` (latitude) in `[-90, 90]` |
| `benchmark` | string | βž– | Default: `Public_AR_Current` |
| `vintage` | string | βž– | Default: `Current_Current` |

---

## πŸ’‘ Example Prompts

Once connected to Claude Desktop, try prompts like:

- *"What Census tract and block is 1600 Pennsylvania Avenue NW, Washington DC in?"*
- *"Geocode these 500 addresses and give me their lat/lon coordinates"* (paste a CSV)
- *"What congressional district is at longitude -87.6298, latitude 41.8781?"*
- *"List all available Census geocoding benchmarks"*
- *"Geocode this Puerto Rico address: 123 Calle Luna, Urb El ParaΓ­so, San Juan, PR 00926"*

---

## βš™οΈ Development

```bash
# Watch mode (recompile on save)
npm run dev

# One-time build
npm run build

# Run the server directly
npm start
```

---

## πŸ“‹ Notes & Limits

- **Batch limit:** 10,000 records per file β€” enforced client-side before the request is sent; exceeding it raises a clear error immediately
- **Batch size limit:** CSV content is additionally capped at **5 MB** (UTF-8 bytes) β€” this catches oversized inputs that could pass the row count check via very long lines
- **No authentication required** β€” all endpoints are public
- **Single-record responses** are JSON; **batch responses** are CSV text
- **Default benchmark:** `Public_AR_Current` (current MAF/TIGER database)
- **Default vintage:** `Current_Current` (required for all geography endpoints)
- **Request timeout:** all API calls time out after **30 seconds**; the server raises an abort error rather than hanging indefinitely
- **Input length validation:** all string parameters have maximum lengths enforced before any network request is made (`address` 200 chars, `street`/`city`/`urb`/`municipio` 100, `state`/`zip` 10, `benchmark`/`vintage` 50, `layers` 500)
- **Coordinate validation:** `longitude` must be in `[-180, 180]` and `latitude` in `[-90, 90]`; out-of-range values are rejected before the request is sent
- **CSV field escaping:** address fields passed to batch endpoints are escaped per RFC 4180 (embedded quotes doubled, fields containing commas or newlines quoted) to prevent malformed CSV
- The underlying API is provided by the [U.S. Census Bureau](https://www.census.gov/) and subject to their [terms of service](https://www.census.gov/data/developers/about/terms-of-service.html)

---

## πŸ“š References

- [Census Geocoding Services API Specification](https://geocoding.geo.census.gov/geocoder)
- [Model Context Protocol](https://modelcontextprotocol.io)
- [@modelcontextprotocol/sdk](https://github.com/modelcontextprotocol/typescript-sdk)

---

## βš–οΈ License

This project is licensed under the [MIT License](LICENSE).

TDQS

A3.8/5.0

Scored across 12 tools

Disambiguation4/5

Tools follow a clear two-axis matrix: geocode (coordinates) vs find_geographies (geographies), crossed with input format (oneline/parsed/pr/coordinates) and batch variants. The oneline/parsed/pr trios return the same content for different address formats, so a careless agent could misselect, but descriptions clearly delineate input requirements and PR-specific handling.

Naming Consistency4/5

Names are predominantly consistent snake_case verb_noun (geocode_oneline_address, find_geographies_pr, batch_geocode_locations). Minor inconsistencies exist (e.g. 'oneline' token placement and the batch_ prefix convention), but the pattern remains predictable and readable.

Tool Count5/5

12 tools is well-scoped for a geocoding API. Each tool earns its place: catalog lookups, three address-format variants, a coordinate lookup, and three batch operations mirror the underlying Census endpoints without redundancy.

Completeness5/5

The surface covers the full lifecycle: benchmark/vintage discovery, single and batch geocoding, coordinate reverse geolookup, geography lookups, and all address formats including PR Urbanization/Municipio. No obvious dead ends for geocoding workflows.

Maintenance

ActivityInactive
ResponsivenessNo issues