Server definition
- Hash
- sha256:54ecc6ba296966a2677d21365a6a7ae075a6ee99b355a0ca7313c87b668509c0
- What it is
- What a remote MCP server returned when asked what it offers: 12 tools
The blob, as servednamed by its sha256
{
"instructions": "Cleaned, standardized Georgia public data (education, Census context, and immigration) as a star schema — lean fact tables (keys + metrics) joined to shared dimensions (district / school / county / demographic labels), described by ODCS data contracts and served over DuckDB.\n\nScope: tabular and structured data only; this server holds no documents. For Georgia government documents (current-session General Assembly bills, recent Supreme Court of Georgia opinions, and the Official Code of Georgia Annotated), use Georgia Commons (https://georgiacommons.org).\n\nPick a tool:\n- list_datasets — enumerate every topic + dimension (the catalog).\n- search_datasets — find a topic by keyword when you don't know its name.\n- describe_dataset — one topic's full schema: columns, the exact filter keys + enum values (read this before query_dataset), FK→dimension joins, example queries, usage, limitations.\n- query_dataset — the core data tool: filtered, dimension-joined rows for one topic.\n- distinct_values — the valid values of one filter column (FK codes / free categoricals) before query_dataset, so a filter doesn't silently return nothing.\n- resolve_entity — turn a place/demographic name or code (e.g. 'Fulton') into the district/school/county/demographic key to filter by.\n- aggregate — server-side avg/sum/min/max/count of a metric grouped by year / FK codes / categoricals (aggregation-first: ask for the summary, don't pull raw rows and reduce in-context).\n- describe_dimension / get_dimension — a dimension's schema (keys, link keys, semantics) and its label rows.\n- get_contract — the raw ODCS contract for a topic or dimension.\n- link_tables / link_query — cross-dataset / cross-topic SQL joins on shared geography (call link_tables first for the exact table paths + join keys).\n\nCommon recipe — top-N ranking ('which schools/districts have the highest/lowest X'): a plain query_dataset with order_by=<the topic's key_metric>, order='desc' (top) or 'asc' (bottom), limit=N, pinned to ONE year (default the latest available) — never an aggregate. Keep the recommended_query pins (demographic total, required single-selects) so rows are comparable; NULL (suppressed) metrics sort last in either direction. Each topic's describe_dataset echoes this recipe as recommended_query.ranking.\n\nRead the data carefully: a NULL metric usually means the value was SUPPRESSED (small counts), not zero — never report it as 0 (see each topic's null_semantics). Within a demographic_category the values are mutually exclusive and the `all` demographic is the denominator — don't sum race/ethnicity rows expecting them to equal `all`. Some topics declare `non_additive` columns (describe_dataset): their rows overlap (a county row counts an application listing several counties, an inmate counts in each offense group), so never add them up — a sum across one is not a total; use the published total row. Year coverage can have gaps (see year_gaps). The districts dimension's district_census_id is a 5-digit Census school-district code (NOT a county FIPS) and a district is not 1:1 with a county. Education topics are district/school-grain; Census topics may be state, county, school-district, or tract grain; immigration topics are state- or county-grain — a county is NOT a school district. Immigration `year` is a CALENDAR year (some topics add a `month`), while ICE and EOIR publish their own statistics by federal fiscal year, so the two will not match; see each topic's limitations.",
"tools": [
{
"description": "Compute a grouped aggregate over one topic — the aggregation-first path. `agg` is one of avg/sum/min/max/count/weighted_rate; `metric` is a metric column (DEFAULTS to the topic key_metric; ignored for count); `group_by` is a list of grain columns (year, FK codes like district_code or county_fips, or categoricals — see describe_dataset). `weighted_rate` computes a true population-weighted SUM(numerator)/SUM(denominator) for a rate key metric (when the contract declares the components) — prefer it over `avg` for a rate across multiple places/years, since `avg` means the per-row rates and ignores population. Supports the same `filters` / `year` / `year_min`-`year_max` / `detail` as query_dataset, plus `order_by`+`order` for top-N (order_by 'value' for the aggregated column; NULL cells sort LAST in either direction). Returns one small row per group with `<metric>_<agg>` (or `row_count`) plus coverage diagnostics (input_rows / non-null counts) so suppression is visible; `aggregation_scope` flags whether rows are source-published at this grain or recomputed from a finer detail (prefer source-published — see the advisory). A `sum`/`weighted_rate` ACROSS a column the contract declares non_additive (overlapping rows, e.g. county rows that count one application in several counties) still returns, but carries a top-level `non_additive` block and a leading `non_additive_sum` advisory: that figure is NOT a total — group_by the column or use the published total row instead. Aggregates SKIP NULLs and NULL means SUPPRESSED not zero. No raw SQL: all identifiers are contract-allowlisted.",
"inputSchema": {
"properties": {
"agg": {
"default": "avg",
"title": "Agg",
"type": "string"
},
"detail": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"title": "Detail"
},
"filters": {
"anyOf": [
{
"additionalProperties": true,
"type": "object"
},
{
"type": "null"
}
],
"default": null,
"title": "Filters"
},
"group_by": {
"anyOf": [
{
"items": {
"type": "string"
},
"type": "array"
},
{
"type": "null"
}
],
"default": null,
"title": "Group By"
},
"limit": {
"anyOf": [
{
"type": "integer"
},
{
"type": "null"
}
],
"default": null,
"title": "Limit"
},
"main_topic": {
"default": "education",
"title": "Main Topic",
"type": "string"
},
"metric": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"title": "Metric"
},
"offset": {
"default": 0,
"title": "Offset",
"type": "integer"
},
"order": {
"default": "asc",
"title": "Order",
"type": "string"
},
"order_by": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"title": "Order By"
},
"topic": {
"title": "Topic",
"type": "string"
},
"year": {
"anyOf": [
{
"type": "integer"
},
{
"type": "null"
}
],
"default": null,
"title": "Year"
},
"year_max": {
"anyOf": [
{
"type": "integer"
},
{
"type": "null"
}
],
"default": null,
"title": "Year Max"
},
"year_min": {
"anyOf": [
{
"type": "integer"
},
{
"type": "null"
}
],
"default": null,
"title": "Year Min"
}
},
"required": [
"topic"
],
"title": "aggregateArguments",
"type": "object"
},
"name": "aggregate",
"outputSchema": {
"additionalProperties": true,
"title": "aggregateDictOutput",
"type": "object"
}
},
{
"description": "Full schema for one topic: every column (name/type/role/unit/value range/null-meaning), the exact `filters` list with enum values (read this before query_dataset — it is the authoritative set of filter keys), the FK→dimension join shape (`foreign_keys`), example queries, usage, limitations, null semantics, tags, and `schema_hash` (for cache/drift detection). The top-level `key_metric` names the single headline column most answers want; each column carries `key_metric_grain_contributor` (a grain axis the key metric is only comparable within — pin or group by it) and `metric_component` (numerator/denominator of a rate/average metric). `recommended_query` gives the safe default query shape (key metric + filters to pin + required single-selects) plus a `ranking` recipe for top/bottom-N asks; `filter_hints` lists paired filters; `non_additive` lists columns whose rows overlap (never sum across them; a metric entry names metric columns never to add together) and `value_implications` values that imply another column's value; each categorical filter carries `has_total` / `requires_single_value`. Pass `verbosity='schema'` for a much smaller payload that drops the prose (description/usage/limitations/example queries/column descriptions) but keeps every field needed to compose a correct query — use it when you only need the filter keys and enums; prefer the default 'full' before reporting conclusions (the limitations prose carries the caveats). On an unknown topic returns a self-describing error listing available topics + a 'did you mean' hint. `main_topic` defaults to 'education'; pass 'census' for Census topics or 'immigration' for immigration topics.",
"inputSchema": {
"properties": {
"main_topic": {
"default": "education",
"title": "Main Topic",
"type": "string"
},
"topic": {
"title": "Topic",
"type": "string"
},
"verbosity": {
"default": "full",
"description": "'full' (default) or 'schema' (drops prose; keeps columns/filters/enums/key_metric/recommended_query).",
"title": "Verbosity",
"type": "string"
}
},
"required": [
"topic"
],
"title": "describe_datasetArguments",
"type": "object"
},
"name": "describe_dataset",
"outputSchema": {
"additionalProperties": true,
"title": "describe_datasetDictOutput",
"type": "object"
}
},
{
"description": "Schema for one dimension (districts / schools / counties / demographics): the (possibly composite) primary key, the attribute columns a join attaches, the cross-dataset `link_keys` (e.g. districts.district_census_id → Census via the crosswalk — a 5-digit school-district code, NOT a county FIPS), and demographics `semantics` (within a category the values are mutually exclusive; `all` is the denominator). Read this before writing a link_query join.",
"inputSchema": {
"properties": {
"name": {
"title": "Name",
"type": "string"
}
},
"required": [
"name"
],
"title": "describe_dimensionArguments",
"type": "object"
},
"name": "describe_dimension",
"outputSchema": {
"additionalProperties": true,
"title": "describe_dimensionDictOutput",
"type": "object"
}
},
{
"description": "List the distinct values of ONE filterable column of a topic — the fast way to learn valid filter values before query_dataset, especially for FREE categoricals and FK codes (district_code/school_code/county_fips/demographic) that carry no enum in describe_dataset (a wrong guess otherwise returns an empty page with no error). `column` must be a filterable column (see describe_dataset's `filters`). Optional `prefix` does a case-insensitive starts-with filter; `limit` caps results (default 50). Enum-bearing columns return their contract enum directly; others run a capped SELECT DISTINCT over the gold data. `truncated` flags when the list is capped.",
"inputSchema": {
"properties": {
"column": {
"title": "Column",
"type": "string"
},
"detail": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"title": "Detail"
},
"limit": {
"default": 50,
"title": "Limit",
"type": "integer"
},
"main_topic": {
"default": "education",
"title": "Main Topic",
"type": "string"
},
"prefix": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"title": "Prefix"
},
"topic": {
"title": "Topic",
"type": "string"
}
},
"required": [
"topic",
"column"
],
"title": "distinct_valuesArguments",
"type": "object"
},
"name": "distinct_values",
"outputSchema": {
"additionalProperties": true,
"title": "distinct_valuesDictOutput",
"type": "object"
}
},
{
"description": "Return the authoritative ODCS v3.2 data contract for a topic (kind='topic') or a dimension (kind='dimension') so you can consume the machine-readable schema without cloning the repo. fmt='yaml' (default) returns the document verbatim as text; fmt='json' returns it parsed. Only approved topics and loaded dimensions expose a contract.",
"inputSchema": {
"properties": {
"fmt": {
"default": "yaml",
"title": "Fmt",
"type": "string"
},
"kind": {
"default": "topic",
"title": "Kind",
"type": "string"
},
"main_topic": {
"default": "education",
"title": "Main Topic",
"type": "string"
},
"name": {
"title": "Name",
"type": "string"
}
},
"required": [
"name"
],
"title": "get_contractArguments",
"type": "object"
},
"name": "get_contract",
"outputSchema": {
"additionalProperties": true,
"title": "get_contractDictOutput",
"type": "object"
}
},
{
"description": "Paginated read of a dimension table — the label lookups (district names, school names, county names, demographic labels). Rows are ordered by the dimension's primary key so paging is stable. Use describe_dimension for the schema and link keys. Small page defaults; `truncated` + a `bulk_export` pointer signal when to pull the full table elsewhere.",
"inputSchema": {
"properties": {
"limit": {
"anyOf": [
{
"type": "integer"
},
{
"type": "null"
}
],
"default": null,
"title": "Limit"
},
"name": {
"title": "Name",
"type": "string"
},
"offset": {
"default": 0,
"title": "Offset",
"type": "integer"
}
},
"required": [
"name"
],
"title": "get_dimensionArguments",
"type": "object"
},
"name": "get_dimension",
"outputSchema": {
"additionalProperties": true,
"title": "get_dimensionDictOutput",
"type": "object"
}
},
{
"description": "Run a cross-dataset / cross-topic analytical SQL query that the per-topic query_dataset filters can't express — e.g. join education, Census, or immigration facts to a dimension (or another dataset) on shared geography (immigration and Census county topics share county_fips directly). READ-ONLY, SANDBOXED DuckDB: one SELECT (or WITH … SELECT); no DDL/DML/COPY/ATTACH/INSTALL/PRAGMA/SET/CALL; you may only read_parquet() the curated gold paths returned by link_tables (call it first and paste the snippets) — querying a file path directly is rejected. Joins use the keys from describe_dimension's link_keys (districts.district_census_id bridges to Census via the crosswalk — it is a school-district code, not a county FIPS, so a district is not 1:1 with a county). Results are row- and byte-capped and time-limited; `truncated` flags when capped — add aggregation or a tighter WHERE rather than dumping rows. NULL means suppressed, not zero. On a violation you get a self-describing error naming the offending token/path.",
"inputSchema": {
"properties": {
"limit": {
"anyOf": [
{
"type": "integer"
},
{
"type": "null"
}
],
"default": null,
"title": "Limit"
},
"sql": {
"title": "Sql",
"type": "string"
}
},
"required": [
"sql"
],
"title": "link_queryArguments",
"type": "object"
},
"name": "link_query",
"outputSchema": {
"additionalProperties": true,
"title": "link_queryDictOutput",
"type": "object"
}
},
{
"description": "List the tables link_query can read (curated gold paths only) and the join keys that bridge facts → dimensions → Census geography. Call this BEFORE writing a link_query. Two-tier to stay context-cheap: with NO arguments it returns a LEAN index — every table's name, grain, detail levels, default `read_parquet(...)` snippet, and join keys (enough to pick tables and write a single-detail join). To get every column and a snippet per detail level for the few tables you actually need, call again with `tables=[\"<name>\", ...]` (a `name` from the index, e.g. 'education/gosa/attendance' or 'attendance', or a dimension like 'districts'). Paste the `read_parquet(...)` snippets verbatim into your SQL — they are exactly what the sandbox accepts.",
"inputSchema": {
"properties": {
"tables": {
"anyOf": [
{
"items": {
"type": "string"
},
"type": "array"
},
{
"type": "null"
}
],
"default": null,
"title": "Tables"
}
},
"title": "link_tablesArguments",
"type": "object"
},
"name": "link_tables",
"outputSchema": {
"additionalProperties": true,
"title": "link_tablesDictOutput",
"type": "object"
}
},
{
"description": "Enumerate every approved Georgia dataset (topic) and the shared dimensions. Each topic entry is a LEAN summary — name/keys, year coverage (year_min/year_max + year_gaps), detail levels + default detail, a has_demographic flag (false = no demographic axis, so there is no all-students demographic row to filter), tags, contract version, and a one-line description — enough to pick a topic; call describe_dataset for its full schema (columns, filters, grain, source, example queries). Each dimension entry carries its primary key, attribute columns, and (for districts) cross-dataset link keys. Call this first to learn what exists — but for a NAMED task (you already know roughly the topic), prefer search_datasets, which returns far fewer bytes than this full catalog.",
"inputSchema": {
"properties": {},
"title": "list_datasetsArguments",
"type": "object"
},
"name": "list_datasets",
"outputSchema": {
"additionalProperties": true,
"title": "list_datasetsDictOutput",
"type": "object"
}
},
{
"description": "Query one topic's gold facts with dimension labels joined in (the district/school/county/demographic names come back on every row). `filters` is a dict of column → value or list-of-values: FK codes (district_code, school_code, county_fips, demographic) and any categorical column — see describe_dataset's `filters` for the exact keys and enum values. Use `year` (exact) OR `year_min`/`year_max` (range), never both. `detail` picks the grain (default is the finest available). Returns `rows` plus a `columns` descriptor array (type/role/unit/null-meaning, and `is_key_metric` flagging the headline column) so you interpret values and NULLs correctly — NULL usually means SUPPRESSED, not zero (see null_semantics). The top-level `key_metric` echoes which column is the answer. Use `columns` to project a subset, `include_labels=false` to skip the joined name columns (codes only), and `order_by`+`order` for server-side top-N instead of over-fetching. Pages are small (default 100, max 500); when `truncated` is true a `bulk_export` block points at the REST CSV/Parquet endpoint and the source path for the full pull — do not loop pagination to dump a table. A bad filter returns a self-describing error listing the valid keys/values.",
"inputSchema": {
"properties": {
"columns": {
"anyOf": [
{
"items": {
"type": "string"
},
"type": "array"
},
{
"type": "null"
}
],
"default": null,
"description": "Project only these output columns (fact columns + joined label columns). Smaller pages / fewer column reads. Omit for all columns.",
"title": "Columns"
},
"detail": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "Grain (e.g. schools/districts/states); default finest.",
"title": "Detail"
},
"filters": {
"anyOf": [
{
"additionalProperties": true,
"type": "object"
},
{
"type": "null"
}
],
"default": null,
"description": "Column → value (or list of values) filters. Keys are FK columns (district_code, school_code, county_fips, demographic) and categorical columns; read describe_dataset's `filters` for the exact keys and enum values FIRST. A value list is a union (OR); multiple keys AND together. A wrong key/value returns a self-describing error listing the valid ones.",
"title": "Filters"
},
"include_labels": {
"default": true,
"description": "Join district/school/county/demographic name columns (default true); false = codes only (faster, leaner).",
"title": "Include Labels",
"type": "boolean"
},
"limit": {
"anyOf": [
{
"minimum": 1,
"type": "integer"
},
{
"type": "null"
}
],
"default": null,
"description": "Page size (default 100, max 500).",
"title": "Limit"
},
"main_topic": {
"default": "education",
"description": "Main topic: 'education', 'census', or 'immigration'.",
"title": "Main Topic",
"type": "string"
},
"offset": {
"default": 0,
"description": "Row offset for paging (>= 0).",
"minimum": 0,
"title": "Offset",
"type": "integer"
},
"order": {
"default": "asc",
"description": "Sort direction for order_by: 'asc' or 'desc'.",
"title": "Order",
"type": "string"
},
"order_by": {
"anyOf": [
{
"type": "string"
},
{
"type": "null"
}
],
"default": null,
"description": "Order by one fact column or joined label column (for server-side top-N). Default order is the row grain. NULL (suppressed) cells sort LAST in either direction, so a metric top-N is never polluted by suppressed rows.",
"title": "Order By"
},
"topic": {
"description": "Topic name, e.g. 'act_scores'.",
"title": "Topic",
"type": "string"
},
"year": {
"anyOf": [
{
"type": "integer"
},
{
"type": "null"
}
],
"default": null,
"description": "Exact year. Use this OR year_min/max.",
"title": "Year"
},
"year_max": {
"anyOf": [
{
"type": "integer"
},
{
"type": "null"
}
],
"default": null,
"description": "Inclusive upper year bound (range).",
"title": "Year Max"
},
"year_min": {
"anyOf": [
{
"type": "integer"
},
{
"type": "null"
}
],
"default": null,
"description": "Inclusive lower year bound (range).",
"title": "Year Min"
}
},
"required": [
"topic"
],
"title": "query_datasetArguments",
"type": "object"
},
"name": "query_dataset",
"outputSchema": {
"additionalProperties": true,
"title": "query_datasetDictOutput",
"type": "object"
}
},
{
"description": "Resolve a place or demographic NAME or CODE to its stable keys + labels — the right way to turn 'Atlanta Public Schools' / 'Fulton' / a raw code into the district_code / school_code / county_fips / demographic to filter by (a wrong code guess otherwise returns an empty query_dataset page). `kind` is district / school / county / demographic ('Fulton' as kind='county' → the county; as kind='district' → the school district — they are different things). Fuzzy-matches and ranks candidates, flags `ambiguous` when several tie, and reads only the small dimension table (no fact scan). A real Georgia place is never matched to a different one: a consolidated city returns its county (matched_on=consolidated_city: 'Columbus' -> Muscogee), another city asked as a county returns the county it lies in flagged ambiguous, and a one-letter misspelling returns matched_on=typo. Read `place_note` and tell the user when it is present.",
"inputSchema": {
"properties": {
"kind": {
"title": "Kind",
"type": "string"
},
"limit": {
"default": 10,
"title": "Limit",
"type": "integer"
},
"query": {
"title": "Query",
"type": "string"
}
},
"required": [
"kind",
"query"
],
"title": "resolve_entityArguments",
"type": "object"
},
"name": "resolve_entity",
"outputSchema": {
"additionalProperties": true,
"title": "resolve_entityDictOutput",
"type": "object"
}
},
{
"description": "Keyword search over the catalog metadata (topic names, descriptions, tags, AND column names/descriptions) — the discovery entry point when you don't know the exact topic name. Returns lean topic summaries per hit with a relevance score and which fields matched, plus a `dimension_matches` list when the query also hits a dimension (e.g. 'district'). Most acronyms work; the short ones `ap`/`el`/`ib` are recognized. Follow up with describe_dataset. `limit` caps results (default 20, max 100).",
"inputSchema": {
"properties": {
"limit": {
"default": 20,
"title": "Limit",
"type": "integer"
},
"query": {
"title": "Query",
"type": "string"
}
},
"required": [
"query"
],
"title": "search_datasetsArguments",
"type": "object"
},
"name": "search_datasets",
"outputSchema": {
"additionalProperties": true,
"title": "search_datasetsDictOutput",
"type": "object"
}
}
]
}Verify it yourself
curl -s https://api.teppi.xyz/v1/evidence/sha256:54ecc6ba296966a2677d21365a6a7ae075a6ee99b355a0ca7313c87b668509c0 | sha256sum