Kubernetes Ephemeral Job

io.github.inhuman由 GitHub 账号 inhuman 发布v0.7.1更新于 Oct 24, 2026

Runs a command in a throwaway Kubernetes pod and returns exit code, output and artifacts.

概览

AI 生成的概览

一个 MCP 服务器,在一次性 Kubernetes Pod 中按允许列表镜像运行命令,并返回退出码、输出和产物。

可以做什么
在全新的临时 Pod 中运行命令,返回退出码、合并输出、耗时和内联产物,随后删除该 Pod。在后台提交长时间任务,稍后获取结果,并支持长轮询等待。可选地在命令运行前把 git 仓库克隆到工作目录。每次任务可传入输入文件、环境变量、资源限制、工作目录和超时时间。
适用场景
适合需要让助手在真实容器中执行构建、测试套件或脚本,使用指定工具链、为依赖访问网络并取回文件产物,同时不留下残留 Pod 的场景。适合已在运行 Kubernetes、希望为智能体提供命名空间级、镜像白名单执行沙箱的团队。
运行要求
以 Docker 镜像本地运行,传输方式为 stdio(或 http/sse)。需要访问 Kubernetes 集群:挂载的 kubeconfig(MCP_K8S_KUBECONFIG)或集群内凭据;一个命名空间(MCP_K8S_NAMESPACE)及针对 jobs、pods 和日志的 Role/RoleBinding;以及非空的镜像允许列表(MCP_K8S_ALLOWED_IMAGES)。可选的克隆功能需要 MCP_K8S_CLONE_IMAGE 和 MCP_K8S_CLONE_SECRET。
安装前请注意
该服务器会创建和删除 Pod,权限面很大,应仅对受信任角色开放并置于工具策略之后。生成的 Pod 可访问网络,出站控制依赖命名空间的 NetworkPolicy。git 令牌密钥(MCP_K8S_CLONE_SECRET)会挂载到克隆初始化容器上。可选的 HTTP/SSE 认证使用 MCP_K8S_AUTH_TOKEN 和 X-MCP-AUTH 请求头。服务器重启会丢失待处理的后台任务令牌。

安装

在 SourceWeft 中

  1. 打开 控制台中的 Kubernetes Ephemeral Job,将其添加到工作区。
  2. 为需要使用其工具的对话启用该服务。

Desktop only,通过 STDIO。 STDIO 服务会启动本地进程,因此需要 SourceWeft 桌面宿主。

其他 MCP 客户端

参照 仓库 中的启动说明。

README

mcp-k8s-ephemeral-job

English | Русский

[Version] [MCP Registry] [Docker Pulls] [Docker Image Version] [Build] [Go Version] [Go Report Card] [License: MIT] [Issues] [Last Commit]

Public OSS MCP server (Go, MIT) that spawns an ephemeral Kubernetes Job/pod from a caller-chosen image, runs a command inside it, returns exit_code / stdout / artifacts, and deletes the pod. The server builds the pod manifest in code — the caller passes parameters only, never raw YAML.

It is the "operating room" counterpart to mcp-exec (the "scalpel"): where mcp-exec runs a single Python file in a locked-down, network-less sandbox in milliseconds, this one spins up a full pod with the toolchain/image you choose, controlled network (clone repos, pull deps), long tasks and file artifacts out. They complement each other.

Works over three transports — stdio / HTTP / SSE — with an identical tool set everywhere (official modelcontextprotocol/go-sdk).

Tools

run_job — run and wait

Input: { image (required), command (required), files?, env?, limits?, timeout_s?, workdir?, clone? } Output: { exit_code, stdout, stderr, duration_ms, status, artifacts, truncated }

  • status is one of succeeded / failed / timeout / error. A non-zero exit_code or a timeout is a normal result, not a tool error. Only invalid input (empty image/command, image not in the allowlist, bad file path) is a tool-call error.
  • stdout carries the container's combined stdout+stderr — Kubernetes merges the two streams in pod logs. stderr is reserved and always empty, so adding stream separation later stays backward-compatible.
  • Artifacts (files from the working directory) come back inline (base64) under a size cap. Exceeding a cap never loses data silently: the matching truncated flag is set.
  • The manifest is built deterministically by the server — no raw YAML from the caller.

