mcpbeat Sign in

Umbra open SAR archive MCP Server

by reesehammer Your server? Claim it
answering

Umbra open SAR archive is answering right now. Last checked moments ago. 144 installs a week from pypi. It exposes 19 tools. Last commit 29 Oct 2026.

Search, preview, and measure Umbra open SAR from any MCP client. Umbra ships no search API.

Installs per day peak 330 · avg 46 · -74% w/w
a month agotoday
Uptime history 13 days of history
13 days agonow
100.0%
Uptime 24h
92 of 92 checks
19
Tools
read from the server
442 ms
Response time
average over 24h
144
Installs / week
npm and PyPI

What changed 2

Every tool that appeared, vanished or quietly changed what it asks for. Recorded since 29 October 2026. No other catalogue keeps this.

29 Oct 2 tool descriptions were rewritten download_asset, get_item

What the code does

We read the source, 6 h ago · tools taken from the live server · rules 995398eba69f

Evidence

Things with no honest explanation: a promise that contradicts the code, code that runs at install time while hiding what it does, data leaving the machine.

Escalates privileges [пакет] deploy/docker-entrypoint.sh:40, deploy/docker-entrypoint.sh:40
os.setuid(u.pw_uid)
Capabilities

What this server is able to do. For an MCP server this is often the job itself — a terminal server runs commands because that is what it is for. Listed so you know what you are plugging in, not as an accusation.

        proc = subprocess.run(["gh", *args], check=False, capture_output=True, text=True)

Is this your server and something here is wrong? Tell us — corrections are free and do not require a plan.

We found things in this code

Code changes quietly between releases, and nobody reads the diff of a dependency. We do, on every release — watch Umbra open SAR archive and you get told the day something new turns up.

Three servers free · no card

Connect this server

Endpoint below is the one we actually reach during checks — not the one copied from a README. Last verified 0 min ago.

run in your terminal
claude mcp add umbra-mcp --transport http https://api.umbra-py.space/mcp
Settings → Connectors → Add custom connector, then paste this address
https://api.umbra-py.space/mcp
~/.codex/config.toml
[mcp_servers.umbra-mcp]
url = "https://api.umbra-py.space/mcp"
.cursor/mcp.json
{
  "mcpServers": {
    "umbra-mcp": {
      "url": "https://api.umbra-py.space/mcp"
    }
  }
}
.vscode/mcp.json
{
  "servers": {
    "umbra-mcp": {
      "type": "http",
      "url": "https://api.umbra-py.space/mcp"
    }
  }
}

Available tools 19

Read directly from the server with tools/list, grouped by what they act on. If a tool disappears, we record the date.

