Skip to main content
Glama

runpod-mcp — Einzelanfertigung eines MCP-Servers für die Learning-to-Swim-Replikation

Hinweis für den Standalone-Betrieb: Dieser Server wurde (mit vollständiger Historie) aus dem Projekt learning-to-swim-replication extrahiert. Relative Links wie ../runbook/RUNBOOK.md beziehen sich auf dieses übergeordnete Projekte und lösen nur auf, wenn dieses Repository darin liegt (oder dorthin symlinklt ist); der Server selbst läuft eigenständig.

Aufgabenförmige Tools (14), die das runbook/RUNBOOK.md des übergeordneten Projekts spiegeln, statt ~50 generischer API-Spiegel. Eigenentwicklung, weil keine RunPod-API Befehle auf einem Pod ausführt — das offizielle MCP deckt nur die Control Plane ab; das Ausführen von pod_setup.sh, der Achsen- Sanity-Sweep und das Training erfordern SSH + rsync, hier mit Cost Guardrails im Code verankert.

Architektur

.mcp.json → run.sh (venv bootstrap) → server.py (FastMCP, stdio; thin)
                                        └── runpod_mcp/
                                            config.py     Keychain key fetch + rpa_ scrubber
                                            api.py        REST v1 (pods/volumes/billing) + unauth GraphQL gpuTypes
                                            guardrails.py one-pod-per-vehicle (unknown refused) · 4090-only · no spot · volume required · confirm gate
                                            ssh.py        hardened ssh/scp/rsync; known_hosts_runpod; 60s conn cache
                                            jobs.py       detached jobs: /workspace/jobs/<id>/{cmd.sh,pid,out.log,exit_code,meta.json}
                                            training.py   DR tables (RUNBOOK/yaml-cross-checked) + verbatim train cmd
                                            supervise.py  Mac-side background CLI: launch→poll→pull→sync→spend→stop (reuses tools.*)
                                            watch.py      Mac-side ADVISORY observation CLI: discover job→tail out.log→parse metrics→page on plateau/failure/stall (read-only; never stops pods)
                                            remote/       job_wrapper.sh · idle_watchdog.sh · apply_bluerov2_patch.py
                                            deadman.py    Mac-side stop-pod fuse: arm --vehicle → sleep → stop with retries (per-vehicle pid/summaries)
