io.github.cyanheads/socrata-mcp-server
Search and query government open-data portals (Socrata SODA API).
Que pouvez-vous faire avec io.github.cyanheads/socrata-mcp-server ?
- Full Socrata SODA 2.1 API integration — SoQL query builder with select, where, group, having, order, search, limit, offset
- Discovery API for cross-portal dataset search and per-portal dataset counts (curated 39-portal catalog, counts cached ~24h)
- App token support (
SOCRATA_APP_TOKEN) for higher per-IP rate limits - Configurable default portal domain via
SOCRATA_DEFAULT_DOMAIN - DataCanvas spillover (DuckDB, bundled) — large query results register as SQL tables for analytical queries
- Assembled SoQL string echoed in every
socrata_query_datasetresponse so agents can learn and refine syntax - Recovery hints on empty results — echoes applied filters with specific suggestions for broadening
- Truncation disclosure —
truncated/shown/capfields when rows fill the limit, with guidance to page or raise the limit, naming the staged table and both dataframe tools when the result spilled
Installation
Ajoutez ceci à la configuration MCP de votre client (Claude Desktop : claude_desktop_config.json ; Cursor : .cursor/mcp.json ; VS Code et les autres utilisent la même forme « mcpServers »).
claude mcp add --transport http socrata-mcp-server 'https://socrata.caseyjhand.com/mcp'
{
"mcpServers": {
"socrata-mcp-server": {
"type": "http",
"url": "https://socrata.caseyjhand.com/mcp"
}
}
}
@cyanheads/socrata-mcp-serverclaude mcp add socrata-mcp-server -e 'SOCRATA_APP_TOKEN=<SOCRATA_APP_TOKEN>' -e 'SOCRATA_DEFAULT_DOMAIN=data.seattle.gov' -e 'SOCRATA_DATAFRAME_DROP_ENABLED=false' -e 'MCP_LOG_LEVEL=info' -- npx -y @cyanheads/socrata-mcp-server@0.5.1
{
"mcpServers": {
"socrata-mcp-server": {
"command": "npx",
"args": [
"-y",
"@cyanheads/socrata-mcp-server@0.5.1"
],
"env": {
"SOCRATA_APP_TOKEN": "<SOCRATA_APP_TOKEN>",
"SOCRATA_DEFAULT_DOMAIN": "data.seattle.gov",
"SOCRATA_DATAFRAME_DROP_ENABLED": "false",
"MCP_LOG_LEVEL": "info"
}
}
}
}
Variables d'environnement
SOCRATA_APP_TOKENSocrata app token (X-App-Token header). Without a token, requests share a throttled pool per source IP. Free to register at any Socrata portal.SOCRATA_DEFAULT_DOMAINDefault portal domain when domain is omitted from tool calls (e.g. data.seattle.gov, data.cityofnewyork.us).SOCRATA_DATAFRAME_DROP_ENABLEDSet to true to enable socrata_dataframe_drop, which deletes staged DataCanvas data. Off, the tool is omitted from the tool list.MCP_LOG_LEVELSets the minimum log level for output (e.g., 'debug', 'info', 'warn').
@cyanheads/socrata-mcp-serverclaude mcp add socrata-mcp-server -e 'MCP_TRANSPORT_TYPE=<MCP_TRANSPORT_TYPE>' -e 'SOCRATA_APP_TOKEN=<SOCRATA_APP_TOKEN>' -e 'SOCRATA_DEFAULT_DOMAIN=data.seattle.gov' -e 'SOCRATA_DATAFRAME_DROP_ENABLED=false' -e 'MCP_HTTP_HOST=127.0.0.1' -e 'MCP_HTTP_PORT=3010' -e 'MCP_HTTP_ENDPOINT_PATH=/mcp' -e 'MCP_AUTH_MODE=none' -e 'MCP_LOG_LEVEL=info' -- npx -y @cyanheads/socrata-mcp-server@0.5.1
{
"mcpServers": {
"socrata-mcp-server": {
"command": "npx",
"args": [
"-y",
"@cyanheads/socrata-mcp-server@0.5.1"
],
"env": {
"MCP_TRANSPORT_TYPE": "<MCP_TRANSPORT_TYPE>",
"SOCRATA_APP_TOKEN": "<SOCRATA_APP_TOKEN>",
"SOCRATA_DEFAULT_DOMAIN": "data.seattle.gov",
"SOCRATA_DATAFRAME_DROP_ENABLED": "false",
"MCP_HTTP_HOST": "127.0.0.1",
"MCP_HTTP_PORT": "3010",
"MCP_HTTP_ENDPOINT_PATH": "/mcp",
"MCP_AUTH_MODE": "none",
"MCP_LOG_LEVEL": "info"
}
}
}
}
Variables d'environnement
MCP_TRANSPORT_TYPESelects the HTTP transport.SOCRATA_APP_TOKENSocrata app token (X-App-Token header). Without a token, requests share a throttled pool per source IP. Free to register at any Socrata portal.SOCRATA_DEFAULT_DOMAINDefault portal domain when domain is omitted from tool calls (e.g. data.seattle.gov, data.cityofnewyork.us).SOCRATA_DATAFRAME_DROP_ENABLEDSet to true to enable socrata_dataframe_drop, which deletes staged DataCanvas data. Off, the tool is omitted from the tool list.MCP_HTTP_HOSTThe hostname for the HTTP server.MCP_HTTP_PORTThe port to run the HTTP server on.MCP_HTTP_ENDPOINT_PATHThe endpoint path for the MCP server.MCP_AUTH_MODEAuthentication mode to use: 'none', 'jwt', or 'oauth'.MCP_LOG_LEVELSets the minimum log level for output (e.g., 'debug', 'info', 'warn').
Documentation
Tirée du README du projet sur GitHub, actualisée chaque semaine. Les liens ouvrent GitHub.
Sommaire
@cyanheads/socrata-mcp-server
Search and query government open-data portals (Socrata SODA API) via MCP. STDIO or Streamable HTTP.
7 Tools • 2 Resources • 1 Prompt
Public Hosted Server: https://socrata.caseyjhand.com/mcp
Overview
Query city, county, state, and federal open-data portals built on Socrata. Discover portals and datasets, inspect column schemas, and run SoQL queries, or SQL over large result sets.
Tools
| Tool | Description |
|---|---|
socrata_list_portals | List known Socrata-powered government open-data portals with domain, organization name, and approximate dataset count |
socrata_find_datasets | Search for datasets across all Socrata portals or scope to one portal via the Discovery API |
socrata_get_dataset | Fetch full metadata and typed column schema for a dataset by ID — required before writing SoQL queries |
socrata_query_dataset | Execute a SoQL query against any dataset: search, select, where, group, having, order, with DataCanvas spillover |
socrata_dataframe_describe | List registered tables in a DataCanvas session — schema, row count, column names |
socrata_dataframe_query | Run SELECT-only SQL against DataCanvas tables populated by socrata_query_dataset |
socrata_dataframe_drop | Drop a DataCanvas canvas, or one table on it — opt-in via SOCRATA_DATAFRAME_DROP_ENABLED=true |
Resources
| Resource | Description |
|---|---|
socrata://datasets/{domain}/{datasetId} | Fetch full metadata and column schema for a dataset by stable URI — same payload as socrata_get_dataset |
socrata://portals | Paginated list of known Socrata portals with organization name and approximate dataset count |
All resource data is also reachable via tools. Use the corresponding tool for agent workflows — resources are for clients that support URI-addressable data.
Prompts
| Prompt | Description |
|---|---|
explore_open_data | Structured six-step civic data investigation workflow: find portal → discover datasets → inspect schema → query → aggregate → synthesize |
Capability reference
socrata_list_portals tool
- Curated catalog of 39 well-known city, county, state, and federal portals — every member verified live in the Discovery catalog
- Per-portal dataset counts fetched live from the Discovery API, cached ~24 hours (
0means the portal exposes no dataset assets to the catalog;nullmeans the count is temporarily unavailable) - Client-side substring filtering on domain or organization name; pagination up to 200 per page with offset
- Returns domain (pass to
socrata_find_datasets), organization name, and approximate dataset count; the count includes datasets a portal federates from another Socrata tenant (Austin, Illinois, Mesa, and San Francisco publish through a data hub; Seattle catalogs under a sibling tenant) - Never fails on an upstream error — a count that cannot be fetched comes back
nulland the listing still returns
socrata_find_datasets tool
- Full-text query across dataset names/descriptions; scope with
domain, filter bycategories/tags, restrictonlyto an asset type (datasets, maps, files, calendars, stories) - Sort by relevance, page views, created date, or updated date; up to 100 per page with offset pagination
- Returns dataset IDs, names, domains, tags, update timestamps, and
column_names— the API field names SoQL takes (cuisine_description, not the display labelCUISINE DESCRIPTION), computed-region columns dropped — callsocrata_get_datasetfor typed schema before writing queries - Recovery hints on empty results — echoes applied filters and suggests how to broaden
domaintakes a bare hostname; URL forms (https://data.cdc.gov/) are reduced to the host. A scoped search covers every dataset the portal publishes, including ones federated from another Socrata tenant, reported under the portal's own domain- Typed errors:
rate_limited(retryable) when the Discovery API returns 429,unknown_domainwhen the Discovery catalog does not index the domain,invalid_domainwhen the domain is not a hostname
socrata_get_dataset tool
- Returns field names, Socrata data types, descriptions, row count (with
row_count_sourceprovenance), and licensing when available - Column
data_typedetermines WHERE clause syntax:Number→ bare literals (year=2023),Text→ single-quoted strings (year='2023') - Excludes computed region columns (
:@computed_region_*) to reduce noise; includes per-column non-null counts when available - Dataset IDs are portal-scoped — pass the
domainfrom the samesocrata_find_datasetsresult; URL-form domains are reduced to the host - Typed errors:
invalid_id(malformed four-by-four ID),not_found(no such dataset on the domain queried — the message names the ID and domain, and the recovery names the portal that holds the ID when the Discovery catalog knows it),unknown_domain(the domain is not serving the Socrata API — it does not resolve, is not a Socrata portal, or redirects elsewhere; fails on the first attempt),invalid_domain(not a hostname),rate_limited(retryable; honors the upstreamRetry-After) - Always call before writing a
socrata_query_datasetWHERE clause
socrata_query_dataset tool
searchfor quick full-text lookup ($q), or combineselect/where/group/having/orderfor full analytical control — clauses reference columns by API field name (field_namefromsocrata_get_dataset), never the display label; operators=,!=,>,<,LIKE,IN(...),BETWEEN,IS NULL,starts_with(),contains(),AND,OR,NOT- Aggregation via
count(*),sum(),avg(),min(),max()withgroup/having not_tabularidentifies assets without queryable rows; search withonly: "datasets".query_rejectedidentifies a gateway refusal of query clauses when a bare row request succeeds; simplify the query and use SoQLcase(condition, value, true, fallback)for conditional aggregates.- Up to 5000 rows per call with offset pagination;
total_countreturned when a plain row query is truncated (absent for grouped/aggregate queries) assembled_queryechoes the SoQL string for learning the syntax; all SODA 2.1 row values are strings except geo/location columns, which return nested objects- When
CANVAS_PROVIDER_TYPE=duckdband the page fillslimit, up to 50,000 matching rows spill to a DataCanvas table whatever thelimit(canvas_id,table_name,canvas_row_count) — list its columns withsocrata_dataframe_describe, then run SQL withsocrata_dataframe_query. A smalllimit(e.g. 10) stages a large match without a large inline page - Typed errors:
not_tabular,query_rejected,invalid_id,not_found(names the ID, the domain queried, and the portal holding the ID when known),unknown_domain,invalid_domain,soql_error(bad SoQL, unknown column, or type mismatch — carries the upstreamsocrataCodeand, when upstream names it, the offendingcolumn; the recovery hint matches the code: API field names for a parse error, both fixes for an unknown identifier, the quoting rule for a type mismatch),rate_limited(retryable; honors the upstreamRetry-After)
socrata_dataframe_describe tool
- Requires
canvas_idfrom a priorsocrata_query_datasetspill — canvases cannot be enumerated, so omitting it fails withcanvas_id_requiredrather than listing tables - Shows table name, row count, and DuckDB column types for each registered table (SODA
number→DOUBLE) - Only meaningful when
CANVAS_PROVIDER_TYPE=duckdbis set - Typed errors:
canvas_id_required,canvas_not_found(expired or unknown token — re-runsocrata_query_datasetto stage a fresh canvas)
socrata_dataframe_query tool
- SELECT-only SQL against a
canvas_idtable staged bysocrata_query_dataset; DDL, DML, file references, file-reading functions, and engine-setting functions (current_schema,current_setting) are rejected. Use direct table references instead ofquery_table;query()accepts only a single SQL string literal. Qualify a staged column namedcurrent_schemawith its table (t.current_schema). - Spilled columns are typed from the SODA response headers:
numbercolumns (aggregate aliases included) areDOUBLE, so numeric comparisons work without a cast (year > 2020,amount < 500); text and timestamp columns stayVARCHAR— compare times withCAST(date AS TIMESTAMP) - Up to 10,000 rows per call, default 1000
- Typed errors:
canvas_disabled(CANVAS_PROVIDER_TYPE=none),canvas_not_found,table_not_found,sql_rejected(non-SELECT, system catalog access, or a denied function) - Works out of the box — DuckDB ships as a regular dependency and is enabled by default;
CANVAS_PROVIDER_TYPE=nonedisables it
socrata_dataframe_drop tool
- Opt-in: omitted from the tool list until
SOCRATA_DATAFRAME_DROP_ENABLED=true; also needsCANVAS_PROVIDER_TYPE=duckdb - Without
table_name, drops the whole canvas — itscanvas_idstops resolving forsocrata_dataframe_describeandsocrata_dataframe_query. Withtable_name, drops that one table and leaves the canvas and its other tables in place - Reports the dropped tables with their row counts and the tables still on the canvas
- Deletes only the staged copy —
socrata_query_datasetcan stage the data again - Typed errors:
canvas_disabled(CANVAS_PROVIDER_TYPE=none),canvas_not_found(unknown, expired, or already dropped),table_not_found(carries the canvas'savailableTables)
socrata://datasets/{domain}/{datasetId} resource
- Returns the same payload as
socrata_get_dataset— field names, data types, descriptions, row count, licensing domainanddatasetIdcome fromsocrata_find_datasets;datasetIdmust match the four-by-four pattern (e.g.kzjm-xkqj)- Fails validation on a malformed ID, and not-found when the dataset doesn't exist on the domain (naming the portal that holds the ID when the Discovery catalog knows it)
socrata://portals resource
- Curated catalog of 39 known Socrata portals, cursor-paginated (
cursorparam, default 50 per page, capped at 200) - Returns domain, organization name, and approximate dataset count (
0= no dataset assets,null= temporarily unavailable), cached ~24 hours - Pass
domaintosocrata_find_datasetsto scope a search to one portal
explore_open_data prompt
- Arguments:
topicrequired;portalandgeographyoptional to skip discovery or scope WHERE clauses - Returns one user message walking a six-step workflow: find portal → discover datasets → inspect schema → query → aggregate → synthesize
Features
Built on @cyanheads/mcp-ts-core: stdio and Streamable HTTP transports, pluggable auth (none / jwt / oauth), swappable storage (in-memory, filesystem, Supabase, Cloudflare KV/R2/D1), structured logging with optional OpenTelemetry tracing.
Socrata-specific:
- Full Socrata SODA 2.1 API integration — SoQL query builder with select, where, group, having, order, search, limit, offset
- Discovery API for cross-portal dataset search and per-portal dataset counts (curated 39-portal catalog, counts cached ~24h)
- App token support (
SOCRATA_APP_TOKEN) for higher per-IP rate limits - Configurable default portal domain via
SOCRATA_DEFAULT_DOMAIN - DataCanvas spillover (DuckDB, bundled) — large query results register as SQL tables for analytical queries
Agent-friendly output:
- Assembled SoQL string echoed in every
socrata_query_datasetresponse so agents can learn and refine syntax - Recovery hints on empty results — echoes applied filters with specific suggestions for broadening
- Truncation disclosure —
truncated/shown/capfields when rows fill the limit, with guidance to page or raise the limit, naming the staged table and both dataframe tools when the result spilled - Typed error reasons across every tool (
invalid_id,not_found,not_tabular,query_rejected,unknown_domain,invalid_domain,soql_error,rate_limited,canvas_id_required,canvas_not_found,table_not_found,sql_rejected,canvas_disabled) with actionable recovery text
Getting started
Add the following to your MCP client configuration file.
Public Hosted Instance
A public instance is available at https://socrata.caseyjhand.com/mcp — no installation required. It serves Streamable HTTP; each client takes its own config shape.
For Claude Code:
claude mcp add --transport http socrata-mcp-server https://socrata.caseyjhand.com/mcp
For Cline (cline_mcp_settings.json):
{
"mcpServers": {
"socrata-mcp-server": {
"type": "streamableHttp",
"url": "https://socrata.caseyjhand.com/mcp"
}
}
}
For Cursor (mcp.json):
{
"mcpServers": {
"socrata-mcp-server": {
"url": "https://socrata.caseyjhand.com/mcp"
}
}
}
Claude Desktop adds remote servers under Settings → Connectors.
Self-Hosted / Local
{
"mcpServers": {
"socrata-mcp-server": {
"type": "stdio",
"command": "bunx",
"args": ["@cyanheads/socrata-mcp-server@latest"],
"env": {
"MCP_TRANSPORT_TYPE": "stdio",
"MCP_LOG_LEVEL": "info"
}
}
}
}
Or with npx (no Bun required):
{
"mcpServers": {
"socrata-mcp-server": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@cyanheads/socrata-mcp-server@latest"],
"env": {
"MCP_TRANSPORT_TYPE": "stdio",
"MCP_LOG_LEVEL": "info"
}
}
}
}
Or with Docker:
{
"mcpServers": {
"socrata-mcp-server": {
"type": "stdio",
"command": "docker",
"args": [
"run", "-i", "--rm",
"-e", "MCP_TRANSPORT_TYPE=stdio",
"ghcr.io/cyanheads/socrata-mcp-server:latest"
]
}
}
}
For Streamable HTTP, set the transport and start the server:
MCP_TRANSPORT_TYPE=http MCP_HTTP_PORT=3010 bun run start:http
# Server listens at http://localhost:3010/mcp
Prerequisites
- Bun v1.4.0 or higher (or Node.js v24+).
- Optional: A Socrata app token — register for free at any portal (e.g. data.seattle.gov) to get higher rate limits (10 req/s per token vs. shared throttled pool without one).
Installation
- Clone the repository:
git clone https://github.com/cyanheads/socrata-mcp-server.git
- Navigate into the directory:
cd socrata-mcp-server
- Install dependencies:
bun install
- Configure environment:
cp .env.example .env
# edit .env and set SOCRATA_APP_TOKEN if you have one
Configuration
All configuration is validated at startup via Zod schemas in src/config/server-config.ts. Key environment variables:
| Variable | Description | Default |
|---|---|---|
SOCRATA_APP_TOKEN | Socrata app token (X-App-Token header). Without a token, requests share a throttled pool per source IP. | — |
SOCRATA_DEFAULT_DOMAIN | Default portal domain when domain is omitted from tool calls. | data.seattle.gov |
MCP_TRANSPORT_TYPE | Transport: stdio or http. | stdio |
MCP_HTTP_PORT | Port for HTTP server. | 3010 |
MCP_SESSION_MODE | Session handling: stateful, stateless, or auto (schema default auto resolves to stateful). This server sets it explicitly to stateless. | stateless |
MCP_AUTH_MODE | Auth mode: none, jwt, or oauth. | none |
MCP_LOG_LEVEL | Log level (RFC 5424): debug, info, notice, warning, error. | info |
CANVAS_PROVIDER_TYPE | DataCanvas spillover for large result sets; none disables it. DuckDB ships with the server — no additional install required. | duckdb |
SOCRATA_DATAFRAME_DROP_ENABLED | Set to true to enable socrata_dataframe_drop. Off, the tool is omitted from the tool list. | false |
LOGS_DIR | Directory for log files (Node.js only). | <project-root>/logs |
STORAGE_PROVIDER_TYPE | Storage backend: in-memory, filesystem, supabase, cloudflare-kv/r2/d1. | in-memory |
OTEL_ENABLED | Enable OpenTelemetry instrumentation. | false |
LOG_TOOL_FAILURE_PAYLOADS | Log each failed tool call's arguments and result (key-name redaction only). | false |
See .env.example for the full list of optional overrides.
Running the server
Local development
- Build and run:
# One-time build bun run rebuild # Run the built server bun run start:stdio # or bun run start:http - Run checks and tests:
bun run devcheck # Lint, format, typecheck, security audit bun run test # Vitest test suite
Docker
docker build -t socrata-mcp-server .
docker run --rm -e MCP_TRANSPORT_TYPE=http -p 3010:3010 socrata-mcp-server
The Dockerfile defaults to HTTP transport, stateless session mode, and logs to /var/log/socrata-mcp-server. OpenTelemetry peer dependencies are installed by default — build with --build-arg OTEL_ENABLED=false to omit them.
Project structure
| Directory | Purpose |
|---|---|
src/index.ts | createApp() entry point — registers tools, resources, prompts, and inits the Socrata service. |
src/config | Server-specific environment variable parsing and validation with Zod. |
src/mcp-server/tools | Tool definitions (*.tool.ts). Seven tools covering portal listing, dataset search, schema fetch, SoQL query, and DataCanvas SQL and cleanup. |
src/mcp-server/resources | Resource definitions (*.resource.ts). Dataset metadata and portal catalog resources. |
src/mcp-server/prompts | Prompt definitions (*.prompt.ts). Civic data investigation workflow prompt. |
src/services/socrata | Socrata service layer — SODA 2.1 API client, Discovery API, query builder, type normalization. |
tests/ | Unit and integration tests mirroring src/. |
Development guide
See CLAUDE.md for development guidelines and architectural rules. The short version:
- Handlers throw, framework catches — no
try/catchin tool logic - Use
ctx.logfor request-scoped logging,ctx.statefor tenant-scoped storage - Call
socrata_get_datasetbefore writing WHERE clauses —field_nameis what SoQL references and columndata_typedetermines quoting - Wrap external API calls: validate raw → normalize to domain type → return output schema; never fabricate missing fields
Contributing
Issues are welcome. Run checks and tests before submitting:
bun run devcheck
bun run test
License
Apache-2.0 — see LICENSE for details.
Questions fréquentes
Qu'est-ce que io.github.cyanheads/socrata-mcp-server ?
Search and query government open-data portals (Socrata SODA API).
Comment installer io.github.cyanheads/socrata-mcp-server ?
Il fonctionne comme serveur distant. Dans Claude Code : claude mcp add --transport http socrata-mcp-server 'https://socrata.caseyjhand.com/mcp'. Pour les autres clients, collez la configuration JSON de cette page dans leurs réglages MCP.
Faut-il une clé API pour io.github.cyanheads/socrata-mcp-server ?
Son entrée au registre n'indique aucune clé ni secret obligatoire. Certaines fonctions peuvent tout de même demander un compte auprès du service auquel il se connecte.
io.github.cyanheads/socrata-mcp-server est-il open source ?
Oui. Son code est sur GitHub sous licence Apache-2.0.
Qui maintient io.github.cyanheads/socrata-mcp-server ?
Il est publié par cyanheads. AI.tn le référence sans le maintenir ni le recommander.
Issu du registre MCP officiel. Être listé n'est pas une recommandation : vérifiez le code et les permissions d'un serveur avant de lui donner accès à vos données.