<!-- https://staircase.co/agents/tools -->
# Tools

# Tools

The tool registry a client connects to: one definition set served over several transports, with a guarded query surface.

One registry is the single source both the standard-input entry and the HTTP entry read, so the two transports cannot drift apart. Every tool carries a title, a description, and a per-parameter description written for a model rather than for a reader who already knows the domain.

- One tool registry is the single source both transports read, so the stdio entry and the HTTP entry cannot drift apart.
- Every tool carries a title, a description, and a per-parameter description written for a model rather than for a reader who already knows the domain.
- Query tools are guarded in four layers: comment and literal stripping, multi-statement rejection, a read-only statement allowlist, and an unconditional row cap.

## How it works

The query tools accept caller-written SQL, which is only safe because the guard is explicit. Four layers, in order: comments and string literals are stripped before any keyword matching, so a value like `'copy'` inside a string cannot trip the filter; multiple statements are rejected; the statement must begin `SELECT` or `WITH`; and the caller's query is wrapped in an outer select with a row cap the caller cannot override.

Stripping literals before matching is what a plain keyword scan does not do. A scan over raw text rejects `owners_text ILIKE '%copy%'`, which is a legitimate query, and a guard that rejects legitimate queries is one callers route around.

Tools that author their own SQL use a separate path with positional binding and skip the caller validator, because they are safe by construction rather than by inspection.

## The registry

### `listClassesByDataGroup`

<!--source:tool-registry-->
List classes by data group
<!--/source-->

<!--source:tool-registry-->
List classes for a property data group with names and descriptions
<!--/source-->

- `groupName`stringrequired
<!--source:tool-registry-->
The data group name, case-insensitive
<!--/source-->

### `listPropertiesByClassName`

<!--source:tool-registry-->
List properties by class name
<!--/source-->

<!--source:tool-registry-->
Lists JSON Schema property names for a property class (excludes source_http_request)
<!--/source-->

- `className`stringrequired
<!--source:tool-registry-->
The class name, case-insensitive
<!--/source-->

### `getPropertySchema`

<!--source:tool-registry-->
Get property schema by class and property
<!--/source-->

<!--source:tool-registry-->
Returns the full JSON Schema object for a class property
<!--/source-->

- `className`stringrequired
<!--source:tool-registry-->
Class name, case-insensitive
<!--/source-->

- `propertyName`stringrequired
<!--source:tool-registry-->
Property name, case-insensitive
<!--/source-->

### `getVerifiedScriptExamples`

<!--source:tool-registry-->
Get verified script examples
<!--/source-->

<!--source:tool-registry-->
Get most relevant working examples of the code, that maps data to the canonical property schema
<!--/source-->

- `query`stringrequired
<!--source:tool-registry-->
Description of the example meaning. Wll be used to search for similar examples.
<!--/source-->

- `topK`number
<!--source:tool-registry-->
Number of results (default 5)
<!--/source-->

### `listOracleProperties`

<!--source:tool-registry-->
List Oracle open-data properties
<!--/source-->

<!--source:tool-registry-->
Paginated discovery of properties for a county. Returns slim entries (propertyId, parcelIdentifier, cid, county, fileSizeBytes) plus summary fields (address, marketValue, ownerName) when served from the query table. Use getOracleProperty to fetch full consolidated data for a specific entry.
<!--/source-->

- `county`string
<!--source:tool-registry-->
Filter by county name (case-insensitive)
<!--/source-->

- `limit`number
<!--source:tool-registry-->
Number of results to return (default 50, max 500)
<!--/source-->

- `offset`number
<!--source:tool-registry-->
Zero-based offset for pagination (default 0)
<!--/source-->

### `getOracleProperty`

<!--source:tool-registry-->
Get Oracle open-data property
<!--/source-->

<!--source:tool-registry-->
Fetch the full consolidated property JSON (appraisal, permits, Sunbiz, BBB) from content-addressed storage. Provide exactly one of parcelIdentifier, propertyId, or cid.
<!--/source-->

- `parcelIdentifier`string
<!--source:tool-registry-->
The property parcel identifier (digits) — looked up in the manifest to resolve its content-addressed storage content address
<!--/source-->

- `propertyId`string
<!--source:tool-registry-->
The property UUID — looked up in the manifest to resolve its content-addressed storage content address
<!--/source-->

- `cid`string
<!--source:tool-registry-->
content-addressed storage content address for the consolidated property JSON
<!--/source-->

- `county`string
<!--source:tool-registry-->
County to look up the parcel/property in (case-insensitive). Selects which county's open data to read when the deployment serves multiple counties.
<!--/source-->

### `getOracleDatasetInfo`

<!--source:tool-registry-->
Get Oracle open-data dataset info
<!--/source-->

<!--source:tool-registry-->
Returns dataset-level metadata for a county: county, propertyCount (live row count when served from the query table), state, and provenance/content address fields on the legacy path. When per-source coverage is configured, also returns datasets[] with, per source (appraisal, permits, sunbiz, bbb), ingestedCount, expectedCount, completionPercent, and first/last loaded timestamps — so callers can qualify partial answers by coverage. For a coverage-only county (no property dataset served) propertyCount is null and propertyDatasetAvailable is false, so callers can distinguish a missing property table from a county with zero properties.
<!--/source-->

