Skip to content

About

Local MCP server, CLI, and agent skill for safe Slurm job management on ETH Zürich's Euler HPC cluster (free public share). Submit, monitor, cancel, and retrieve jobs; check quotas; transfer data. 48 cores / 128 GiB / no GPUs enforced.

Topics

Resources

Stars

2 stars

Watchers

0 watching

Forks

Latest commit

 

History

37 Commits

Folders and files

Repository files navigation

ETH Euler HPC MCP

Local, open-source MCP server, CLI, and agent skill for local, validated access to the ETH Zurich Euler HPC cluster (Slurm) from a laptop — designed for the free public share (standard student plan).

The MCP server gives AI agents (Claude, Cursor, VS Code Copilot, Prime Agent, Codex-style tools) a local interface to submit, monitor, cancel, and retrieve Slurm jobs. Resource limits and execution controls are implemented in the server.

eth-euler-hpc-mcp
├── eth-euler-hpc-mcp        MCP server (stdio, local, open source)
├── eth-hpc                  human CLI (bash, macOS/Linux)
└── skills/eth-euler-hpc     agent skill (operating rules + workflow)

Euler is a shared supercomputer with defined resource, storage, and access constraints. This project encodes the relevant public-share limits in the validation and execution workflow: non-compliant job requests are rejected before submission.


Table of contents

  1. Why
  2. Contents
  3. Requirements
  4. Install the MCP server
  5. Connect an MCP client
  6. Prime Agent integration
  7. MCP tool reference
  8. CLI reference
  9. Public-share limits (encoded in the validator)
  10. Validation and security model
  11. Storage and data workflow
  12. Operational workflow
  13. Development
  14. Troubleshooting
  15. Observed behavior
  16. Disclaimer & license

Why

  • Managing Slurm manually is tedious and error-prone. The MCP server provides a structured interface for common job, queue, cancellation, and data-transfer operations.
  • The public share has fixed limits (48 CPU cores, 128 GiB of memory, no GPUs) and a priority below shareholder groups.
  • The MCP server validates job requests against the public-share limits, requires an explicit time limit, rejects GPU requests, and reports scratch-data retention requirements before submission.
  • The server runs locally over stdio, uses the user's SSH configuration and (off campus) the ETH VPN, and does not transmit keys or passwords.

Contents

eth-euler-hpc-mcp/
├── pyproject.toml                        ← MCP server package (hatchling)
├── src/eth_euler_hpc_mcp/
│   ├── server.py                         ← MCP tools (FastMCP, stdio)
│   ├── validate.py                       ← public-share validator (pure, tested)
│   ├── runner.py                         ← SSH/rsync subprocess helpers
│   ├── config.py                         ← ~/.config/eth-hpc/config loader
│   └── templates/job.sbatch              ← public-share job-script template
├── skills/
│   ├── eth-euler-hpc/                    ← agent skill (symlink into ~/.agents/skills)
│   │   ├── SKILL.md                      ← operating rules + workflow (agent-facing)
│   │   ├── scripts/eth-hpc               ← the CLI (bash 3.2 compatible — macOS default)
│   │   ├── templates/job.sbatch          ← CLI's public-share job-script template
│   │   └── references/limits.md          ← researched limits + sources + machine state
│   └── eth-euler-hpc-mcp/                ← Prime Agent Python skill (stdio client)
│       ├── SKILL.md
│       └── src/eth_euler_hpc_mcp/        ← McpIntegration client (euler_hpc singleton)
├── tests/
│   ├── test-eth-hpc.sh                   ← 24 CLI unit tests (no cluster needed)
│   ├── test_validate.py                  ← validator unit tests
│   ├── test_config.py                    ← config loader tests
│   └── test_server_e2e.py                ← boots the MCP server over stdio (no cluster)
├── install.sh                            ← symlink skills + CLI into place
├── Makefile                              ← install / test / lint
└── LICENSE                               ← MIT

