uxarray-mcp 0.1.2__tar.gz → 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {uxarray_mcp-0.1.2 → uxarray_mcp-0.2.0}/PKG-INFO +75 -8
- {uxarray_mcp-0.1.2 → uxarray_mcp-0.2.0}/README.md +74 -7
- uxarray_mcp-0.2.0/pyproject.toml +108 -0
- uxarray_mcp-0.1.2/pyproject.toml → uxarray_mcp-0.2.0/pyproject.toml.orig +5 -1
- {uxarray_mcp-0.1.2 → uxarray_mcp-0.2.0}/src/uxarray_mcp/__init__.py +1 -1
- {uxarray_mcp-0.1.2 → uxarray_mcp-0.2.0}/src/uxarray_mcp/cli.py +46 -12
- {uxarray_mcp-0.1.2 → uxarray_mcp-0.2.0}/src/uxarray_mcp/domain/area.py +7 -2
- {uxarray_mcp-0.1.2 → uxarray_mcp-0.2.0}/src/uxarray_mcp/domain/mesh.py +8 -3
- uxarray_mcp-0.2.0/src/uxarray_mcp/domain/vector_calc.py +518 -0
- {uxarray_mcp-0.1.2 → uxarray_mcp-0.2.0}/src/uxarray_mcp/provenance.py +17 -0
- {uxarray_mcp-0.1.2 → uxarray_mcp-0.2.0}/src/uxarray_mcp/registry.py +4 -4
- {uxarray_mcp-0.1.2 → uxarray_mcp-0.2.0}/src/uxarray_mcp/remote/agent.py +58 -6
- {uxarray_mcp-0.1.2 → uxarray_mcp-0.2.0}/src/uxarray_mcp/remote/compute_functions.py +628 -32
- {uxarray_mcp-0.1.2 → uxarray_mcp-0.2.0}/src/uxarray_mcp/state.py +30 -13
- {uxarray_mcp-0.1.2 → uxarray_mcp-0.2.0}/src/uxarray_mcp/tools/advanced.py +170 -2
- {uxarray_mcp-0.1.2 → uxarray_mcp-0.2.0}/src/uxarray_mcp/tools/capabilities.py +228 -48
- {uxarray_mcp-0.1.2 → uxarray_mcp-0.2.0}/src/uxarray_mcp/tools/execution_control.py +35 -6
- {uxarray_mcp-0.1.2 → uxarray_mcp-0.2.0}/src/uxarray_mcp/tools/frontdoor.py +58 -3
- {uxarray_mcp-0.1.2 → uxarray_mcp-0.2.0}/src/uxarray_mcp/tools/inspection.py +57 -16
- {uxarray_mcp-0.1.2 → uxarray_mcp-0.2.0}/src/uxarray_mcp/tools/orchestration.py +39 -3
- {uxarray_mcp-0.1.2 → uxarray_mcp-0.2.0}/src/uxarray_mcp/tools/scientific_agent.py +1 -1
- {uxarray_mcp-0.1.2 → uxarray_mcp-0.2.0}/src/uxarray_mcp/tools/vector_calc.py +95 -12
- uxarray_mcp-0.1.2/src/uxarray_mcp/domain/vector_calc.py +0 -270
- {uxarray_mcp-0.1.2 → uxarray_mcp-0.2.0}/LICENSE +0 -0
- {uxarray_mcp-0.1.2 → uxarray_mcp-0.2.0}/src/uxarray_mcp/__main__.py +0 -0
- {uxarray_mcp-0.1.2 → uxarray_mcp-0.2.0}/src/uxarray_mcp/app.py +0 -0
- {uxarray_mcp-0.1.2 → uxarray_mcp-0.2.0}/src/uxarray_mcp/domain/__init__.py +0 -0
- {uxarray_mcp-0.1.2 → uxarray_mcp-0.2.0}/src/uxarray_mcp/domain/plotting.py +0 -0
- {uxarray_mcp-0.1.2 → uxarray_mcp-0.2.0}/src/uxarray_mcp/domain/variable.py +0 -0
- {uxarray_mcp-0.1.2 → uxarray_mcp-0.2.0}/src/uxarray_mcp/domain/zonal.py +0 -0
- {uxarray_mcp-0.1.2 → uxarray_mcp-0.2.0}/src/uxarray_mcp/py.typed +0 -0
- {uxarray_mcp-0.1.2 → uxarray_mcp-0.2.0}/src/uxarray_mcp/remote/__init__.py +0 -0
- {uxarray_mcp-0.1.2 → uxarray_mcp-0.2.0}/src/uxarray_mcp/remote/config.py +0 -0
- {uxarray_mcp-0.1.2 → uxarray_mcp-0.2.0}/src/uxarray_mcp/remote/health.py +0 -0
- {uxarray_mcp-0.1.2 → uxarray_mcp-0.2.0}/src/uxarray_mcp/tools/__init__.py +0 -0
- {uxarray_mcp-0.1.2 → uxarray_mcp-0.2.0}/src/uxarray_mcp/tools/catalog.py +0 -0
- {uxarray_mcp-0.1.2 → uxarray_mcp-0.2.0}/src/uxarray_mcp/tools/plotting.py +0 -0
- {uxarray_mcp-0.1.2 → uxarray_mcp-0.2.0}/src/uxarray_mcp/tools/remote_tools.py +0 -0
- {uxarray_mcp-0.1.2 → uxarray_mcp-0.2.0}/src/uxarray_mcp/tools/stateful.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: uxarray-mcp
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: MCP server for analyzing unstructured meshes with UXarray
|
|
5
5
|
Keywords: uxarray,mcp,unstructured grids,scientific computing,globus compute
|
|
6
6
|
Author: Rajeev Jain, Dayan Abdulla
|
|
@@ -299,6 +299,7 @@ uv tool install --python 3.12 uxarray-mcp
|
|
|
299
299
|
# Or from a fresh clone (developer path)
|
|
300
300
|
git clone https://github.com/UXARRAY/uxarray-mcp-server.git
|
|
301
301
|
cd uxarray-mcp-server && uv sync --python 3.12
|
|
302
|
+
# or: bash SETUP.sh (does the sync + runs the local test suite in one step)
|
|
302
303
|
```
|
|
303
304
|
|
|
304
305
|
> **Why `--python 3.12`?** The server uses Globus Compute to submit work to
|
|
@@ -350,8 +351,9 @@ client's MCP docs.
|
|
|
350
351
|
uxarray-mcp doctor
|
|
351
352
|
```
|
|
352
353
|
|
|
353
|
-
|
|
354
|
-
remote checks
|
|
354
|
+
Prints a JSON diagnostic report. With no endpoints configured it reports a
|
|
355
|
+
passing local setup and skips the remote checks; the process exits `0` when
|
|
356
|
+
`passed` is true.
|
|
355
357
|
|
|
356
358
|
### Step 5 — Ask the AI to do something
|
|
357
359
|
|
|
@@ -361,6 +363,19 @@ In your client, try:
|
|
|
361
363
|
|
|
362
364
|
That's it for local use.
|
|
363
365
|
|
|
366
|
+
**A few more things to try:**
|
|
367
|
+
|
|
368
|
+
- `Use run_analysis with operation="inspect_mesh" and grid_path="healpix:4"` —
|
|
369
|
+
no sample file needed; HEALPix meshes are generated on the fly.
|
|
370
|
+
- `Run a complete scientific analysis on healpix:4` — the autonomous
|
|
371
|
+
Analyze → Plan → Execute → Verify agent (see
|
|
372
|
+
[docs/scientific-agent.md](docs/scientific-agent.md)).
|
|
373
|
+
- `Create a session called baseline-analysis, register <grid> and <data> in
|
|
374
|
+
it, then run the workflow for <variable>` — persisted, resumable
|
|
375
|
+
multi-step runs (see [docs/workflows.md](docs/workflows.md)).
|
|
376
|
+
- `Diagnose my configured endpoint status` — once you've added an endpoint
|
|
377
|
+
below, this is the fastest way to check it's healthy.
|
|
378
|
+
|
|
364
379
|
---
|
|
365
380
|
|
|
366
381
|
## Going beyond your laptop
|
|
@@ -380,21 +395,55 @@ Both paths assume you've finished local install above.
|
|
|
380
395
|
|
|
381
396
|
## What the MCP exposes
|
|
382
397
|
|
|
383
|
-
Intent-shaped tools, not raw UXarray bindings:
|
|
398
|
+
Intent-shaped tools, not raw UXarray bindings — all local by default:
|
|
384
399
|
|
|
385
400
|
- `get_capabilities` — what can I do with this mesh?
|
|
386
401
|
- `analyze_dataset` — deterministic first-look: inspect, validate, area, zonal mean, plots.
|
|
387
402
|
- `run_analysis` — one operation at a time (gradient, curl, subset, remap, …).
|
|
388
403
|
- `plot_dataset` — mesh, geographic, variable, or zonal-mean plots.
|
|
389
|
-
- `diagnose_endpoint`, `probe_path_access` — endpoint health + file readability.
|
|
390
404
|
- `run_workflow`, `resume_workflow`, `get_status`, `get_result`, `manage_session` —
|
|
391
405
|
persisted sessions and multi-step workflows.
|
|
392
406
|
|
|
393
|
-
Tools that can run remotely take `use_remote: bool` and optional `endpoint: str`.
|
|
394
|
-
The dispatcher falls back to local if the endpoint is unhealthy.
|
|
395
|
-
|
|
396
407
|
Full schema: [docs/tools.md](docs/tools.md).
|
|
397
408
|
|
|
409
|
+
**Once you've configured an HPC endpoint** (optional — see
|
|
410
|
+
[Going beyond your laptop](#going-beyond-your-laptop) below): most tools above
|
|
411
|
+
also take `use_remote: bool` and `endpoint: str`, falling back to local if the
|
|
412
|
+
endpoint is unhealthy. Two more tools exist purely for that case:
|
|
413
|
+
`diagnose_endpoint` and `probe_path_access` (endpoint health + file
|
|
414
|
+
readability). Ignore all of this until you actually have an endpoint to point
|
|
415
|
+
at.
|
|
416
|
+
|
|
417
|
+
---
|
|
418
|
+
|
|
419
|
+
## Transparency & correctness safeguards
|
|
420
|
+
|
|
421
|
+
Because agent-driven analysis needs to be *trustworthy*, every result is
|
|
422
|
+
auditable and the server actively flags common scientific pitfalls:
|
|
423
|
+
|
|
424
|
+
- **Provenance on everything.** Each result carries a `_provenance` block:
|
|
425
|
+
the tool that ran, timestamp, input arguments, `execution_venue`
|
|
426
|
+
(`local` or `hpc:<endpoint>`), and the UXarray/Python versions used.
|
|
427
|
+
- **Derivative unit convention is never hidden.** `gradient`, `curl`, and
|
|
428
|
+
`divergence` echo `scale_by_radius` in both the result and provenance, so a
|
|
429
|
+
unit-sphere result can never be mistaken for a physical (per-metre) one.
|
|
430
|
+
Gradient and curl default to physical scaling, matching UXarray; pass
|
|
431
|
+
`scale_by_radius=False` explicitly for unit-sphere output.
|
|
432
|
+
- **Vector-calculus sanity guard.** `curl`/`divergence` warn (without blocking)
|
|
433
|
+
when the two inputs are the same field, or when neither carries a
|
|
434
|
+
velocity/flux-like `units` attribute — the classic "vorticity from two random
|
|
435
|
+
scalars" mistake now surfaces a warning in `_provenance.warnings` and a
|
|
436
|
+
machine-actionable `scientific_status` with stable warning codes.
|
|
437
|
+
- **Applicability is not suitability.** `get_capabilities` reports whether
|
|
438
|
+
vector operations are structurally computable separately from whether
|
|
439
|
+
metadata supports physical interpretation.
|
|
440
|
+
- **Local/remote version drift is surfaced.** Remote results record the
|
|
441
|
+
worker's *actual* UXarray version (`remote_uxarray_version`) and emit a
|
|
442
|
+
warning when it differs from the local version, so silent numerical
|
|
443
|
+
differences between venues can't slip through.
|
|
444
|
+
- **Validation gating.** `analyze_dataset` validates a dataset (NaN/Inf/fill
|
|
445
|
+
checks) before computing statistics like the zonal mean.
|
|
446
|
+
|
|
398
447
|
---
|
|
399
448
|
|
|
400
449
|
## CLI reference
|
|
@@ -410,6 +459,24 @@ Full schema: [docs/tools.md](docs/tools.md).
|
|
|
410
459
|
|
|
411
460
|
---
|
|
412
461
|
|
|
462
|
+
## Upgrading
|
|
463
|
+
|
|
464
|
+
```bash
|
|
465
|
+
uv tool upgrade --python 3.12 uxarray-mcp # or your original install method
|
|
466
|
+
```
|
|
467
|
+
|
|
468
|
+
> **⚠️ Restart your AI client after upgrading.** MCP servers are launched once
|
|
469
|
+
> when your client (Claude Desktop, Claude Code, Cursor, …) starts and are **not
|
|
470
|
+
> hot-reloaded**. After upgrading the package, **fully quit and reopen your AI
|
|
471
|
+
> client** so it relaunches `uxarray-mcp serve` with the new code. Until you do,
|
|
472
|
+
> the running server keeps executing the *old* version — new tools and fixes
|
|
473
|
+
> won't appear, and you may see confusing errors (for example, a `use_remote`
|
|
474
|
+
> call on an HPC-only path failing with "file not found" because the old,
|
|
475
|
+
> local-only tool is still loaded). If in doubt, run `uxarray-mcp doctor` and
|
|
476
|
+
> check the reported version.
|
|
477
|
+
|
|
478
|
+
---
|
|
479
|
+
|
|
413
480
|
## Risks (read before relying on output)
|
|
414
481
|
|
|
415
482
|
AI agents can misread prompts, pick the wrong file, get units wrong (e.g.,
|
|
@@ -63,6 +63,7 @@ uv tool install --python 3.12 uxarray-mcp
|
|
|
63
63
|
# Or from a fresh clone (developer path)
|
|
64
64
|
git clone https://github.com/UXARRAY/uxarray-mcp-server.git
|
|
65
65
|
cd uxarray-mcp-server && uv sync --python 3.12
|
|
66
|
+
# or: bash SETUP.sh (does the sync + runs the local test suite in one step)
|
|
66
67
|
```
|
|
67
68
|
|
|
68
69
|
> **Why `--python 3.12`?** The server uses Globus Compute to submit work to
|
|
@@ -114,8 +115,9 @@ client's MCP docs.
|
|
|
114
115
|
uxarray-mcp doctor
|
|
115
116
|
```
|
|
116
117
|
|
|
117
|
-
|
|
118
|
-
remote checks
|
|
118
|
+
Prints a JSON diagnostic report. With no endpoints configured it reports a
|
|
119
|
+
passing local setup and skips the remote checks; the process exits `0` when
|
|
120
|
+
`passed` is true.
|
|
119
121
|
|
|
120
122
|
### Step 5 — Ask the AI to do something
|
|
121
123
|
|
|
@@ -125,6 +127,19 @@ In your client, try:
|
|
|
125
127
|
|
|
126
128
|
That's it for local use.
|
|
127
129
|
|
|
130
|
+
**A few more things to try:**
|
|
131
|
+
|
|
132
|
+
- `Use run_analysis with operation="inspect_mesh" and grid_path="healpix:4"` —
|
|
133
|
+
no sample file needed; HEALPix meshes are generated on the fly.
|
|
134
|
+
- `Run a complete scientific analysis on healpix:4` — the autonomous
|
|
135
|
+
Analyze → Plan → Execute → Verify agent (see
|
|
136
|
+
[docs/scientific-agent.md](docs/scientific-agent.md)).
|
|
137
|
+
- `Create a session called baseline-analysis, register <grid> and <data> in
|
|
138
|
+
it, then run the workflow for <variable>` — persisted, resumable
|
|
139
|
+
multi-step runs (see [docs/workflows.md](docs/workflows.md)).
|
|
140
|
+
- `Diagnose my configured endpoint status` — once you've added an endpoint
|
|
141
|
+
below, this is the fastest way to check it's healthy.
|
|
142
|
+
|
|
128
143
|
---
|
|
129
144
|
|
|
130
145
|
## Going beyond your laptop
|
|
@@ -144,21 +159,55 @@ Both paths assume you've finished local install above.
|
|
|
144
159
|
|
|
145
160
|
## What the MCP exposes
|
|
146
161
|
|
|
147
|
-
Intent-shaped tools, not raw UXarray bindings:
|
|
162
|
+
Intent-shaped tools, not raw UXarray bindings — all local by default:
|
|
148
163
|
|
|
149
164
|
- `get_capabilities` — what can I do with this mesh?
|
|
150
165
|
- `analyze_dataset` — deterministic first-look: inspect, validate, area, zonal mean, plots.
|
|
151
166
|
- `run_analysis` — one operation at a time (gradient, curl, subset, remap, …).
|
|
152
167
|
- `plot_dataset` — mesh, geographic, variable, or zonal-mean plots.
|
|
153
|
-
- `diagnose_endpoint`, `probe_path_access` — endpoint health + file readability.
|
|
154
168
|
- `run_workflow`, `resume_workflow`, `get_status`, `get_result`, `manage_session` —
|
|
155
169
|
persisted sessions and multi-step workflows.
|
|
156
170
|
|
|
157
|
-
Tools that can run remotely take `use_remote: bool` and optional `endpoint: str`.
|
|
158
|
-
The dispatcher falls back to local if the endpoint is unhealthy.
|
|
159
|
-
|
|
160
171
|
Full schema: [docs/tools.md](docs/tools.md).
|
|
161
172
|
|
|
173
|
+
**Once you've configured an HPC endpoint** (optional — see
|
|
174
|
+
[Going beyond your laptop](#going-beyond-your-laptop) below): most tools above
|
|
175
|
+
also take `use_remote: bool` and `endpoint: str`, falling back to local if the
|
|
176
|
+
endpoint is unhealthy. Two more tools exist purely for that case:
|
|
177
|
+
`diagnose_endpoint` and `probe_path_access` (endpoint health + file
|
|
178
|
+
readability). Ignore all of this until you actually have an endpoint to point
|
|
179
|
+
at.
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
## Transparency & correctness safeguards
|
|
184
|
+
|
|
185
|
+
Because agent-driven analysis needs to be *trustworthy*, every result is
|
|
186
|
+
auditable and the server actively flags common scientific pitfalls:
|
|
187
|
+
|
|
188
|
+
- **Provenance on everything.** Each result carries a `_provenance` block:
|
|
189
|
+
the tool that ran, timestamp, input arguments, `execution_venue`
|
|
190
|
+
(`local` or `hpc:<endpoint>`), and the UXarray/Python versions used.
|
|
191
|
+
- **Derivative unit convention is never hidden.** `gradient`, `curl`, and
|
|
192
|
+
`divergence` echo `scale_by_radius` in both the result and provenance, so a
|
|
193
|
+
unit-sphere result can never be mistaken for a physical (per-metre) one.
|
|
194
|
+
Gradient and curl default to physical scaling, matching UXarray; pass
|
|
195
|
+
`scale_by_radius=False` explicitly for unit-sphere output.
|
|
196
|
+
- **Vector-calculus sanity guard.** `curl`/`divergence` warn (without blocking)
|
|
197
|
+
when the two inputs are the same field, or when neither carries a
|
|
198
|
+
velocity/flux-like `units` attribute — the classic "vorticity from two random
|
|
199
|
+
scalars" mistake now surfaces a warning in `_provenance.warnings` and a
|
|
200
|
+
machine-actionable `scientific_status` with stable warning codes.
|
|
201
|
+
- **Applicability is not suitability.** `get_capabilities` reports whether
|
|
202
|
+
vector operations are structurally computable separately from whether
|
|
203
|
+
metadata supports physical interpretation.
|
|
204
|
+
- **Local/remote version drift is surfaced.** Remote results record the
|
|
205
|
+
worker's *actual* UXarray version (`remote_uxarray_version`) and emit a
|
|
206
|
+
warning when it differs from the local version, so silent numerical
|
|
207
|
+
differences between venues can't slip through.
|
|
208
|
+
- **Validation gating.** `analyze_dataset` validates a dataset (NaN/Inf/fill
|
|
209
|
+
checks) before computing statistics like the zonal mean.
|
|
210
|
+
|
|
162
211
|
---
|
|
163
212
|
|
|
164
213
|
## CLI reference
|
|
@@ -174,6 +223,24 @@ Full schema: [docs/tools.md](docs/tools.md).
|
|
|
174
223
|
|
|
175
224
|
---
|
|
176
225
|
|
|
226
|
+
## Upgrading
|
|
227
|
+
|
|
228
|
+
```bash
|
|
229
|
+
uv tool upgrade --python 3.12 uxarray-mcp # or your original install method
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
> **⚠️ Restart your AI client after upgrading.** MCP servers are launched once
|
|
233
|
+
> when your client (Claude Desktop, Claude Code, Cursor, …) starts and are **not
|
|
234
|
+
> hot-reloaded**. After upgrading the package, **fully quit and reopen your AI
|
|
235
|
+
> client** so it relaunches `uxarray-mcp serve` with the new code. Until you do,
|
|
236
|
+
> the running server keeps executing the *old* version — new tools and fixes
|
|
237
|
+
> won't appear, and you may see confusing errors (for example, a `use_remote`
|
|
238
|
+
> call on an HPC-only path failing with "file not found" because the old,
|
|
239
|
+
> local-only tool is still loaded). If in doubt, run `uxarray-mcp doctor` and
|
|
240
|
+
> check the reported version.
|
|
241
|
+
|
|
242
|
+
---
|
|
243
|
+
|
|
177
244
|
## Risks (read before relying on output)
|
|
178
245
|
|
|
179
246
|
AI agents can misread prompts, pick the wrong file, get units wrong (e.g.,
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "uxarray-mcp"
|
|
3
|
+
version = "0.2.0"
|
|
4
|
+
description = "MCP server for analyzing unstructured meshes with UXarray"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
keywords = [
|
|
7
|
+
"uxarray",
|
|
8
|
+
"mcp",
|
|
9
|
+
"unstructured grids",
|
|
10
|
+
"scientific computing",
|
|
11
|
+
"globus compute",
|
|
12
|
+
]
|
|
13
|
+
classifiers = [
|
|
14
|
+
"Operating System :: OS Independent",
|
|
15
|
+
"Intended Audience :: Science/Research",
|
|
16
|
+
"Topic :: Scientific/Engineering",
|
|
17
|
+
"Programming Language :: Python",
|
|
18
|
+
"Programming Language :: Python :: 3",
|
|
19
|
+
"Programming Language :: Python :: 3.12",
|
|
20
|
+
"License :: OSI Approved :: Apache Software License",
|
|
21
|
+
]
|
|
22
|
+
requires-python = ">=3.12,<3.13"
|
|
23
|
+
dependencies = [
|
|
24
|
+
"toolregistry-server[mcp]>=0.4.0",
|
|
25
|
+
"holoviews>=1.19.0",
|
|
26
|
+
"matplotlib>=3.9.0",
|
|
27
|
+
"pyyaml>=6.0",
|
|
28
|
+
"uxarray>=2026.6.0",
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
[[project.authors]]
|
|
32
|
+
name = "Rajeev Jain"
|
|
33
|
+
email = "rajeeja@gmail.com"
|
|
34
|
+
|
|
35
|
+
[[project.authors]]
|
|
36
|
+
name = "Dayan Abdulla"
|
|
37
|
+
email = "dayanabdulla000@gmail.com"
|
|
38
|
+
|
|
39
|
+
[project.license]
|
|
40
|
+
file = "LICENSE"
|
|
41
|
+
|
|
42
|
+
[project.optional-dependencies]
|
|
43
|
+
openapi = ["toolregistry-server[openapi]>=0.4.0"]
|
|
44
|
+
hpc = [
|
|
45
|
+
"academy-py>=0.3.1",
|
|
46
|
+
"globus-compute-sdk>=4.5.0",
|
|
47
|
+
]
|
|
48
|
+
docs = [
|
|
49
|
+
"sphinx>=7.0",
|
|
50
|
+
"sphinx-book-theme>=1.1.0",
|
|
51
|
+
"myst-parser>=3.0",
|
|
52
|
+
]
|
|
53
|
+
|
|
54
|
+
[project.scripts]
|
|
55
|
+
uxarray-mcp = "uxarray_mcp.cli:main"
|
|
56
|
+
|
|
57
|
+
[project.urls]
|
|
58
|
+
Documentation = "https://uxarray-mcp-server.readthedocs.io"
|
|
59
|
+
Source = "https://github.com/UXARRAY/uxarray-mcp-server"
|
|
60
|
+
Tracker = "https://github.com/UXARRAY/uxarray-mcp-server/issues"
|
|
61
|
+
|
|
62
|
+
[dependency-groups]
|
|
63
|
+
dev = [
|
|
64
|
+
"mypy>=1.10.0",
|
|
65
|
+
"pre-commit>=4.3.0",
|
|
66
|
+
"pytest>=9.0.2",
|
|
67
|
+
"pytest-asyncio>=1.0.0",
|
|
68
|
+
"ruff>=0.15.2",
|
|
69
|
+
]
|
|
70
|
+
|
|
71
|
+
[tool.mypy]
|
|
72
|
+
python_version = "3.12"
|
|
73
|
+
check_untyped_defs = true
|
|
74
|
+
ignore_missing_imports = true
|
|
75
|
+
warn_unused_ignores = true
|
|
76
|
+
warn_return_any = false
|
|
77
|
+
exclude = [
|
|
78
|
+
"tests/",
|
|
79
|
+
"docs/",
|
|
80
|
+
]
|
|
81
|
+
|
|
82
|
+
[[tool.mypy.overrides]]
|
|
83
|
+
module = "yaml"
|
|
84
|
+
ignore_missing_imports = true
|
|
85
|
+
|
|
86
|
+
[[tool.mypy.overrides]]
|
|
87
|
+
module = "uxarray_mcp.remote.agent"
|
|
88
|
+
warn_unused_ignores = false
|
|
89
|
+
|
|
90
|
+
[tool.ruff]
|
|
91
|
+
extend-exclude = ["docs/architecture.html"]
|
|
92
|
+
|
|
93
|
+
[tool.ruff.lint]
|
|
94
|
+
extend-select = ["I"]
|
|
95
|
+
|
|
96
|
+
[tool.ruff.lint.per-file-ignores]
|
|
97
|
+
"notebooks/*.py" = ["E402"]
|
|
98
|
+
"notebooks/*.ipynb" = ["E402"]
|
|
99
|
+
|
|
100
|
+
[tool.ruff.lint.isort]
|
|
101
|
+
known-first-party = ["uxarray_mcp"]
|
|
102
|
+
|
|
103
|
+
[tool.ruff.format]
|
|
104
|
+
docstring-code-format = true
|
|
105
|
+
|
|
106
|
+
[build-system]
|
|
107
|
+
requires = ["uv_build>=0.9.26,<0.10.0"]
|
|
108
|
+
build-backend = "uv_build"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "uxarray-mcp"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.2.0"
|
|
4
4
|
description = "MCP server for analyzing unstructured meshes with UXarray"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
keywords = ["uxarray", "mcp", "unstructured grids", "scientific computing", "globus compute"]
|
|
@@ -93,6 +93,10 @@ extend-exclude = ["docs/architecture.html"]
|
|
|
93
93
|
|
|
94
94
|
[tool.ruff.lint]
|
|
95
95
|
extend-select = ["I"]
|
|
96
|
+
# Notebooks (and their jupytext-paired .py scripts) intentionally scatter
|
|
97
|
+
# imports across per-cell (# %%) blocks, so E402 (module-import-not-at-top)
|
|
98
|
+
# is expected style there, not a lint violation.
|
|
99
|
+
per-file-ignores = { "notebooks/*.py" = ["E402"], "notebooks/*.ipynb" = ["E402"] }
|
|
96
100
|
|
|
97
101
|
[tool.ruff.lint.isort]
|
|
98
102
|
known-first-party = ["uxarray_mcp"]
|
|
@@ -308,8 +308,8 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
308
308
|
choices=("core", "deferred-full"),
|
|
309
309
|
default="core",
|
|
310
310
|
help=(
|
|
311
|
-
"core: gateway + control + list_datasets + prompts (~
|
|
312
|
-
"deferred-full: also load
|
|
311
|
+
"core: gateway + control + list_datasets + prompts (~31 tools). "
|
|
312
|
+
"deferred-full: also load 32 raw tools as deferred, gated "
|
|
313
313
|
"behind discover_tools / admin promotion."
|
|
314
314
|
),
|
|
315
315
|
)
|
|
@@ -317,7 +317,7 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
317
317
|
"--transport",
|
|
318
318
|
choices=("stdio", "sse", "http"),
|
|
319
319
|
default="stdio",
|
|
320
|
-
help="MCP transport. stdio for
|
|
320
|
+
help="MCP transport. stdio for subprocess use by MCP clients.",
|
|
321
321
|
)
|
|
322
322
|
serve.add_argument("--host", default="127.0.0.1", help="Bind host for SSE/HTTP.")
|
|
323
323
|
serve.add_argument("--port", type=int, default=8001, help="Port for SSE/HTTP.")
|
|
@@ -330,8 +330,8 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
330
330
|
choices=("core", "deferred-full"),
|
|
331
331
|
default="core",
|
|
332
332
|
help=(
|
|
333
|
-
"core: gateway + control + list_datasets + prompts (~
|
|
334
|
-
"deferred-full: also load
|
|
333
|
+
"core: gateway + control + list_datasets + prompts (~31 tools). "
|
|
334
|
+
"deferred-full: also load 32 raw tools as deferred, gated "
|
|
335
335
|
"behind discover_tools / admin promotion."
|
|
336
336
|
),
|
|
337
337
|
)
|
|
@@ -347,7 +347,11 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
347
347
|
"--execution-mode",
|
|
348
348
|
default="auto",
|
|
349
349
|
choices=["local", "auto", "hpc"],
|
|
350
|
-
help=
|
|
350
|
+
help=(
|
|
351
|
+
"Default execution mode (default: auto). 'auto' runs local-only "
|
|
352
|
+
"until you add an endpoint with `endpoints add`, then uses it "
|
|
353
|
+
"automatically. Most users never need to change this."
|
|
354
|
+
),
|
|
351
355
|
)
|
|
352
356
|
setup.add_argument("--force", action="store_true", help="Overwrite existing file.")
|
|
353
357
|
setup.set_defaults(func=cmd_setup)
|
|
@@ -377,12 +381,42 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
377
381
|
ep_remove.add_argument("name")
|
|
378
382
|
ep_remove.set_defaults(func=cmd_endpoints_remove)
|
|
379
383
|
|
|
380
|
-
doctor = sub.add_parser(
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
384
|
+
doctor = sub.add_parser(
|
|
385
|
+
"doctor",
|
|
386
|
+
help="Check that the install is healthy (local-only or HPC).",
|
|
387
|
+
description=(
|
|
388
|
+
"Run local-only by default; add an HPC endpoint (`endpoints add`) "
|
|
389
|
+
"to also validate remote readiness. With no endpoint configured, "
|
|
390
|
+
"this reports a passing local-only result — HPC is opt-in."
|
|
391
|
+
),
|
|
392
|
+
)
|
|
393
|
+
doctor.add_argument(
|
|
394
|
+
"--sample-path",
|
|
395
|
+
action="append",
|
|
396
|
+
default=[],
|
|
397
|
+
help="Remote file path to test-read (repeatable). Requires an endpoint.",
|
|
398
|
+
)
|
|
399
|
+
doctor.add_argument(
|
|
400
|
+
"--timeout-seconds",
|
|
401
|
+
type=int,
|
|
402
|
+
default=180,
|
|
403
|
+
help="Seconds to wait for the remote worker probe (ignored if no endpoint).",
|
|
404
|
+
)
|
|
405
|
+
doctor.add_argument(
|
|
406
|
+
"--endpoint",
|
|
407
|
+
default=None,
|
|
408
|
+
help="Named endpoint to validate. Omit to check the default/local-only setup.",
|
|
409
|
+
)
|
|
410
|
+
doctor.add_argument(
|
|
411
|
+
"--skip-remote-probe",
|
|
412
|
+
action="store_true",
|
|
413
|
+
help="Skip submitting a test job to the endpoint (config/auth checks only).",
|
|
414
|
+
)
|
|
415
|
+
doctor.add_argument(
|
|
416
|
+
"--no-netcdf",
|
|
417
|
+
action="store_true",
|
|
418
|
+
help="Skip opening --sample-path as NetCDF; just check readability.",
|
|
419
|
+
)
|
|
386
420
|
doctor.set_defaults(func=cmd_doctor)
|
|
387
421
|
|
|
388
422
|
claude = sub.add_parser(
|
|
@@ -14,11 +14,16 @@ def compute_area_stats(grid: Any) -> dict:
|
|
|
14
14
|
Returns
|
|
15
15
|
-------
|
|
16
16
|
dict
|
|
17
|
-
Keys: total_area, mean_area, min_area, max_area, area_units, n_face
|
|
17
|
+
Keys: total_area, mean_area, min_area, max_area, area_units, n_face.
|
|
18
|
+
``area_units`` is ``None`` when the grid carries no ``units``
|
|
19
|
+
attribute at all -- reporting a fabricated ``"m^2"`` default in
|
|
20
|
+
that case would silently invent metadata the source file never
|
|
21
|
+
provided, which is the exact failure mode this server's
|
|
22
|
+
provenance and guardrail mechanisms exist to prevent.
|
|
18
23
|
"""
|
|
19
24
|
face_areas = grid.face_areas
|
|
20
25
|
|
|
21
|
-
area_units =
|
|
26
|
+
area_units = None
|
|
22
27
|
if hasattr(face_areas, "attrs") and "units" in face_areas.attrs:
|
|
23
28
|
area_units = face_areas.attrs["units"]
|
|
24
29
|
|
|
@@ -47,17 +47,22 @@ def load_dataset(grid_path: str, data_path: str) -> Any:
|
|
|
47
47
|
Loaded dataset object.
|
|
48
48
|
"""
|
|
49
49
|
import uxarray as ux
|
|
50
|
+
import xarray as xr
|
|
50
51
|
|
|
51
|
-
# HEALPix
|
|
52
|
+
# HEALPix and GIS grids don't round-trip through ux.open_dataset() as a
|
|
53
|
+
# "grid file" the way a real UGRID/MPAS/SCRIP file does: grid.to_xarray()
|
|
54
|
+
# returns a minimal representation (e.g. HEALPix has no node coordinates)
|
|
55
|
+
# that the generic UGRID reader rejects. Attach the data directly to the
|
|
56
|
+
# already-loaded Grid object instead.
|
|
52
57
|
if grid_path.lower().startswith("healpix"):
|
|
53
58
|
parts = grid_path.split(":")
|
|
54
59
|
zoom = int(parts[1]) if len(parts) > 1 else 1
|
|
55
60
|
grid = ux.Grid.from_healpix(zoom=zoom)
|
|
56
|
-
return ux.
|
|
61
|
+
return ux.UxDataset(xr.open_dataset(data_path), uxgrid=grid)
|
|
57
62
|
|
|
58
63
|
ext = os.path.splitext(grid_path.lower())[1]
|
|
59
64
|
if ext in [".shp", ".geojson"]:
|
|
60
65
|
grid = ux.Grid.from_file(grid_path, backend="geopandas")
|
|
61
|
-
return ux.
|
|
66
|
+
return ux.UxDataset(xr.open_dataset(data_path), uxgrid=grid)
|
|
62
67
|
|
|
63
68
|
return ux.open_dataset(grid_path, data_path)
|