Skip to main content
Glama
ny-data-registry-mcp

ny-data-registry-mcp

ny-data-registry-mcp

An MCP (Model Context Protocol) server that finds open data across every New York data portal in BetaNYC's NY data portal registry: which portal covers a place or topic, how to query it, and, with the harvested index, which datasets and columns exist and in what formats.

It complements the socrata MCP rather than replacing it. This server knows where data is (140 sources across state, county, city, agency, library and nonprofit portals) and hands the agent the right Socrata domain or ArcGIS service. Querying the data stays with socrata and the other BetaNYC MCPs.

Built by BetaNYC. Status: 0.1.0. Package: @betanyc/ny-data-registry-mcp.


What it does

Nine tools, in two groups.

Registry tools (work out of the box, no index needed):

Tool

What it answers

find_sources

Which portals cover this topic or place? Filters: place, tier, platform family, has API, key required, status (default live)

list_sources_for_place

Everything for one county, city, NYC borough or region. Says whether the place has a portal, was checked and has none, or is not in the registry (unchecked, not absent)

how_to_query

Endpoint (verbatim from the registry, never guessed), API type, key and env var, example request, and which MCP to hand off to. Flags UNCONFIRMED, undocumented, gated, dead and no-portal rows

check_source

One live request to a portal URL: status, redirect, response time, and whether that matches the registry

registry_info

Row counts, registry date and origin, unmapped platform strings, index size and last harvest

Index tools (need the harvested SQLite index):

Tool

What it answers

search_datasets

Dataset titles, descriptions, tags and column names across Socrata and ArcGIS portals. Filters: place, portal, platform family, format (csv, geojson, shapefile, kml, api, xlsx …), updated since

get_dataset_columns

Fields and types, every distribution (format + URL), license, publisher, extent, and per-field provenance

find_joinable

Datasets sharing a join key (BBL, BIN, borough, community district, council district, census tract, SWIS, FIPS, ZIP), from a curated key-column map, not raw name equality

whats_new

Datasets added, changed or removed since a date

All tool inputs are strict: an unknown parameter is rejected by name, never silently dropped (same rule as nyc-311-mcp).


Related MCP server: Akil MCP

Questions to try

Ask your AI assistant these once the server is connected. Where we ran a question on 2026-11-05, what it found is noted. Portals change, so your results may differ.

Which datasets match my words? (search_datasets)

  • "Which New York cities publish housing code violation data? Search for 'housing code violations' and 'code enforcement', and list the results by city." Finds New York City (HPD violations, NYCHA violations, tenant complaints, DOB violations), Buffalo (code violations) and Syracuse (code violations, unfit properties, rental registry).

  • "Do any New York cities publish zoning data? If so, what formats do they publish them in?" Finds New York City, Buffalo, Rochester, Glens Falls and Cortland, plus zoning layers on the Westchester, Albany, Dutchess, Tioga, Wayne, Oswego and Warren county portals.

  • "Find flood zones, as GeoJSON." Finds the statewide FEMA flood hazard layer, plus Buffalo and Cortland County.

  • "Find restaurant inspection data." Top hit: NYC's DOHMH restaurant inspection results.

  • "Where are the farmers markets?" Finds the statewide list on data.ny.gov and Albany County's.

  • "Find EV charging stations, as a CSV file." Finds two NYC charging datasets and the statewide alternative fuel stations list.

  • "Housing code violations in Syracuse." / "Bike lanes in Buffalo." / "Street trees in Buffalo." Narrow a search to one place.

  • "Where are the polling places?" Finds Suffolk and Tompkins counties.

  • "Crash data updated since 2026-10-01." Use a date to see only datasets that changed recently.

Who has data, and for where? (find_sources, list_sources_for_place)

  • "Which New York portals have health data?"

  • "Which state agencies have a real API?" / "Which portals need an API key?"

  • "What open data does Erie County have?" Erie County was checked and has no portal; the answer says so.

  • "What can I get for Syracuse?" / "What covers Queens, or the North Country?"