supervise.sh → caffeinate -i wrapper around  python -m runpod_mcp.supervise
watch.sh     → caffeinate -i wrapper around  python -m runpod_mcp.watch   (live-pod behavior UNVERIFIED — fixture/mock-verified only; see CLAUDE.md §D)
deadman.sh   → caffeinate -i wrapper around  python -m runpod_mcp.deadman (arm/cancel REQUIRE --vehicle; bare status reports all vehicles)
  • Zustandslos & pro Fahrzeug: „der Pod“ = das, was GET /pods als Treffer auf den konfigurierten Namen des gewählten Fahrzeugs zurückgibt (hippocampuslts-replication, bluerov2lts-replication-bluerov2; das vehicle-Argument jedes Tools ist standardmäßig hippocampus, stop_pod/terminate_pod verlangen es ausdrücklich); Konsole und MCP stimmen immer überein. Einziger lokaler Zustand: ein 60-Sekunden-Cache für (Host, Port) pro Fahrzeug-Runtime.

  • Asynchrone Jobs: Ein SSH-Aufruf führt setsid bash job_wrapper.sh <dir> <pod_id> <ceiling> <auto_stop> aus; der Zustand liegt auf dem Netzwerk-Volume und übersteht so MCP-Neustarts, Mac-Schlafmodus und Pod-Stopp. timeout --kill-after erzwingt Wanduhr-Deadlines (exit 124); das Auto-Stop-Suffix läuft erst, NACHDEM der exit_code geschrieben ist, sodass ihn ein Timeout nie aushebeln kann. Die Pod-ID wird per argv injiziert (Container-Env-Vars sind in abgekoppelten BatchMode-Shells unzuverlässig); /etc/rp_environment wird gesourced, um die runpodctl-Zugangsdaten zu laden; beim Scharfschalten prüft auto_stop runpodctl synchron und schlägt laut fehl, falls es nicht funktionieren kann. Die Probe (2026-08-09) ist eine Dreier-Diagnostik: Die Bare-Shell-Checks entscheiden nichts (sie beantworten H1-vs-H2 und halten den nackten PATH fest), danach wird /etc/rp_environment bedingungslos gesourced, und das gelieferte Paar (SOURCED) trägt das Urteil — NO_RUNPODCTL (Binary auch nach dem Sourcen nicht vorhanden, exit 90), NO_RUNPODCTL_AUTH_SOURCED (verweigert auch nach dem Sourcen weiterhin, exit 91). In 91, PROBE_OK (nur gesourcer Erfolg; NO_RUNPODCTL_AUTH_BARE ist die Stufen-Diagnose, die fortläuft).

  • Idle-Watchdog: wird bei jedem Übergang in „running“ neu installiert — die Container-Platten-Bereinigung entfernt das zur Laufzeit installierte Material (idle_watchdog.sh selbst, die apt X11/GL-Bibliotheken, rsync), deshalb bleibt die Installation-bei-jedem-Übergang; runpodctl wird mit dem Image mitgeliefert und ist nach jedem Boot wieder da (eine Löschung stellt die Platte aus dem Image wider her, sie leert sie nicht — korrigiert 2026-08-09). Alle 5 Minuten: keine offene Job-PID + keine sshd-Sitzung + /workspace/.keepalive älter als 60 min → runpodctl stop pod. touch /workspace/.keepalive ist die Fluchtklappe für manuelle Sitzungen. Eine erfolgreiche Installation meldet armed (stop path unverified) — die Sonde zertifiziert READ (get pod), der Watchdog braucht WRITE (stop pod); die erste echte Bestätigung ist ein erfolgreicher Stop-Eintrag in /workspace/.idle_watchdog.log. Status (2026-08-09): Die Installations-Sonde ist bei jedem aufgezeichneten Bring-up fehlgeschlagen (undurchsichtiges rc=91vor dem Fix) — der Watchdog hat sich tatsächlich nie geschärft (armed); Defekt 2 geht als DIAGNOSED, nicht als CLOSED raus, und der Sentinel des nächsten Bring-ups entscheidet das.idle_watchdog: FAILED` ⇒ vor jedem Job den Mac-seitigen Deadman scharfschalten.

  • Guardrails sind Code: ein the Pod pro deklariertem Fahrzeug (jeder andere Pod-Name auf dem Konto wird verweigert), RTX 4090 ×1, festgezogen SECURE, interruptible erzwungen false, Netzwerk-Volume erforderlich, terminate_pod braucht ein ext products vehicle plus die wörtliche Zeichenkette terminate <Podnamen dieses Fahrzeugs> (z. B. terminate lts-replication) und ohne force nur ein Job pro Pod.

Installation / Registrierung

Registrieren Sie den Server in einem .mcp.json eines Projekts (Claude Code) mit einem absoluten Pfad zu run.shrun.sh erzeugt sein eigenes .venv beim ersten Start:

{
  "mcpServers": {
    "runpod": {
      "command": "bash",
      "args": ["/path/to/runpod-mcp/run.sh"]
    }
  }
}

Einrichtung

  1. API-Schlüssel (niemals auf Datenträger/Git/argv — nur macOS Keychain; der Server liest ihn über security find-generic-password und entfernt rpa_-Werte aus jedem Fehler und Log):

    security add-generic-password -a kyle -s runpod-api-key -w '<KEY>'

    (Der Kontoname-Name für den Lookup ist derzeit in runpod_mcp/config.py auf kyle hartkodiert — passen Sie beides an, falls sich Ihr macOS-Konto unterscheidet.)

  2. SSH-Schlüssel: Es muss ~/.ssh/id_ed25519(.pub) existieren; die .pub wird bei der Pod-Erstellung über die Umgebungsvariable PUBLIC_KEY injiziert (was die Abbilder runpod/pytorch tatsächlich beachten — selbst-live verifiziert; SSH_PUBLIC_KEY wird zusätzlich gesetzt als doppelte Absicherung). Direkte SSH zu root@publicIp:portMappings["22"] — der Proxy-SSH von RunPod wird nicht genutzt (kein scp). Host-Key landen mit in einer dedizierten ~/.ssh/known_hosts_runpod, die bei jedem Pod-Start abgeschnitten wird (die Platten-Bereinigung des Containers generiert Host-Key neu; überholte Einträge fällen nur falsche MITM-Fehler aus).

  3. Sonst nichts — run.sh erzeugt .venv/ und installiert beim ersten Start requirements.txt (durch eine Stempel-Datei gesteuert).

Tests

runpod-mcp/.venv/bin/python -m pytest runpod-mcp/tests -q          # offline (default)
RUNPOD_MCP_LIVE=1 runpod-mcp/.venv/bin/python -m pytest \
    runpod-mcp/tests/test_live.py -q                               # live $0 read-only

Offline-Tests verwenden httpx.MockTransport + duck-typisiertes Fake-SSH — kein Netzwerk, kein Schlüssel. Live-Tests sind lesende GETs + ein MCP-stdio-Handshake über run.sh (prüft, dass alle 14 Tools registriert werden). DR-Tabellen werden per Quervergleich geprüft durch Analysieren von BLUEROV2/config/bluerov2_heavy.yaml, RUNBOOK.md und APPLY.md; das Patch-Skript wird gegen committete Fixture-Auszüge der gepinnten 7c5ebe7-Quellen ausgeführt (zusätzlich ein SHA-Gate-Test gegen den vorhandenen referenzierten Klon — nur lesen, tmp-Kopien).

test_supervise.py treibt den Kern der supervise-CLI mit injizierten Stubs + einer Fake-Uhr (kein echtes Warten), und deckt jeden Sicherheits- Zweig ab: normaler Abschluss, Jobfehler, Spländung Force-Stop, Ablehnung bei nicht laufendem Pod, Ablehnung beim Start, kurzzeitige Poll-Fehler, Scheitern des Zwischenzustands stoppt trotzdem, --no-stop, und in jedem Fall wird geprüft dass terminate_pod nie aufgerufen wird.

Root-Repo pytest -q ignoriert diesen Ordner (conftest.py collect_ignore) — die schlanke Root-Venv hat kein mcp/httpx.

Überwachte Läufe (supervise.sh)

Ein einziger Befehl, der einen kompletten Lauf verketten: prüfen-ob-Pod-läuft → dry-run eine endliche Wanduhr-Decke ableiten → launch(auto_stop=false) → pollte job_status → bedingungslos /workspace/jobs/<job_id>/ ziehen + sync_logs + spend_reportstop_pod → dauerhafte JSON-Zusammenfassung — so startet der Agent es einmal hintergründig und wird bei Abschluss benachrichtigt. Er verwendet runpod_mcp.tools.* (keine Logikverdopplung, alle Guardrails geerbt) und ruft niemals terminate_pod. Das ist eine CLI für die Mac-Seite, kein 15. MCP-Tool: ein Minuten Polling-Tool würde den stdio-Server blockieren.

# training run (background task)
supervise.sh --training curee --dr DR_0 --seed 1 \
    [--interval 45] [--max-wait N] [--backstop 300] [--no-stop] \
    [--sync-subdir rsl_rl/warpauv_direct] [--summary-path PATH]

# generic job — --sync-subdir REQUIRED (pass 'none' to skip the analysis sync;
# the job-dir pull always happens); --vehicle routes the pod (default
# hippocampus; --training mode derives it from the training vehicle instead)
supervise.sh --job-name eval --command "…" --workdir /workspace \
    --sync-subdir <dir|none> [--max-runtime-sec N] [--vehicle bluerov2]

Geld-Sicherheit: Die Poll-Schleife hat exakt zwei Ausgänge — normaler Abschluss → stop_pod; oder --max-wait (immer finite) verstrichen, während der Status noch running ist → Force-Stop + Exit-Kode ungleich 0 + force_stopped-Flag in der Zusammenfassung. Eine Scheme*-Weigerung beim Start → kein Stopp (reparieren und neu), exit 2. Die Zusammenfassung supervise-<job_id>.json im Logverzeichnis des Fahrzeugs (logs/pod/ für hippocampus, logs/pod/bluerov2/ für bluerov2) ist der Wiederherstellungsvertrag (eine spätere Sitzung gleicht den Stopp-Zustand daraus ab). Liveness-Hinweise: caffeinate -i schützt vor Schlaf bei Leerlauf, aber nicht vor zugeklapptem Deckel; das Überleben von run_in_background über WarmLifecycle-Reaping ist ungeprüft — die timeout-Grenze des Jobs ist der garantierte Rückfall; der Pod-idle watchdog würde ihn abstützen, hat sich aber in keinem aufgezeichneten Bring-up je gescharft (DIAGNOSED, nicht CLOSED — siehe der Idle-Watchdog-Punkt), also aktivieren Sie den Mac-seitigen Notaus, wenn ensure_pod idle_watchdog: FAILED meldet.

Kampagnen-Ketten (CUREE/chains/)

Ein Bash-Skript pro Kampagne (benannt nach Kampagnen-ID, z. B. chain-011-CUREE_Adaptive-weights.sh): die komplette pod-seitige Job-Sequenz der Kampagne — Patches, Gates, Trainings, Evals, Syncs — als geordnete, SHA-gepinnte Verbindungen. Ketten werden über supervise.sh gestartet (das das Abfangen-und-Anhalten übernimmt), niemals von Hand ausgeführt; sie sind das dauerhafte Protokoll dessen, welche genau Kampagne was ausgeführt hat.

Dry Läufe

ensure_pod, run_pod_setup, run_job, launch_training, apply_bluerov_patches akzeptieren alle dry_run=true und geben die genauen geplanten Payloads/Edits/Befehle alle zurück, ohne irgendetwas zu verändern ($0). supervise nutzt diesen Dry-Run-Pfad, um vor dem echten Start seine endliche --max-wait abzuleiten.

NGC-Fallback-Image (manueller Wechsel — zuerst lesen)

nvcr.io/nvidia/isaac-sim:4.5.0 (Day-1-Fallback aus RUNBOOK) hat kein sshd — es bricht die ganze SSH-Geschichte dieses Servers. Ein Wechsel erfordert einen Docker-Start-Befehl, der sshd installiert/ststartet (keine Einzeilenänderung): Kyle frühzeitig informieren, bevor Sie jemals image_name in pod_defaults.yaml tauschen.

Bekannte Risiken (bei der Planung akzeptiert)

  • Die Download-URL für Isaac Sim 4.5 in pod_setup.sh kann 404 liefern — das taucht im Log-Ende von job_status auf; die Behebung ist eine Runbook- Änderung, keine MCP-Änderung.

  • Der 4090-Bestand schwankt je Datenknoten; das Netzwerk-Volume pinnt einen DC. gpu_availability(data_center_id=...) zusammen mit dem „Kein-GPU“-RecoveryRezept von ensure_pod deckt das ab; schlimmstenfalls legen Sie ein zweites Volume in einem anderen DC an.

Lizenz

MIT — siehe LICENSE.

-
license - not tested
Not graded
quality - not tested
B
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Connectors

  • Operate Linux, macOS and Windows from your LLM. Every action runs through an auditable allowlist.

  • Create and manage AI agents that collaborate and solve problems through natural language interacti…

  • Build, validate, and deploy multi-agent AI solutions from any AI environment.

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/kyle-nelson-berkeley/runpod-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server