Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
134 changes: 134 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,140 @@ built against; see `docs/release.md`. Versions through `0.3.1` were SemVer.

## Unreleased

## 2026.10.0 — 2026-10-23
### Added
- `subset_advanced` selects faces near a point, by radius
(`subset.bounding_circle`) or by neighbour count
(`subset.nearest_neighbor`). An empty selection is refused with the mesh
extent, the same rule the bounding box already followed; UXarray's own
message says the circle was empty without saying where a circle would
not be.
- `regrid_elements` carries a field between nodes, edges and faces through
the `topological_*` reductions. This is what makes node-centred output
usable: every vector-calculus operation needs face-centred data, and the
refusal a caller got had no repair behind it because the server could not
run the standard one.
- `domain/uxarray_surface.py` records which of UXarray this server reaches
and which it deliberately does not, one verdict per method with a reason.
Tests hold it to the installed library, so a new method upstream fails
until someone decides about it and a renamed one fails the day the rename
lands rather than the next time a user follows our advice into a method
that is gone. Deliberately not a count: "we expose N of M" goes red on
every upstream release for reasons nobody can act on.

All three came from replaying the notebooks in UXARRAY/uxarray-community
through the server. They are separate tools rather than new `run_analysis`
operations because #89 already measures that front door as too large to
describe itself.

