runpod-mcp
runpod-mcp — servidor MCP personalizado para la replicación Learning-to-Swim
Nota independiente: este servidor fue extraído (con su historial completo) del proyecto
learning-to-swim-replication. Los enlaces relativos como../runbook/RUNBOOK.mdremiten a ese proyecto padre y solo se resuelven cuando este repositorio está dentro de él (o mediante un symlink); el servidor en sí se ejecuta de forma independiente.
Herramientas con forma de tarea (14) que reflejan el runbook/RUNBOOK.md
del proyecto padre en lugar de ~50 espejos genéricos de API. Es un servidor
personalizado porque ninguna API de RunPod ejecuta comandos en un pod —
el MCP oficial solo cubre el plano de control; ejecutar pod_setup.sh,
el barrido de verificación de los ejes y el entrenamiento exige SSH + rsync,
codificado aquí con salvaguardas de coste en el código.
Arquitectura
.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)Las arquitecturas clave:
Sin estado y por vehículo: «el pod» = lo que
GET /podsdevuelva que coincida con el nombre configurado del vehículo seleccionado (hippocampus→lts-replication,bluerov2→lts-replication-bluerov2; el parámetrovehiclede cada herramienta utiliza por defecto hippocampus;stop_pod/terminate_podlo exigen de forma explícita); consola y MCP siempre coinciden. Solo hay estado local: una caché de 60 segundos (host, port) por Runtime del vehículo.Trabajos asíncronos: una sola llamada SSH ejecuta
setsid bashjob_wrapper.sh <dir> <pod_id> <ceiling> <auto_stop>; el estado vive en el volumen de red, por lo que sobrevive a reinicios del MCP, a la suspensión del Mac y a paradas del pod.timeout --kill-afterimpone los topes de tiempo de reloj (exit 124); el sufijo de auto-stop se ejecuta DESPUÉS de que se escribaexit_code, así que un timeout nunca puede anularlo. El id del pod se inyecta como argv (las variables de entorno del contenedor no son fiables en shells BatchMode separados); se obtiene/etc/rp_environmentpara las credenciales de runpodctl; al armarauto_stopse sondea runpodctl de forma síncrona y si no funciona falla de forma ruidosa. La sonda (2026-08-09) es un diagnóstico de tres vías: las comprobaciones de shell puro no deciden nada (responden a H1-vs-H2 y captura el PATH puro), luego se hacesourcede/etc/rp_environmentde forma incondicional, y el par tras elsourcelleva el veredicto —NO_RUNPODCTL(binario ausente incluso después delsource, salida 90),NO_RUNPODCTL_AUTH_SOURCED(aún rechazado después delsource, salida 91),PROBE_OK(éxito solo tras elsource;NO_RUNPODCTL_AUTH_BAREes el diagnóstico intermedio que continúa).Watchdog de inactividad: se reinstala en cada transición a running — el borrado del disco del contenedor elimina el material instalado en runtime (el propio
idle_watchdog.sh, las librerías apt X11/GL,rsync), por eso se mantiene la instalación en cada transición;runpodctlviene EN LA IMAGEN y vuelve en cada arranque (un borrado restaura el disco desde la imagen, no lo vacía — corregido el 2026-08-09). Cada 5 min: sin pid de trabajo activosin sesión sshd +
/workspace/.keepalivecon más de 60 min de antigüedad →runpodctl stop pod.touch /workspace/.keepalivees la vía de escape de la sesión manual. Una instalación correcta informa dearmed (stop path unverified)— la sonda certifica lectura (get pod), el watchdog requiere escritura (stop pod); la primera confirmación real es una entrada de parada correcta en/workspace/.idle_watchdog.log. Estado (2026-08-09): la sonda de instalación ha fallado en cada puesta en marcha registrada (rc=91 opaco antes del arreglo) — el watchdog nunca se ha llegado a armar; el defecto 2 llega DIAGNOSTICADO, no CERRADO, y el centinela de la próxima puesta en marcha lo resuelve.idle_watchdog: FAILED⇒ arma el deadman del Mac antes de cualquier trabajo.
Las salvaguardas son código: un pod por vehículo declarado (se rechaza cualquier otro nombre de pod en la cuenta), RTX 4090 ×1, SECURE,
interruptibleforzado a false, volumen de red obligatorio,terminate_podexigevehicleexplícito más la cadena literalterminate <pod_name del vehículo>(p. ej.,terminate lts-replication), un trabajo a la vez por pod salvo conforce.
Related MCP server: RunPod MCP Server
Instalación / registro
Registra el servidor en el .mcp.json de un proyecto (Claude Code) con una
ruta absoluta a run.sh — run.sh prepara su propio .venv en el primer
arranque:
{
"mcpServers": {
"runpod": {
"command": "bash",
"args": ["/path/to/runpod-mcp/run.sh"]
}
}
}Configuración
Clave de API (nunca en disco/git/argv — solo en el Llavero de macOS; el servidor la lee con
security find-generic-passwordy elimina los valoresrpa_de cualquier error y registro):security add-generic-password -a kyle -s runpod-api-key -w '<KEY>'(El nombre de cuenta de la consulta está actualmente fijo a
kyleenrunpod_mcp/config.py— ajústalos conjuntamente si tu cuenta de macOS difiere.)Clave SSH: debe existir
~/.ssh/id_ed25519(.pub); el.pubse inyecta en la creación del pod mediante la variable de entornoPUBLIC_KEY(lo que las imágenesrunpod/pytorchrealmente respetan — verificado en vivo;SSH_PUBLIC_KEYtambién se define como refuerzo extra). SSH directo aroot@publicIp:portMappings["22"]; el SSH proxy de RunPod no se usa (no scp). Las claves de host van a un~/.ssh/known_hosts_runpoddedicado, truncado en cada arranque del pod (la restauración del disco del contenedor regenera las claves de host; las entradas obsoletas solo causan falsos fallos de MITM).Nada más —
run.shcrea.venv/e instala requirements.txt en el primer arranque (condicionado por una marca).
Pruebas
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-onlyLas pruebas offline usan ttpx.MockTransport + un SSH falso con duck typing
— sin red, sin clave. Las pruebas en vivo son GETs de solo lectura + un
handshake de stdio de MCP a través de run.sh (verifican que se registran
las 14 herramientas). Las tablas de DR se comparan de forma cruzada
analizando BLUEROV2/config/bluerov2_heavy.yaml,
RUNBOOK.md y APPLY.md;
el script de parches se ejercita contra extractos fixture confirmados de las
fuentes fijadas en 7c5ebe7 (más una prueba condicionada por SHA contra el
clon de referencia real cuando está disponible — solo lectura, copias
temporales).
test_supervise.py conduce la CLI de supervise con fakes inyectados +
un reloj falso (sin esperas reales) y cubre todas las ramas de seguridad:
finalización normal, fallo del trabajo, parada forzada por --max-wait,
negativa de pod no en ejecución, negativa de lanzamiento, errores de sondeo
transitorios, fallo de captura que aun así detiene, --no-stop y se afirma
que terminate_pod nunca se llama en ningún caso.
El pytest -q del repositorio raíz ignora esta carpeta (conftest.py
collect_ignore) — el venv de la raíz, reducido al mínimo, no tiene
mcp/httpx.
Ejecuciones supervisadas (supervise.sh)
Un único comando que encadena una ejecución completa: comprobar que el pod
está en ejecución → derivar con dry-run un tope de tiempo de reloj finito
→ launch(auto_stop=false) → sondear job_status → traer de forma
incondicional /workspace/jobs/<job_id>/ + sync_logs + spend_report →
stop_pod → resumen JSON persistente — de modo que el agente lo lanza
una sola vez como tarea de fondo y recibe el aviso de finalización.
Reutiliza runpod_mcp.tools.* (sin duplicación de lógica, hereda todas las
salvaguardas) y nunca llama a terminate_pod. Esta es una CLI del Mac,
no una herramienta MCP número 15: una herramienta que sondee durante
minutos bloquearía el servidor stdio.
# 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]Seguridad del gasto: el bucle de sondeo tiene exactamente dos salidas: salida
normal → stop_pod; o --max-wait (siempre finito) agotado mientras el
estado sigue en running → parada forzada + código de salida no cero +
marcador force_stopped en el resumen. Una denegación del lanzamiento → no
se detiene (arregla y reintenta), salida 2. El resumen
supervise-<job_id>.json, que se escribe en el directorio de logs del
vehículo (logs/pod/ para hippocampus, logs/pod/bluerov2/ para bluerov2),
es el contrato de recuperación (una sesión posterior concilia el estado de
parada a partir de él). Advertencias de vitalidad: caffeinate -i protege
contra la suspensión por inactividad pero no contra el cierre de la tapa; la
supervivencia del run_in_background_ a través del reap de WarmLifecycle no
está verificada — el tope del timeout en el trabajo es el respaldo
garantizado; el watchdog de inactividad del pod lo secundaría, pero aún no se
ha armado en ninguna puesta en marcha registrada (DIAGNOSTICO, no CERRADO —
ver el punto anterior sobre el watchdog de inactividad), así que arma el
deadman del Mac cuando ensure_pod reporte idle_watchdog: FAILED.
Cadenas de campaña (CUREE/chains/)
Un script bash por campaña (denominado por el ID de campaña, p. ej.
chain-011-CUREE_Adaptive-weights.sh): toda la secuencia de trabajos del lado
del pod de la campaña — parches, gates, entrenamientos, evaluaciones, syncs —
como enlaces ordenados y fijados por SHA. Las cadenas se lanzan con
supervise.sh (que posee la captura-y-parada), nunca a mano; son el registro
duradero de lo que ejecutó exactamente una campaña.
Dry runs
ensure_pod, run_pod_setup, run_job, launch_training,
apply_bluerov_patches aceptan dry_run=true y devuelven las cargas
útiles/ediciones/comandos exactos sin modificar nada ($0). supervise
usa esa vía de dry run para derivar su --max-wait finito antes del
lanzamiento real.
Imagen de respaldo NGC (cambio manual — leer antes)
nvcr.io/nvidia/isaac-sim:4.5.0 (respaldo de Día 1 en el RUNBOOK) no trae
sshd — lo que rompe toda la historia SSH de este servidor. Cambiarla exige
un comando docker-start que instale/arranque sshd (no es un cambio de una
línea: avisar a Kyle antes de tocar image_name en pod_defaults.yaml.
Riesgos conocidos (aceptados en la planificación)
La URL de descarga de IsaacSim 4.5.0 en pod_setup.sh puede devolver 404 — sale a la luz en el rastro del log de
job_status; el arreglo es una edición del runbook, no un cambio del MCP.La disponibilidad de la 4090 fluctúa según el DC; el volumen de red fija un DC;
gpu_availability(data_center_id=...)y la receta de sarrecuperación sin GPU de ensure_pod lo cubren; en el peor caso, crea un segundo volumen en otro DC.
Licencia
MIT — consulta LICENSE.
This server cannot be deployed
Maintenance
Related MCP Connectors
On-demand GPU nodes for agents: create nodes, run commands, and submit jobs, billed by the minute.
Remote MCP for RunComfy: ComfyUI deployments, hosted models, LoRA training. 31 tools.
Deploy and manage your apps, databases, storage, and scheduled jobs from your AI agent
LLM chat, text tools, image generation, editing, batch image jobs, and asynchronous video generation
Related MCP Servers
- AlicenseNot gradedqualityFmaintenanceEnables comprehensive management of Vast.ai GPU cloud instances, including searching for GPU offers, creating and managing instances, executing remote commands via SSH, and monitoring background tasks for ML training workflows.12MIT
- AlicenseCqualityDmaintenanceEnables interaction with the RunPod REST API to manage GPU pods, serverless endpoints, templates, network volumes, and container registry authentications through natural language.261MIT
- FlicenseAqualityDmaintenanceControls Unity ML-Agents training runs from Claude Code, enabling launch, stop, resume, monitor, compare, and export via natural language.18-
- FlicenseNot gradedqualityBmaintenanceEnables LLMs to manage and run machine learning training jobs on a remote server, including syncing code, submitting experiments, monitoring progress, reading TensorBoard metrics, and receiving completion notifications.-