docsMCP ServerAvailable Tools

Available Tools

The Ontyx MCP server exposes a set of geospatial reference tools. All tools require an API key with the read:data scope — the key you generated in the dashboard carries this scope by default.

Each tool call currently costs 1 credit against your plan’s monthly allotment, metered only when the call succeeds (per-call credit costs). The response includes a quota envelope so your agent can surface remaining headroom.

Coordinate order. Every coordinate in these tools is [longitude, latitude] (WGS84 / GeoJSON order) — longitude first. This applies to inputs and to the position field of every result.

Demographic lookups

Backed by the latest US Census American Community Survey (ACS) 5-year estimates. The zip tools resolve a 5-digit zip to its ZIP Code Tabulation Area (ZCTA) — the Census geography that approximates a zip — so results carry the ACS geoid, vintage (acs_year, survey), and a margin of error (moe) rather than a city/state label.

ToolInputReturns
get_population{ zip: string } (5-digit US zip){ ok, zip, population, moe, acs_year, survey, geoid }
get_average_income{ zip: string } (5-digit US zip){ ok, zip, median_household_income_usd, moe_usd, currency: "USD", acs_year, survey, geoid }
get_underwriting_bundle{ tract_geoid: string } (11-digit Census tract) or { lat: number, lng: number }{ ok, tract_geoid, acs_year, survey, …metrics } (see below)

get_average_income returns the ACS median household income (table B19013) in the field median_household_income_usd — the tool name is kept for backward compatibility, but the value is a median, not a mean. Top-coded medians (e.g. 250001) mean “that amount or more”.

get_underwriting_bundle returns a commercial-real-estate underwriting snapshot at Census tract grain — pass a tract geoid directly, or a lat/lng coordinate which is resolved to its tract automatically (point-in-polygon). It carries ~22 metrics: total_population, total_households, median_household_income_usd, median_gross_rent_usd, median_home_value_usd, median_owner_costs_usd, rent_to_income_ratio, owner_cost_to_income_ratio, total_housing_units, vacancy_rate, five share_built_* era buckets, bachelors_share, total_employed, industry_hhi, and four transport_*_share mode splits. Individual metrics may be null where the ACS does not publish a value.

Example

> What's the population of zip code 90210?

The agent picks get_population, sends { "zip": "90210" }, and Ontyx responds:

{
  "ok": true,
  "zip": "90210",
  "population": 19004,
  "moe": 1101,
  "acs_year": 2024,
  "survey": "5yr",
  "geoid": "860Z200US90210",
  "quota": { "used": 12, "limit": 1000, "percent": 1, "state": "ok", "period": "2026-06" }
}

For zips the ACS has no data for, ok is false and reason is "unknown_zip". Invalid inputs (non-5-digit zips) are rejected at the MCP protocol layer before the tool runs.

Market outlook

Two county-grain tools covering what current demographics cannot: what supply is coming, and what a fair rent is.

ToolInputReturns
get_supply_pipelineone of { county_geoid } (5-digit), { tract_geoid } (11-digit), or { lat, lng }{ ok, geography_grain, resolved_from, …, observed, outlook, benchmarks, data_quality, assumptions }
get_rent_benchmarksame three shapes{ ok, geography_grain, resolved_from, …, rents, trend, vs_market, affordability, benchmarks }

Both accept a tract geoid or a coordinate for convenience, but the answer is always county grain — building permits publish a complete monthly picture only at county, and fair-market rents have no sub-county form in most of the country. Every response says so via geography_grain and resolved_from, so you can always tell when a narrower question was answered at county level.

get_supply_pipeline

Turns building-permit authorizations into a forward view. The response keeps observation and forecast in separate blocks, and it is worth preserving that split when you report the numbers:

  • observed — arithmetic on authorized permit counts: trailing-12-month units, multifamily share, year-over-year change, standing pipeline, and permit intensity per 1,000 existing homes.
  • outlookmodelled: expected deliveries over the next 12 months, expected stock growth, and supply_pressure / supply_momentum labels.
  • assumptions — the authorization-to-completion lags and completion rates used to produce outlook, on every response, so the estimate is reconstructable. These are calibration defaults rather than measurements.
  • benchmarks — national and in-state percentile ranks.
  • data_quality — whether the trailing windows are complete, and how much of the permit count was reported rather than imputed by the survey.

supply_pressure is distributional, not absolute: elevated means top-quintile national expected stock growth, limited means bottom quintile.

get_rent_benchmark

A standardized fair-market rent by unit size, plus the three comparisons that give it meaning: trend (growth in nominal and inflation-adjusted terms), vs_market (against median in-place rent), and affordability (against local incomes and area income limits).

Three things to carry into any answer built on it:

  • Rents are gross — rent plus tenant-paid utilities. Comparable to gross-rent survey measures, not to contract rent, and not the same as a current market asking rent.
  • fmr_percentile (40 or 50) is definitional. An area measured at the 50th percentile is not more expensive than one measured at the 40th; it is measured at a different point of its own distribution. Never rank across the two without saying which is which.
  • fmr_2br_to_acs_median_rent_ratio is a screening ratio that overstates the true mark-to-market gap, because the survey median it divides by is a five-year average across every unit size. It is not an appraisal input.