submit_job / fetch_job — run in the background

submit_job takes the same arguments as run_job but returns a job_token immediately; fetch_job collects the result later. This is what lets an agent start a long job (a full test battery, a build) and keep working instead of idling inside one synchronous call for its whole wall-clock time.

  • fetch_job returns status=running while the job is in flight; pass wait_s (≤120) to long-poll instead of hammering. It answers as soon as the job is done rather than sitting out the full wait.
  • Results are retained for 60 minutes and can be fetched more than once.
  • Arguments are validated at submit time, so a bad image fails the submit while the caller can still fix it.
  • Handles live in memory (the server is single-replica by design): a restart drops pending tokens and the caller simply resubmits.

Cloning a repository

With clone: { repo_url, ref, subdir? } an init container checks the repo out into the working directory before the command runs. The caller never handles credentials: the server holds a secret with one token per git host, mounts it only on the cloner, and the token is masked in .git/config afterwards — the main container never sees it. The clone field is accepted only when the operator has configured MCP_K8S_CLONE_IMAGE + MCP_K8S_CLONE_SECRET.

Security model (invariants)

Per run: a fresh ephemeral pod, deleted afterwards (success / failure / timeout). The server's RBAC is namespace-scoped (Role/RoleBinding, never ClusterRole) — create/delete jobs,pods plus pods/log, pods/exec in one namespace. Spawned pods run with cap-drop=ALL, no-privilege-escalation, seccomp RuntimeDefault. The blast radius is that one namespace: LimitRange (per-pod default+max) + ResourceQuota (namespace ceiling) + wall-clock timeout (→ kill) + TTL/owner-reference GC + a concurrency cap. Images must pass a strict allowlist (MCP_K8S_ALLOWED_IMAGES; empty = nothing runs). Caller data (command / files / output / artifacts) is never persisted and never logged in full — only metadata.

run_job is the most powerful surface there is (it creates pods). When embedding it in an agent, gate it behind that agent's tool-policy (trusted roles only).

Network note: the pod's network is not disabled (it's needed to clone repos / pull deps). Egress is controlled by a namespace NetworkPolicy (allowlist) as a deployment concern, not a right baked into the code. The invariant is ephemerality + deletion, not the absence of network.

Resources: the server's MCP_K8S_DEFAULT_CPU/MEMORY are pod requests (the scheduler's reservation). Limits are set only when the caller passes limits; otherwise the ceiling comes from the namespace LimitRange. Passing limits.memory also raises the memory request to match, since memory is incompressible and the pod must land on a node that actually has it.

Run

Published in the MCP Registry as io.github.inhuman/mcp-k8s-ephemeral-job; the image is on Docker Hub as idconstruct/mcp-k8s-ephemeral-job.

bash
docker run --rm -i \  -v "$HOME/.kube:/kube:ro" \  -e MCP_K8S_KUBECONFIG=/kube/config \  -e MCP_K8S_NAMESPACE=ephemeral-dev \  -e MCP_K8S_ALLOWED_IMAGES=busybox:1.36,python:3.12-slim \  idconstruct/mcp-k8s-ephemeral-job:latest

From source, against a dev cluster, over stdio:

bash
go build -o mcp-k8s-ephemeral-job ./cmd/mcp-k8s-ephemeral-jobexport MCP_K8S_KUBECONFIG=$HOME/.kube/configexport MCP_K8S_NAMESPACE=ephemeral-devexport MCP_K8S_ALLOWED_IMAGES=busybox:1.36,python:3.12-slim./mcp-k8s-ephemeral-job

In production the server runs in-cluster as a Deployment with its own ServiceAccount + Role/RoleBinding on the ephemeral namespace + ResourceQuota/LimitRange (+ optional egress NetworkPolicy), usually on the http transport.