- `county`string
<!--source:tool-registry-->
County to report dataset info for (case-insensitive). Selects which county's open data to read when the deployment serves multiple counties.
<!--/source-->

### `getPropertyPermits`

<!--source:tool-registry-->
Get property permits (on-demand)
<!--/source-->

<!--source:tool-registry-->
Fetch permit records for a property by parcel ID. Returns cached permits immediately if available. If not cached, enqueues a harvest job (reuses the permit-harvest Lambda) and returns a status indicating the harvest is in progress — poll again after ~90 seconds. Permits are cached to content-addressed storage after harvest completes.
<!--/source-->

- `parcelId`stringrequired
<!--source:tool-registry-->
The property parcel identifier (digits, e.g. '1234567890000')
<!--/source-->

- `countyFips`string
<!--source:tool-registry-->
County FIPS code (default: 12071 = Lee County FL)
<!--/source-->

### `findPropertiesInArea`

<!--source:tool-registry-->
Find properties in an area
<!--/source-->

<!--source:tool-registry-->
Returns the set of properties whose centroid (latitude/longitude) falls inside a user-supplied bounding box or polygon. Provide exactly one of bbox or polygon. Reads the per-county property query table (falls back to the derived geo index); no NOAA/FEMA geometry is used.
<!--/source-->

### `sumPropertyValueInArea`

<!--source:tool-registry-->
Sum property value in an area
<!--/source-->

<!--source:tool-registry-->
Returns the exact sum of avm_value over the properties whose centroid falls inside a user-supplied bounding box or polygon, plus the in-area count. Null valuations are treated as 0. Provide exactly one of bbox or polygon. Reads the per-county property query table (falls back to the derived geo index).
<!--/source-->

### `queryProperties`

<!--source:tool-registry-->
Query properties (SQL)
<!--/source-->

<!--source:tool-registry-->
Run a read-only SQL SELECT against a county's flat property query table (view name 'properties', one row per property) backed by embedded DuckDB. Use getPropertyQuerySchema first to see available columns. SAFETY: a single SELECT statement only (a leading WITH/CTE is allowed); multiple statements and any mutating or file/extension keyword (INSERT/UPDATE/DELETE/COPY/ATTACH/INSTALL/LOAD/PRAGMA/CALL/SET …) are rejected; results are always capped at 1000 rows.
<!--/source-->

- `county`stringrequired
<!--source:tool-registry-->
County to query (case-insensitive), e.g. 'Lee'.
<!--/source-->

- `sql`stringrequired
<!--source:tool-registry-->
A single read-only SELECT statement over the 'properties' view.
<!--/source-->

- `limit`number
<!--source:tool-registry-->
Max rows to return (default 100, max 1000). Always enforced.
<!--/source-->

### `getPropertyQuerySchema`

<!--source:tool-registry-->
Get property query schema
<!--/source-->

<!--source:tool-registry-->
Returns the column list, DuckDB types, and a one-line description of each column of the 'properties' query table for a county, so queryProperties can be written without guessing. Notes that some coverage-dependent fields may be NULL.
<!--/source-->

- `county`stringrequired
<!--source:tool-registry-->
County to describe (case-insensitive), e.g. 'Lee'.
<!--/source-->

### `queryPermits`

<!--source:tool-registry-->
Query permits (SQL)
<!--/source-->

<!--source:tool-registry-->
Run a read-only SQL SELECT against a county's flat permit query table (view name 'permits', one row per building permit) backed by embedded DuckDB. Use getPermitQuerySchema first to see available columns and getPermitCoverage to qualify aggregate answers by source. SAFETY: a single SELECT statement only (a leading WITH/CTE is allowed); multiple statements and any mutating or file/extension keyword (INSERT/UPDATE/DELETE/COPY/ATTACH/INSTALL/LOAD/PRAGMA/CALL/SET …) are rejected; results are always capped at 1000 rows.
<!--/source-->

- `county`stringrequired
<!--source:tool-registry-->
County to query (case-insensitive), e.g. 'Lee'.
<!--/source-->

- `sql`stringrequired
<!--source:tool-registry-->
A single read-only SELECT statement over the 'permits' view.
<!--/source-->

- `limit`number
<!--source:tool-registry-->
Max rows to return (default 100, max 1000). Always enforced.
<!--/source-->

### `getPermitQuerySchema`

<!--source:tool-registry-->
Get permit query schema
<!--/source-->

<!--source:tool-registry-->
Returns the column list, DuckDB types, and a one-line description of each column of the 'permits' query table for a county, so queryPermits can be written without guessing. Notes that date/value fields are frequently NULL depending on the permit source.
<!--/source-->

- `county`stringrequired
<!--source:tool-registry-->
County to describe (case-insensitive), e.g. 'Lee'.
<!--/source-->

### `getPermitCoverage`

<!--source:tool-registry-->
Get permit coverage by source
<!--/source-->

<!--source:tool-registry-->
Returns per-source-system permit coverage for a county from the 'permits' query table: each source_system with its permit_count and completion_date range (earliest/latest), plus the overall total. The donphan agent uses this to QUALIFY aggregate permit answers (permit data lags appraisals and some sources may have NULL dates).
<!--/source-->

- `county`stringrequired
<!--source:tool-registry-->
County to report permit coverage for (case-insensitive), e.g. 'Lee'.
<!--/source-->
