Skip to main content
Glama
frogr

nyc-open-data-mcp

by frogr

nyc-open-data-mcp

An MCP server that gives Claude, Cursor, or any MCP client read-only access to NYC Open Data: 2,400+ city datasets served through the Socrata SODA API. It includes two ready-made tools for the questions people ask most (restaurant health grades and 311 complaints) and two general tools that let the model find and query any other dataset. Inputs are validated, every value placed in a query is escaped, results are paginated and capped in size, upstream errors come back as plain-language hints the model can act on. No API key is required.

It runs two ways: over stdio on your own machine (npx -y github:frogr/nyc-open-data-mcp), or as a remote server over Streamable HTTP (the current MCP transport for servers on the web) with a web playground where you can try every tool in a browser. Screenshots of the playground are in docs/screenshots/ and what was verified is in PROOF.md.

Playground running restaurant_inspections against live data

What you can ask

Which ramen spots in 10003 have an A grade?

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

Are there any restaurants on St. Marks Place with critical violations in their latest inspection?

Find the dataset for NYC street tree census and tell me the most common species in Brooklyn.

How did noise complaints in 11211 change between June and August?

Related MCP server: DataSF MCP

Install

Requires Node.js 20 or newer.

The package is not on npm yet. The commands below install it straight from GitHub with npx -y github:frogr/nyc-open-data-mcp: npm clones the repo, installs dependencies and builds it (a prepare script runs npm run build). The first start takes about 20 seconds while that happens, so run it once in a terminal before adding it to a client. If you'd rather not run a build through npx, use From source.

After the package is published to npm, npx -y nyc-open-data-mcp will do the same thing. Until then, don't run that name: nothing has been published under it by this project.

Claude Desktop

Add this to claude_desktop_config.json (macOS: ~/Library/Application Support/Claude/, Windows: %APPDATA%\Claude\), then restart Claude Desktop:

{
  "mcpServers": {
    "nyc-open-data": {
      "command": "npx",
      "args": ["-y", "github:frogr/nyc-open-data-mcp"],
      "env": {
        "SOCRATA_APP_TOKEN": "optional-but-recommended"
      }
    }
  }
}

Claude Code

claude mcp add --transport stdio nyc-open-data -- npx -y github:frogr/nyc-open-data-mcp

# with an app token, available in every project:
claude mcp add --env SOCRATA_APP_TOKEN=your-token --transport stdio --scope user nyc-open-data -- npx -y github:frogr/nyc-open-data-mcp

Cursor

Add to ~/.cursor/mcp.json (all projects) or .cursor/mcp.json (this project):

{
  "mcpServers": {
    "nyc-open-data": {
      "command": "npx",
      "args": ["-y", "github:frogr/nyc-open-data-mcp"]
    }
  }
}

From source

git clone https://github.com/frogr/nyc-open-data-mcp && cd nyc-open-data-mcp
npm ci   # also builds dist/ through the prepare script
# then use "command": "node", "args": ["/absolute/path/to/nyc-open-data-mcp/dist/index.js"]

Configuration

Env var

Default

Purpose

SOCRATA_APP_TOKEN

none

Free Socrata app token. Unauthenticated requests share a small IP-based rate limit; a token raises it a lot.

SOCRATA_TIMEOUT_MS

20000 (stdio), 15000 (HTTP)

Per-request timeout.

