Skip to content

Commit ce3e758

Browse files
authored
Fix release docs and Chrysalis YAC smoke (#55)
1 parent 2e47bc2 commit ce3e758

11 files changed

Lines changed: 426 additions & 223 deletions

File tree

CHANGELOG.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,9 @@ under their entry.
2323
`endpoints add/list/remove`, `doctor`, `install-claude`.
2424
- Multi-endpoint config schema (`hpc.endpoints.<name>` with
2525
`endpoint_id`, `path_prefixes`, `timeout_seconds`) and config
26-
discovery order: `$UXARRAY_MCP_CONFIG``~/.config/uxarray-mcp/config.yaml`
27-
`./config.yaml`.
26+
discovery order: `$UXARRAY_MCP_CONFIG``./config.yaml` in the current
27+
working directory → `~/.config/uxarray-mcp/config.yaml` → editable-install
28+
repo fallback.
2829
- YAC remote build script (`scripts/hpc_build_yac.py`) and runtime
2930
fallback that loads `yac.core` via `importlib.machinery` when the
3031
upstream `__init__.py` is unconditional.

README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,10 @@ The ``uxarray-mcp`` CLI exposes:
8282
| ``install-claude`` | print or merge the Claude Desktop ``mcpServers`` block |
8383

8484
Config is discovered in this order: ``$UXARRAY_MCP_CONFIG``
85-
``~/.config/uxarray-mcp/config.yaml````./config.yaml`` (repo root).
85+
``./config.yaml`` in the current working directory →
86+
``~/.config/uxarray-mcp/config.yaml`` → the editable-install repo config
87+
fallback. The project-local file wins inside a checkout so development
88+
endpoints are not shadowed by an empty user config.
8689

8790
## Most Users Should Read These in Order
8891

conda/recipe/meta.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ requirements:
2323
- pip
2424
- uv-build >=0.9.26,<0.10.0
2525
run:
26+
# Core package only for the initial feedstock. Add optional HPC support as
27+
# a second output/variant after globus-compute-sdk and academy-py solver
28+
# behavior is validated on conda-forge.
2629
- python >={{ python_min }}
2730
- fastmcp >=3.4.0
2831
- holoviews >=1.19.0

docs/architecture.html

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@
100100
<div class="logo-box"></div>
101101
<div class="hdr-text">
102102
<h1>UXarray MCP Server — Architecture Diagram</h1>
103-
<p>Mesh-aware assistant · provenance on every output · local or Argonne Improv HPC via Globus Compute · dynamic tool registration</p>
103+
<p>Mesh-aware assistant · provenance on every output · local or named HPC endpoints via Globus Compute · unified tool surface</p>
104104
</div>
105105
<div class="branch">
106106
<svg width="12" height="12" viewBox="0 0 16 16" fill="#58a6ff"><path d="M11.75 2.5a.75.75 0 1 0 0 1.5.75.75 0 0 0 0-1.5zm-2.25.75a2.25 2.25 0 1 1 3 2.122V6A2.5 2.5 0 0 1 10 8.5H6a1 1 0 0 0-1 1v1.128a2.251 2.251 0 1 1-1.5 0V5.372a2.25 2.25 0 1 1 1.5 0v1.836A2.492 2.492 0 0 1 6 7h4a1 1 0 0 0 1-1v-.628A2.25 2.25 0 0 1 9.5 3.25z"/></svg>
@@ -205,7 +205,7 @@ <h1>UXarray MCP Server — Architecture Diagram</h1>
205205
<text x="540" y="96" text-anchor="middle" font-size="15" font-weight="700" fill="#58a6ff" font-family="Inter,sans-serif">Receive tool call</text>
206206
<line x1="400" y1="103" x2="682" y2="103" stroke="#112238" stroke-width="1"/>
207207
<text x="540" y="121" text-anchor="middle" font-size="13" fill="#8b949e" font-family="Inter,sans-serif">run_scientific_agent(path)</text>
208-
<text x="540" y="139" text-anchor="middle" font-size="11.5" fill="#484f58" font-family="Inter,sans-serif">HPC tools only if endpoint configured</text>
208+
<text x="540" y="139" text-anchor="middle" font-size="11.5" fill="#484f58" font-family="Inter,sans-serif">Remote execution selected per call</text>
209209

210210
<!-- Agent: Begin analysis -->
211211
<rect x="747" y="76" width="306" height="74" rx="8" fill="#0d0a20" stroke="#6e40c9" stroke-width="2"/>
@@ -261,13 +261,13 @@ <h1>UXarray MCP Server — Architecture Diagram</h1>
261261
<text x="1260" y="383" text-anchor="middle" font-size="11.5" fill="#8b949e" font-family="Inter,sans-serif">MPAS · UGRID · SCRIP · HEALPix</text>
262262
<text x="1260" y="398" text-anchor="middle" font-size="11" fill="#2ea043" font-family="Inter,sans-serif" font-style="italic">→ n_face · n_node · n_edge · format</text>
263263

264-
<!-- HPC: inspect_mesh_hpc() -->
264+
<!-- HPC: inspect_mesh(..., use_remote=True) -->
265265
<rect x="1467" y="320" width="306" height="86" rx="8" fill="#180e00" stroke="#d29922" stroke-width="2"/>
266-
<text x="1620" y="342" text-anchor="middle" font-size="15" font-weight="700" fill="#e3b341" font-family="Inter,sans-serif">inspect_mesh_hpc( )</text>
266+
<text x="1620" y="342" text-anchor="middle" font-size="15" font-weight="700" fill="#e3b341" font-family="Inter,sans-serif">inspect_mesh(use_remote)</text>
267267
<line x1="1480" y1="350" x2="1762" y2="350" stroke="#201400" stroke-width="1"/>
268268
<text x="1620" y="362" text-anchor="middle" font-size="11.5" fill="#39d0d0" font-family="Inter,sans-serif" font-style="italic">✓ _endpoint_is_ready( ) pre-flight</text>
269269
<text x="1620" y="377" text-anchor="middle" font-size="13" fill="#8b949e" font-family="Inter,sans-serif">remote/compute_functions.py</text>
270-
<text x="1620" y="392" text-anchor="middle" font-size="11.5" fill="#8b949e" font-family="Inter,sans-serif">Globus Executor → Improv cluster</text>
270+
<text x="1620" y="392" text-anchor="middle" font-size="11.5" fill="#8b949e" font-family="Inter,sans-serif">Globus Executor → named endpoint</text>
271271
<text x="1620" y="406" text-anchor="middle" font-size="11" fill="#d29922" font-family="Inter,sans-serif" font-style="italic">File stays on HPC · timeout 300 s</text>
272272

273273
<!-- validate_dataset box -->
@@ -348,13 +348,13 @@ <h1>UXarray MCP Server — Architecture Diagram</h1>
348348
<text x="1260" y="772" text-anchor="middle" font-size="11.5" fill="#2ea043" font-family="Inter,sans-serif" font-style="italic">Runs entirely on local machine</text>
349349
<text x="1260" y="787" text-anchor="middle" font-size="11" fill="#484f58" font-family="Inter,sans-serif">_provenance: venue=local</text>
350350

351-
<!-- HPC: calc_area_hpc + zonal_mean_hpc -->
351+
<!-- HPC: calculate_area/calculate_zonal_mean with use_remote=True -->
352352
<rect x="1467" y="688" width="306" height="108" rx="8" fill="#180e00" stroke="#d29922" stroke-width="2"/>
353353
<text x="1620" y="710" text-anchor="middle" font-size="15" font-weight="700" fill="#e3b341" font-family="Inter,sans-serif">HPC Execution</text>
354354
<line x1="1480" y1="718" x2="1762" y2="718" stroke="#201400" stroke-width="1"/>
355355
<text x="1620" y="733" text-anchor="middle" font-size="11.5" fill="#39d0d0" font-family="Inter,sans-serif" font-style="italic">✓ _endpoint_is_ready( ) pre-flight</text>
356-
<text x="1620" y="749" text-anchor="middle" font-size="13" fill="#8b949e" font-family="Inter,sans-serif">calculate_area_hpc( )</text>
357-
<text x="1620" y="765" text-anchor="middle" font-size="13" fill="#8b949e" font-family="Inter,sans-serif">calculate_zonal_mean_hpc( )</text>
356+
<text x="1620" y="749" text-anchor="middle" font-size="13" fill="#8b949e" font-family="Inter,sans-serif">calculate_area(use_remote)</text>
357+
<text x="1620" y="765" text-anchor="middle" font-size="13" fill="#8b949e" font-family="Inter,sans-serif">calculate_zonal_mean(use_remote)</text>
358358
<line x1="1480" y1="776" x2="1762" y2="776" stroke="#3d1400" stroke-width="1" stroke-dasharray="4,3"/>
359359
<text x="1620" y="790" text-anchor="middle" font-size="12" fill="#f85149" font-family="Inter,sans-serif" font-style="italic">⚠ auto-fallback if endpoint unreachable</text>
360360

@@ -446,15 +446,15 @@ <h1>UXarray MCP Server — Architecture Diagram</h1>
446446
Flow runs <strong>top → bottom</strong>, arrows crossing lanes are handoffs.
447447
<strong style="color:#e3b341">Orange diamonds</strong> = routing decisions made automatically by the agent.
448448
<strong style="color:#56d364">Green boxes</strong> = compute runs on your local machine.
449-
<strong style="color:#e3b341">Orange boxes</strong> = dispatched to Argonne Improv via Globus Compute — the file never leaves the cluster.
450-
<strong style="color:#39d0d0">Teal dashed boxes</strong> = steps that only activate under certain conditions (data_path provided, endpoint configured).
449+
<strong style="color:#e3b341">Orange boxes</strong> = dispatched to a named HPC endpoint via Globus Compute — the file never leaves the cluster.
450+
<strong style="color:#39d0d0">Teal dashed boxes</strong> = steps that only activate under certain conditions (data_path provided, remote mode requested).
451451
<strong style="color:#f85149">Red dashed</strong> inside HPC boxes = automatic local fallback when the endpoint is unreachable.
452-
<strong>Dynamic registration:</strong> HPC tools (rows 10–13) only appear in Claude's tool list when <code>endpoint_id</code> is set in <code>config.yaml</code>.
452+
<strong>Unified registration:</strong> tools are registered once; remote execution is selected with <code>use_remote=True</code> and optional endpoint names.
453453
</div>
454454

455455
<!-- Tool table -->
456456
<div class="card">
457-
<div class="card-h">All MCP Tools &nbsp;<span style="font-weight:400;color:#6e7681;font-size:12px">9 always registered · 4 conditional on endpoint_id</span></div>
457+
<div class="card-h">Representative MCP Tools &nbsp;<span style="font-weight:400;color:#6e7681;font-size:12px">unified local / remote surface</span></div>
458458
<table>
459459
<thead>
460460
<tr><th>#</th><th>Tool Name</th><th>Source File</th><th>Runs On</th><th>Domain Module</th><th>What It Does</th></tr>
@@ -469,14 +469,14 @@ <h1>UXarray MCP Server — Architecture Diagram</h1>
469469
<tr><td>7</td><td><code>get_execution_mode</code></td><td><code>execution_control.py</code></td><td><span class="tag tl">LOCAL</span></td><td></td><td>Returns current execution mode (local / hpc / auto) and whether an HPC endpoint is configured.</td></tr>
470470
<tr><td>8</td><td><code>set_execution_mode</code></td><td><code>execution_control.py</code></td><td><span class="tag tl">LOCAL</span></td><td></td><td>Switch execution mode from the Claude UI without editing config.yaml directly.</td></tr>
471471
<tr><td>9</td><td><code>run_scientific_agent</code></td><td><code>scientific_agent.py</code></td><td><span class="tag ta">AUTO</span></td><td>All 4 modules</td><td>Autonomous 4-stage pipeline: Analyze → Plan → Execute → Verify. Validation-gated. Returns full reasoning trace + provenance + artifacts.</td></tr>
472-
<tr><td>10</td><td><code>inspect_mesh_hpc</code></td><td><code>remote_tools.py</code></td><td><span class="tag th">HPC*</span></td><td><code>mesh.py</code> on HPC</td><td>Mesh inspection via Globus Compute on Improv. Pre-flight health check. Auto-fallback to local.</td></tr>
473-
<tr><td>11</td><td><code>calculate_area_hpc</code></td><td><code>remote_tools.py</code></td><td><span class="tag th">HPC*</span></td><td><code>area.py</code> on HPC</td><td>Face area calculation via Globus Compute. Pre-flight health check. Auto-fallback to local.</td></tr>
474-
<tr><td>12</td><td><code>inspect_variable_hpc</code></td><td><code>remote_tools.py</code></td><td><span class="tag th">HPC*</span></td><td><code>variable.py</code> on HPC</td><td>Variable inspection via Globus Compute. Pre-flight health check. Auto-fallback to local.</td></tr>
475-
<tr><td>13</td><td><code>calculate_zonal_mean_hpc</code></td><td><code>remote_tools.py</code></td><td><span class="tag th">HPC*</span></td><td><code>zonal.py</code> on HPC</td><td>Zonal mean via Globus Compute. Pre-flight health check. Auto-fallback to local.</td></tr>
472+
<tr><td>10</td><td><code>inspect_mesh</code></td><td><code>remote_tools.py</code></td><td><span class="tag th">HPC*</span></td><td><code>mesh.py</code> on HPC</td><td>Mesh inspection with <code>use_remote=True</code>. Pre-flight health check. Auto-fallback to local.</td></tr>
473+
<tr><td>11</td><td><code>calculate_area</code></td><td><code>remote_tools.py</code></td><td><span class="tag th">HPC*</span></td><td><code>area.py</code> on HPC</td><td>Face area calculation with <code>use_remote=True</code>. Pre-flight health check. Auto-fallback to local.</td></tr>
474+
<tr><td>12</td><td><code>inspect_variable</code></td><td><code>remote_tools.py</code></td><td><span class="tag th">HPC*</span></td><td><code>variable.py</code> on HPC</td><td>Variable inspection with <code>use_remote=True</code>. Pre-flight health check. Auto-fallback to local.</td></tr>
475+
<tr><td>13</td><td><code>calculate_zonal_mean</code></td><td><code>remote_tools.py</code></td><td><span class="tag th">HPC*</span></td><td><code>zonal.py</code> on HPC</td><td>Zonal mean with <code>use_remote=True</code>. Pre-flight health check. Auto-fallback to local.</td></tr>
476476
</tbody>
477477
</table>
478478
<div style="padding:10px 15px;font-size:11.5px;color:#6e7681;border-top:1px solid #21262d;">
479-
* HPC tools are only registered when <code>endpoint_id</code> is set in <code>config.yaml</code>. Without an endpoint, Claude's tool list shows 9 tools.
479+
* Remote execution is selected per call with <code>use_remote=True</code>. Endpoint readiness controls remote dispatch and fallback.
480480
</div>
481481
</div>
482482

docs/architecture.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,21 @@ of exposing separate HPC-only tool names.
114114

115115
**Validation gating** — The scientific agent runs dataset validation before zonal mean. If validation fails, zonal mean is skipped rather than producing unreliable results.
116116

117+
## Maintenance Notes
118+
119+
The current implementation favors a small number of broad tool modules while
120+
the MCP surface is still evolving. That keeps related behavior easy to audit
121+
for the first release, but the largest files should be split once the public
122+
contracts settle:
123+
124+
- `remote/compute_functions.py` should be divided by remote capability family
125+
(inspection, plotting, vector calculus, remapping, diagnostics).
126+
- `tools/advanced.py` should be divided into spatial, comparison, remapping,
127+
temporal/ensemble, and export modules.
128+
129+
Keep those refactors behavior-preserving and test-backed; they are polish and
130+
maintainability work, not blockers for the core conda package.
131+
117132
## Interactive Diagram
118133

119134
An interactive architecture diagram is available at `docs/architecture.html` in the repository.

docs/chrysalis.md

Lines changed: 47 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -19,69 +19,44 @@ which hosts the E3SM next-generation mesh library.
1919
are sent as source code via `AllCodeStrategies` and only need `uxarray` + deps
2020
in the worker environment.
2121
- Login nodes **kill compute processes** — always use the Slurm backend.
22-
- `unset PYTHONPATH` before every endpoint start — the conda `uxarray-yac` env
23-
injects broken `pydantic_core` paths that crash workers.
22+
- YAC remapping needs the Python 3.12 `uxarray-yac` environment plus YAC, MKL,
23+
MPICH, NetCDF, and local shim library paths. Use
24+
`scripts/chrysalis_endpoint.sh` instead of hand-writing those paths.
25+
- If a remote probe times out after the endpoint is `registered`, inspect the
26+
endpoint logs on Chrysalis with `scripts/chrysalis_endpoint.sh logs`.
2427

2528
## Worker Environment
2629

2730
| Item | Value |
2831
|---|---|
29-
| Venv | `~/venvs/globus-compute-py313` (Python 3.13) |
32+
| UXarray/YAC env | `~/.conda/envs/uxarray-yac` (Python 3.12) |
33+
| Endpoint helper venv | `~/venvs/globus-compute-py313` |
3034
| Slurm partition | `debug` (4h walltime, 20 nodes) |
3135
| Compute nodes | 251 GB RAM, 128 CPUs |
3236
| Endpoint name | `uxarray-chrysalis` |
3337

3438
## First-Time Setup
3539

40+
The checked-in helper script writes the endpoint profile, YAC runtime library
41+
paths, and small BLAS/LAPACK shims needed by the current YAC build:
42+
3643
```bash
37-
# 1. Create Python 3.13 conda env
38-
/gpfs/fs1/soft/chrysalis/manual/miniforge3/25.3.1/bin/conda create \
39-
-n gc-py313 python=3.13 -y
40-
41-
# 2. Build the globus-compute venv
42-
~/.conda/envs/gc-py313/bin/python -m venv ~/venvs/globus-compute-py313
43-
~/venvs/globus-compute-py313/bin/pip install \
44-
"globus-compute-endpoint==4.12.0" \
45-
uxarray xarray netCDF4 h5netcdf numpy matplotlib holoviews cartopy
46-
47-
# 3. Configure the endpoint (Slurm-backed)
48-
unset PYTHONPATH
49-
~/venvs/globus-compute-py313/bin/globus-compute-endpoint configure uxarray-chrysalis
50-
51-
cat > ~/.globus_compute/uxarray-chrysalis/user_config_template.yaml.j2 << 'EOF'
52-
endpoint_setup: ""
53-
engine:
54-
type: GlobusComputeEngine
55-
max_workers_per_node: 4
56-
provider:
57-
type: SlurmProvider
58-
partition: debug
59-
nodes_per_block: 1
60-
init_blocks: 0
61-
min_blocks: 0
62-
max_blocks: 2
63-
walltime: "04:00:00"
64-
worker_init: |
65-
unset PYTHONPATH
66-
launcher:
67-
type: SrunLauncher
68-
idle_heartbeats_soft: 10
69-
idle_heartbeats_hard: 5760
70-
EOF
71-
72-
cat > ~/.globus_compute/uxarray-chrysalis/user_environment.yaml << 'EOF'
73-
PYTHONPATH: ""
74-
PATH: "/home/<username>/venvs/globus-compute-py313/bin:/usr/bin:/bin"
75-
EOF
44+
git clone https://github.com/UXARRAY/uxarray-mcp-server.git
45+
cd uxarray-mcp-server
46+
bash scripts/chrysalis_endpoint.sh configure slurm-debug
47+
bash scripts/chrysalis_endpoint.sh check-yac
7648
```
7749

50+
The `check-yac` command runs a tiny Slurm job that imports `yac.core`, imports
51+
UXarray's YAC helper, and remaps HEALPix zoom 2 to zoom 3. It should report
52+
`yac_core_ok: true` and `remap_ok: true` before the endpoint is used by MCP.
53+
7854
## Starting the Endpoint
7955

8056
Run this every time you log in:
8157

8258
```bash
83-
unset PYTHONPATH
84-
~/venvs/globus-compute-py313/bin/globus-compute-endpoint start uxarray-chrysalis
59+
bash scripts/chrysalis_endpoint.sh start
8560
```
8661

8762
The endpoint prints its UUID. Add it to your private local config on your
@@ -98,6 +73,8 @@ From your laptop after the endpoint is running:
9873

9974
```bash
10075
uv run python scripts/hpc_doctor.py --endpoint chrysalis --timeout-seconds 120
76+
uv run --extra hpc python scripts/yac_smoke_test.py \
77+
--endpoint chrysalis --timeout-seconds 300
10178
```
10279

10380
Or manually:
@@ -110,6 +87,14 @@ print(validate_hpc_setup(endpoint='chrysalis', run_remote_probe=True,
11087
probe_timeout_seconds=120))
11188
```
11289

90+
If the manager reports `registered` but worker probes time out, inspect the
91+
remote side on Chrysalis:
92+
93+
```bash
94+
bash scripts/chrysalis_endpoint.sh logs
95+
squeue -u "$USER"
96+
```
97+
11398
## E3SM Next-Generation Ocean Meshes
11499

115100
Available at `/lcrc/group/e3sm/ac.xylar/polaris_1.0/chrysalis/test_20260520/unified-mesh-topo-cull2/`:
@@ -123,8 +108,23 @@ Available at `/lcrc/group/e3sm/ac.xylar/polaris_1.0/chrysalis/test_20260520/unif
123108

124109
## Troubleshooting
125110

126-
**`ENDPOINT_NOT_ONLINE`** — the Slurm debug job timed out (4h limit). Restart with `unset PYTHONPATH && ~/venvs/globus-compute-py313/bin/globus-compute-endpoint start uxarray-chrysalis`.
111+
**`ENDPOINT_NOT_ONLINE`** — the Slurm debug job timed out (4h limit). Restart
112+
with `bash scripts/chrysalis_endpoint.sh restart`.
113+
114+
**Worker probe timeout after `registered`** — the manager is connected, but a
115+
Slurm worker did not return. Run `bash scripts/chrysalis_endpoint.sh logs` on
116+
Chrysalis and inspect the latest submit script/log pair.
117+
118+
**`pydantic_core` not found** — the worker is running from the wrong Python
119+
environment. Re-run `bash scripts/chrysalis_endpoint.sh configure slurm-debug`
120+
and restart the endpoint.
127121

128-
**`WorkerLost` or `SystemError`** — PYTHONPATH is set. Always `unset PYTHONPATH` before starting the endpoint.
122+
**`libnetcdf.so.22`, `liblapack.so.3`, or `libblas.so.3` not found** — the YAC
123+
runtime paths or local MKL shims are missing. Re-run
124+
`bash scripts/chrysalis_endpoint.sh configure slurm-debug`, then
125+
`bash scripts/chrysalis_endpoint.sh check-yac`.
129126

130-
**`pydantic_core` not found** — conda env leaked into the worker. Check `user_environment.yaml` has `PYTHONPATH: ""` and restart.
127+
**`PMI_Init failed` or `WorkerLost` during YAC import** — YAC initializes MPI.
128+
Inside a Globus Compute worker, run the YAC smoke/remap through the dedicated
129+
smoke path, which launches the native YAC child process with
130+
`srun --ntasks 1` when `SLURM_JOB_ID` is present.

docs/release.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,13 @@ The conda package should install the core MCP server and CLI. HPC-specific
123123
Globus Compute dependencies can be added to the feedstock later if conda-forge
124124
availability and solver behavior are acceptable.
125125

126+
The seed recipe intentionally targets the **core** package only. Keep
127+
`globus-compute-sdk` and `academy-py` out of the initial conda-forge recipe
128+
until those dependencies and their transitive solver behavior are validated on
129+
conda-forge. If conda-native HPC support becomes necessary, prefer a second
130+
output such as `uxarray-mcp-hpc` or a feedstock variant rather than making every
131+
local-only user solve the remote-execution stack.
132+
126133
## Privacy Check
127134

128135
Before every release, verify endpoint UUIDs and local config did not re-enter

0 commit comments

Comments
 (0)