How do I use it? (how_to_query, get_dataset_columns, find_joinable)

  • "How do I pull data from the NYS Department of Health portal?"

  • "Does Westchester County publish zoning data, and what columns does it have?"

  • "What columns does Buffalo's code violations dataset have?"

  • "What other datasets can I join to NYC's housing violations by BBL?"

Is it current? (whats_new, check_source, registry_info)

  • "What datasets were added or changed since October 1?"

  • "Is Albany's data portal still working?"

  • "How many sources do you track, and when was the list last updated?"

Tips

  • Try more than one wording. Cities name the same thing differently: "code enforcement" finds datasets that "housing code violations" misses.

  • New York City crowds the results. NYC's matches tend to fill the top of the list. To see the rest of the state, name a place or ask a second time for results outside New York City.

  • Add a date to skip old data. "School enrollment" alone returns NYC tables from 2010–2011; add a date, such as "updated since 2025-01-01", to skip them.

  • An empty result can still have data elsewhere. The index covers Socrata and ArcGIS portals only. A city may publish on a site this server does not search.

  • Some maps are not official. The Westchester and Tioga county zoning layers say they are not the legal boundaries. Check the town's own map before relying on one.

  • It finds the data; another tool fetches it. Hand a Socrata dataset to the socrata MCP. For an ArcGIS layer, open or download it from the portal.


Install

