Skip to main content
Glama
codefordayton

OpenDayton

Official

OpenDayton

An MCP server that lets an AI assistant answer questions from curated public datasets about the City of Dayton and Montgomery County, Ohio — crime and calls for service, housing condition, lead service lines, trash pickup days, capital projects, and more — straight from the agencies' own published data.

Ask Claude "which neighborhoods had the most violent crime last year?" or "does 275 Linden Ave have a lead service line?" and it answers from the City's data, with the source cited.

A Code for Dayton project, built for Hacktoberfest 2026 and inspired by the City of Boston's OpenContext. It's meant to be small enough to read in an afternoon and to spin up for your own city.

Connect

The public endpoint is https://opendayton.org/mcp (landing page at https://opendayton.org).

  • Claude.ai / Claude Desktop: Settings → Connectors → Add custom connector → paste the endpoint.

  • Claude Code: claude mcp add --transport http opendayton https://opendayton.org/mcp

  • Anything else that speaks MCP over Streamable HTTP: point it at the endpoint.

Then ask a question. The server's instructions tell the model to call list_datasets, read describe_dataset before querying, and prefer aggregates.

Related MCP server: mcp-data-cincinnati

What's in it

The catalog is datasets/layers.yaml. Every dataset there is one the publisher intentionally surfaces to the public — a Hub site, an "OpenData" folder, a public dashboard, a _public service. The server knows about nothing else. See docs/DATASETS.md for the inclusion criteria and the full survey.

Theme

Datasets

Reference

neighborhoods, public facilities

Public safety

crimes (NIBRS), arrests, calls for service, use of force

Housing

housing condition survey 2025 (+2023), City-supported housing projects, City-owned parcels

Infrastructure

lead service lines, trash pickup days, storm drains

Capital

active & completed capital projects, ARPA project applications

County (SQL)

Montgomery County tax roll, delinquent list, sales 2001→, CAMA parcels / dwellings / permits / apartments

Coming: HUD subsidized housing, MVRPC regional housing study, and the Code for Dayton parcel geocoder.

Tools

Tool

What it does

list_datasets(theme?)

What exists, by theme

describe_dataset(id)

Fields you may query, coded values, caveats, example questions, live record count

arcgis_query(id, where, out_fields, order_by, limit, offset, near_*)

Rows

arcgis_stats(id, group_by, stat_type, stat_field, where, ...)

Server-side counts / sums / averages, grouped

county_schema(table?)

Tables and columns of the County database, with meanings, join keys, caveats, as-of dates

county_sql(sql, limit)

One read-only SELECT (DuckDB SQL) against the County database

Every ArcGIS tool takes a dataset id, never a URL. Every field in a where, out_fields, group_by, stat_field, or order_by must be on that dataset's allowlist in layers.yaml; anything else is rejected with a message naming the allowed fields. county_sql must be a single SELECT; the database is opened read-only with external access disabled, queries are row-capped and time-limited, and the schema is exactly what datasets/county.yaml documents. Results carry the dataset title, publisher, source page, and as-of dates.

Run it locally

uv sync
uv run python -m county.build     # ~200 MB of County downloads → county/county.duckdb (~90 MB), a few minutes
uv run uvicorn server.main:app --reload --port 8000
# landing page: http://localhost:8000/   MCP endpoint: http://localhost:8000/mcp
uv run pytest

The server runs without county.duckdb (the county tools report unavailable), so you can skip the build if you only care about the ArcGIS layers.

Try it from Claude Code: claude mcp add --transport http opendayton http://localhost:8000/mcp

How it's built

Claude / any MCP client
        │  Streamable HTTP
        ▼
server/main.py      MCPServer (official mcp SDK) — tools, instructions, landing page
server/catalog.py   loads datasets/layers.yaml; the allowlist lives here
server/where.py     tokenizing validator: only allowlisted fields, literals, fixed operators
server/arcgis.py    read-only ArcGIS REST client: schema (cached), query, statistics, `near`
server/county.py    read-only DuckDB: single-SELECT guard, row cap, timeout, schema from county.yaml
county/build.py     County ZIPs → county.duckdb (typed, renamed, PII dropped, conformed to county.yaml)
datasets/layers.yaml  the curated ArcGIS catalog — add a dataset here, no Python required
datasets/county.yaml  the County database documentation — the build makes the DB match it exactly

The County database is rebuilt monthly by a GitHub Action and published as the county-data release asset, which the Dockerfile bakes into the image.

Design notes and the reasoning behind them are in docs/DECISIONS.md. The short version: curated, not crawled; ids not URLs; allowlist not blocklist; read-only; cite everything.

Adding a dataset

  1. Confirm the publisher surfaces it to the public on purpose. Note where (public_via, source_page).

  2. Add an entry to datasets/layers.yaml with the full layer URL and a fields allowlist — only the fields a resident needs, with plain-language descriptions. Leave out anything that identifies a person (names, account numbers, contact details) and editor-tracking junk.

  3. Write caveats for the traps you found and two or three example_questions.

  4. uv run pytest, then run the server and ask the example questions.

Deploy

Railway, from the Dockerfile, on every push to main. railway.toml sets the health check. Set OPENDAYTON_PUBLIC_URL to the public URL so the landing page shows the right endpoint. The Dockerfile downloads county.duckdb from the county-data GitHub release; run the Rebuild County database workflow (or gh release upload county-data county/county.duckdb --clobber after a local build) to refresh it.

Credits & license

Design inspired by the City of Boston's OpenContext (MIT). Dataset curation draws on Code for Dayton's Dayton / Montgomery County data inventory. Data belongs to its publishers; check each dataset's source_page for terms.

MIT.

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    A
    maintenance
    Enables AI assistants to search, explore, and query any CKAN open data portal through natural language, making public datasets accessible without requiring knowledge of the portal's API.
    20
    495 npm
    58
    MIT
  • A
    license
    Not graded
    quality
    C
    maintenance
    Provides access to Cincinnati open data via the Socrata SODA API, allowing users to query datasets using natural language or direct tool calls.
    3 npm
    MIT
  • A
    license
    A
    quality
    B
    maintenance
    Enables AI assistants to answer DFW civic and property questions using official data sources without API keys.
    9
    87 npm
    2
    Apache 2.0
  • A
    license
    Not graded
    quality
    B
    maintenance
    Enables querying and exploring the City of Worcester's open data portal (ArcGIS Hub) through natural language, including dataset search, type filtering, schema inspection, spatial point-in-polygon queries, and geocoding.
    MIT