aicesat
aicesat is an MCP server for cross-mission ICESat-2/GLAS altimetry: it surveys coverage, extracts photons, co-registers scenes, manages a Parquet lake, and serves interactive 3-D/time-series UIs.
Survey ATL03 and GLAH06 coverage by region, bbox, month, or laser campaign without fetching data.
Extract ICESat-2 ATL03 land-ice photons over a region/bbox/polygon into 3-D scenes using the H3 chunk index and byte-range reads.
Add ICESat/GLAS GLAH06 shots to scenes and run ITRF2014 + epoch co-registration (plate motion) to compute Δh statistics.
Open the unified Explore/Lake/Scene UI inline or in a browser; view points draped on DEM/imagery and Δh panels.
Manage the persistent Parquet lake: check status, load/evict H3 cells, set storage limits, and monitor background jobs.
Find and view elevation time-series candidates, with confidence, trends, and caveats noted.
List demo regions and built scenes; start scene builds or cell loads as background jobs and check job status.
Fetching NASA data requires an Earthdata token; browsing the existing lake does not.
Provides access to NASA Earthdata altimetry: fetches ICESat-2 ATL03 photons and ICESat/GLAS GLAH06 shots, checks coverage, builds 3D scenes, and computes co-registered Δh statistics.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@aicesat Show ICESat-2 photons over the Juneau Icefield and add GLAS shots"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
aicesat — cross-mission altimetry
An MCP server for Claude Desktop that pulls real ICESat-2 ATL03 photons and ICESat/GLAS GLAH06 shots over a
chosen area, renders them as a 3D point cloud (deck.gl), and — on a toggle — applies an ITRF2014 + epoch
co-registration (plate motion, ITRF2014-PMM / NOAM) with pyproj, updating the co-located Δh statistics. Started from
docs/cross-mission-altimetry-mcp-spec.md, which carries the full design and rationale.
The co-registration removes plate motion between epochs. It does not remove ice flow, GIA, geoid/tide, firn
compaction, or the vertical datum, and the widget says so on every answer. GLAS heights are converted
TOPEX/Poseidon → WGS84 ellipsoid using the product's d_deltaEllip, with the saturation correction d_satElevCorr
applied; that is recorded in every comparability block.
Setup
uv sync # Python 3.13
uv run pytest # offline unit testsEarthdata Login: fetching NASA data needs a token; browsing the existing lake does not. The server resolves a
token in this order: EARTHDATA_TOKEN (env) → a token file at ~/.edl/token.prod (override the path with
AICESAT_EDL_FILE) → ~/.netrc. Generate a token at https://urs.earthdata.nasa.gov (User Profile → Generate
Token). The server never writes to stdout (stdio MCP transport); logs go to stderr.
Related MCP server: mcp-opentopography
Claude Desktop
Add to ~/Library/Application Support/Claude/claude_desktop_config.json, using the absolute path to your checkout.
Put your Earthdata token in the env block — this is machine-independent (no token file needed) and the
recommended way for a new user to configure the server:
{
"mcpServers": {
"aicesat": {
"command": "/opt/homebrew/bin/uv",
"args": ["--directory", "/ABSOLUTE/PATH/TO/aicesat", "run", "aicesat-server"],
"env": { "EARTHDATA_TOKEN": "<your Earthdata Login token>" }
}
}
}If you keep your token in a file instead, drop the EARTHDATA_TOKEN line and either place it at ~/.edl/token.prod
or add "AICESAT_EDL_FILE": "/path/to/your/token" to env. Restart Claude Desktop. The unified UI renders inline as an MCP App; the server also serves it at
http://127.0.0.1:8765/ (port via AICESAT_PORT) for use in a browser.
Getting an Earthdata Login token (step by step)
Create a free Earthdata Login account if you don't have one: https://urs.earthdata.nasa.gov → Register.
Sign in, then open your profile (top-right) → the Generate Token tab → Generate Token. Copy the long token string (a JWT that starts with
eyJ…). Tokens last ~60 days — regenerate when one stops working.Paste it into the Claude Desktop config shown above, as the value of
EARTHDATA_TOKENin the server'senvblock. The config file is at~/Library/Application Support/Claude/claude_desktop_config.json(in Claude Desktop: Settings → Developer → Edit Config).Restart Claude Desktop. The Lake view works without a token; building a scene / checking coverage will now authenticate. If it can't, the UI shows an error banner naming what to fix.
A ~/.netrc with your Earthdata username/password also works, but a token in env is the most reliable and is what
we recommend for sharing the server with a colleague.
Tools: survey_coverage, elevation_change and show_timeseries (the place-first ladder: coverage, change, one
cell's record), open_ui, list_regions, list_scenes, check_coverage, show_photons (region, bbox, or polygon),
add_glas, coregister, find_timeseries_candidates, lake_status, lake_load_cells, job_status.
By default (the demo profile) only the three ladder tools are exposed, with the demo UI (how the UWG demo was
recorded). AICESAT_PROFILE=full exposes every tool above and the full UI.
Asking for a time series
find_timeseries_candidates ranks the places in an already-built scene where an elevation time series can actually
be measured, and show_timeseries shows one of them. Both open the Time series view inline.
The search bins every mission's points into H3 cells and fixed time windows, keeps cells seen in three or more
windows, and fits one local reference plane per cell so surface slope is removed rather than mistaken for change.
Each cell gets a deterministic 0–1 confidence dominated by within-cell roughness — the real failure mode, where two
missions sample different sub-cell relief and fake a trend. why names the top two limiters in plain language.
The candidate list is summarised for the caller (no per-cell series, no geometry) and reports n_candidates_total
alongside what it returned, so a truncated answer still says how much it left behind; the view shows every cell.
trend_cm_yr is the least-squares rate through the window medians and is uncorrected for inter-campaign /
inter-sensor bias and for GIA — quote it with that caveat, as params.notes states on every answer.
UI
One self-contained page (built from src/aicesat/ui/* + vendored deck.gl / h3-js by scripts/build_ui.py, pure
Python; the server rebuilds it on start when sources change), served both inline in Claude Desktop and at /:
Explore — a 3-D globe (Natural Earth basemap, no flat projection). Navigate anywhere, draw a box or polygon, check coverage, and build a scene as a background job; scene footprints and the loaded H3 cells are shown on the globe.
Lake — the persistent Parquet lake as an H3 grid: per-cell stats on hover, a storage limit that auto-evicts the least-recently-used cells, and background loading or eviction of selected cells.
Scene — the 3-D viewer: ICESat-2 and GLAS points draped on a DEM, an Adjustments panel of correction toggles, and the co-located Δh histograms. The true plate-motion shift is sub-pixel at scene scale, so the clouds do not visibly move — the effect is read from the Δh panel, not an exaggerated visual. Panels collapse and close.
Time series (
#ts/<scene_id>) — the same candidate list and chart the Scene panel carries, given the whole page and no 3-D. It needs only the scene metadata and one search call, so unlike the Scene view it renders completely inside Claude Desktop, where the point cloud's push stream cannot follow (tools/callis request/response). Selecting a different cell is a local redraw: the view already holds every candidate's series.
The UI talks to a transport-neutral API (api.py) exposed two ways: the localhost /api/* routes for the browser,
and visibility:["app"] MCP tools the host proxies for the inline app. scripts/e2e_apps.py checks the MCP-App wiring.
Architecture
The ATL03 path does not open HDF5 at query time. Per granule, an index build (index.py) records each chunk's byte
range, filter pipeline, and the H3 cells it touches. Queries resolve a bbox/polygon to cells, fetch only the needed
chunks by HTTPS range request (EDL bearer token → presigned URL), decode them without an HDF5 library, and materialize
photons into a hive-partitioned Parquet lake (lake.py) with per-row provenance and co-registered coordinates. DuckDB
answers over the lake (api.py); a coverage table records what is materialized so repeat queries fetch nothing. The
earlier earthaccess.open + h5py path is kept as atl03.extract_legacy for comparison. scripts/bench_access.py
compares the access methods; see the spec (Appendix C) for the approach.
Scene geometry is latitude-aware (scene.frame_crs): polar-stereographic near the poles, a per-scene
azimuthal-equidistant projection elsewhere, so scenes render anywhere on Earth.
DEM and imagery
The scene surface is a real DEM chosen by region, always on WGS84-ellipsoid heights (the same vertical reference as
ATL03), read by window over /vsicurl/ and cached: ArcticDEM v4.1 (Arctic, EPSG:3413) and REMA v2.0 (Antarctic,
EPSG:3031) — both already ellipsoidal — and Copernicus GLO-30 elsewhere, whose EGM2008 orthometric heights are
converted to ellipsoidal by adding the NGA egm08 geoid undulation (h = H + N). There is no photon-interpolated
fallback: a scene shows a DEM only where one covers it. Scene imagery is Sentinel-2 cloudless (EOX, CC BY-NC-SA 4.0),
draped on the surface. The Explore/Lake globe basemap is Natural Earth 50 m land (public domain). Attributions appear
on screen.
Scripts
uv run scripts/check_coverage.py --region egig_west_flank # granule counts by month / laser campaign
uv run scripts/ingest.py egig_west_flank # index + byte-range ingest + lake query
uv run scripts/build_index.py --region egig_west_flank # offline index pre-build (amortized off the query path)
uv run scripts/make_scene.py egig_west_flank --glas --coreg # full pipeline for a region
uv run scripts/serve.py # widget server only, for local testing
uv run scripts/bench_access.py # access-method comparisonData (data/) is gitignored; delete it to force a re-fetch.
How Δh is measured
For each GLAS shot, the ICESat-2 surface height at the footprint centre comes from a local along-track linear fit of
the signal photons within the co-location radius. A disc median is an order statistic and cannot resolve the sub-cm
slope effect; the fit is continuous in position, so it can. Only the along-beam component of the plate-motion shift is
observable on a single beam, and the per-pair artifact panel keeps the mm-level vertical part of the frame step
separate from the slope effect. Every answer carries the unresolved list and states "plate motion applied", never
"the missions agree".
Available Tools
24 toolsadd_glasC
Slice 2: add ICESat/GLAS GLAH06 40 Hz shots (2003-2009 campaigns) to an existing scene, in native coordinates (ITRF2008; heights converted TOPEX/Poseidon -> WGS84 ellipsoid). Returns provenance by campaign.
Inputs: scene_id (required), time_window, max_granules.
check_coverageB
How many ATL03 (ICESat-2) and GLAH06 (ICESat/GLAS) granules touch a region, by month / laser campaign. Give either a region name (see list_regions) or an explicit bbox [W, S, E, N]. No data is fetched.
Inputs: bbox, region, glas_window, atl03_window.
coregisterB
Slice 3: run the ITRF2014 + epoch co-registration (plate motion, ITRF2014-PMM NOAM) on both missions in a scene, co-locate GLAS shots with ICESat-2 photons, and compute delta-h statistics in native and co-registered coordinates. Live pyproj on first call, cached after. Returns the comparability block. exaggeration <= 0 picks a display exaggeration automatically (~3% of scene span); it is always labelled on screen.
Inputs: scene_id (required), common_epoch, exaggeration, colocation_radius_m.
job_statusC
Status and log of a background build job (scene or cell load).
Inputs: job_id (required).
lake_load_cellsB
Materialize H3 (res 6) cells into the lake in the background (cell ids as decimal strings); returns a job id.
Inputs: cells (required), time_window, max_granules.
lake_statusA
Parquet lake summary: cells, files, rows, bytes, storage limit and usage, recent evictions.
Inputs: no named parameters.
list_regionsB
Named candidate Greenland demo regions (bbox = west, south, east, north) with validation notes.
Inputs: no named parameters.
list_scenesA
Scenes built so far (newest first) with status ready | loading | error, area, series present, and widget URL.
Inputs: no named parameters. Returns: result
open_uiC
URL of the unified UI: Explore (imagery map, draw a box or polygon on Sentinel-2 imagery, coverage check, build scenes, open the 3-D viewer) and Lake (H3 grid with per-cell stats, storage limit, background loading, eviction).
Inputs: view.
show_photonsA
Slice 1: extract real ICESat-2 ATL03 land-ice signal photons (strong beams, medium+high confidence) over an area and create a 3D scene with an imagery base layer. Area = region name, bbox [W,S,E,N], or polygon [[lon,lat],...]. Uses the H3 chunk index + byte-range reads + Parquet lake: first touch of an area fetches only the chunks it needs, later calls hit the lake. Returns the widget URL to open plus extraction/access provenance.
Inputs: bbox, region, polygon, question, +2 more.
ui_benchD
Inputs: no named parameters.
ui_coregisterD
Inputs: scene_id (required).
ui_coverageD
Inputs: bbox, polygon.
ui_extractD
Inputs: bbox, polygon, question, with_glas, +2 more.
ui_jobD
Inputs: job_id (required).
ui_jobsD
Inputs: no named parameters.
ui_lake_cellsD
Inputs: stats.
ui_lake_evictD
Inputs: cells (required).
ui_lake_loadD
Inputs: cells (required), max_granules.
ui_lake_settingsD
Inputs: max_bytes.
ui_lake_summaryD
Inputs: no named parameters.
ui_regionsD
Inputs: no named parameters.
ui_scene_partD
Inputs: scene_id (required), part, chunk, stride.
ui_scenesD
Inputs: no named parameters.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 24 tools
The substantive tools are distinct, but the large block of ui_* tools have empty descriptions and overlapping names such as ui_job vs ui_jobs, ui_coverage vs check_coverage, and ui_lake_cells vs ui_lake_summary. An agent cannot reliably distinguish UI shortcuts from actual data operations, so several tool boundaries are unclear.
All names use lower_snake_case and are readable, but conventions vary between verb-first names like show_photons and check_coverage, noun-style names like lake_status and job_status, and the large ui_* prefix group. The ui_* group is internally consistent but the overall set lacks a single predictable naming pattern.
With 24 tools, the server sits at the heavy end of the acceptable range. Many ui_* tools are simple navigation shortcuts that could plausibly be consolidated, while the real operational core is closer to 10 tools, making the overall surface feel larger than its actual scope.
The core scientific workflow is covered: coverage checking, scene creation, adding GLAS data, co-registration, scene listing, lake loading, and job status. However, there are notable gaps such as no scene deletion or update, no job listing/cancellation, and no programmatic lake eviction, with the ui_* links only partially compensating for those missing operations.
Maintenance
Related MCP Connectors
Look up elevation worldwide, profile route ascent/descent, grid areas, check terrain line of sight.
Geometry and CAD file metadata extraction for STL, OBJ, PLY, PCD, LAS/LAZ, glTF/GLB.
SibFly — satellite-measured ground motion (subsidence / uplift) for any US address.
Search, preview, and measure Umbra open SAR from any MCP client. Umbra ships no search API.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceEnables advanced geospatial analysis including elevation queries, line-of-sight calculations, terrain visualization, and autonomous drone mission planning with real-time safety analysis and 3D visualization capabilities.MIT
- AlicenseNot gradedqualityCmaintenanceEnables querying OpenTopography global DEM rasters and point elevations through tools like point_elevation, dem, and datasets.316 npm1MIT
- FlicenseNot gradedqualityDmaintenanceEnables querying 3D point cloud files (PLY/PCD) using natural language. Provides tools for point cloud description, plane detection, and ICP alignment.-
- AlicenseNot gradedqualityCmaintenanceEnables users to retrieve satellite-measured ground motion data for any US address, including subsidence/uplift velocity, coverage checks, and historical time series.316 npmMIT