Example call (run_job)

json
{  "image": "python:3.12-slim",  "command": ["python", "gen.py"],  "files": [{ "path": "gen.py", "content_b64": "<base64 of a script writing out.png>" }],  "limits": { "cpu": "500m", "memory": "256Mi" },  "timeout_s": 30}

Returns exit_code, captured output, and out.png inline in artifacts. Afterwards the pod is gone (kubectl get jobs,pods -n $NS is empty).

Optional auth (HTTP/SSE)

Set MCP_K8S_AUTH_TOKEN to require every HTTP/SSE request to carry a matching X-MCP-AUTH header (constant-time compare; 401 otherwise). Empty token disables it. Not applicable to stdio.

Configuration

Env varPurposeDefault
MCP_K8S_TRANSPORTstdio | http | ssestdio
MCP_K8S_ADDRlisten address for http/sse:8080
MCP_K8S_NAMESPACEnamespace where ephemeral pods are spawnedmcp-ephemeral
MCP_K8S_DEFAULT_TIMEOUT_Sdefault wall-clock timeout (s)60
MCP_K8S_MAX_TIMEOUT_Stimeout ceiling (s)600
MCP_K8S_MAX_OUTPUT_BYTEScombined stdout+stderr cap1048576
MCP_K8S_MAX_ARTIFACT_BYTEStotal artifacts size cap10485760
MCP_K8S_DEFAULT_CPUpod CPU request (scheduling reservation; limits come from the caller's limits or the namespace LimitRange)1
MCP_K8S_DEFAULT_MEMORYpod memory request (see above)512Mi
MCP_K8S_MAX_CONCURRENTmax concurrent ephemeral pods (over → queue/error)10
MCP_K8S_ALLOWED_IMAGESstrict image allowlist (CSV); empty = deny everything``
MCP_K8S_SIDECAR_IMAGEhelper sidecar image for artifact collection (pinned)busybox:1.36
MCP_K8S_CLONE_IMAGEimage with git for the clone init container; empty = clone unavailable``
MCP_K8S_CLONE_SECRETsecret holding one token per git host (key = host); mounted only on the cloner``
MCP_K8S_CACHE_PVCexisting PVC mounted into every job pod as a shared cache; empty = no cache``
MCP_K8S_CACHE_MOUNT_PATHwhere the cache PVC is mounted, e.g. /go/pkg/mod``
MCP_K8S_JOB_EXTRA_ENVJSON object {"KEY":"value"} added to every job pod; caller keys win``
MCP_K8S_KUBECONFIGpath to kubeconfig; empty = in-cluster``
MCP_K8S_AUTH_TOKENif set, http/sse require X-MCP-AUTH header (constant-time); empty = off``

Both cache variables must be set together for the cache to mount. The PVC itself is provisioned out-of-band (helm/manifest); the server only references it by name and fails fast at startup if it is missing.

Not implemented

PVC / object-storage delivery for artifacts too large to inline, multi-cluster support, and proxying other MCP servers into the pod.

License

MIT.

来源:README.md,提交 1623e64

工具

0
SourceWeft 尚未在沙箱中启动过该软件包,因此其工具未知。

版本历史

1
  1. v0.7.1最新Sep 16, 2026

评分

已满足适用检查项的 50%

该评分衡量此收录条目的完整程度,依据注册中心条目、端点与公开来源计算得出。它不是对服务器本身的评价,也不表示服务器安全或当前可用:可连通性是带日期的检查结果,评分也不包含安装量或用户评价。

  • 近 30 天内可连通25 分

    请保持端点能够响应 MCP 握手;SourceWeft 每 30 天探测一次。

  • 已声明认证方式10 分

    请在注册中心条目中声明凭据请求头,或保持端点能够响应探测,以便判定其认证方式。

  • 已列出工具15 分

    请让服务器在无凭据时响应 tools/list,或发布可在沙箱中无凭据启动的软件包。

  • 描述必需15 分
  • 图标10 分
  • 文档15 分
  • 许可证10 分