runpod-mcp
runpod-mcp — 用于 Learning-to-Swim 复现的自定义 MCP 服务器
独立部署说明: 本服务器是从
learning-to-swim-replication项目(完整历史)中提取出来的。类似../runbook/RUNBOOK.md这类相对链接指向父项目,仅当本仓库位于该父项目内部(或通过符号链接指向它)时才能解析;服务器本身可以独立运行。
这 14 个任务形态的工具与父项目的 runbook/RUNBOOK.md 相对应,而不是提供 ~50 个通用 API 镜像。之所以是自定义服务器,是因为 没有任何 RunPod API 能在 pod 上执行命令 —— 官方 MCP 只覆盖控制平面;运行 pod_setup.sh、轴健康扫描和训练均需要 SSH + rsync,并以代码中的成本护栏形式实现。
架构
.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)无状态且按载具(per-vehicle)隔离:"pod" =
GET /pods返回结果中,匹配所选载具配置名称的那一个(hippocampus→lts-replication;bluerov2→lts-replication-bluerov2;每个工具的vehicle参数默认为 hippocampus,stop_pod/terminate_pod必须显式指定);控制台与 MCP 始终一致。唯一的本地状态:每个载具 Runtime 的 60 秒(host, port)缓存。异步任务:一次 SSH 调用运行
setsid bash job_wrapper.sh <dir> <pod_id> <ceiling> <auto_stop>;状态存放在网络卷上,因此能经受住 MCP 重启、Mac 睡眠和 pod 停止。timeout --kill-after强制执行墙钟时间上限(退出码 124);auto_stop后缀在 exit_code 已写入之后才运行,因此超时永远不会阻止它发起电码。Pod id 通过 argv 注入(容器环境变量在分离的 BatchMode shell 中不可靠);会 source/etc/rp_environment以获取 runpodctl 凭据;启用 auto_stop 时会同步探测 runpodctl,若不可用则明确报错。该探测(2026-08-09)是三路诊断:bare-shell 检查不下定论(它们区分 H1-vs-H2,并捕获 bare PATH),随后无条件 source/etc/rp_environment,而“source 后”那一对承载了结论——NO_RUNPODCTL(source 后仍无该二进制,退出码 90)、NO_RUNPODCTL_AUTH_SOURCED(source 后仍被拒绝认证,退出码 91)、PROBE_OK(仅当 source 后成功);NO_RUNPODCTL_AUTH_BARE是继续执行的中间诊断。空闲看门狗:每次转换到 running 都会重新安装 —— 容器磁盘清机会移除运行时安装的内容(
idle_watchdog.sh本身、apt 的 X11/GL 库、rsync),所以每次转换都保留安装;runpodctl随镜像提供,每次开机都会恢复(清机是从镜像恢复磁盘,而不是清空磁盘;2026-08-09 修正)。每 5 分钟:没有活动任务,需要刷新任务 + 没有 sshd 会话 +/workspace/.keepalive超过 60 分钟未刷新 → 运行runpodctl stop pod。touch /workspace/.keepalive是“手动会话”的逃生舱口。成功安装会报告armed (stop path unverified)—— 该探测只证明 READ(get pod),而看门狗需要 WRITE(stop pod);第一个真正的确认是/workspace/.idle_watchdog.log中出现成功停止的条目。状态(2026-08-09):安装探测在此前每次记录的 bring-up 中均失败(修复前是不透明的 rc=91)——看门狗从未真正布防;缺陷 2 以 DIAGNOSED 而非 CLOSED 交付,并将由下一次 bring-up 的哨兵(sentinel)来定案。idle_watchdog: FAILED⇒ 在任何任务开始前先布防 Mac 侧的死手开关。护栏即代码:每个声明的载具只能有一个 pod(账号上任何其他名称的 pod 都会被拒绝)、RTX 4090 ×1、SECURE、interruptible 强制为 false、必须使用网络卷、
terminate_pod需要显式的vehicle参数以及逐字匹配的字符串terminate <对应载具的 pod_name>(例如terminate lts-replication)、在没有force的情况下每个 pod 同一时间只能运行一个任务。
安装 / 注册
在项目的 .mcp.json(Claude Code)中,以 run.sh 的绝对路径注册该服务器 —— run.sh 会在首次启动时自行创建 .venv:
{
"mcpServers": {
"runpod": {
"command": "bash",
"args": ["/path/to/runpod-mcp/run.sh"]
}
}
}设置
API key(绝不出现在磁盘/DVC 中,也不在 Git/argv 里 —— 仅存于 macOS Keychain;服务器通过
security find-generic-password读取,并从所有错误和日志中清除rpa_值):security add-generic-password -a kyle -s runpod-api-key -w '<KEY>'(当前
runpod_mcp/config.py中查找账户硬编码为kyle—— 如果你的 macOS 账户不同,请一起调整这两处。)SSH 密钥:
~/.ssh/id_ed25519(.pub)必须存在;.pub通过PUBLIC_KEY环境变量在创建 pod 时注入(这是runpod/pytorch镜像实际支持的环境变量——已实际验证;同时设置SSH_PUBLIC_KEY作为双保险)。直接 SSH 连接root@publicIp:portMappings["22"];不使用 RunPod 的代理 SSH(没有 scp)。主机密钥存放在专用的~/.ssh/known_hosts_runpod中,每次 pod 启动时截断(容器磁盘清机会重新生成主机密钥,过时条目只会造成误报的中间人失败)。其他项无需处理 ——
run.sh在首次启动时创建.venv/并安装 requirements.txt(受 stamp 门控)。
测试
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离线测试使用 httpx.MockTransport + 鸭子类型的伪 SSH —— 不联网、不读取 SSH 密钥。实时测试为只读 GET + 通过 run.sh 完成的 MCP stdio 握手(断言所有 14 个工具均已注册)。DR 数据表通过解析 BLUEROV2/config/bluerov2_heavy.yaml、RUNBOOK.md 和 APPLY.md 进行交叉核对;补丁脚本对已固定提交 7c5ebe7 的源码做 fixture 摘录进行运行(若存在真正的引用克隆,则外加 SHA 门控测试;该测试只读、使用 tmp 副本)。
test_supervise.py 用注入的 fakes + 假时钟驱动 supervise CLI 的核心(不需要真实等待),并覆盖每个安全分支:正常完成、任务失败、最大等待强制停止、pod 未运行拒绝、启动拒绝、瞬态轮询错误、捕获失败仍停止、--no-stop,以及所有场景下断言 terminate_pod 从未被调用。
仓库根的 pytest -q 通过 conftest.py 的 collect_ignore 忽略此目录 — 精简后的根 venv 没有 mcp/httpx。
监督运行(supervise.sh)
一条命令串联整次运行:验证 pod 运行中 → dry-run 推导出一个有限的墙钟时间上限 → launch(auto_stop=false) → 轮询 job_status → 无条件拉取 /workspace/jobs/<job_id>/ + sync_logs + spend_report → stop_pod → 输出持久的 JSON 摘要 —— 因此智能体只需作为后台任务启动它一次,并在完成时收到通知。它复用 runpod_mcp.tools.*(无逻辑重复,继承全部护栏),并且从不调用 terminate_pod。这是一个 Mac 侧 CLI,不是第 15 个 MCP 工具:需要轮询几分钟的工具会阻塞 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]资金安全:轮询循环只有两个退出出口 —— 正常完成 → stop_pod;或 --max-wait(始终有限)超时后仍处于 running → 强制停机 + 非零退出码 + force_stopped 摘要标记。启动 拒绝 → 不停机(修复后重试),退出码为 2。载具日志目录中的 supervise-<job_id>.json 摘要(hippocampus 为 logs/pod/,bluerov2 为 logs/pod/bluerov2/)是恢复契约(后续会话根据它协调停机状态)。存活保障说明:caffeinate -i 可防止空闲睡眠,但无法防止休眠回调;run_in_background 能否在 WarmLifecycle 回收后存活尚未验证 —— 作业的 timeout 上限是保证的兜底;pod 侧空闲保护前的 counter 会回也称不上完全可靠(DIAGNOSED,未 CLOSED —— 见上文的“空闲看门狗”条目),所以当 ensure_pod 报告 idle_watchdog: FAILED 时,请布防 Mac 侧死手保护开关。
Campaign 链(CUREE/chains/)
每个 campaign 一个 bash 脚本(按 campaign ID 命名,例如 chain-011-CUREE_Adaptive-weights.sh):该 campaign 的整个 pod 侧任务序列——补丁、门槛、训练、评测、同步——作为有序的、按 SHA 固定的链接。这些链通过 supervise.sh 启动(由它负责捕获与停机),绝不手动执行;它们是某个 campaign 实际执行内容的数据保留记录。
Dry-run
ensure_pod、run_pod_setup、run_job、launch_training、apply_bluerov_patches 都接受 dry_run=true,并返回精确的目标负载/改动/命令,且不修改任何内容($0)。supervise 在正式启动之前利用这条 dry-run 路径来推导其有限的 --max-wait。
NGC 后备镜像(手动切换——请先阅读)
nvcr.io/nvidia/isaac-sim:4.5.0(RUNBOOK Day-1 后备方案)没有 sshd —— 这会让本服务器的整个 SSH 机制失效。切换需要替换 docker-start 命令,使其能安装/启动 sshd(这不是一行改动):在切换 pod_defaults.yaml 中的 image_name 之前,请先告诉 Kyle 考虑是否需要更改。
已知风险(计划时已接受)
pod_setup.sh 中 IsaacSim 4.5.0 的下载 URL 可能 404 —— 会在
job_status日志尾部出现;修复方法是修改 runbook,而不是修改 MCP。各数据中心 4090 库存不稳定;网络卷会锁定单个 DC。用
gpu_availability(data_center_id=...)+ensure_pod的“无 GPU”事件恢复方案覆盖;最坏情况下,在另一个 DC 中创建第二个卷。
许可证
MIT — 见 LICENSE。
This server cannot be installed
Maintenance
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.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
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