Requires Node 22.16 or later (it uses Node's built-in node:sqlite, so there is nothing native to compile; 22.16 is the first official Node whose SQLite includes FTS5 full-text search).

Add it to your MCP client's config (Claude Desktop, Claude Code, Cursor, VS Code and others use this shape):

"ny-data-registry-mcp": {
  "command": "npx",
  "args": ["-y", "@betanyc/ny-data-registry-mcp"]
}

That's all. The first time it starts, it downloads the dataset index by itself (about 19 MB, unzipped to about 110 MB). This takes a few seconds on a normal connection. The five registry tools work right away. If you ask a dataset question while the download is still running, the tool waits up to 45 seconds and then says "try again in a minute".

From source instead:

git clone https://github.com/BetaNYC/ny-data-registry-mcp && cd ny-data-registry-mcp
npm install            # also builds
npm test               # offline; no network

and point your client at node /path/to/ny-data-registry-mcp/dist/index.js.

The dataset index

The four index tools (search_datasets, get_dataset_columns, find_joinable, whats_new) read a prebuilt SQLite index. It holds metadata only: titles, descriptions, column names and types, formats and links. No dataset contents.

  • Where it comes from: the index-latest release, at a fixed URL: https://github.com/BetaNYC/ny-data-registry-mcp/releases/download/index-latest/ny-data-index.sqlite.gz. Dated snapshots are kept as index-YYYY-MM-DD releases.

  • Checked before use: the server checks both SHA-256 sums in the release's SHA256SUMS file (the .gz and the unzipped .sqlite) and the schema version. A file that fails any check is never installed.

  • Where it goes: ${HOME}/.cache/betanyc/ny-data-registry/ny-data-index.sqlite (change with NY_DATA_INDEX).

  • When: only when the file is missing. An existing index is never replaced on its own.

  • Get or refresh it by hand:

    npx -y -p @betanyc/ny-data-registry-mcp ny-data-registry download          # if missing
    npx -y -p @betanyc/ny-data-registry-mcp ny-data-registry download --force  # replace with the newest
  • Turn the download off: set NY_DATA_INDEX_AUTO_DOWNLOAD=0. Then the dataset tools reply with these steps instead. Or build your own index with ny-data-registry harvest (see "Refresh").

Environment variables (all optional)

Variable

Default

Purpose

NY_DATA_INDEX

${HOME}/.cache/betanyc/ny-data-registry/ny-data-index.sqlite

Where the index lives

NY_DATA_INDEX_AUTO_DOWNLOAD

on

0 = never download the index on first run

NY_DATA_INDEX_URL

the index-latest release

Download the index from somewhere else (it must have a SHA256SUMS beside it)

NY_DATA_CACHE_DIR

${HOME}/.cache/betanyc/ny-data-registry

Registry refresh cache and HTTP cache

NY_DATA_REGISTRY_REFRESH

on

0 = never fetch the published registry; use the bundled copy

NY_DATA_OFFLINE

off

1 = refuse every network call, including the first-run download (tests use this)

NY_DATA_MAX_LAYERS

250

Per-portal cap on ArcGIS field requests per harvest

NY_DATA_MIN_INTERVAL_MS

1000

Minimum gap between requests to one host (cannot be set below 1000)

SOCRATA_APP_TOKEN

none

Sent as X-App-Token to the Socrata Discovery API


Data sources

  • The registry. BetaNYC/grounding-ai-with-ny-open-data resources/ny-data-portal-registry.json (140 rows). A copy is bundled in data/. On startup the server makes a conditional request (ETag) for the published copy on GitHub Pages, caches it under ${HOME}/.cache/betanyc/, and falls back to the bundled copy on any failure. This server never edits the registry.

  • Human-editable tables in data/:

    • platform-families.json: maps each of the registry's 101 free-text platform strings to one family (socrata, arcgis-hub, arcgis-enterprise, arcgis-server, ckan, geoblacklight, custom-api, none). A string not in the table is reported as unmapped, never guessed; 7 strings are deliberately null because the registry marks them UNCONFIRMED.

    • ny-places.json: 62 counties, 62 cities, 5 boroughs, 8 regions.

    • key-columns.json: the curated join keys.

    • handoffs.json: which BetaNYC MCP serves which registry row.

    • harvest-config.json: skips (ANHD, Rockland, Columbia GeoBlacklight) and caps.

    • synonyms.json: a short civic synonym list ("property" → parcels, tax lots, BBL).

  • The harvested index, built from the portals themselves (next section).


The harvest

node dist/cli.js harvest reads every live registry row and harvests the ones a documented catalog API covers:

Platform

Adapter

Columns from

Socrata

Discovery API (api.us.socrata.com/api/catalog/v1, scroll_id paging)

the search response itself

ArcGIS Hub

OGC API - Records (/api/search/v1/collections/dataset/items), plus the DCAT-US 1.1 feed for formats; falls back to the all collection, then DCAT only, on older sites

FeatureServer?f=json, then each layer ?f=json

ArcGIS Enterprise

<portal>/sharing/rest/search, filtered to the portal's own org (portals/self), or to its own service host when the org id is hidden

same second hop

ArcGIS Online org (agency REST directories such as DEC, Parks, DCP)

www.arcgis.com/sharing/rest/search?q=orgid:<id>

same second hop

Everything else (custom APIs, CKAN, GeoBlacklight, standalone ArcGIS Server directories, pages with no API) is described by the registry tools but not harvested in this version.

Guards

  • Esri's own content is excluded (owner esri…, org P3ePLMYs2RVChkJx). For Enterprise portals and ArcGIS Online orgs, items from another org are not counted under that portal; this is what turns Monroe's 108 search hits into its own content. Hub sites are not filtered by org, because a Hub's catalog is curated by its owner and aggregators like the State GIS Clearinghouse deliberately list other agencies' layers.

  • Out-of-state guard: an item whose extent lies wholly outside New York is dropped; a portal whose items are mostly outside New York is refused and purged. Placeholder extents ([0,0,0,0]) and projected coordinates (State Plane) count as unknown, never as outside.

  • Stable paging: Hub pages are requested sorted by id. A listing that comes back with fewer unique items than the portal's own total is treated as incomplete, so nothing is marked removed from a shaky listing.

  • Politeness: one descriptive User-Agent with no email address; at least 1 s between requests to a host; robots.txt Disallow and Crawl-delay honored (every Hub site asks for 60 s, so Hub listing is slow on purpose); conditional requests by ETag; one bounded retry on 429; a per-portal cap on layer requests. A bot block is reported, never worked around.

  • Never called: rows needing a key, ANHD, Rockland County, undocumented endpoints.

The index stores metadata only (titles, descriptions, field names, links). It never downloads data.

Incremental: datasets carry first_seen, last_seen and a content hash. A layer whose modified date has not changed keeps its stored columns and is not re-requested. A dataset is marked removed only after a complete harvest of its portal.


Refresh

  • Registry: picked up on next server start (conditional GET of the Pages copy).

  • Index: re-run node dist/cli.js harvest. .github/workflows/harvest.yml is written to do this nightly in GitHub Actions and publish the SQLite file as a release asset, but it is not enabled (schedule commented out). Index releases are published by hand for now; the first is index-2026-11-05.


How it was checked

  • npm test: offline tests from recorded fixtures (test/fixtures/, each says which request produced it), including a stdio smoke test that starts the server and calls all nine tools.

  • node dist/cli.js eval: a fixed set of real questions (eval/questions.json) scored against the real index. Results: eval/runs/.

Latest results (2026-11-05, office-mac), all in eval/runs/:

  • npm test: 44 / 44 pass.

  • Question set: 27 / 27 in the top 3, 0 invariant violations. The set was written by the same agent that built the tool, so treat 100% as an upper bound; two checks (Q01, Q13) were tightened during the build when a loose check passed on the wrong county's parcels.

  • Index: 9,569 datasets, 185,352 columns (5,729 with a join key) from 35 of 36 harvestable portals; 110 MB SQLite (about 18 MB gzipped). Full harvest: 924 s and 3,694 requests; an ArcGIS-only re-run: 878 s and 1,403 requests (unchanged layers are not re-requested).

  • 311 (erm2-nwe9) joins: BBL 426, borough 1,073, community district 510, council district 426 datasets (Scout, name equality only: 331 / 512 / 286).


Limits

  • Coverage is the registry's. A place not in the registry has not been checked; the tools say so.

  • Not harvested yet: standalone ArcGIS Server REST directories (NYS DOT, ITS, many counties), GeoBlacklight (Cornell), CKAN (catalog.data.gov), custom APIs.

  • Row counts are not collected (Socrata Discovery does not return them; ArcGIS would need one more request per layer). downloads and update_frequency exist only for Socrata.

  • Columns for ArcGIS are capped at 250 layer requests per portal per run; the rest show columns_status: not-fetched and fill in on later runs.

  • Search is keyword search (SQLite FTS5 with a small synonym list), not semantic search.

  • Join keys are by column name only; values are not compared.

  • Orange County's portal is unreachable from Node because its server does not send its intermediate TLS certificate. Certificate checks are not disabled.

  • Syracuse's Hub returned 126 unique items of 136 on one run even with sorted paging; the guard kept the index whole, but a short listing can still miss new items until the next run.

  • The index is about 110 MB uncompressed, larger than the scoping estimate (low tens of MB), mostly Socrata column metadata and the full-text index.

Terms of use

Read before any adapter was written; details and quotes in docs/terms.md.

  • Socrata / Tyler: the Discovery API's stated purpose is "automated searching, research, and exploration"; read-only access needs no authentication; an app token is recommended. Per-portal terms pages were not found (not verified).

  • Esri: ArcGIS Online public content is for personal or noncommercial use under each item's own terms, and owners who share publicly grant end users permission to use and reproduce it. The Master Agreement's "may not scrape, download, or store Data" clause defines Data as Esri's own commercial datasets, which are excluded here. Hub robots.txt asks for a 60 s crawl delay, which is honored.

License

MIT

Available Tools

9 tools
check_sourceA

Live check of one source's portal URL: one polite request, redirects reported (not followed), HTTP status, response time, and whether that matches the registry's status. Never calls key-required or account-gated sources, and reports a bot block rather than working around it.

Inputs: url, source_id, registry_id.

find_joinableA

Datasets that share a join key with this one, grouped by key: BBL, BIN, borough, community district, council district, census tract, SWIS, FIPS, ZIP. Uses a curated key-column map, not raw column-name equality.

Inputs: dataset (required), column, limit_per_key.

find_sourcesA

Find New York open data portals and sources (state, county, city, agency, library, nonprofit) in BetaNYC's registry by topic, name or place. Returns each source's portal URL, platform family, API type, dataset count, status and last-verified date. Searches portal names and notes, not dataset titles (use search_datasets for datasets). Default status filter is 'live'.

Inputs: query (required), tier, limit, place, +4 more.

get_dataset_columnsB

Column names, field names, types and descriptions for one dataset from the index, plus every distribution (format + URL), license, publisher, extent and per-field provenance (from the portal vs derived here).

Inputs: dataset (required).

how_to_queryA

How to get data from one registry source: its endpoint (copied verbatim, never guessed), API type, whether a key is needed and which env var holds it, an example request, and which MCP server to hand off to (the socrata MCP with the right domain, or the matching @betanyc MCP). Flags UNCONFIRMED, undocumented, gated, dead and no-portal rows.

Inputs: entity, source_id, registry_id.

list_sources_for_placeA

Everything the registry knows for one New York place (county, one of the 62 cities, an NYC borough, or a region): its own portal(s), sources for cities inside it, regional sources, and statewide sources that cover it. Says plainly whether the place has a portal, was checked and has none ('no-portal-exists'), or is not in the registry ('not-in-registry', which means unchecked, not absent).

Inputs: place (required), include_statewide.

registry_infoA

Registry row counts by status, tier and platform family; when the registry was generated and where this copy came from (bundled or refreshed from GitHub Pages); unmapped platform strings; and the harvested index's size and last harvest date.

Inputs: no named parameters.

search_datasetsA

Search dataset titles, descriptions, tags and column names across the harvested Socrata and ArcGIS portals in New York (needs the harvested index). Filter by place, portal, platform family, download format (csv, geojson, shapefile, kml, api, xlsx) or last-updated date. Each result gives the portal, link, formats, update date, and for Socrata the domain + 4x4 to hand to the socrata MCP.

Inputs: query (required), limit, place, format, +3 more.

whats_newA

Datasets added, changed or removed in the index since a date, and datasets whose portal says they were updated since then. Optional portal filter.

Inputs: since (required), limit, portal.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    A3.7/5.0

    Scored across 9 tools

    Disambiguation4/5

    Most tools have clearly distinct roles: source discovery, place-level source lookup, query instructions, live checks, registry metadata, dataset search, column inspection, join discovery, and change tracking. The only meaningful overlap is find_sources versus list_sources_for_place when a place is used as a search term, but the descriptions clarify the difference between a general search and a comprehensive place profile.

    Naming Consistency4/5

    The set mostly follows a consistent snake_case verb_noun convention: find_sources, list_sources_for_place, check_source, search_datasets, get_dataset_columns, find_joinable. Minor deviations are registry_info and whats_new, which are noun/phrase-style rather than verb_noun, but overall the naming remains predictable.

    Tool Count5/5

    Nine tools is well-scoped for a registry/discovery server, with each tool covering a distinct part of the workflow: source discovery, place lookup, query guidance, health checks, registry metadata, dataset search, column inspection, joins, and change tracking. No tool feels redundant or missing from a count perspective.

    Completeness4/5

    The surface covers the core lifecycle well: finding sources, understanding how to query them, checking liveness, searching harvested datasets, inspecting columns, finding joinable datasets, and tracking changes. Minor gaps remain, such as a dedicated single-source detail tool beyond how_to_query or a direct dataset-detail lookup beyond column inspection, but agents can work around these.

    Maintenance

    ActivityMaintained
    ResponsivenessNo recent community issues

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      Not graded
      quality
      B
      maintenance
      Enables querying NYC Open Data datasets using natural language, with tools to search datasets, run SoQL queries, and retrieve metadata, all without an API key.
      200 npm
      MIT
    • A
      license
      Not graded
      quality
      C
      maintenance
      Enables AI agents to query NYC public records through a hosted MCP server, supporting civic workflows such as property research, business evaluation, public money tracking, and influence mapping.
      18 npm
      MIT
    • A
      license
      A
      quality
      C
      maintenance
      Enables agents to discover and access Norwegian open government data by asking questions in natural language, performing filtered searches, running SPARQL queries, and downloading actual dataset files from publishers.
      6
      MIT
    • F
      license
      Not graded
      quality
      A
      maintenance
      Enables AI agents to turn natural-language questions into precise paths to authoritative primary data sources, recommending vetted government, international, and research datasets and providing step-by-step navigation instructions for locating and downloading the underlying data.
      -