UCR Research Computing ClusterWand
MCP server/CLI/One Go binary

ClusterWand

The Bifrost between AI and your HPC cluster.

Safe, typed MCP tools that let AI assistants do real work on a Slurm cluster over SSH. One Go binary on your own machine that is both a stdio MCP server and a CLI, reaching the cluster through your own OpenSSH login: Claude, Copilot, Gemini, Hermes, Codex, OpenCode and your own scripts get audited, read-mostly access to job status, failure diagnosis, costs, software modules, batch-script checks, results, and (opt-in, two-step) submit and cancel.

  • Go 1.26
  • stdio MCP, your own SSH
  • MIT
You:      Why did job 236 fail?
Claude:   [job_explain 236]
          It failed after one minute on the gpu partition: the log says
          "ModuleNotFoundError: No module named 'pandas'". The script calls python
          without an environment. Load the site's PyTorch or SciPy module
          before running it, or create a uv/Pixi environment inside the job.
That diagnosis came from the live cluster. The server holds no language model: it runs allow-listed scheduler queries and fixed diagnosis rules, returns JSON with the evidence and the exact commands it ran, and the assistant does the reasoning. Nothing a model sends is ever run as a shell command.
What it does

Typed answers about the cluster

Everything the MCP tools do is also a command, and every command takes --json. Every result is an envelope with the data, the evidence, and the scheduler commands that produced it, so an answer can be checked rather than trusted.

Explain failed jobs

job_explain runs fixed rules over the accounting record and the redacted log tail: out of memory, timeouts, missing modules, Python import errors, CUDA problems, disk full and more. Each finding carries its evidence and a fix.

Status and cost now

cluster_status and partitions: nodes powered up or down, queue depth, limits, GPUs, prices per node-hour, and the dollars per hour being spent right now.

Check a batch script

script_check statically checks a script against the real cluster: partition, limits, modules and their MPI prerequisite, GPU request, a core request on shared partitions, and worst-case cost. No submission.

Find software

modules_search, module_show and recipes: module versions, MPI prerequisites, GPU builds, prebuilt containers, known-good recipes and the shared reference databases.

Usage and waste

my_usage and waste_report: node-hours, core-hours, CPU efficiency and estimated cost, plus avoidable spend from idle cores, idle nodes and repeated failures.

Logs, files and results

Paged, redacted log windows; listings and chunked reads of your own home, scratch and job folders; job folders copied to your machine over the same SSH connection.

Interactive sessions

interactive_help returns the exact srun/salloc command, its hourly and worst-case cost, and how to reach the login node. It runs no job.

Submit and cancel

Opt-in tier A1, granted per person: plan, show worst-case cost, then confirm with a single-use token. Caps on nodes, hours and dollars are enforced before anything reaches the scheduler.

What you can ask

Plain questions, real cluster data

  • "What's running on the cluster and what is it costing per hour?"
  • "Why did my job 236 fail?"
  • "Which modules give me LAMMPS with GPU support?"
  • "Check this batch script before I submit it."
  • "How much did I spend last month, and how much was wasted on idle cores?"
  • "Write a script for PyTorch on one GPU for 4 hours, show me the cost, then submit it."
  • "Show the last 200 lines of job 265's stderr, then give me download links for its results."
  • "Give me the exact command for an interactive session with 8 cores and a GPU."
  • Staff "Which nodes are down?" / "Draft a reply to this ticket about job 236."
The CLI

Same tools, in a terminal

Everything the MCP tools do is also a clusterwand command. Two examples from the README:

$ clusterwand check train.sbatch
PROBLEMS FOUND  partition cpu  worst-case cost $X.XX  (1 cores, 6% of a node)
  error   line 4: module "python3" not found in the cluster catalog; closest: python-sci, python-ml
  error   cpu shares nodes between jobs: this script asks for no cores, so Slurm gives
          it 1 core and about 8 GB on cpu. Ask for the cores it needs ...
  warning calls python without loading python-sci/python-ml or an environment
$ clusterwand submit hello.sh
Plan (nothing submitted yet):
  partition  cpu, 1 node(s), time limit 0 h 05 min
  scheduler  would start at ... on ... (cpu)
  worst case $X.XX  (today so far $0.00 of your day cap)
Submit this job? [y/N] y
Submitted job 265 ...  folder on the cluster: ~/clusterwand-jobs/<stamp>-hello

Submitting needs tier A1. Each submitted job runs in its own ~/clusterwand-jobs/<stamp>-<name> folder, so its results are exactly that folder. Caps limit nodes, hours and spend per job and per day; a plan over a cap is refused before anything reaches the scheduler.

How it works

A narrow, well-guarded crossing

The assistant sits on one side and the scheduler on the other. clusterwand is the bridge: tier checks, rate limits, a short-TTL cache, redaction and audit, and a backend that can only build allow-listed commands.

Runs on your machine

Your own SSH login, no server.

  • clusterwand mcp over stdio, plus the full CLI
  • No network listener, no sign-in, no credentials of its own
  • Tiers come from your local config

SSH only

