Skip to main content
Glama
socrata-mcp

socrata-mcp

by jcscocca

socrata-mcp

An MCP server that gives LLM agents typed, cached access to civic open-data portals. Speaks Socrata (SODA 2.1 + Discovery API) today; the provider layer is a thin interface so CKAN can be added later without touching the tool surface.

Highlights:

  • Server-side profiling — null rates, distinct counts, min/max, top values computed via aggregate SoQL; the dataset is never downloaded.

  • Hard row caps with honest truncation — every query result carries a truncated flag; paging uses a stable :id order.

  • Disk cache under ~/.socrata-mcp/cache keyed by query hash, with short TTLs for metadata and configurable TTLs for query results.

  • Polite by default — request throttling, retries with backoff that honor Retry-After, optional SOCRATA_APP_TOKEN sent as X-App-Token.

  • Loud failures — the portal's actual error message is surfaced to the agent, never swallowed.

  • Tableau-ready CSV export — streamed, paged download designed to chain into vizforge's csv_to_dashboard.

Install

git clone <this repo> && cd socrata-mcp
python3 -m venv .venv
.venv/bin/pip install -e ".[dev]"

Register with your MCP client (see .mcp.json.example):

{
  "mcpServers": {
    "socrata": {
      "command": "/absolute/path/to/socrata-mcp/.venv/bin/python",
      "args": ["-m", "socrata_mcp"],
      "env": { "SOCRATA_APP_TOKEN": "optional-app-token" }
    }
  }
}

Related MCP server: ontario-data-mcp

Tools

Tool

What it does

search_datasets(query, domain?, category?, limit?, offset?)

Full-text catalog search via the Socrata Discovery API.

get_dataset(domain, dataset_id)

Columns with types, row count, update cadence, license, attribution.

query(domain, dataset_id, …)

Structured SoQL (select/where/group/order/limit/offset, within_circle, within_box) or raw soql. Validated, paged, row-capped, truncated flag.

profile_dataset(domain, dataset_id)

Per-column null rates, distinct counts, min/max for dates/numbers, top values for categoricals — all portal-side.

sample(domain, dataset_id, n?)

First n rows (capped at 100) to see real values.

export_csv(domain, dataset_id, out_path, …)

Streamed, paged CSV export of any query.

report(domain, dataset_id, out_path, where?, title?)

One-call HTML report: auto-detected trend chart, top-category charts, numeric summary, data-quality flags. Self-contained file, no JS, no external requests. Also available without MCP: socrata-mcp-report data.seattle.gov tazs-3rd5.

Example agent flow:

search_datasets("crime", domain="data.seattle.gov")
get_dataset("data.seattle.gov", "tazs-3rd5")
profile_dataset("data.seattle.gov", "tazs-3rd5")
query("data.seattle.gov", "tazs-3rd5",
      where="offense_date > '2026-06-10T00:00:00'",
      order="offense_date DESC", limit=100)
export_csv("data.seattle.gov", "tazs-3rd5", "out/spd_30d.csv",
           where="offense_date > '2026-06-10T00:00:00'")
# → vizforge: csv_to_dashboard("out/spd_30d.csv", ...)

Configuration

All optional, via environment variables:

Variable

Default

Meaning

SOCRATA_APP_TOKEN

unset

Sent as X-App-Token; raises portal rate limits.

SOCRATA_MCP_CACHE_DIR

~/.socrata-mcp/cache

Disk cache root.

SOCRATA_MCP_METADATA_TTL

300

Seconds to cache dataset metadata.

SOCRATA_MCP_CATALOG_TTL

300

Seconds to cache catalog searches.

SOCRATA_MCP_QUERY_TTL

3600

Seconds to cache query/profile results (0 disables).

SOCRATA_MCP_DEFAULT_LIMIT

100

Rows returned when a query gives no limit.

SOCRATA_MCP_MAX_ROWS

5000

Hard row cap for inline query results.

SOCRATA_MCP_MAX_EXPORT_ROWS

1000000

Hard row cap for CSV exports.

SOCRATA_MCP_PAGE_SIZE

1000

Rows fetched per HTTP request.

SOCRATA_MCP_THROTTLE_INTERVAL

0.2

Minimum seconds between portal requests.

SOCRATA_MCP_TIMEOUT

30

Per-request timeout in seconds.

Cache layout: cache/<kind>/<sha256>.json (kind ∈ catalog, metadata, query, profile), each file {"cached_at": <epoch>, "data": …}. Deleting the directory is always safe.

Notes:

  • Discovery searches use the US endpoint (api.us.socrata.com); EU-hosted portals are still directly queryable via get_dataset/query on their domain.

  • Raw soql exports run as a single request, so give them an explicit LIMIT (default cap 50,000); structured exports page automatically.

Development

.venv/bin/pytest              # unit tests (all HTTP mocked)
.venv/bin/pytest -m network   # live smoke tests against data.seattle.gov

Architecture: deterministic core (soql.py, cache.py, http_client.py, profile.py, export.py) with the MCP layer (socrata_mcp/mcp/) as thin wrappers over a Provider interface (providers/base.py). To add CKAN, implement Provider in providers/ckan.py — the tool surface stays unchanged.

License

MIT — see LICENSE.

Available Tools

6 tools
export_csvA

Export query results to a Tableau-ready CSV via streamed, paged download.

Accepts the same query parameters as query (structured or raw soql) and writes matching rows to out_path. Designed to chain into vizforge's csv_to_dashboard. Point/location values are serialized as JSON strings.