The HTTP server also reads PORT (3000), HOST (0.0.0.0), RATE_LIMIT_PER_MINUTE (30), DAILY_REQUEST_LIMIT (5000), MAX_BODY_BYTES (65536), REQUEST_TIMEOUT_MS (30000), CORS_ORIGINS (*) and TRUST_PROXY (0, set to 1 behind one reverse proxy such as Render's). All are listed in .env.example.

Use it remotely

npm start runs the HTTP server. It serves:

Route

What it is

POST /mcp

The MCP endpoint (Streamable HTTP, stateless, JSON responses). GET and DELETE return 405 because there are no sessions.

GET /

The playground: example questions, a form for each tool built from its input schema, results as a table or raw JSON, and copy-paste client config.

GET /health

Status, version, whether an app token is set, and the current limits. Never calls Socrata.

Once it is deployed (see Deploy), point a client at https://<your-host>/mcp:

# Claude Code
claude mcp add --transport http nyc-open-data https://<your-host>/mcp
// Cursor: ~/.cursor/mcp.json
{ "mcpServers": { "nyc-open-data": { "url": "https://<your-host>/mcp" } } }
// Claude Desktop without a custom connector: claude_desktop_config.json, via the mcp-remote bridge
{ "mcpServers": { "nyc-open-data": { "command": "npx", "args": ["-y", "mcp-remote", "https://<your-host>/mcp"] } } }

In Claude Desktop or claude.ai you can also add it under Settings > Connectors > Add custom connector, if your plan has custom connectors. To poke at it by hand, run npx @modelcontextprotocol/inspector, choose Streamable HTTP and paste the URL.

Limits on the public endpoint. These protect the shared Socrata quota and keep one visitor from using it all up.

  • Per-IP token bucket on POST /mcp, 30 requests per minute by default. A blocked call gets HTTP 429 with Retry-After.

  • A global cap per UTC day (5000 by default), also 429.

  • Request bodies over 64 KB get 413, checked while streaming, so a missing Content-Length doesn't get around it.

  • Each /mcp request has a 30 second wall-clock limit (504), and each Socrata call a 15 second timeout. Slow clients are cut off by Node's header and request timeouts.

  • CORS is open (*) by default so browser-based clients work. Set CORS_ORIGINS to a list to restrict it.

  • Internal errors are logged and visitors get a generic message, never a stack trace.

  • The limiter lives in memory, so counts reset on restart and are per instance. That is fine for one free-tier instance.

Tools

Tool

What it does

Key inputs

Returns

restaurant_inspections

DOHMH restaurant grades (43nn-pn8j)

name, zip_code, borough, cuisine (at least one), limit ≤ 50, offset

Per restaurant: latest grade and what it means, latest inspection date, type, score, and deduplicated violations with critical flags

service_requests_311

311 complaint summary (erm2-nwe9)

zip_codes[] (≤ 10), borough, complaint_type (substring), start_date, end_date (default: last 30 days, max 366), top_n, sample_size

Total requests, top complaint types with counts and share, remainder count, most recent example requests

search_datasets

Search the NYC catalog

query, category, limit ≤ 25, offset, include_columns

Dataset id, name, short description, category, last-updated date, URL, field:type column list

query_dataset

Read-only SoQL against any dataset

dataset_id, select, where, order, group, q, limit ≤ 500, offset

Rows, has_more, next_offset, and a note when the results were cut off

Every tool is marked readOnlyHint: true and declares an outputSchema. Results come back as JSON text, which works in any client, and as structuredContent for clients that use it.

Design notes

Why these four tools. The two specific tools cover the questions people actually ask, and they do the awkward parts on the server. The restaurant dataset stores one row per violation, so the tool first groups by restaurant to page through restaurants, then fetches the history for only that page and works out the latest grade. That grade isn't always from the latest inspection: a re-inspection can leave a grade pending. The 311 dataset has about 22.7 million rows, so the tool runs three small aggregate queries (total, group-by, recent samples) on Socrata's side instead of downloading rows. The two general tools are the fallback for everything else, and search_datasets returns column names so the model can write a valid where clause on its first try.

Pagination. Every list result includes next_offset, which is null on the last page. query_dataset fetches limit + 1 rows so has_more is exact rather than guessed.

Limits. query_dataset caps limit at 500 rows. Each response is also capped at about 60 KB of JSON; when that cap removes rows, the response says so and gives the offset to continue from. 311 date ranges max out at 366 days so queries don't time out upstream. Long descriptions are shortened.

Safety.

  • Every user value that goes into SoQL (names, ZIPs, complaint types, dates, ids) is passed through soqlString(), which doubles single quotes, so x' OR '1'='1 stays an ordinary string. In "contains" searches, the LIKE wildcards % and _ are removed from user input.

  • Dataset ids must match xxxx-xxxx. ZIP codes must be 5 digits. Dates must be real YYYY-MM-DD dates. Boroughs come from a fixed list.

  • Parameters are encoded with URLSearchParams, so a & inside a clause can't add extra query parameters.

  • query_dataset passes SoQL clauses through as written, which is the point of that tool. That's safe because the Socrata endpoint is read-only public data and the tool can only send GET requests to /resource/{id}.json.

Rate limits and reliability.

  • Every request has a timeout.

  • 429 and 5xx responses are retried up to 2 times with exponential backoff, following Retry-After up to 5 seconds.

  • Identical requests are cached in memory for 60 seconds, so an agent that asks the same thing again doesn't send another request.

  • An optional app token raises the rate limit.

Errors. Upstream failures are mapped to tool errors (isError: true) that say what went wrong and what to try next, for example:

Dataset 'zzzz-zzzz' was not found on data.cityofnewyork.us. (HTTP 404, dataset.missing)
Hint: Use search_datasets to find a valid dataset id (format: xxxx-xxxx).

Deploy

The HTTP server needs no database and no secrets. A free tier is enough.

Render (free web service; Render is a hosting platform that reads render.yaml from the repo):

  1. Push this repo to GitHub.

  2. In Render: New > Blueprint, pick the repo. It reads render.yaml (plan free, build npm ci && npm run build, start npm start, health check /health, TRUST_PROXY=1).

  3. Optional: set SOCRATA_APP_TOKEN when Render asks for it (it is marked sync: false, so it is never stored in the repo).

  4. Open https://<service>.onrender.com/ for the playground. The MCP URL is the same host plus /mcp.

Free Render services sleep after a while without traffic, so the first request after a quiet spell takes longer.

Docker (any host that runs containers):

docker build -t nyc-open-data-mcp .
docker run -p 3000:3000 -e TRUST_PROXY=1 nyc-open-data-mcp

Anywhere with Node 20+:

npm ci && npm run build
PORT=3000 npm start
curl localhost:3000/health

Development

npm install
npm test          # vitest, recorded fixtures only, no network
npm run build     # compiles to dist/
npm run smoke     # spawns the server over stdio, runs initialize + tools/list
node scripts/smoke.mjs --live   # also makes one real call to Socrata
npm run smoke:http              # starts the HTTP server, checks /health, CORS, initialize, tools/list, /
node scripts/smoke-http.mjs --live   # also makes real tools/calls over HTTP
npm start                       # HTTP server + playground on PORT (default 3000)
npm run screenshots             # playground screenshots with Chromium, needs network

Tests use hand-built fixtures in test/fixtures/ that match Socrata's response shapes, plus a mocked fetch that throws on any request it doesn't recognize. test/server.test.ts runs the whole MCP protocol in memory: tool listing, schema validation, output-schema checks, and error mapping. test/http.test.ts starts the HTTP server on a real local socket and talks to it with the official SDK client, then checks CORS, limits, timeouts and error responses. test/rateLimit.test.ts covers the limiters with a fake clock.

src/
  index.ts            stdio entrypoint (the package bin)
  http.ts             HTTP entrypoint (npm start): Node adapter, body limit, client IP
  app.ts              routes: /mcp, /health, /, CORS, rate limits, timeouts
  rateLimit.ts        per-IP token bucket and daily cap
  server.ts           tool registration
  socrata.ts          HTTP client: timeouts, retries, cache, error mapping
  soql.ts             literal escaping and predicate builders
  tools/              one file per tool: zod input/output schemas + handler
public/index.html     the playground (one file, no build step)
test/                 vitest suites + fixtures/
scripts/smoke.mjs     raw JSON-RPC stdio smoke test
scripts/smoke-http.mjs  same, over HTTP
scripts/screenshots.mjs Playwright screenshots of the playground

License

MIT © Austin French


Need an MCP server for your own API? austn.net

Available Tools

4 tools
query_datasetQuery a dataset (SoQL)A
Read-onlyIdempotent

Run a read-only SoQL query against any NYC Open Data dataset by id. Supports select / where / order / group / full-text q, with limit (max 500) and offset paging; the response says when more rows exist. Get the dataset id and column names from search_datasets first. For counts, prefer select="count(*)" or a group-by over pulling raw rows.

ParametersJSON Schema
NameRequiredDescriptionDefault
qNoFull-text search across all text columns.
groupNoSoQL $group, required when $select mixes aggregates and plain columns.
limitNoRows to return (1-500, default 100).
orderNoSoQL $order, e.g. "inspection_date DESC".
whereNoSoQL $where, e.g. "zipcode = '10003' AND grade = 'A'". Single-quote string literals; double any quote inside ('O''Brien').
offsetNoRows to skip; pass next_offset from a previous call to page. Use a stable order when paging.
selectNoSoQL $select, e.g. "borough, count(*) as n". Default: all columns.
dataset_idYesSocrata dataset id, e.g. 43nn-pn8j (restaurant inspections) or erm2-nwe9 (311).

Output Schema

ParametersJSON Schema
NameRequiredDescription
noteNo
rowsYes
offsetYes
has_moreYes
returnedYes
dataset_idYes
next_offsetYes

TDQS

A4.2/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint, idempotentHint, openWorldHint and destructiveHint=false, so the safety profile is covered. The description adds useful behavioral context on top: the 500-row limit ceiling, offset-based paging, and that the response signals when more rows exist.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three sentences, front-loaded with the core action and scope, followed by capabilities and then the routing tip. No filler or repetition.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With an output schema present, return-value explanation is unnecessary, and the description still notes the paging signal in responses. The one omission is that it never routes users to the specialized restaurant/311 siblings, which an agent working in that domain would benefit from knowing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100% and each SoQL clause already carries its own documented example and quoting rules, so the schema does the heavy lifting. The description largely restates the clause list (select/where/order/group/q/limit/offset) and the max-500 limit, adding little syntax or format detail beyond it.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb (run) and resource (read-only SoQL query against any NYC Open Data dataset by id), and immediately scopes it as read-only. It is clearly distinguishable from search_datasets, which is named as the discovery step rather than the query execution tool.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives explicit prerequisite guidance (get dataset id and column names from search_datasets first) and a concrete recommendation for count queries (select="count(*)" or group-by rather than raw rows). It stops short of explaining when to prefer the specialized restaurant_inspections and service_requests_311 siblings over a generic SoQL query, which is a real routing gap.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

restaurant_inspectionsNYC restaurant inspection gradesA
Read-onlyIdempotent

Look up NYC restaurant health inspections (DOHMH). Filter by name fragment, ZIP code, borough and/or cuisine (at least one). Returns each restaurant's latest letter grade, latest inspection date, score and a violations summary. Paginated, most recently inspected first.

ParametersJSON Schema
NameRequiredDescriptionDefault
nameNoRestaurant name or part of it, case-insensitive, e.g. "ramen" or "Joe's Pizza".
limitNoRestaurants per page (1-50, default 10).
offsetNoPagination offset; pass next_offset from a previous call.
boroughNoBorough name.
cuisineNoCuisine contains, e.g. "Japanese", "Pizza", "Thai".
zip_codeNo5-digit NYC ZIP code, e.g. 10003.

Output Schema

ParametersJSON Schema
NameRequiredDescription
noteNo
has_moreYes
returnedYes
next_offsetYes
restaurantsYes

TDQS

A3.9/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare read-only, idempotent, open-world, non-destructive, so the safety profile is covered. The description adds genuinely new behavioral context beyond the annotations: results are 'most recently inspected first' and results are paginated, which the agent needs to page correctly.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three sentences, each earning its place: identity, filtering constraint, return shape and ordering. The critical 'at least one' constraint is front-loaded rather than buried.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With an output schema present and full schema coverage, the description only needs to add calling context, and it does: it names the data source, the required filter condition, the sort order and the pagination model. Nothing blocking a correct call is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, with examples and constraints for name, cuisine, zip_code, borough enum, limit and offset already documented. The description restates the filter set but adds no syntax or format detail beyond the schema, so the baseline 3 applies.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ('Look up NYC restaurant health inspections (DOHMH)') and enumerates the filter dimensions, so the agent knows exactly what it returns. It does not explicitly distinguish itself from generic dataset siblings like search_datasets or query_dataset, so sibling differentiation is left implicit.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The parenthetical '(at least one)' establishes a real constraint on how to invoke the tool, which is useful guidance. However, it never says when to prefer this over search_datasets/query_dataset, so usage is only implied rather than routed.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

search_datasetsSearch NYC Open Data catalogA
Read-onlyIdempotent

Search the NYC Open Data catalog (data.cityofnewyork.us) by keyword. Returns dataset ids, names, short descriptions, last-updated dates and column names. Use this first to find a dataset id and its columns before calling query_dataset.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNoResults per page (1-25, default 10).
queryYesKeywords, e.g. "restaurant inspections", "bike lanes", "rat sightings".
offsetNoPagination offset; pass next_offset from a previous call.
categoryNoOptional NYC Open Data category, e.g. "Health", "Transportation", "Housing & Development".
include_columnsNoInclude each dataset's column names and types (needed to write query_dataset filters).

Output Schema

ParametersJSON Schema
NameRequiredDescription
noteNo
totalYesTotal matching datasets.
offsetYes
datasetsYes
returnedYes
next_offsetYesPass as offset for the next page; null when there are no more.

TDQS

A4.5/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint, idempotentHint, openWorldHint and destructiveHint=false, so the safety profile is covered. The description adds valuable context by listing the exact fields returned and the downstream workflow purpose. It does not mention rate limits or pagination semantics, which would be a further improvement.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three tight sentences with no filler: scope, return payload, then the workflow directive. The most actionable guidance (use before query_dataset) is placed last as a clear call to action.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With an output schema present, the description need not explain return values, and the annotations carry the safety profile. Combined with 100% schema coverage, everything an agent needs to invoke this tool and route correctly afterward is present.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so every parameter (query, limit, offset, category, include_columns) is already fully documented in the schema with examples and ranges. The description only restates the keyword-search nature of the query parameter, adding no syntax or format detail beyond the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb (search) and resource (NYC Open Data catalog), names the host domain, and enumerates what is returned (ids, names, descriptions, dates, column names). It is clearly distinguishable from the query sibling, which executes queries rather than discovering datasets.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Explicitly directs the agent to 'use this first to find a dataset id and its columns before calling query_dataset', establishing a clear ordering relationship with the named alternative. The condition that selects this tool over query_dataset is stated outright.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

service_requests_311NYC 311 complaint summaryA
Read-onlyIdempotent

Summarize NYC 311 service requests for an area and time window. Filter by ZIP codes, borough and/or complaint type (substring); dates default to the last 30 days (max range 366 days). Returns the total, top complaint types with counts and share, and a few recent example requests.

ParametersJSON Schema
NameRequiredDescriptionDefault
top_nNoHow many complaint types to rank (1-50, default 10).
boroughNoBorough: Manhattan, Brooklyn, Queens, Bronx or Staten Island.
end_dateNoInclusive end date YYYY-MM-DD. Default: today (New York time).
zip_codesNoOne or more 5-digit ZIP codes. Neighborhoods span several, e.g. East Village = ["10003","10009"].
start_dateNoInclusive start date YYYY-MM-DD. Default: 30 days before end_date.
sample_sizeNoMost recent example requests to include (0-25, default 5).
complaint_typeNoComplaint type contains (case-insensitive), e.g. "noise", "heat", "rodent", "illegal parking".

Output Schema

ParametersJSON Schema
NameRequiredDescription
noteNo
filtersYes
samplesYes
date_rangeYes
total_requestsYes
other_types_countYesRequests not in the top_n types.
top_complaint_typesYes

TDQS

A3.9/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnly, idempotent, non-destructive and openWorld, so safety is covered. The description adds real behavioral context beyond them: the 30-day default window, the 366-day hard range cap, and that complaint_type is a substring match rather than exact -- useful constraints an agent cannot get from annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three tight sentences: what it summarizes, how to scope it, and what comes back. Filtering and the default date window are front-loaded, and no sentence is filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With a 100%-documented schema, full annotation coverage and an output schema, the description need not explain return fields, and it correctly gestures at them briefly. The remaining gap is that it does not clarify how multiple filters combine or whether ZIP and borough are intersected, which matters for a faceted summary tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so baseline is 3, but the description contributes value the schema does not: the 366-day maximum range and the implied 'and/or' combination of ZIP, borough and complaint_type filters. It still leaves the AND/OR interaction between filters ambiguous, keeping it below 5.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a precise verb+resource: summarize NYC 311 service requests, with scope (area + time window) and output shape. It is clearly more specific than the generic sibling query_dataset, but it never names or contrasts those siblings, so it stops short of a 5.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Usage is implied by the description (aggregate/summarize view of 311 complaints), and the filtering options give a sense of when it applies. However there is no explicit when-to-use vs when-not guidance and no routing to query_dataset or search_datasets for raw-record needs.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

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

  1. 4 tool updatesv0.2.0
    • First observedquery_dataset
    • First observedrestaurant_inspections
    • First observedsearch_datasets
    • First observedservice_requests_311

TDQS

A3.9/5.0

Scored across 4 tools

Disambiguation4/5

search_datasets (catalog discovery) and query_dataset (SoQL execution) are clearly separated by a documented workflow. However, restaurant_inspections and service_requests_311 overlap with what query_dataset could do against those same datasets, so an agent may wonder when to use the specialized tools versus the generic query tool.

Naming Consistency3/5

All names are snake_case, which is good, but conventions are mixed: search_datasets and query_dataset use verb_noun, while restaurant_inspections and service_requests_311 are noun phrases with no verb. Readable but not a predictable pattern.

Tool Count4/5

Four tools is on the lean side but each earns its place: one discovery, one general query, and two high-value pre-built domain queries. A reasonable, well-scoped set for the server's purpose.

Completeness4/5

search_datasets plus query_dataset give broad coverage of the whole NYC Open Data catalog, and the two specialized tools add convenience aggregations. Minor gaps like dataset metadata/column-listing or write operations exist but read-only SoQL over any dataset covers most needs.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • F
    license
    A
    quality
    D
    maintenance
    Enables AI assistants to search, explore, and query San Francisco's open data portal through a standardized interface for public datasets. It supports SQL-like querying via the Socrata platform and includes features like fuzzy column matching and schema caching.
    4
    -
  • A
    license
    Not graded
    quality
    B
    maintenance
    Enables AI agents to query and retrieve San Francisco open data from data.sfgov.org via the Socrata SODA API.
    246 npm
    MIT
  • A
    license
    Not graded
    quality
    B
    maintenance
    Provides access to Cincinnati open data via the Socrata SODA API, allowing users to query datasets using natural language or direct tool calls.
    371 npm
    MIT
  • 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.
    254 npm
    MIT