### Changed
- Floors `uxarray[geo,viz]>=2026.10.0`. The previous pin's `<2026.10`
ceiling excluded the release outright. The resolve is quiet -- 189
packages, uxarray the only one that moves -- and 2026.10.0's `viz` extra
caps `cartopy<0.26`, which settles UXARRAY/uxarray#1773 upstream rather
than here.
- Floors `toolregistry>=0.19.0` and `toolregistry-server>=0.7.0`. 0.19
carries the heterogeneous-union fix (Oaklight/ToolRegistry#269) this
project has been waiting on. What we serve for `lat_spec` is still not
upstream's rendering: `tuple` and `list` both become `array`, so the
union arrives with the same branch twice and untyped items. The schema
helper now replaces that rendering rather than filling an absence, and
the core tool specification got smaller as a result -- 36271 to 36143
bytes.
- The container declares its Python version once. It was written in four
places, two `FROM` lines and two `--python 3.12` flags, and Dependabot
rewrites `FROM` and nothing else -- so its 3.14 bump produced an image
whose base had no 3.12 beside a sync that demanded one.
- `SETUP.sh` reproduces CI. It ran a bare `uv sync` and an unfiltered
`pytest`, from before the suite moved to marker selection, so following
it produced 19 failures that were all missing optional dependencies. It
also gated on the ambient `python3`, which is not the interpreter the
project runs on: a machine with 3.9 on `PATH` could not run the script at
all while `uv sync` on that same machine builds a working 3.12
environment.

### Fixed
- A YAC-backed remap that loses its worker is told what killed it. The
message said memory and advised subsetting; on the UCAR endpoint the
cause is MPI, and subsetting cannot fix it. Importing YAC calls
`MPI_Init`, which Open MPI serves as a singleton and MPICH aborts, and
an abort inside a Globus worker arrives with nothing mentioning MPI.
Measured on Casper: the identical conservative remap runs in 0.09s
under `check_remote_yac` and kills the worker through `run_analysis` --
same node, same YAC, same mesh, the only difference being that the
probe runs it in a subprocess. Any other lost worker still gets the
memory advice, which is right for it.
- `check_remote_yac` exercises the conservative path. It only ever ran
`nearest_neighbor`, which uses uxarray's own engine and never calls
into YAC, so it reported "YAC OK" on a worker where every conservative
remap died. `conservative_ok` now separates "YAC is broken" from "YAC
cannot run in this process", which is the distinction that picks the
repair.
- `scripts/ucar_endpoint.sh check-mpi` runs the bare and launched imports
side by side and names which case the site is in. Chrysalis has had an
equivalent since it hit this; UCAR had nothing.
- The MCP SDK exception is found under either spelling. `McpError` on
1.x, `MCPError` on 2.x; importing one name fails on the other SDK, and
only the weekly floor job resolves 1.x, so the wrong one went unnoticed
until that run went red.
- Tool annotations reach clients on mcp 1.x, not only 2.x. At the declared
floor the shim attached nothing, so every tool was served with no hints
at all -- the `readOnlyHint` a client reads to decide what it may
auto-approve was absent exactly where it is documented as present. Three
SDK differences, each independently sufficient: the handler dict is keyed
by method string on 2.x and by request type on 1.x, 1.x returns a
`ServerResult` envelope whose `.root` holds the tools, and the attribute
itself is public on one and private on the other.
- Our annotations outrank inferred ones. toolregistry-server 0.7.0 began
attaching annotations of its own and marked `manage_session` and
`resume_workflow` `readOnlyHint=True`; the first resets session state and
clears artifacts, the second runs pending workflow steps. Nine tools left
deliberately unrated came back carrying hints. Where this server has
rated a tool its rating is what goes out, and where it has not, nothing
does -- absence is honest, an unchecked inference is not.
- `get_capabilities` says which of what it lists is executable. It
advertises native UXarray calls, several of which have no operation
behind them -- `grid.get_ball_tree()`, `var.integrate()` -- and nothing
distinguished them, so a model reading the result would plan a call that
does not exist.
- The "not face-centered" refusals name their repair. They were correct and
a dead end: the standard fix is `topological_mean("face")`, which this
server could not run. They now point at `regrid_elements`, and a test
follows that advice to confirm it works rather than landing on a second
error.
- An unknown tool name is reported as JSON-RPC `-32602` rather than a
result carrying `isError`, following toolregistry-server 0.7.0. That is
the correct reading: `isError` is for a tool that ran and failed, and an
unknown name means nothing ran.
- The oversized-SCRIP test fixture no longer needs 8 GiB. It built each
dask array as a single 6.5 GiB chunk and `to_netcdf` materialized one at
a time, so the test peaked at 7.45 GiB before the code under test ran.
That fits GitHub's runners and not a laptop. Chunking along
`grid_corners` holds each write to ~191 MiB; the full suite now peaks at
1.36 GiB.

### Documentation
- `docs/globus-basics.md` says what Globus is before anything asks you to
use it. The explanation existed, in the middle of `remote-hpc.md`,
under a heading the README routes HPC users straight past -- written
down and unreachable, which reads the same as missing. It also names
the identity trap that costs the most time: a Globus login is not
automatically tied to a cluster account, and signing in with a personal
account produces an identity that authenticates fine and sees none of
your endpoints, which looks exactly like a dead endpoint.
- `docs/ucar.md` says endpoints are per-user. A colleague's working
Casper endpoint gives you nothing: it runs as their account and reads
their files. The conda and YAC paths on that page are per-`$USER` too.

### Security
- Clears 55 advisories in the lockfile, all in transitively-resolved
packages: 44 against pillow 12.3.0 (5 Critical), PYSEC-2026-4320 against
certifi 2026.7.22 (9.1 Critical), and four against virtualenv 21.14.7.
certifi is the trust store behind every outbound HTTPS call this server
makes -- Globus Compute submission, transfers, remote dataset reads -- so
a Critical there is not a transitive detail. A further six against
filelock 4.1.1 cleared the same way.

## 2026.9.1 — 2026-09-25
### Changed
- Floors `uxarray[geo,viz]>=2026.9.1`. Upstream 2026.9.1 moved cartopy,
Expand Down
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,15 @@ You are most likely one of:
1. **Local user** — laptop only, no HPC. → [Local install](#local-install).
2. **HPC user, endpoint already exists** — someone at your lab gave you a
Globus Compute endpoint UUID. → [Local install](#local-install), then
[docs/remote-hpc.md](docs/remote-hpc.md).
[docs/connect-a-cluster.md](docs/connect-a-cluster.md).
3. **HPC user, your own personal endpoint** — you have a Globus identity and
shell access to an HPC machine, and want to stand up an endpoint just for
yourself. → [Local install](#local-install), then
[docs/operating-an-endpoint.md](docs/operating-an-endpoint.md#solo-personal-endpoint-quickstart).

> **New to Globus?** Paths 2–4 all assume an identity linked to your HPC
> account. [docs/globus-basics.md](docs/globus-basics.md) is ten minutes and
> saves most of the common dead ends.
4. **Group / shared endpoint operator** — you're standing one up for a team,
project, or lab. → [Local install](#local-install), then the full
[docs/operating-an-endpoint.md](docs/operating-an-endpoint.md) including
Expand Down
2 changes: 1 addition & 1 deletion conda/recipe/meta.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{% set name = "uxarray-mcp" %}
{% set version = "2026.9.1" %}
{% set version = "2026.10.0" %}
{% set python_min = "3.12" %}

package:
Expand Down
17 changes: 8 additions & 9 deletions docs/connect-a-cluster.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@
Your data is on an HPC machine. You want to ask an AI about it without
downloading anything. This page gets you there.

> **Never used Globus?** Ten minutes on
> [globus-basics.md](globus-basics.md) first. It covers the identity, why it
> has to be your institutional one, and why Compute is not Transfer — the
> three things that account for most stuck setups.

It is machine-agnostic: the same five commands work for Chrysalis, Casper,
Perlmutter or a university cluster nobody has written a page about. Site
pages ([chrysalis](chrysalis.md), [ucar](ucar.md), [improv](improv.md))
Expand Down Expand Up @@ -39,15 +44,9 @@ Three things, in three places, each of which can be wrong independently:
Diagnose in that order. Most "it doesn't work" is the first or the third,
and the error usually names the second.

### Two things people reasonably expect, which are not true

- **An endpoint is per-user, not per-machine.** It runs *as your HPC
account* and can read exactly what you can read. There is no shared
"Chrysalis endpoint" unless someone deliberately operates one for a group.
- **Compute is not Transfer.** Globus Compute runs code; Globus Transfer
moves files. Separate services, separate consents. A working endpoint
cannot copy a file. You usually do not need Transfer at all — see
[data-transfer.md](data-transfer.md).
Two surprises worth knowing before you start — endpoints are per-user, and
Compute is not Transfer. Both in
[globus-basics.md](globus-basics.md#three-surprises).

---

Expand Down
74 changes: 74 additions & 0 deletions docs/globus-basics.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# Globus, the short version

Ten minutes, a browser, no cost. Read this once before connecting a cluster.

## Which bit you need

Globus is several services. You need Auth (login) and Compute (runs your
code). You almost certainly do not need Transfer.

**Compute is not Transfer.** Different services, separate consents. A
working Compute endpoint cannot copy a file. If the word "consent" shows up
and you were not trying to move a file, you are in the wrong service.

The point of this server is that the analysis goes to the data and a number
comes back, so moving files is a separate decision:
[data-transfer.md](data-transfer.md).

## Get an identity

Already used Globus for HPC file transfers? You have one. Skip ahead.

Otherwise: go to <https://app.globus.org> and **sign in with your
institution** — Argonne, NCAR/UCAR, NERSC, most universities. Use the same
account that holds your HPC login.

That last part matters. A Globus identity is not automatically tied to your
cluster account. Sign in with a personal Google account when your HPC login
is `you@institution.edu` and you get an identity that authenticates fine and
then sees none of your endpoints — which looks exactly like a dead endpoint.
Already done it? Link the institutional identity at
<https://app.globus.org/account/identities> rather than starting over.

## Check it

```bash
uxarray-mcp doctor
uxarray-mcp endpoints discover
```

The first Globus command opens a browser. Finish the login and re-run.

`discover` asks Globus what your identity can see, which also answers *where
do I get a UUID?* — it is printed once when an endpoint registers and is easy
to lose. Nobody has to send you one.

| You see | Next |
|---|---|
| An endpoint listed | [connect-a-cluster.md](connect-a-cluster.md) |
| Nothing | [Create one](connect-a-cluster.md#create-20-minutes) |
| `ENDPOINT_NOT_ONLINE` | It is asleep — see below |
| A login loop | Wrong identity; see above |

## Three surprises

**An endpoint is per-user, not per-machine.** It runs as *your* cluster
account and reads what you can read. Your colleague's working endpoint tells
you nothing about whether you have one.

**Registered is not working.** It means a manager process phoned home. It
does not mean a worker exists, has `uxarray`, or can read your file. Those
fail separately, which is why `doctor`, `validate_hpc_setup` and
`probe_path_access` check them in that order.

**Endpoints sleep.** On a scheduler-backed endpoint the worker lives in a
batch allocation. When it ends, the endpoint stays registered with nothing to
run on. `ENDPOINT_NOT_ONLINE` usually means *ask again in a minute*. A slow
first call after an idle period is normal.

## One caution

An endpoint's operator can see the functions you submit and the paths you
name. Submit nothing you would not put in a ticket.
[SECURITY.md](https://github.com/UXARRAY/uxarray-mcp-server/blob/main/SECURITY.md)
has the rest.
1 change: 1 addition & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ HTTP clients) from a single install.
:maxdepth: 2
:caption: Running on HPC (optional)

globus-basics
connect-a-cluster
remote-hpc
data-transfer
Expand Down
Loading
Loading