Requirements

  • macOS or Linux with ssh, rsync (the CLI needs only what macOS ships).
  • Python ≥ 3.10 and uv (or pipx) for the MCP server.
  • An ETH account with Euler access (auto-created on first SSH login).
  • Off-campus: the ETH VPN (https://www.vpn.ethz.ch/).

Install the MCP server

# from this repo (recommended: get the CLI + skills too):
git clone https://github.com/gbrlpzz/eth-euler-hpc-mcp
cd eth-euler-hpc-mcp
./install.sh                      # symlinks skills + CLI
uv tool install .                 # installs the 'eth-euler-hpc-mcp' command

# or from PyPI (server only):
uv tool install eth-euler-hpc-mcp

Configure your account (~/.config/eth-hpc/config):

USERNAME="your-eth-username"
SSH_KEY="~/.ssh/euler"     # any filename works — never commit real keys

Smoke-test:

eth-euler-hpc-mcp --help          # server binary present
eth-hpc doctor                    # CLI preflight (config, VPN, SSH auth)

Connect an MCP client

The server speaks the standard MCP protocol over stdio — the same transport Claude Desktop, Cursor, VS Code, and others use for local servers. Add it to your client's mcpServers config:

// Claude Desktop: ~/Library/Application Support/Claude/claude_desktop_config.json
// Cursor / VS Code: settings → MCP servers
{
  "mcpServers": {
    "eth-euler-hpc-mcp": {
      "command": "eth-euler-hpc-mcp",
      "args": []
    }
  }
}

If you cloned the repo instead of installing the command:

{
  "mcpServers": {
    "eth-euler-hpc-mcp": {
      "command": "uv",
      "args": ["run", "--project", "/absolute/path/to/eth-euler-hpc-mcp", "eth-euler-hpc-mcp"]
    }
  }
}

Environment overrides (set in the client or your shell): ETH_HPC_CONFIG (alternate config file), ETH_HPC_USER (username fallback).

Prime Agent integration

The repo ships a Prime Agent Python skill at skills/eth-euler-hpc-mcp (installed by ./install.sh into ~/.agents/skills/eth-euler-hpc-mcp). It spawns the local server over stdio via the McpIntegration base class and exposes the tools as async methods on an euler_hpc singleton:

from eth_euler_hpc_mcp import euler_hpc

await euler_hpc.euler_doctor()                          # preflight
await euler_hpc.euler_limits()                          # limits + storage rules
await euler_hpc.euler_validate_submit(script_path="job.sbatch")
await euler_hpc.euler_submit(script_path="job.sbatch")  # dry-run preview
await euler_hpc.euler_submit(script_path="job.sbatch", dry_run=False, confirm=True)
await euler_hpc.euler_queue(job_id="12345")
await euler_hpc.euler_cancel(job_id="12345", confirm=True)
await euler_hpc.euler_push(local_path="data/", scratch=True, dry_run=False, confirm=True)
await euler_hpc.euler_pull(remote_path="~/eth-hpc-jobs/slurm-*.out", local_dir="results/", confirm=True)

Restart the agent session (or /reload) after ./install.sh so the skill is picked up. Overrides: ETH_EULER_HPC_MCP_CMD and ETH_EULER_HPC_MCP_REPO.

MCP tool reference

Tool Purpose State changes
euler_limits Print researched public-share limits + storage rules none
euler_gen Return a public-share #SBATCH template none
euler_doctor Preflight: config, VPN/network reachability, SSH auth none
euler_quota Storage quotas (lquota) none
euler_queue [job_id] Your jobs (myjobs, squeue fallback) none
euler_validate_submit(script_path) Validate a job script against limits none
euler_submit(script_path, dry_run=true, confirm=false) Validate, then submit dry-run default; confirm required
euler_cancel(job_id, confirm=false) Cancel a job confirm required
euler_push(local_path, remote_dir?, scratch?, dry_run=true, confirm=false) Upload via rsync dry-run default; confirm required
euler_pull(remote_path, local_dir?, dry_run=true, confirm=false) Download via rsync dry-run default; confirm required

Every state-changing tool defaults to dry-run and requires confirm=true before execution. This makes the generated commands reviewable before they are sent to Euler.

CLI reference

eth-hpc doctor | ssh [--first-login] | quota | queue [jobid] |
        submit <file> | gen [file] | interactive [--cpus N --time H:MM:SS --mem-per-cpu G] |
        cancel <jobid> | push <local> [dir|--scratch] | pull <remote> [dir] |
        tunnel start|status|stop | limits | version | help

Global flags: --dry-run (print, don't execute), -y/--yes, --config FILE.

Command Purpose
doctor Preflight: config, VPN/network reachability, SSH auth
ssh [--first-login] Interactive shell on a login node
quota Storage quotas (lquota)
queue [jobid] Jobs (myjobs, squeue fallback)
gen [file] Generate a public-share job template
submit <file> Validate against public-share limits, then submit (script is copied to ~/eth-hpc-jobs/ on Euler)
interactive Small srun --pty compute session for debugging
cancel <jobid> Cancel (confirmation required unless -y)
push <local> [dir|--scratch] Upload via rsync (uses configured SSH key)
pull <remote> [dir] Download via rsync
tunnel start|status|stop Manage an euler-tunnel batch job
limits Print researched public-share limits
version / help Version / usage

Public-share limits (encoded in the validator)

Resource Limit Source
CPU cores per job 48 max docs.hpc.ethz.ch/users/shareholders
Memory per job 128 GiB max docs.hpc.ethz.ch/users/shareholders
GPUs none shareholders-only hardware
Default time limit 1 h (Slurm) — tool requires --time, maximum 24 h docs.hpc.ethz.ch
$HOME 50 GB, 500k files, backed up docs.hpc.ethz.ch/hardware/storage
$SCRATCH 2.5 TB, 1M files, purged after 15 days, no backup docs.hpc.ethz.ch/hardware/storage

All limits are configurable in ~/.config/eth-hpc/config (MAX_TIME, MAX_CPUS, MAX_MEM_GB) and documented with sources in skills/eth-euler-hpc/references/limits.md.

Validation and security model

Area Implementation
CPU and memory limits validate_sbatch in the MCP server and CLI (rejects non-compliant requests)
GPU requests validator (the public share has no GPUs)
Time limit validator requires --time and applies the configured maximum (24 h by default)
Compute placement workflow guidance; interactive uses srun --pty rather than running compute on login nodes
State-changing operations MCP tools default to dry-run and require confirm=true; the CLI provides --dry-run and cancellation confirmation
Credential handling server runs locally over stdio and uses the user's ~/.ssh/ configuration; no hosted credential service
Input handling job IDs are numeric; remote paths use a restricted character set; subprocesses use argument lists (no shell=True)
Scratch retention validator reports the 15-day purge and lack of backup; results should be copied back

The server runs locally over stdio, does not open a network listener, and does not persist credentials. SSH authentication uses the user's local configuration.

Storage and data workflow

Path Capacity Backup Purge Use for
$HOME (/cluster/home/<user>) 50 GB, 500k files nightly + snapshots on account deletion code, results, small important files
$SCRATCH (/cluster/scratch/<user>) 2.5 TB, 1M files none after 15 days large datasets, intermediate data
$TMPDIR (node-local) node-dependent none at job end fast I/O during a job (request with --tmp)
Project/Work shareholder-only — — not available on the public share
  • Large datasets → $SCRATCH (euler_push(local_path=..., scratch=True)).
  • Always rsync final results back to $HOME/$SLURM_SUBMIT_DIR before the job ends (euler_pull(remote_path="~/eth-hpc-jobs/...", local_dir="results/", confirm=True)).
  • Many small files → tar them first.
  • TB transfers → contact cluster support.

Operational workflow

  1. Run euler_doctor before remote operations. If network reachability fails, connect to the ETH VPN and retry.
  2. Preview every submission: euler_submit(script_path=...) prints the exact commands; then call again with dry_run=false, confirm=true.
  3. Use the validator for every submission. GPU requests, missing --time, and requests above the configured CPU or memory limits are rejected.
  4. Submit compute through Slurm. Use login nodes for editing, compiling, and submitting; use batch jobs or the CLI's interactive command for computation.
  5. Large-data workflow: euler_push(..., scratch=True) → the job reads $SCRATCH (or $TMPDIR for I/O-heavy steps) → euler_pull copies results back. Scratch is purged after 15 days and is not backed up.
  6. Treat PENDING as a queue state. Public-share jobs can wait behind shareholder jobs. Poll euler_queue(job_id=...); do not resubmit solely because
  7. Keep credentials local. The config is stored at ~/.config/eth-hpc/config and keys at ~/.ssh/; do not echo or commit them.
  8. Clean up completed runs. Remove temporary remote job files (~/eth-hpc-jobs/) when a session is complete.

Development

make test        # 24 bash unit tests + Python validator/MCP e2e tests (no cluster needed)
make lint        # shellcheck, if installed (brew install shellcheck)
make install     # same as ./install.sh

Contributor notes:

  • The CLI targets bash 3.2 (macOS default) — no bash 4+ features, no external dependencies beyond what macOS ships.
  • The MCP server is a thin Python package over the same validator; the validator logic is intentionally duplicated in Bash and Python and kept in sync by tests. test_validate.py and test_server_e2e.py cover it.
  • Remote commands use SSH. State-changing operations require an explicit command or confirmation, depending on the operation.
  • Limits are researched from the official docs (2026-08) and re-verified live via lquota, myjobs, sacctmgr show qos — policy can change.

Troubleshooting

Symptom Cause / fix
Connection timed out on ssh Not on ETH network/VPN → connect VPN first
Too many authentication failures Many local keys → the CLI pins IdentitiesOnly=yes when SSH_KEY is set
No verification-code prompt on first login Use eth-hpc ssh --first-login (-o PreferredAuthentications=keyboard-interactive -o PubkeyAuthentication=no)
Job stuck PENDING (Priority / ReqNodeNotAvail) Normal for public share; wait, poll euler_queue(job_id=...)
slurmstepd: error: poll(): Bad address Memory/network issue; check job memory usage
sudo needed on Euler No sudo on Euler — install in $HOME or use modules
module load fails Use module avail / module spider to find the right module
Compute node can't reach the internet module load eth_proxy (use sparingly — shared limited proxy)
LC_CTYPE locale warnings (macOS) Comment out SendEnv LANG LC_* in /etc/ssh/ssh_config
MCP client says "server not found" Install the server (uv tool install eth-euler-hpc-mcp) or use the uv run --project <repo> command form

Observed behavior

Verified live on 2026-08-07 (public share):

  • A small batch job completed successfully (exit 0) on a compute node and its output was pulled back; interactive srun sessions work too.
  • Public-share jobs queue behind shareholders: expect ~5–10 minutes of wait even on an idle cluster. Plan for it.
  • Cancellation, push/pull round trips, validation rejections, and euler-tunnel were verified.

Disclaimer & license

Community-maintained, not affiliated with ETH Zurich. Verify limits against the official documentation and your account before relying on them. Follow the ETH Acceptable Use Policy and current cluster documentation.

MIT — see LICENSE.

About

Local MCP server, CLI, and agent skill for safe Slurm job management on ETH Zürich's Euler HPC cluster (free public share). Submit, monitor, cancel, and retrieve jobs; check quotas; transfer data. 48 cores / 128 GiB / no GPUs enforced.

Topics

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages