Tool reference
Arguments, filter operators, response shapes, and worked examples for all eight parcel MCP tools.
Every tool returns a single text content block containing JSON. The examples below show the arguments object you (or the model) pass to tools/call, and the parsed JSON that comes back.
Two conventions apply everywhere:
parcel_schemafirst. It is cheap, and it is how a model learns thatstatefpis a two-digit FIPS string and thatusedesc— notusecode— holds the human-readable land use. Filters written without it tend to invent column names.- Geometry is never returned in rows. The
geomcolumn is excluded from every response, even if you ask for it, because raw WKT wrecks a model's context window. Usecentroidx/centroidyfor a point, or the spatial tools (parcel_near_point,parcel_within_bbox,parcel_within_polygon) to search by shape. If you need polygon geometry, use the WFS endpoint directly.
Shared arguments
| Argument | Applies to | Default | Notes |
|---|---|---|---|
limit | every list-returning tool | 10 | Clamped to 1,000 no matter what is requested |
offset | every list-returning tool | 0 | For pagination; must be a non-negative integer |
columns | every row-returning tool | all columns except geom | Validated against the published schema; lrid is always included so results can be correlated |
Passing an unknown column name, a non-integer limit, or a negative offset returns a clean error rather than a malformed query — see error shapes.
Filter operators
parcel_query and parcel_count take a where array of {column, op, value} conditions. All conditions are AND-ed together — there is no OR, and no nesting. Use in for set membership, or run two calls and combine the results.
op | Meaning | value type |
|---|---|---|
eq | equals | string, number, or boolean |
ne | not equals | string, number, or boolean |
gt ge | greater than / greater or equal | number (or string) |
lt le | less than / less or equal | number (or string) |
like | case-sensitive pattern (% wildcard) | string |
ilike | case-insensitive pattern (% wildcard) | string |
in | member of a list | non-empty array |
is_null | null test | true for IS NULL, false for IS NOT NULL |
Values are type-checked and escaped before they reach the query layer, and column names are checked against the allow-list, so an invented column or a quote inside a string fails cleanly instead of producing a broken filter.
parcel_schema
Lists every queryable column with its type and description. No arguments.
{}Response:
{
"count": 50,
"columns": [
{ "name": "lrid", "type": "uuid", "description": "Unique parcel identifier (UUID). Use this for parcel-by-id lookups." },
{ "name": "parcelid", "type": "string", "description": "Parcel ID assigned by the county assessor." },
"…"
]
}The full column set, as advertised to the model:
| Column | Type | Description |
|---|---|---|
lrid | uuid | Unique parcel identifier — use with parcel_by_id |
parcelid | string | Parcel ID assigned by the county assessor |
parcelid2 | string | Alternate parcel ID |
ogparcelid | string | Original parcel ID from the source record |
geoid | string | Census GEOID (state + county + tract + block) |
statefp | string | Two-digit state FIPS code (e.g. 06 for California) |
countyfp | string | Three-digit county FIPS code |
countyname | string | County name (e.g. Alameda) |
taxacctnum | string | Tax account number |
taxyear | integer | Tax year the assessment applies to |
usecode | string | Land use code |
usedesc | string | Land use description (e.g. Single Family Residential) |
zoningcode | string | Zoning code |
zoningdesc | string | Zoning description |
numbldgs | integer | Number of buildings on the parcel |
numunits | integer | Number of units (multi-family) |
yearbuilt | integer | Year the primary structure was built |
numfloors | float | Number of floors in the primary structure |
bldgsqft | float | Total building square footage |
bedrooms | float | Number of bedrooms |
halfbaths | integer | Number of half bathrooms |
fullbaths | integer | Number of full bathrooms |
imprvalue | number | Improvement value (USD) |
landvalue | number | Land value (USD) |
agvalue | number | Agricultural use value (USD) |
totalvalue | number | Total assessed value (USD) |
taxacres | float | Tax acres |
saleamt | number | Most recent sale amount (USD) |
ownername | string | Owner name |
owneraddr | string | Owner mailing address |
ownercity | string | Owner mailing city |
ownerstate | string | Owner mailing state |
ownerzip | string | Owner mailing ZIP |
parceladdr | string | Parcel site address (street) |
parcelcity | string | Parcel site city |
parcelstate | string | Parcel site state (two-letter) |
parcelzip | string | Parcel site ZIP |
legaldesc | string | Legal description (PLSS reference) |
township | string | PLSS township |
section | string | PLSS section |
qtrsection | string | PLSS quarter section |
range | string | PLSS range |
plssdesc | string | PLSS full description |
book | string | Record book reference |
page | string | Record page reference |
updated | datetime | Last update timestamp |
centroidx | float | Parcel centroid longitude (WGS84) |
centroidy | float | Parcel centroid latitude (WGS84) |
surfpointx | float | Parcel surface point longitude (WGS84) |
surfpointy | float | Parcel surface point latitude (WGS84) |
geom | geometry | Parcel polygon — advertised but never returned; use the spatial tools or WFS |
Attribute coverage varies by county — see Coverage Statistics for what is populated where, and Data Content for the full field semantics.
parcel_query
Run a structured filter and return matching rows.
{
"where": [
{ "column": "countyname", "op": "eq", "value": "Alameda" },
{ "column": "usedesc", "op": "ilike", "value": "%single family%" },
{ "column": "yearbuilt", "op": "gt", "value": 1950 },
{ "column": "totalvalue", "op": "ge", "value": 750000 }
],
"columns": ["parceladdr", "parcelcity", "ownername", "totalvalue", "yearbuilt"],
"limit": 25,
"offset": 0
}Response:
{
"type": "FeatureCollection",
"numberMatched": 4137,
"numberReturned": 25,
"returned": 25,
"features": [
{
"lrid": "6f1c2a94-4b3e-4f2a-9c11-0f7b2d5e8a31",
"parceladdr": "1234 Oak St",
"parcelcity": "Oakland",
"ownername": "DOE JOHN A & JANE B",
"totalvalue": 982000,
"yearbuilt": 1962
},
"…"
]
}features holds flat attribute objects, not GeoJSON features — there is no nested properties or geometry key. numberMatched is the total matching the filter (useful for paging); numberReturned and returned are what came back in this call.
Omit where entirely and you get an unfiltered page of the layer — occasionally useful for a sanity check, rarely what you want.
parcel_count
The same where filter, returning only a number. Prefer this whenever the question is "how many" — it keeps thousands of rows out of the model's context.
{
"where": [
{ "column": "statefp", "op": "eq", "value": "51" },
{ "column": "usedesc", "op": "ilike", "value": "%agricultur%" },
{ "column": "taxacres", "op": "ge", "value": 100 }
]
}{ "count": 28914 }parcel_by_id
One parcel by its lrid UUID. Non-UUID input is rejected before any query runs.
{ "lrid": "6f1c2a94-4b3e-4f2a-9c11-0f7b2d5e8a31" }Found:
{
"found": true,
"parcel": {
"lrid": "6f1c2a94-4b3e-4f2a-9c11-0f7b2d5e8a31",
"parcelid": "1438267944",
"parceladdr": "1234 Oak St",
"ownername": "DOE JOHN A & JANE B",
"totalvalue": 982000,
"…": "…"
}
}Not found:
{ "found": false, "lrid": "6f1c2a94-4b3e-4f2a-9c11-0f7b2d5e8a31" }parcel_by_address
Geocodes a free-form address, then returns the parcels within a radius of the resolved point. The geocode result comes back alongside the rows so the caller can confirm the match — addresses are frequently ambiguous, and this is what lets an assistant say "I matched 123 Main St in Oakland, CA" before reporting an owner.
| Argument | Default | Notes |
|---|---|---|
address | required | Free-form; include city and state for best results |
distance_meters | 100 | Search radius around the geocoded point. Increase for rural lots and large parcels |
size | 1 | How many geocoder candidates to fetch (capped at 5). The top hit is always the one used |
limit | 10 | Max parcels returned |
columns | all but geom | Subset of columns |
{
"address": "123 Main St, Oakland, CA",
"distance_meters": 150,
"limit": 5,
"columns": ["parceladdr", "ownername", "totalvalue", "taxacres"]
}{
"geocode": {
"longitude": -122.2711,
"latitude": 37.8044,
"label": "123 Main St, Oakland, CA, USA",
"confidence": 0.96,
"layer": "address",
"source": "openaddresses"
},
"distance_meters": 150,
"number_matched": 3,
"parcels": [
{
"lrid": "0a9d7f13-58c2-4e77-b0a6-2c1e94f3d5b8",
"parceladdr": "123 Main St",
"ownername": "MAIN STREET HOLDINGS LLC",
"totalvalue": 1450000,
"taxacres": 0.18
},
"…"
]
}If the geocoder finds nothing, the call returns a clean error naming the address rather than an empty result — retry with more of the address (city, state, ZIP).
parcel_near_point
Parcels within a distance of a WGS84 point, backed by a PostGIS DWITHIN test.
| Argument | Required | Notes |
|---|---|---|
longitude | yes | WGS84 longitude (x) |
latitude | yes | WGS84 latitude (y) |
distance_meters | yes | Must be positive; meters, not feet or degrees |
limit / offset / columns | no | See shared arguments |
{
"longitude": -76.27836,
"latitude": 36.87886,
"distance_meters": 250,
"limit": 20
}The response is the same FeatureCollection-style envelope as parcel_query. Note the argument order: longitude first, latitude second — the same convention as GeoJSON, and the opposite of how people usually say coordinates aloud.
parcel_within_bbox
Parcels whose geometry intersects a WGS84 bounding box. This is the viewport-shaped search.
{
"minx": -122.28,
"miny": 37.80,
"maxx": -122.26,
"maxy": 37.82,
"limit": 100
}minx/maxx are longitudes, miny/maxy latitudes; each min must be less than or equal to its max or the call is rejected. Same response envelope as parcel_query.
parcel_within_polygon
Parcels intersecting a GeoJSON Polygon or MultiPolygon — city limits, a service area, a hand-drawn shape.
{
"geojson": {
"type": "Polygon",
"coordinates": [[
[-122.28, 37.80],
[-122.26, 37.80],
[-122.26, 37.82],
[-122.28, 37.82],
[-122.28, 37.80]
]]
},
"limit": 500
}Rules:
- Only
PolygonandMultiPolygonare accepted. AFeatureorFeatureCollectionwrapper is not — pass the bare geometry object (itsgeometryvalue). - Coordinates are
[longitude, latitude]in WGS84. - Each ring needs at least four positions; an unclosed ring is closed automatically.
Same response envelope as parcel_query. For very large or complex shapes, prefer a coarse parcel_within_bbox followed by a filter, or the WFS endpoint — the 1,000-row cap applies here too.
Connecting a client
Wire the Parcel MCP Server into Claude Code, Claude Desktop, or any MCP-aware client — Bearer token, OAuth 2.0 with PKCE, and raw JSON-RPC over Streamable HTTP.
Examples
Conversational recipes plus working agent code — the Claude API MCP connector, the Python and TypeScript MCP SDKs, and prompt guidance that keeps an agent's tool use cheap.