---
name: eocube-stac-search
description: "Find Sentinel and other EOCube items in the stac.eocube.ro catalog with pystac-client or the eocube MCP tools: CQL2 filters (pystac-client only), areas of interest from Romanian counties and administrative units, date windows, sorting and paging, and how to read the results file the MCP search returns."
license: Apache-2.0
compatibility: "EOCube.RO JupyterHub notebooks (notebooks.eocube.ro) with the eocube MCP sidecar (eocube-tools 0.2.x); pystac-client parts also work on a laptop with `eocube auth login`."
---

## Which client

- **MCP `stac_search`** for quick, conversational lookups: it accepts collections, bbox,
  `datetime` or `start`/`end`, `last_days`, ids, `intersects` (GeoJSON) or
  `intersects_file` (a path such as the one `geo_*` tools return) and `sortby`. It has
  **no CQL2 `filter` and no `query` parameter**. It writes the FeatureCollection to a
  file and returns a summary with `results_file`.
- **pystac-client** for anything with attribute filters, large result sets, or code the
  user will keep. Open the catalog once:

```python
from pystac_client import Client
cat = Client.open("https://stac.eocube.ro")
```

Start by listing collections (`stac_list_collections` or `cat.get_collections()`) and use
their exact ids; guessing ids returns empty results.

## Areas of interest

Romanian counties and administrative units are one tool call away and come back as
GeoJSON (EPSG:4326). The default `geometry="file"` returns a path:

- `geo_county_by_name("Timiș")`, `geo_county_by_mnemonic("TM")`
- `geo_admin_unit_by_name("Timișoara")`, `geo_admin_unit_by_code(<SIRUTA>)`

Pass that path as `intersects_file` to `stac_search`, or load it for pystac-client:

```python
import geopandas as gpd
aoi = gpd.read_file(path_from_geo_tool).to_crs(4326)
geom = aoi.geometry.union_all().__geo_interface__
search = cat.search(collections=["<collection-id>"], intersects=geom, datetime="2026-06-01/2026-08-31")
```

A `bbox` is `[west, south, east, north]` in degrees. Simplify large polygons before
sending them as `intersects`.

## Dates

- MCP: `last_days=30`, or `start`/`end` as ISO 8601, or `datetime="2026-06-01/2026-08-31"`.
- pystac-client: `datetime="2026-06-01/2026-08-31"`, open intervals with `..`, single
  days `"2026-07-15"`. Composite products carry `start_datetime`/`end_datetime`; filter on
  those properties when `datetime` is null.

## Cloud cover and other attribute filters: CQL2 through pystac-client only

The catalog has no `query` extension. Use the `filter` extension via pystac-client, or
filter client-side after the search.

```python
search = cat.search(
    collections=["<collection-id>"],
    intersects=geom,
    datetime="2026-06-01/2026-08-31",
    filter={"op": "<", "args": [{"property": "eo:cloud_cover"}, 20]},
    filter_lang="cql2-json",
    sortby=[{"field": "properties.datetime", "direction": "desc"}],
    max_items=200,
)
items = search.item_collection()
```

Combine conditions with `{"op": "and", "args": [...]}`. Text form is also accepted:
`filter="eo:cloud_cover < 20", filter_lang="cql2-text"`. Client-side fallback:
`[i for i in items if i.properties.get("eo:cloud_cover", 100) < 20]`.

## Sorting and paging

- `sortby=[{"field": "properties.datetime", "direction": "desc"}]` works in both clients.
- pystac-client pages automatically; cap with `max_items`. `search.matched()` gives the
  total without fetching everything. Keep `limit` (page size) at the default unless you
  know the server accepts more.
- `stac_search` returns a bounded result; if the summary says more matched than were
  returned, narrow the query or switch to pystac-client.

## Reading the MCP results file

```python
import geopandas as gpd, pystac
gdf = gpd.read_file(results_file)                      # footprints + properties
items = pystac.ItemCollection.from_file(results_file)  # full STAC items
```

The file lives under `~/.eocube/mcp-artifacts/` and is disposable. If the path does not
exist in the notebook, ask for `inline=true` or a smaller result, or run the same search
with pystac-client.

## Pitfalls

- Collections you are not allowed to read (`eocube:visibility`) simply return nothing;
  there is no error. Check `auth_whoami` and the collection's visibility before
  concluding the data does not exist.
- `intersects` must be a bare geometry, not a Feature; the MCP tool unwraps Features and
  FeatureCollections for you, pystac-client does not.
- Mixed CRS: reproject the AOI to EPSG:4326 before searching.
- Use the item's `assets[...].href` only after signing or rewriting it; see
  `eocube-data-access`.