Args: domain: Portal hostname, e.g. "data.seattle.gov". dataset_id: Socrata 4x4 id, e.g. "tazs-3rd5". out_path: Destination .csv path (parent directories are created). select/where/group/order/limit/soql/within_circle/within_box: as in query. max_rows: Safety cap for this export (default 1,000,000).

Returns: {path, rows_written, truncated, columns, notes}.

Inputs: domain (required), out_path (required), dataset_id (required), soql, +8 more. Returns: object

get_datasetA

Dataset metadata: columns with types, row count, update cadence, license.

Args: domain: Portal hostname, e.g. "data.seattle.gov". dataset_id: Socrata 4x4 id, e.g. "tazs-3rd5".

Returns: {name, description, columns: [{field_name, name, type, description}], row_count, license, attribution, created_at, data_updated_at, update_frequency, tags, source_url}. Use columns' field_name values in query/profile calls.

Inputs: domain (required), dataset_id (required). Returns: object

profile_datasetA

Profile every column: null rate, distinct count, min/max, top values.

Computed portal-side via aggregate SoQL — the dataset is never downloaded. Dates and numbers get min/max (numbers also avg); low-cardinality text columns get their top 10 values with counts.

Args: domain: Portal hostname, e.g. "data.seattle.gov". dataset_id: Socrata 4x4 id, e.g. "tazs-3rd5".

Returns: {row_count, columns: [{field_name, type, null_rate, non_null_count, distinct_count, min?, max?, avg?, top_values?, error?}], notes}.

Inputs: domain (required), dataset_id (required). Returns: object

queryA

Query a dataset with structured SoQL parameters OR one raw SoQL string.

Structured mode (recommended): pass any of select/where/group/order/ limit/offset plus optional geo filters. Raw mode: pass soql only (e.g. "SELECT offense, count(*) GROUP BY offense LIMIT 50").

Args: domain: Portal hostname, e.g. "data.seattle.gov". dataset_id: Socrata 4x4 id, e.g. "tazs-3rd5". select: Columns/expressions, e.g. ["offense", "count(*) as n"]. where: SoQL filter, e.g. "offense_date > '2026-06-01T00:00:00'". group: GROUP BY columns (pair with aggregate select expressions). order: e.g. "offense_date DESC". Defaults to ":id" for stable paging. limit: Max rows returned (default 100, hard cap applies). offset: Row offset for pagination. soql: Raw SoQL query — mutually exclusive with all structured params. within_circle: {field, lat, lon, radius_m} geo filter on a point column. within_box: {field, nw_lat, nw_lon, se_lat, se_lon} geo filter.

Returns: {rows, row_count, truncated, query: {params, effective_limit, clamped}}. truncated: true means more rows matched than were returned — narrow the query or use export_csv for bulk extraction.

Inputs: domain (required), dataset_id (required), soql, group, +7 more. Returns: object

sampleA

Fetch the first n rows of a dataset (n capped at 100).

Args: domain: Portal hostname, e.g. "data.seattle.gov". dataset_id: Socrata 4x4 id, e.g. "tazs-3rd5". n: Number of rows (default 10, max 100).

Returns: {rows, row_count, note}. Rows are in :id order — a peek at real values, not a random sample.

Inputs: domain (required), dataset_id (required), n. Returns: object

search_datasetsA

Search open-data catalogs for datasets (Socrata Discovery API).

Args: query: Full-text search, e.g. "crime reports" or "building permits". domain: Restrict to one portal, e.g. "data.seattle.gov". category: Portal category, e.g. "Public Safety". limit: Max results (default 20, cap 100). offset: Pagination offset into the result set.

Returns: {results: [{id, name, domain, description, updated_at, category, permalink}], count, total, offset}. Use each result's domain + id with the other tools.

Inputs: query (required), limit, domain, offset, +1 more. Returns: object

Tool Schema Changelog

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

Loading tool schema changelog…

    TDQS

    A4.4/5.0

    Scored across 6 tools

    Disambiguation5/5

    Each tool targets a distinct operation: search_datasets for discovery, get_dataset for metadata, profile_dataset for column profiling, query for flexible querying, sample for quick peeks, and export_csv for bulk download. No functional overlap.

    Naming Consistency4/5

    Most tools follow verb_noun pattern (export_csv, get_dataset, profile_dataset, search_datasets). 'query' and 'sample' are single verbs but remain clear and consistent with the style; minor deviation from the full pattern.

    Tool Count5/5

    6 tools is well-scoped for a read-only Socrata data exploration server. Each tool addresses a distinct need without bloat, and the count feels appropriate for the domain.

    Completeness5/5

    The tool surface covers the full lifecycle of data exploration: search for datasets, inspect metadata, profile columns, query data, sample rows, and export. No obvious gaps for the intended purpose of querying and exporting open data.

    Related MCP Connectors

    Related MCP Servers

    • F
      license
      Not graded
      quality
      D
      maintenance
      An MCP server that provides tools for intelligently querying, analyzing, and retrieving datasets from Toronto's CKAN-powered open data portal. It enables AI assistants to perform natural language searches, inspect data structures, and track dataset update frequencies across the city's open data catalog.
      12
      -
    • A
      license
      A
      quality
      C
      maintenance
      An MCP server for discovering, downloading, querying, and analyzing datasets from Ontario's open data portals, allowing natural language questions and high-performance analytics via DuckDB.
      23
      1
      MIT
    • A
      license
      Not graded
      quality
      B
      maintenance
      Enables AI agents to query U.S. government datasets via the Data.gov CKAN API, wrapped as an MCP server.
      109 npm
      MIT