Skip to content

Releases: UXARRAY/uxarray-mcp-server

uxarray-mcp 2026.9.0

Choose a tag to compare

@rajeeja rajeeja released this 12 Sep 06:24
5abb0ad

🌎 uxarray-mcp 2026.9.0

An MCP server for UXarray. Point an AI
assistant at an unstructured climate mesh and it can subset it, compute on it,
plot it, and hand back an answer that carries its own checks.

Built against uxarray 2026.9. Python 3.11+.


🧭 What you can ask it to do

Describe a mesh. Topology, variables, what is face-centered and what is
not, whether the grid declares a radius, how much of the sphere it covers.

Compute on it. Area, zonal means and anomalies, gradient, curl,
divergence, azimuthal profiles, cross sections, temporal means, ensemble mean
and spread, bias, RMSE, pattern correlation.

Cut it down. Bounding box, polygon, and cross-section subsetting, each
reporting how many faces it started with and how many it kept.

Move between grids. Mesh-to-mesh and mesh-to-rectilinear remapping, with
nearest-neighbor, inverse-distance-weighted and bilinear on the UXarray
engine, and conservative, nnn, dnn and average through YAC.
Conservative is the one that preserves the field integral, and you ask for it
by that name.

Draw it. Wireframe mesh, geographic mesh over coastlines and borders,
choropleth of a variable, temporal mean, zonal profile.

Find your data. Walk a directory, classify what is a grid and what is
data, and say which operations each file supports.

Most of this goes through a single front door, run_analysis, which
dispatches by intent. 33 tools in the core surface.


🛡️ Answers that check themselves

A tool that returns a confident wrong number costs more than one that returns
nothing, because nothing downstream can tell the difference.

So every operation carries preconditions and postconditions. A call
that cannot be answered correctly comes back as a structured refusal that
names the repair, rather than a plausible-looking float.

  • 📐 Ask for area on a grid that declares no radius and you are told to pass
    sphere_radius, not handed steradians labeled as square metres.
  • 🗺️ Every area result reports what fraction of the sphere it summed over, so
    a regional mesh and a global one do not read the same.
  • 🧮 Subsets, cross sections and zonal bands return coverage blocks with source
    and selected face counts, and refuse a selection that is empty or entirely
    non-finite.
  • 🔬 Remap coverage names the method it judged, so a non-conservative warning
    tells you which non-conservative method ran.
  • 🚫 NaN and Infinity never reach the wire. They are not JSON.

Refusals are multi-round-trip shaped, so a model can read the repair and try
again without a human in the loop.


🔌 Works with your client, and with other agents

Speaks the 2026-07-28 MCP specification, and negotiates the 2025-11-25
era cleanly for clients that ask for it. Result caching hints (ttlMs,
cacheScope) are sent when the negotiated era supports them and omitted when
it does not.

Figures and other large outputs are MCP resources. A plot too big to
inline comes back as a fetchable file:// link, reads are confined to the
artifact directory, and resources/list pages at 100.

🐍 It is not only an MCP server. One tool registry projects to three
surfaces, so the same checked operations are reachable from whatever you are
building:

uxarray-mcp serve        # MCP, for Claude / any MCP client
uxarray-mcp openapi      # REST + OpenAPI schema, for HTTP agents
# or import the functions directly, no protocol involved
from uxarray_mcp.tools.frontdoor import run_analysis

result = run_analysis(
    operation="calculate_area",
    grid_path="mesh.nc",
    sphere_radius=6371000.0,
)

The preconditions, the coverage blocks and the refusals are in the functions,
not in the transport, so a Python agent framework gets the same guarantees as
an MCP client. Multi-agent setups can put the REST surface behind one agent
and the Python calls inside another.


🖥️ Runs where the data already is

Point it at an HPC endpoint and the work goes to the cluster over Globus
Compute. The mesh never moves. Only the result comes back.