The affordability block comes back empty where area income-limit data is unavailable.

Location Services

Geocoding, reverse geocoding, nearby place search, and satellite static maps, served from AWS Location. Positional results (geocode, reverse_geocode, search_nearby) also carry an h3 field — the H3 cell id for that point, handy as a spatial join/index key. Control the cell size with the optional h3Resolution (0–15, default 9).

ToolInputReturns
geocode{ query: string, maxResults?: number, biasPosition?: [lon, lat], countryFilter?: string[] (ISO-3166-1 alpha-3), h3Resolution?: number }{ results: [{ label, position, relevance?, country?, region?, postalCode?, h3 }] }
reverse_geocode{ position: [lon, lat], maxResults?: number, h3Resolution?: number }{ results: [{ label, position, distanceMeters?, country?, region?, postalCode?, h3 }] }
search_nearby{ position: [lon, lat], radiusMeters?: number (≤50000), maxResults?: number, categories?: string[] (AWS Location category IDs), h3Resolution?: number }{ results: [{ placeId, label, position, distanceMeters?, categories?, address?, h3 }] }
static_map{ framing, aspectRatio?, orientation?, quality?, geojsonOverlay?, pois? } (see below){ url, expiresAt, width, height, bucketKey }

search_nearby has no free-text query — filter with categories (AWS Location place category IDs), not keywords.

static_map framing

framing is required and is exactly one of:

  • { "type": "center", "center": [lon, lat], "zoom": number }zoom 0–20.
  • { "type": "boundingBox", "boundingBox": [west, south, east, north] } — auto-fits the box.
  • { "type": "boundedPositions", "positions": [[lon, lat], …] } — two or more points to auto-fit.

Optional knobs: aspectRatio (16:9 default, 3:2, 4:3, 5:4, 1:1), orientation (landscape default, portrait), quality (standard default; high composites tiles to 1080p-class, e.g. 16:9 → 1920×1080), geojsonOverlay (a GeoJSON FeatureCollection, serialized ≤ 4200 chars), and pois (show built-in POI icons).

The response is a short-lived signed S3 URL to the rendered image — not image bytes. expiresAt is the URL’s ISO-8601 expiry.

Example — geocode + H3

> Where is the Golden Gate Bridge?

The agent calls geocode with { "query": "Golden Gate Bridge, San Francisco", "maxResults": 1 }:

{
  "results": [
    {
      "label": "Golden Gate Brg, San Francisco, CA 94129, United States",
      "position": [-122.47846, 37.8183],
      "country": "USA",
      "region": "CA",
      "postalCode": "94129",
      "relevance": 1,
      "h3": "89283087037ffff"
    }
  ],
  "quota": { "used": 14, "limit": 1000, "percent": 1, "state": "ok", "period": "2026-06" }
}

Example — static_map

static_map with { "framing": { "type": "center", "center": [-122.4783, 37.8199], "zoom": 14 } }:

{
  "url": "https://…s3.us-west-2.amazonaws.com/static-maps/3482a7c9….jpg?X-Amz-Algorithm=…",
  "expiresAt": "2026-06-14T00:21:29Z",
  "width": 1400,
  "height": 788,
  "bucketKey": "static-maps/3482a7c9….jpg"
}

Scope requirements

Every API key you create is granted the read:data scope by default. This scope is required for all data tools. A future release may introduce write scopes for annotation workflows; existing keys are unaffected.

If a tool call fails with an authentication error, verify that:

  1. ONTYX_API_KEY is set correctly in your MCP server config.
  2. The key has not been revoked — check the Dashboard API Keys section.

There is no account-status gate beyond the key itself — every account has at least the free tier’s monthly credits, permanently.

Error handling

Tool errors come back as structured JSON inside the MCP tool result. Gateway errors (auth, quota, rate limiting) use these codes:

  • UNAUTHENTICATED — the bearer token was missing, malformed, or revoked
  • RATE_LIMITED — too many requests in too short a window; the envelope carries a retryAfterMs hint
  • QUOTA_EXCEEDED — the free tier’s monthly credits are used up (they reset at the start of the next month). On Standard the default is soft overage — the call succeeds and the warning rides along in the quota envelope — so this code appears only when a hard-stop policy is explicitly enabled
  • INVALID_REQUEST — the input failed schema validation before the tool ran

Location Services and demographic-data errors surface the upstream service’s envelope verbatim as { "error": { "code", "message" } }, with stable codes:

  • invalid_request — parameters the upstream service rejected
  • unknown_route — the requested operation is not recognized
  • not_found — no data for the requested geography (the zip tools translate this to a { "reason": "unknown_zip" } result instead)
  • upstream_error — an upstream failure (e.g. an out-of-range coordinate, or a data query error)
  • upstream_timeout — a data query exceeded its time budget

Note that failed Location Services and upstream data-query calls are not metered against your quota.

See API Reference for the full control-plane envelope schema.