Nothing installed or changed on the cluster.

  • Reaches the login node the way you would by hand: any OpenSSH host alias, with your keys, agent, ProxyJump and MFA (docs/SSH.md)
  • One multiplexed SSH connection reused for every call: about 0.3 s per command once warm
  • A normal researcher login is enough
Tools

Three tiers, granted per caller

A caller only sees, and can only call, the tiers granted to them (tiers in your local config). Read tools carry readOnlyHint: true, act tools false.

R1

Read

Everyone

  • cluster_status
  • partitions
  • jobs_list
  • job_show
  • job_explain
  • job_log_tail
  • job_results
  • modules_search
  • module_show
  • recipes
  • script_check
  • my_usage
  • waste_report
  • storage_usage
  • files_list
  • files_read
  • env_check
  • interactive_help
  • my_allocation
A1

Act

Opt-in per person

  • job_submit
  • job_submit_confirm
  • job_cancel
  • job_cancel_confirm
  • job_hold
  • job_hold_confirm
  • job_release
  • job_release_confirm
R2

Staff

Research Computing

  • jobs_list_all
  • job_show_any
  • job_explain_any
  • usage_report
  • waste_report_all
  • health
  • ticket_draft

ticket_draft writes a reply draft for a "my job failed" ticket. It is never sent.

Also: resources hpc://catalog and hpc://policies; prompts diagnose_job, write_batch_script, monthly_usage_summary, triage_ticket. Full catalog: SPEC section 9.

Quick start

Your laptop + any cluster over SSH

If ssh mycluster works without a prompt, ClusterWand works too: it uses your own OpenSSH login and keeps one multiplexed connection open. Or try it first with no cluster at all.

A 60-second demo, no cluster

The repo ships anonymized recordings of real Slurm output; the fixture backend replays them:

git clone https://github.com/UCR-Research-Computing/clusterwand && cd clusterwand
go build -o clusterwand ./cmd/clusterwand
printf 'backend: fixture\nfixtures_dir: %s/testdata\ntiers: [R1, R2, A1]\naudit_path: /tmp/cw-audit.jsonl\nstate_path: /tmp/cw-a1.json\n' "$PWD" > /tmp/cw-demo.yaml
./clusterwand job explain 236 --config /tmp/cw-demo.yaml

Then try status, jobs, modules lammps, or mcp to serve the same over stdio.

B Install and point it at your cluster

Linux/macOS release binary to ~/.local/bin, checksum-verified, with the short alias wand:

curl -fsSL https://raw.githubusercontent.com/UCR-Research-Computing/clusterwand/main/scripts/get.sh | sh

Or with Go:

go install github.com/UCR-Research-Computing/clusterwand/cmd/clusterwand@latest

Then set up and check:

clusterwand config init     # writes ~/.config/clusterwand/config.yaml
#   set ssh.host: mycluster (see docs/SSH.md)
clusterwand doctor          # checks the config, SSH to the login node, the catalog
clusterwand status

Add it to a client as a stdio server, in any JSON config:

{ "mcpServers": { "cluster": { "command": "clusterwand", "args": ["mcp"] } } }
Clients

One line for each common client

Every client starts clusterwand mcp as a local stdio server. The server name cluster is your choice.

Hermes Agent

hermes mcp add cluster --command clusterwand --args mcp

Claude Code

claude mcp add cluster -- clusterwand mcp

Add -s user for every project.

Claude Desktop

{"mcpServers": {"cluster": {
  "command": "clusterwand", "args": ["mcp"]}}}

In claude_desktop_config.json.

Codex CLI

codex mcp add cluster -- clusterwand mcp

OpenCode

"cluster": {"type": "local",
  "command": ["clusterwand", "mcp"]}

Under mcp in opencode.jsonc.

Gemini CLI

gemini mcp add -s user cluster clusterwand mcp

GitHub Copilot (VS Code)

{"servers": {"cluster": {"type": "stdio",
  "command": "clusterwand", "args": ["mcp"]}}}

In .vscode/mcp.json, then enable the tools in Copilot Chat agent mode.

Scripts

clusterwand job explain 236 --json
python3 examples/mcp_client.py call job_explain '{"job_id": "236"}'

Every tool is a CLI command with --json; the example is a stdio MCP client in one file.

Config-file versions and troubleshooting for every client: docs/CLIENTS.md.

Self-hosting

Bring it to your own cluster

Nothing in ClusterWand is specific to one site except configuration: the login node, the catalog (partitions, prices, modules, recipes), log roots and caps. Start with clusterwand config init, point ssh.host at your login node, and write a catalog. Site profiles and clusterwand discover are on the roadmap to replace the hand-written catalog.

ClusterWand never needs anything installed or changed on the cluster: a normal researcher login is enough, and the catalog can live in your own home folder. Ursa Major's notes live under sites/ursa-major/.

Tested against real output

Development

make check                  # gofmt, go vet, go test -race, build   (before every commit)
scripts/mutation_check.sh   # every safety guard must be caught by a test
scripts/mcp_smoke.py        # drives `clusterwand mcp` over stdio like a real client

Tests run against anonymized recordings of real cluster output in testdata/ (Slurm 25.11.4). scripts/make_fixtures.py regenerates them from a read-only probe and refuses to write if a real username survives; CI checks again.