File movement is its own tool set when you want it: transfer_ls,
transfer_put, transfer_get, transfer_status, backed by Globus Transfer.
They appear only when an endpoint declares a globus_transfer block, so an
install that moves no files never shows them to the model.

uxarray-mcp transfer setup     # walks through auth and collections
uxarray-mcp doctor             # says what is actually broken

doctor checks local auth, endpoint liveness, worker bootstrap, and whether
your write root is readable as well as writable.


⚡ Cheap to keep in context

The tool catalog is re-sent on every turn, so its size is paid for repeatedly.
The served catalog is 8,974 tokens for all 33 tools, and results are
budgeted per operation with tests that fail when one grows.


📖 See it on real data

CONUS precipitation case study →

Ten years of ne120np4 CONUS precipitation on NCAR Casper. 37 GB of source
data, a 777,602-face spectral element mesh, subset to 23,510 faces. One prompt
from a laptop, about eight minutes, and back comes a 178 KB PNG with the PBS
job ID attached. Nothing staged off the cluster.

Documented end to end, Casper setup included, so you can reproduce it.


📦 Install

pip install uxarray-mcp                    # core
pip install "uxarray-mcp[hpc]"             # + Globus Compute
pip install "uxarray-mcp[hpc,transfer]"    # + Globus Transfer
pip install "uxarray-mcp[openapi]"         # + REST surface

Register it with your MCP client and open with:

"What can you tell me about this mesh?"

get_capabilities answers with the operations that particular grid supports,
so the model does not spend a call finding out the hard way.

README ·
HPC endpoints ·
CHANGELOG

🔢 On version numbers

Releases are CalVer, <year>.<month>.<patch>, tracking the uxarray release
they are built against. Running uxarray 2026.9 means you want uxarray-mcp
2026.9.x.

⚠️ PEP 440 orders 2026.9.0 above 0.3.1, so a loose uxarray-mcp>=0.3
will move you onto the CalVer line. Pin ~=2026.9.0 to stay put.


🤝 Come break it

The most useful thing you can do is find where it gives you a bad answer.

  • 🐛 Open an issue,
    especially for a tool that returned something confidently wrong. That
    outranks a crash here.
  • 🔧 PRs welcome. New operations, sharper preconditions, better refusal
    messages, more client and agent integrations.
  • 🤖 Using it from an agent framework we have not tried? Tell us what the
    interface got in your way.
  • 💬 Tell us the analysis you wanted to run and could not.

v0.3.1

Choose a tag to compare

@github-actions github-actions released this 05 Sep 08:54

Automated release 0.3.1.

Changes since v0.3.0: 10 commit(s).

v0.3.0

Choose a tag to compare

@github-actions github-actions released this 29 Aug 05:55

Automated release 0.3.0.

Changes since v0.2.2: 27 commit(s).

v0.2.2

Choose a tag to compare

@github-actions github-actions released this 30 Jul 16:03

Automated release 0.2.2.

Changes since v0.2.1: 4 commit(s).

v0.2.1

Choose a tag to compare

@github-actions github-actions released this 29 Jul 07:25

Automated release 0.2.1.

Changes since v0.2.0: 3 commit(s).

v0.2.0

Choose a tag to compare

@github-actions github-actions released this 29 Jul 06:58

Automated release 0.2.0.

Changes since v0.1.3: 5 commit(s).

v0.1.3

Choose a tag to compare

@github-actions github-actions released this 21 Jul 05:27

Automated release 0.1.3.

Changes since v0.1.2: 21 commit(s).

v0.1.2

Choose a tag to compare

@github-actions github-actions released this 05 Jul 07:59

Automated release 0.1.2.

Changes since v0.1.1: 18 commit(s).

v0.1.1

Choose a tag to compare

@github-actions github-actions released this 05 Jun 08:46

Automated release 0.1.1.

Changes since v0.1.0: 9 commit(s).

v0.1.0

Choose a tag to compare

@github-actions github-actions released this 04 Jun 03:11

Automated release 0.1.0.

Initial release.