Skip to content

Data catalog

Markdown

Count and sum a cloud GeoParquet per zone

A count, sum or average over a large public GeoParquet for a place or per zone (state, county, region, district), without downloading it.

RESTPOST https://mcp.terra-lab.ai/v1/tools/aggregate_dataset
MCP toolaggregate_dataset on https://mcp.terra-lab.ai/mcp
GroupData catalog

Request

curl -X POST https://mcp.terra-lab.ai/v1/tools/aggregate_dataset \
  -H "Authorization: Bearer $TERRALAB_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"dataset":"portolan:harmonized-field-data/glocab","zones":{"per":"state","units":["Arkansas"]}}'
import os
import requests

response = requests.post(
    "https://mcp.terra-lab.ai/v1/tools/aggregate_dataset",
    headers={"Authorization": f"Bearer {os.environ['TERRALAB_API_KEY']}"},
    json={
        "dataset": "portolan:harmonized-field-data/glocab",
        "zones": {
            "per": "state",
            "units": ["Arkansas"],
        },
    },
    timeout=120,
)
response.raise_for_status()
print(response.json()["structuredContent"])
const response = await fetch('https://mcp.terra-lab.ai/v1/tools/aggregate_dataset', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.TERRALAB_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    "dataset": "portolan:harmonized-field-data/glocab",
    "zones": {
      "per": "state",
      "units": [
        "Arkansas"
      ]
    }
  }),
});
const data = await response.json();
console.log(data.structuredContent);

Other valid request bodies:

{"dataset":"portolan:nasa-firms-active-fire/detections","zones":{"per":"county","within":"California"},"where":[{"column":"year","op":"eq","value":2024}]}

Parameters

A JSON object in the request body; in an MCP client, the arguments of the tool call.

NameTypeRequiredDescription
datasetstringYesA portolan: id from find_datasets (open_registry), or the https URL of a public GeoParquet file, a hive glob such as https://.../year=*/data.parquet, an s3:// or gs:// address, or a STAC collection.json. Up to 400 characters.
areaobjectNoThe area: a bbox, or a place (name, address or coordinates) with an optional radius in metres. The place the user asks about, not the user's own location.
area.bboxarray of numberNo[west, south, east, north] in decimal degrees (WGS 84). Exactly 4 items.
area.placestring or objectNoA place name or address, or a point.
area.radius_mnumberNoRadius around the place in metres. From 1 to 100000.
zonesobjectNoOne row per zone instead of one area.
zones.perstringYesThe zone level. One of "state", "county", "zip", "place", "country", "region", "locality", "neighborhood", "adm1", "adm2", "adm3", "adm4".
zones.withinstringNoThe place whose zones are read: "California", "Kenya", or a zone id (US-STATE-06). Up to 200 characters.
zones.unitsarray of stringNoOnly these zones, by name or code, at the level per gives. 0 to 200 items.
wherearray of objectNoConditions that must all hold. A condition on a partition key (year) picks the files before any byte is read; without one, only the newest partition is read. 0 to 12 items.
group_byarray of stringNoColumns to group by, one row per value (per zone). 0 to 2 items.
aggregatesarray of objectNoValues beside the count: sum, mean, min or max of a column; area_m2 of the geometries. 0 to 8 items.
limitintegerNoRows returned, largest count first; distinct_groups says how many there are. From 1 to 100. Default 50.
dry_runbooleanNoOnly the plan: files, row groups and bytes the query would read. Default false.

Response

structuredContent holds the result, content the same result as text for a model, and isError is true when the tool refused the request or failed; then structuredContent.code, message and hint say what to change.

{
  "structuredContent": {
    "sources": [{ "name": "...", "licence": "...", "url": "..." }],
    "downloads": [{ "label": "...", "format": "...", "url": "...", "name": "..." }]
  },
  "content": [{ "type": "text", "text": "..." }],
  "isError": false
}

Every result lists its sources with their licence. File results come as downloads: each link builds its file when opened.

Errors

A missing key answers 401, an empty balance 402, too many calls 429. A call that fails or finds nothing is not counted. See Errors and rate limits.

Description for agents

The text the server gives an agent to decide when to call this tool:

Use this when the user wants a count, sum or average over a large public GeoParquet for a place or per zone, without downloading it: "how many crop fields are in Arkansas?", "active fires per county in California in 2024", "total building footprint area in Berlin Mitte", "fields per crop type". Give dataset (a portolan: id from find_datasets, or a GeoParquet URL; inspect_dataset lists its columns and partitions), then the area: area (a named place is cut to its outline; a place with radius_m is a disk; a bbox), or zones for one row per zone (per: state, county, zip, place in the US; country, region, locality, neighborhood anywhere; adm1 to adm4 official units; within: the parent place, or units: the zones by name). Without either, the whole file. where filters rows (a condition on the partition key, such as year, picks the files; without one only the newest partition is read). group_by gives one row per value (up to 2 columns); aggregates adds sum, mean, min or max of a column, or area_m2 of the geometries (reads the geometry, heavier). A row counts in a zone when the centre of its bounding box lies inside. Only the row groups the zones touch are read (a state from a multi-GB file in seconds); a query over the caps gets too_large with the estimate. Returns the table (zone, group, count, values), totals, distinct_groups and what was read. No features: extract_dataset cuts them. Never use the user's current location unless they type it. Give the place as the user names it (place name, address or coordinates): this tool locates it, so find_places is not needed first.

Updated 2026-11-15