← All work

NYC Open Data MCP

Ask Claude about New York City's public data and get real rows back. Restaurant grades, 311 complaints, and the other 2,400 datasets the city publishes, through one tool it can call.

I live in New York, and the city publishes an absurd amount of data: every restaurant inspection, every 311 call, every street tree. Getting an answer out of it means knowing which dataset, which columns, and how Socrata (the API the city publishes on) wants the query written. I wanted to skip all of that and just ask.

So this is an MCP server for NYC Open Data. MCP (Model Context Protocol) is the open standard that lets AI apps like Claude and Cursor call outside tools. Connect it once. Then "what were the top 311 complaints in the East Village last month?" turns into a tool call, real rows come back, and the answer is made from them. No API key needed.

Ask it

These ran against live city data on 2026-10-07.

  1. You

    What were the top 311 complaints in the East Village last month?

  2. Claude callsservice_requests_311(zip_codes: ["10003", "10009"], start_date: "2026-09-01", end_date: "2026-09-30", top_n: 10)
  3. nyc-open-data-mcp returns
    total_requests
    4,823
    top types
    Encampment 746 (15.5%), Noise - Residential 472 (9.8%), Illegal Parking 359 (7.4%), Noise - Street/Sidewalk 267, Noise - Commercial 245
    samples
    5 recent requests with address, agency and status
  4. Claude

    There were 4,823 requests to 311 from ZIP codes 10003 and 10009 in September 2026. The most common was Encampment, with 746, about one in six. Then Noise - Residential at 472 and Illegal Parking at 359. Three of the top five were noise complaints of one kind or another.

  1. You

    What inspection grades do the ramen spots in 10003 have?

  2. Claude callsrestaurant_inspections(name: "ramen", zip_code: "10003", limit: 10)
  3. nyc-open-data-mcp returns
    returned
    4 restaurants, each with its latest grade, the date it was posted, and the latest inspection's score and violations
    rows
    Ramen by Ra, A. Kyuramen, B. Ramen Takumi, A. Marufuku Ramen, B.
  4. Claude

    Four ramen places in 10003 have inspection records. Ramen by Ra on East 1st Street and Ramen Takumi on University Place both hold an A. Kyuramen on East 14th Street and Marufuku Ramen on 2nd Avenue hold a B. Kyuramen's most recent inspection, in July 2026, scored 33 points with three critical violations, so its posted B may change after the re-inspection.

Both of these run in the live playground, along with the other four examples.

What it's for

  • Neighbors and renters. "Any rodent complaints in Flushing in the last 30 days?" "How are the pizza places in Williamsburg doing on inspections?"
  • Journalists and researchers, who know the question but not the dataset. "Is there a dataset of NYC street trees?" finds the 2015 Street Tree Census with its 45 columns. "What are the most common street trees in Brooklyn?" runs a grouped query on it: London planetree (34,886), honeylocust (16,921), pin oak (12,343).
  • Civic tech. Anyone building on city data gets input validation, escaping, paging and plain-language errors for free, and a model-friendly shape for every response.
  • Your own data. The city's API is just an API. The same server shape works for yours: a couple of tools for the questions people actually ask, and a general one for everything else. That's the custom MCP server on my hire page.

Four tools, chosen on purpose

Two tools for the questions people ask most, two for everything else. Nothing reaches the city's API without going through the validator.
  • restaurant_inspections. The city stores one row per violation, so the tool groups by restaurant to page through restaurants, then fetches the history for just that page and works out the latest grade. That grade isn't always from the latest inspection, because a re-inspection can leave it pending. I didn't know that until the data told me.
  • service_requests_311. The 311 dataset is about 22.7 million rows. The tool runs three small aggregate queries on Socrata's side instead of downloading anything.
  • search_datasets returns column names with each dataset, so the model can write a valid filter on its first try.
  • query_dataset runs read-only queries against any dataset, capped at 500 rows and about 60 KB per response, with the offset to continue from when it cuts something off.

Safe to put on the internet

  • Every value that goes into a query is escaped, so x' OR '1'='1 stays a string. Dataset ids, ZIPs, dates and boroughs are validated before any request.
  • Upstream errors come back as plain hints the model can act on ("use search_datasets to find a valid dataset id"), never as stack traces.
  • The remote server has a per-IP rate limit (30 a minute), a daily cap, a 64 KB body limit checked while streaming, and timeouts on every request.
  • 61 tests, none of which touch the network. The HTTP tests start the real server on a local port and connect with the official MCP SDK client, the same one Claude Desktop and Cursor build on.

The playground

Every MCP server I build ships with one of these. It runs the real tools against live city data, builds each form from the tool's own input schema, and shows the raw JSON next to the friendly view.

The playground's Run a tool section: the restaurant_inspections form with name ramen and ZIP 10003, and four restaurants on the right with their letter grades, inspection dates, scores and critical violation counts
The ramen question as the assistant sees it: four restaurants, their grades, and the violations behind them.

What's next

  • More ready-made tools for the next questions people ask: housing violations, crashes, school data. Each one is a day's work on top of what's here.
  • Other cities. Socrata runs hundreds of open-data portals. The server is written against Socrata, not against New York, so a Chicago or Seattle version is mostly configuration.
  • Your data. A company's own database behind the same shape: a few tools for the real questions, a validated general query, and the model never touching SQL directly.