similar
find_similar
Find archived scenes that **look like** the acquisition at ``url``. This is the one search that lives in the pixels, not the metadata: where ``search_catalog`` filters by date/bbox/task-name, this renders the query item's quicklook, embeds it, and ranks the pre-embedded scene index by visual (cosine) similarity — "find scenes that look like this flooded field / crowded berth / runway". The query item is excluded from its own results. Requires a scene-embedding index built ahead of time with ``umbra embed build`` (a sidecar ``catalog.embed.db`` beside the catalog index) and a user-supplied multimodal embedding key (``OPENAI_API_KEY``, optionally ``OPENAI_BASE_URL`` / ``UMBRA_SCENE_EMBED_MODEL``) — the only model call is turning the query image into a vector; ranking is deterministic. ``asset`` picks which product's quicklook to embed (default ``GEC``; match how the index was built); ``model`` must match the index's embedding model. Returns the ranked matches as compact records (each with the acquisition's ``href``, so hand it to ``get_item`` / ``quicklook`` / ``change_composite`` next) plus the attribution line.
find_similar_text
Find archived scenes matching a plain-language ``query`` over their imagery. Text-to-scene search: embeds ``query`` ("ships at a berth", "a flooded field") and ranks the stored *image* vectors by cosine similarity. It only works when the index was built and this query is embedded with the **same** joint CLIP-family model whose text and image encoders share a vector space (a dimension mismatch is reported as an error; ``model`` selects it). Requires the same prebuilt ``catalog.embed.db`` (``umbra embed build``) and embedding key as ``find_similar``; the only model call is turning the text query into a vector. Returns the ranked matches as compact records (each with the acquisition's ``href`` for ``get_item`` / ``quicklook``) plus attribution.
stack
stack_provenance
Check whether a site's passes are one measurement, before measuring them. ``stack_stats`` refuses a selection whose rasters were made by *different* ``umbra convert`` settings — a calibrated pass differenced against an uncalibrated one puts the difference between two conversions on the time axis and reports it as change on the ground. This is the question that refusal answers, asked ahead of it: it reads each acquisition's ``UMBRA_*`` conversion record straight from its raster header (kilobytes per pass, no pixels, no co-registration) and groups the selection by what its pixel values *are*. Returns ``agrees`` (whether ``stack_stats`` would accept the selection), ``groups`` largest-first — each with the conversion ``record`` its members share, their ``item_ids`` and the ``hrefs`` to re-run on — plus ``shared`` (the record they all agree on) when they agree, ``refusal`` (verbatim the error ``stack_stats`` would raise) when they do not, and ``unreadable`` for any source that could not be opened at all, which is an incomplete answer rather than a mixed one. Reach for it in two situations. **After** a ``stack_stats`` call fails with a provenance refusal: ``groups[0]["hrefs"]`` is the largest set of passes that *do* agree, so re-running ``stack_stats`` on those is a measurement rather than a guess — the refusal itself cannot name that subset. And **before** measuring a long series you did not assemble yourself, since the check costs a header read per pass and saves the whole cube when the answer is no. A series of Umbra's published products as they ship (the common case) agrees trivially: every pass reads as ``"(unrecorded)"``, which is one value, so ``agrees`` is true and ``shared`` is empty. **No model is called.**
stack_stats
Measure how much a site changed across its passes, as numbers. The quantitative counterpart to ``change_composite`` / ``timescan``: those return a picture of *where* change is, this returns the *how much*. Pass the STAC URLs of a site's passes (2 or more, any order); they are co-registered onto one shared grid and reduced to a per-pass statistics series plus a net baseline-to-latest record — mean/median/spread of each pass, the signed decibel change against the pass before it, the fraction of ground that moved past ``change_threshold_db``, and that fraction as an **area in km²**. Change is always reported in decibels (a ratio of backscatter is a difference on the log scale). The default ``crs="utm"`` puts the grid in the site's UTM zone so every cell covers the same ground and the area figures mean something; pass ``crs="EPSG:4326"`` for a lon/lat grid, and the areas come back ``None`` rather than wrong. ``extent="intersection"`` (the default) compares only ground every pass covers; ``"union"`` keeps all of it and compares per cell where two passes overlap. ``bbox`` (lon/lat ``[min_lon, min_lat, max_lon, max_lat]``) narrows the measurement to a sub-area, e.g. one facility inside the scene. ``blocks=N`` adds the *where* to the *how much*: the scene is cut into an N x N grid (6 is a good default; 0, the default, skips it) and the extra ``spatial`` key reports each block's own net change, a compass label and lon/lat centre to locate it by, and the consecutive pair of passes that block moved most between — plus ``peak_block`` and an ASCII ``grid_text`` heat-map of the net change. Ask for it whenever the question is *where on this site* something happened, or when a scene-wide mean looks small but the picture from ``change_composite`` shows an obvious hot spot: a change confined to one corner is averaged away scene-wide and stands out per block. ``block_series=True`` (which needs ``blocks``) keeps each block's *whole* pass-to-pass sequence rather than only its peak interval, so a block that drifted a little every pass reads differently from one that jumped once and held. It is the largest payload this tool emits — ``blocks`` × ``blocks`` × (passes − 1) records — so ask for it only when the *shape* of the history is the question, not the size of the change. ``speckle_filter="boxcar"`` or ``"lee"`` averages **speckle** down before anything is measured. Speckle is not sensor noise: it is the interference pattern coherent illumination makes on rough ground, and on single-look imagery it scatters as widely as its own mean — so on a quiet site an unfiltered per-cell decibel delta is mostly interference rather than change. ``"boxcar"`` averages every window (most variance removed, blind to the edges it averages across); ``"lee"`` averages only where a window is no more variable than speckle alone explains, so edges and bright points survive. Reach for it when the change you are looking for is small, or when the numbers look noisy against a picture that looks quiet; leave it off when the question is about *fine* structure, because what it spends is resolution — a ``speckle_window``-cell window (odd, 5 by default) makes each cell report ground that many cells across. The returned ``caveats`` say so, so a quoted figure carries the trade with it. Refuses to mix polarizations (HH and VV are not comparable). **No model is called** — this is arithmetic over streamed COG overviews, so the numbers are reproducible and safe to quote. Read the ``caveats`` before interpreting them: Umbra's open products are not radiometrically calibrated, so a decibel value is relative to the same ground on another date, and look geometry moves backscatter too.
stamp
stamp_description
Validate a client-produced scene reading and stamp provenance. This is the interpretation boundary for the no-key kit path: you read the picture ``describe_scene`` returned, produce ``{summary, observed_features, confidence, caveats}``, and pass it here. The library checks the shape, stamps CC-BY and the AI-interpretation note (the model never sets those), and — when ``image`` is the record from the kit — appends a deterministic caveat if the picture was a smaller baked preview. Until this returns, the reading is unvalidated prose, not pipeline output.
stamp_narration
Validate a client-produced change narration and stamp provenance. Sibling of :func:`stamp_description` for the no-key ``narrate_change`` kit. Pass the JSON you produced plus the ``change_stats`` grid from the kit so the stamped object carries the numbers the prose was grounded in.
catalog
search_catalog
Search Umbra's catalog and return compact context cards. Filters (all optional, combine freely): ``bbox`` as ``[min_lon, min_lat, max_lon, max_lat]`` in WGS84 degrees; ``intersects`` as a GeoJSON polygon object (a ``Polygon`` / ``MultiPolygon`` geometry, or a ``Feature`` / ``FeatureCollection`` wrapping one) to keep only footprints intersecting it -- a tighter spatial filter than the rectangular ``bbox``, and mutually exclusive with ``bbox`` / ``place``; ``place`` as a free-text name geocoded to a bbox; ``area`` as a substring of the Umbra task (site) name (set ``fuzzy`` to match it loosely -- word-order- and punctuation-independent and typo-tolerant, resolved deterministically with no model call, so ``"beet piler"`` still reaches ``"Beet Piler - ND"`` and ``"utah centerfield"`` still reaches ``"Centerfield, Utah"``; set ``semantic`` instead to treat ``area`` as a plain-language *description* of a site whose name you don't know and resolve it to the closest task names by *meaning* via the prebuilt embedding index, so ``"grain storage north dakota"`` reaches ``"Beet Piler - ND"`` -- a query that shares no word with the label, which ``fuzzy`` cannot and should not fake; ``semantic`` and ``fuzzy`` are mutually exclusive, and the resolved task name is returned as ``resolved_area`` with the ranked candidates in ``semantic_matches`` so you can audit and retry with a different one, and ``min_score`` -- a cosine threshold in ``[0, 1]`` -- drops weak aliases, so a description that matches nothing confidently returns an empty result rather than an arbitrary top pick); ``start``/``end`` as acquisition-date bounds -- an ISO ``YYYY-MM-DD`` date, a bare year/month (``2024``, ``2024-03``), or a relative expression (``today``, ``yesterday``, ``3 months ago``, ``last month``), resolved deterministically with no model call; ``products`` to restrict to product types (any of GEC, SICD, SIDD, CPHD); the SAR acquisition-property filters ``polarizations`` (keep items exposing any of these, e.g. ``["VV"]`` -- the filter that keeps a change comparison like-with-like), ``min_incidence`` / ``max_incidence`` (view incidence-angle bounds in degrees) and ``max_resolution`` (keep items at least this fine, in metres) -- each a hard predicate that excludes items missing that metadata; ``canonical`` to keep the preferred copy of a collect Umbra republished under another prefix (``True``), only the other copies (``False``), or every item when omitted; ``limit`` to cap results; ``max_per_task`` to cap items per site (use 1 for a one-pin-per-site overview). ``local`` selects the backend: leave it unset to use the on-disk index when present (instant) and fall back to a live S3 walk otherwise. When the server is configured with a Canopy token (``$UMBRA_CANOPY_TOKEN``), the search runs against Umbra's authenticated commercial archive instead and ``source`` is reported as ``"canopy-archive"``. Returns the per-item context cards, not full STAC JSON, to protect the context window; call ``get_item`` for one item's full metadata. Prefer ``fuzzy=True`` (or :func:`find_repeat_sites`) when ``area`` shares words with a task name; that path is deterministic and needs no key. ``semantic=True`` is only for a description that shares **no** tokens with the label. It requires a task-embedding index built ahead of time with ``umbra semantic build`` and a user-supplied embedding key (``OPENAI_API_KEY``, optionally ``OPENAI_BASE_URL`` / ``UMBRA_EMBED_MODEL``); it raises a helpful setup error otherwise, so it never runs implicitly. The only model call is turning ``area`` into a vector -- the resolved search over the chosen backend stays fully deterministic.
change
change_composite
Composite 2-3 passes of one site into a change image. Pass the STAC URLs of two or three acquisitions of the *same* site in time order. Colors encode change: bright green = signal appeared after the first date, magenta = signal vanished, grey = unchanged. Refuses to mix polarizations (HH vs VV are not comparable). Returns the image block plus a caption naming the color semantics and attribution -- and, when a pass carries no polarization metadata so same-polarization could not be *verified*, a structured caution text block naming that gap.
describe
describe_scene
Return a SAR-literate reading of the scene at ``url``. The picture and the metadata card are produced deterministically; a model only interprets them. How that model is reached depends on whether this process holds a vision key: - **No key** (the public community host): this tool does **not** call a vision model. It returns the PNG plus a reading kit (SAR rules, the metadata card, the JSON schema). Produce the JSON, then call :func:`stamp_description` — that is the validation boundary (provenance-stamped ``{summary, observed_features, confidence, caveats}``). Until you stamp it, your prose is not pipeline output. A baked preview is typically 512 px; the kit names the size and that a local server can stream a higher-resolution GeoTIFF. - **Key configured** (local stdio): sends the picture to that vision model, validates through ``parse_description``, and returns the stamped object directly. The packaged SAR rules are the value over just looking at ``quicklook``: brightness is backscatter not sunlight, speckle is not structure, a dark patch may be radar shadow not water, one frame is not change. ``preview`` defaults to ``"auto"``: a baked catalog preview if this server has one, else a fresh render. ``"render"`` is refused on the public host.
download
download_asset
Download one asset of an item to local disk, gated by a size check. SAR products can be multiple GB, so this is a two-step handshake: call it first with ``confirm=False`` (the default) to get the byte size without downloading, then again with ``confirm=True`` to fetch it. On a local or stdio server, ``confirm=True`` returns ``{asset, path, bytes}``. ``confirm=False`` returns a size-and-hint object and does not write a file. On a public host (``umbra serve --public``, COG streaming off), ``confirm=True`` does not transfer the file. It returns a link plan: ``mode`` ``"link"``, ``downloaded`` false, ``asset``, ``filename`` (a basename), the direct public ``href``, ``bytes``, and ``how_to_fetch``. When the href is https on the public open-data bucket, ``how_to_fetch`` is ``curl -L -C - -o <filename> <href>`` and ``umbra download ITEM --asset ASSET --dest DIR``. ``bytes`` is the STAC ``file:size`` or ``size`` when that is a non-negative integer; otherwise the host HEADs that href for ``Content-Length``, with no redirects and no retries. A href that is not on that bucket is not requested: ``how_to_fetch.curl`` and ``how_to_fetch.umbra`` are null, ``bytes`` is null, and ``note`` says the commands were omitted. A 404 or 410 from an allowlisted HEAD raises. Any other HEAD failure, including a full quick-request slot, leaves ``bytes`` null and still returns the plan. The hosted server never transfers the file. A local ``uvx --from 'umbra-py[mcp]' umbra-mcp`` server downloads to your own disk. ``confirm=False`` on this host returns the same size-and-hint object and does not write a file. It HEADs for ``Content-Length`` only when the href is https on the public open-data bucket, with no retries, and it does not follow redirects. A 404 or 410 raises, and so does any other non-redirect HEAD error. A redirect leaves ``bytes`` null. A href outside the bucket is not requested; ``bytes`` is the STAC ``file:size`` or ``size`` when that is a non-negative integer, otherwise null. A local server still HEADs the href and follows redirects. ``url`` is a STAC JSON URL, or a product filename (``2025-05-17-17-43-46_UMBRA-08_GEC.tif``) or acquisition stem (``2025-05-17-17-43-46_UMBRA-08``). A filename or stem is resolved through the catalog index the same way as ``get_item``. The hosted server already has that index. A local server needs ``umbra index fetch``. ``asset`` selects the file. All of these work: - a product type, any case: ``GEC``, ``gec``, ``SICD`` - a schema v2 key, including a collision suffix: ``gec``, ``gec_2``, ``csi``, ``sidd``, ``csi_sidd``, ``sicd``, ``cphd``, ``metadata``, ``stac_metadata``, ``di_gif``, ``file``, ``file_tif`` - the original filename (the asset's ``umbra-py:filename``) ``GEC`` and ``gec`` are the primary file. ``gec_2`` is the second GEC, not the primary. A name that matches nothing raises with the item's v2 keys listed (``gec, sicd, cphd``, …), not the uppercase product types.
geocode
geocode_place
Resolve a free-text place name to a bounding box via Nominatim. Returns ``{"bbox": [min_lon, min_lat, max_lon, max_lat], "display_name": ...}`` so the agent can inspect the box before searching, or pass it straight to ``search_catalog``'s ``bbox``.
index
index_stats
Report on the local catalog index, if one has been built or fetched. Lets a long-running agent decide whether searches will be instant (index present) or pay a live S3 walk, and how stale the snapshot is. ``end`` is the newest acquisition that is not ``datetime_suspect``. ``datetime_suspect_count`` is how many kept rows carry that flag. ``quarantined_future_count`` is how many source rows the last build dropped for a datetime more than a day ahead (they are not in the index). ``unique_captures`` is how many rows are the canonical copy of their collect; it equals ``items`` until republished copies have been linked.
item
get_item
Return the full context card for one item. ``url`` is the JSON URL of an open-data STAC sidecar, a GEC/SICD (or other product) filename such as ``2025-05-17-17-43-46_UMBRA-08_GEC.tif``, or the acquisition stem ``2025-05-17-17-43-46_UMBRA-08``. A filename or stem is resolved through the local catalog index: the stem is matched against asset hrefs, which is what distinguishes ``sar-data/tasks/``, ``sar-data/task-data/``, and ``open-data/`` — the name itself does not. Zero matches is an error. More than one match lists the candidate STAC URLs; pass one of those. The hosted server resolves a filename or stem with the catalog index it already loaded. A local server does the same after ``umbra index fetch``. When the index contains a STAC URL, the indexed snapshot is returned — the same document ``search_catalog`` already answered from — rather than a live re-fetch that can disagree on asset filenames or product types. When the server is configured with a token (``$UMBRA_CANOPY_TOKEN``) and ``url`` is a bare acquisition id (no ``://``, and not a filename or stem), it is instead looked up in Umbra's authenticated commercial archive by a keyed STAC search — the retrieval complement to ``search_catalog`` over the paid catalog, since archive cards carry an ``id`` (not a fetchable sidecar URL) as their hand-off pointer. A filename or stem still resolves from the local open-data index when a token is set. On the public host a URL is fetched only when it is https on Umbra's public open-data bucket. Any other URL is refused before a request. A bare id that is not in the local index is refused the same way. The card (:meth:`UmbraItem.to_llm_context`) carries the id, ISO datetime, place, bbox, resolution, polarization *with the change-detection caveat*, the per-product-type explanations and asset URLs, and the mandatory attribution line — everything a model needs to reason about the scene.
narrate
narrate_change
Return a grounded reading of *what changed* between passes. Sibling of :func:`describe_scene`. The composite and the per-block dB grid are produced deterministically; a model only narrates change those numbers support. Pass the STAC URLs in time order; mixed polarizations are refused. This tool **streams Umbra COGs** to build the composite, so a public community host refuses it (use a local ``uvx --from 'umbra-py[mcp]' umbra-mcp`` server, or describe each pass with :func:`describe_scene` / :func:`quicklook` on baked previews). On a local server: - **No vision key:** returns the composite as an MCP image block plus a reading kit (SAR change primer, the dB grid, the JSON schema). **You** (the client's model) narrate, grounded in the numbers. - **Key configured:** sends picture + grid to that vision model, validates through ``parse_narration``, and returns a provenance-stamped ``{summary, changes[], confidence, caveats[], change_stats}`` object.
pick
pick_change_interval
Scan a site's whole series and name the two passes worth narrating. The deterministic first half of the "watch a site over time" story, and the step that turns a wall of passes into an answer. Given the STAC URLs of a site's passes (2 or more, any order), it co-registers them into a ``to_stack`` cube, reduces it with ``stack_stats``, and returns the *one* consecutive pass-pair whose measured change stands **furthest clear of the speckle detection floor** — the pair a reader should look at first. No model is called: a number picks the frames (the determinism boundary applied to frame selection), so the choice is reproducible and safe to quote. Why this exists as its own tool: ``narrate_change`` reads a *pair* of passes, but a site has many, and "which two?" is a measurement question, not a model one — a picture past three dates encodes nothing a model can separate, so the pair has to be chosen before the model is called. This answers it. The returned ``urls`` are the two chosen passes in time order, ready to hand straight to ``narrate_change`` (or ``change_composite`` to see them): the two tools chain into scan → narrate with no server and no model in the selection. Returns ``selected_interval`` (the interval's ``earlier_id`` / ``later_id``, their datetimes, the ``changed_fraction`` and signed ``mean_delta_db``, the cube's ``false_alarm_fraction`` speckle floor, the ``excess`` of the change over that floor, and ``stands_clear`` — whether the change cleared the floor by a usable margin), ``urls`` (the earlier then the later pass's STAC URL), and the license/attribution. When ``stands_clear`` is false the series' own largest change is still inside the speckle, so the pair is offered but the reader is warned the difference may be interference rather than change. ``selected_interval`` is ``None`` (and ``urls`` empty) when the series has fewer than two comparable passes or no interval reported any change — there is no pair to narrate. ``crs="utm"`` (the default) grids the cube in the site's UTM zone so the change fractions weigh equal ground; ``max_size`` and ``change_threshold_db`` match ``stack_stats``. Refuses to mix polarizations (HH vs VV are not comparable). Requires the ``load`` extra.
quicklook
quicklook
Return a scene's quicklook PNG as an image block. Prefers a baked catalog preview (``preview="auto"``, the default) so a public host can show the radar scene without proxying Umbra COGs. **A baked preview is typically 512 px** (the published weekly sidecar). ``max_size`` applies only to a fresh ``preview="render"`` stream; when a cached thumbnail is smaller than requested, the caption says ``max_size`` was ignored *and* includes a JSON ``{"image": {source, width, height, requested_max_size, substituted}}`` sidecar. For a higher-resolution picture (typically 1024 px), run a local ``uvx --from 'umbra-py[mcp]' umbra-mcp`` server, or download the GEC href from ``get_item``. ``preview="baked"`` refuses rather than stream; ``preview="render"`` always streams (needs ``[viz]``, refused on ``umbra serve --public``).
repeat
find_repeat_sites
Rank the archive's most repeat-imaged sites — where change detection has something to measure. The discovery step *before* every analysis verb. ``change`` / ``timescan`` / ``stack`` / ``pick_change_interval`` / ``narrate_change`` all answer *what* changed at a site, but each assumes the caller already knows *which* site to point them at. Umbra files every pass of an area under one task (site), so a site's coverage is just how many dated passes share its task; the sites with the most are exactly the ones where a time series exists to analyse. This tool ranks them, so ``find_repeat_sites → pick_change_interval → narrate_change`` is a complete chain a model can drive with no site known in advance — the deterministic answer to "where is there something to look at?" (the discovery moat; no model is called). Reach for this (or ``search_catalog(area=..., fuzzy=True)``) before ``semantic=True`` when the site name is roughly known. The filters scope the *pool* of acquisitions the ranking is computed over, and are the same ones ``search_catalog`` takes: ``bbox`` (``[min_lon, min_lat, max_lon, max_lat]`` WGS84), ``intersects`` (a GeoJSON polygon, mutually exclusive with ``bbox`` / ``place``), ``place`` (free-text, geocoded to a bbox), ``area`` (a task-name substring, loosened by ``fuzzy``), ``start`` / ``end`` date bounds, ``products`` (GEC / SICD / SIDD / CPHD), and the SAR property filters ``polarizations`` / ``min_incidence`` / ``max_incidence`` / ``max_resolution``. ``top`` caps how many sites are returned, and ``min_passes`` is how many passes a site needs to qualify (2 is the minimum a change composite can use; raise it to find only deeply-revisited series), counted on the depth ``rank_by`` measures -- raw dated passes by default, the usable (comparable) series' depth under ``rank_by="comparable"``. ``rank_by`` chooses the order: ``"passes"`` (the default) ranks by raw pass count; ``"comparable"`` ranks by *analysable* depth instead -- the ``comparable_passes`` largest same-polarization dated subset a change verb can actually difference -- so a deep single-polarization series is not outranked by a broader mixed-polarization site whose passes no analysis verb can compare, and ``min_passes`` then floors that same usable depth, so ``rank_by="comparable", min_passes=3`` returns only sites whose differenceable series is at least three passes deep. The two agree when every dated pass of every site shares one polarization; prefer ``"comparable"`` when the next step is ``pick_change_interval`` / ``stack_stats``, which consume exactly that differenceable subset. Three *temporal* orders rank on the axes the filters below select on: ``"recency"`` orders by each site's newest dated pass (the still-active site to monitor or task, which a depth order buries under a deeper but dormant series), ``"span"`` by each site's observation baseline (the site watched long enough for slow change to show), and ``"cadence"`` by each site's *typical* revisit gap, tightest first (the most-frequently-imaged site -- the median gap, not the worst one, so a single outage does not bury an otherwise reliably-imaged series; the worst-case reading stays the ``max_revisit_days`` filter). ``min_passes`` still qualifies a site on raw depth under any of the three, so a temporal order returns only sites with a series worth ordering. ``active_since`` keeps only sites still imaged *on or after* that date -- a recency filter on each site's newest pass, so a deep series that stopped long ago is dropped and an actively-revisited one is kept. Use it to find live monitoring targets ("repeat-imaged sites still being collected"). It accepts an ISO date (``"2024-06-01"``), a bare year/month, or a relative expression (``"6 months ago"``), is orthogonal to ``rank_by`` / ``min_passes`` (it gates on the site's latest pass, whatever depth the ranking measures), and differs from ``start`` / ``end``: those bound which *passes* enter the pool (truncating every series to a window), whereas this selects whole sites by recency and keeps each survivor's full history. ``active_before`` is the complement -- keep only sites whose newest pass is *on or before* that date (a dormant series that stopped imaging). Pass both to select sites whose latest pass falls *within* a window (``active_since <= last <= active_before``). Same grammar as ``active_since``, but a bare year/month covers the whole named period (``active_before="2024"`` is "last imaged on or before 2024-12-31"), symmetric with ``end``. ``first_since`` / ``first_before`` are the onset (first-seen) twins of the ``active_*`` pair -- they gate each site's *earliest* pass rather than its newest. ``first_since`` keeps **newly-appeared** series (first imaged on or after the date -- ones that entered the archive recently) and ``first_before`` keeps **long-established** ones (first imaged on or before it -- watched since before then); pass both to bound the onset to a window (``first_since <= first <= first_before``), exactly as the ``active_*`` pair bounds the newest pass. Same grammar as ``active_since``, ``first_before`` snapping a bare year/month to its last day. Orthogonal to the ``active_*`` recency filters, since when a site started and whether it is still going are independent axes -- so ``first_since=X, active_before=Y`` finds series that appeared after X and have already gone dormant by Y. ``None`` (the default) applies no onset filter. ``max_revisit_days`` keeps only sites revisited *at least this often* -- a cadence filter on each site's **worst-case** revisit gap (in days), so a series with any stretch longer than that between consecutive passes is dropped and a reliably imaged one kept. Use it to find monitorable targets ("repeat-imaged sites with no blind spot longer than N days"). It gates the cadence ``rank_by`` measures -- the whole dated series by default, the usable (comparable) series' worst gap under ``rank_by="comparable"`` -- is orthogonal to ``active_since`` / ``active_before``, and drops a site with fewer than two passes in the gated series. A non-positive value is a ``ValueError``. ``median_revisit_days`` is the *typical*-cadence twin of ``max_revisit_days`` -- keep only sites whose **median** revisit gap (in days) is at most that, so a site *usually* imaged often is kept even if a single stretch runs long, where ``max_revisit_days`` drops it the moment any gap exceeds the bound. Use it for "usually imaged frequently" rather than "never blind for longer than N days"; set both to combine the two. It gates the cadence ``rank_by`` measures (the usable series' typical gap under ``rank_by="comparable"``), is orthogonal to ``max_revisit_days`` and the recency filters, and drops a site with fewer than two passes in the gated series. A non-positive value is a ``ValueError``. ``min_span_days`` keeps only sites imaged over *at least this long* -- a baseline filter on each site's observation **span** (in days, first pass to last), so a series confined to a short window is dropped and a long-baseline one kept. Use it to find targets for *slow* change (subsidence, construction, deforestation) that needs a long observation window to be visible. It is a different axis from ``max_revisit_days``: cadence is the worst *gap* (how reliably a site is watched), span is the total *baseline* (how long it has been watched at all). It gates the span ``rank_by`` measures -- the whole dated series by default, the usable (comparable) series' span under ``rank_by="comparable"`` -- is orthogonal to ``active_since`` / ``active_before`` / ``max_revisit_days``, and drops a site with fewer than two passes in the gated series. A non-positive value is a ``ValueError``. ``max_span_days`` is the upper twin of ``min_span_days`` -- keep only sites imaged over *at most this long* (a short-lived series, a burst of imaging now over), the complement of the floor. Set with ``min_span_days`` the two bound each site's baseline to a window (``min_span_days <= span <= max_span_days``), exactly as ``active_since`` / ``active_before`` bound the newest pass to one. It gates the same span ``rank_by`` measures (the usable series under ``rank_by="comparable"``) and, like the floor, drops a site with no measurable span so the window admits only a confirmed baseline. A non-positive value is a ``ValueError``. ``limit`` sizes the *live/token* pool only. On the local index a site's depth is measured **whole-archive** — one ``GROUP BY task`` over the entire index (:meth:`CatalogIndex.rank_sites`), so a deeply-imaged site is ranked by all its passes rather than by whatever the first ``limit`` rows admitted, and ``limit`` does not apply. On the live open bucket or the Canopy archive there is no index to group over, so ``limit`` sizes the single re-listed pool the ranking is computed from (bigger finds more sites and deeper series but scans more of the archive; ``area`` narrows it instead). Returns ``sites``, best-covered first: each a ``SiteCoverage`` with the site's ``task`` codename and place ``label``, its ``passes`` count, ``first`` / ``last`` dates and ``span_days``, ``min_revisit_days`` / ``median_revisit_days`` / ``max_revisit_days`` cadence (the last being the longest gap, i.e. the site's worst-case temporal resolution) with ``comparable_min_revisit_days`` / ``comparable_median_revisit_days`` / ``comparable_max_revisit_days`` the same shortest / typical / worst triple over just the differenceable subset (each can diverge from its raw twin when a cross-polarization pass fills a gap the analysis verbs cannot use), union ``bbox`` footprint, ``products`` and ``polarizations`` — with ``comparable_polarizations`` naming *which* single polarization the usable series is (the signature the ``comparable_*`` figures are all measured over, a strict subset of ``polarizations`` when the site is mixed) — and the ``hrefs`` — the site's pass STAC URLs, **oldest-first**, ready to hand straight to ``pick_change_interval`` (or ``change_composite`` / ``stack_stats``). Also ``count`` (sites returned), the pool ``source``, the resolved ``place`` / ``bbox`` / ``area``, a ``query`` echo of the ranking-and-selection inputs (``rank_by``, ``top``, ``min_passes`` and the recency / onset / cadence / baseline bounds, as you passed them) so the answer records how it was ranked and filtered, and the attribution line. ``local`` selects the backend exactly as ``search_catalog`` does — unset uses the on-disk index when present (instant) and a live S3 walk otherwise. With a Canopy token configured (``$UMBRA_CANOPY_TOKEN``) the pool is drawn from the commercial archive instead. The ranking is single-sourced with the static showcase's featured-gallery selector, so this and ``umbra sites`` cannot disagree about what "most repeat-imaged" means.
timescan
timescan
Summarize a whole time series into one activity image. Pass the STAC URLs of a site's passes; the timescan encodes per-pixel temporal statistics so bright/colored areas are *where activity happened* over the series. Returns the image block plus attribution.
watch
watch_site
Report only the acquisitions **new** since the last check of this site. SAR's value for monitoring is its cadence, so the natural workflow is *standing*: run the same search each time and act only on what changed. This tool packages that idempotent delta — call it in one turn, come back a day (or a session) later and call it again, and it returns just the passes published in between, not the whole list again. The scheduler is you: ask the agent to check the site, and it reports the delta. Define the site with the same filters as ``search_catalog`` — ``place`` (geocoded to a bbox), ``area`` (task/site-name substring, loosened by ``fuzzy``), ``bbox``, ``products``, and ``start``/``end`` date bounds. The watch is identified by a stable ``name`` derived from those filters (pass an explicit ``name`` to run several independent watches over the same site); ``reset=True`` re-establishes the baseline, reporting everything as new. State persists in the local catalog index's ``meta`` table (created on first use), so a watch survives across sessions with no extra setup. ``local`` selects the search backend exactly as in ``search_catalog`` — leave it unset to use the on-disk index when present and a live S3 walk otherwise; for true monitoring a live walk (``local=false``) catches freshly published passes. **No model is called** — this is pure set arithmetic over the deterministic search — and the returned ``new_items`` are context cards ready to hand straight to ``change_composite`` / ``timescan``, closing the standing-analyst loop (new pass → composite → describe) without leaving the conversation.

Endpoints

URLTransportStateLatencyChecked
https://api.umbra-py.space/mcp streamable-http answering 475 ms 0 min ago

Alternatives to Umbra open SAR archive

same job, measured the same way
Lostinnotes
by lostinnotes

Save and search your LostInNotes notes by meaning from any MCP client. OAuth sign-in or token.

answering
Aurora
by aurora

Create, edit, preview, and deploy full-stack web apps to a live URL from any MCP client.

answering
Redlisten Reddit
by redlisten

Search, monitor and measure Reddit posts and comments. Previews work with no API key.

2 tools answering
Scratchpad
by scratchpad-sh

Search, read, write, and organize your Scratchpad notes from any MCP client.

371 installs/wk answering
Whatsapp
by wacli

Your own WhatsApp as an MCP server: read, search and send from any MCP client.

answering
NerdyChefs Prompts
by mayeu20

Search, browse and pick from the free NerdyChefs.ai prompt library from any MCP client.

56 installs/wk local only
SMSPM MCP Server
by alesav

Send SMS via the SMSPM API from any MCP client.

40 installs/wk local only
Bussin MCP
by sgebb

MCP server for Azure Service Bus. Peek, search and replay dead-letter messages from any MCP client.

498 installs/wk local only

Umbra open SAR archive — questions

Answers built from our own checks of this server.

What can Umbra open SAR archive do?
It exposes 19 tools, read directly from the server on our last check. Among them: change_composite, describe_scene, download_asset, find_repeat_sites, find_similar, find_similar_text and 13 more. The full list with descriptions is on this page — we take it from the server itself via tools/list, not from a README. How MCP servers expose tools in the first place →
What is Umbra open SAR archive mostly used for?
Its tools cluster around stack, similar and stamp. That is what this server is built to work with — the grouping comes from the actual tool names, not from a category we assigned.
Is Umbra open SAR archive working right now?
We send a real MCP handshake every 15 minutes. Over the last 24 hours 92 of 92 checks got a reply (100.0%), average response time 442 ms. The bar chart above shows every period we have measured.
How do I connect Umbra open SAR archive?
Copy the ready config from this page. We generate it for Claude Code, Claude Desktop, Codex, Cursor and VS Code, each in the form that client actually reads. It is a remote server, so there is nothing to install — the client connects to the address.
Does Umbra open SAR archive need an API key?
No. Umbra open SAR archive completed a full MCP handshake with us as an anonymous client and listed its tools without asking for anything. All 19 of them are readable on this page. This is what we observed, not what the docs claim.
How fast is Umbra open SAR archive?
It answers our handshake in 442 ms on average, which is faster than 28% of all working MCP servers we measure. The comparison comes from our own checks across the whole registry, every 15 minutes.
How many people use Umbra open SAR archive?
The pypi package umbra-py was installed 144 times in the last week. Week over week that is -74%. We show installs rather than GitHub stars on purpose: a star is a bookmark, an install is someone actually running it.
Is Umbra open SAR archive open source?
The code is open source: it is published under the Apache-2.0 licence, written in Python, 2 stars on GitHub and 1 open issue. The licence covers the code in the repository, not the hosted service at api.umbra-py.space, which runs on its operator's own terms.