jobd
Manage a jobd broker and worker fleet through MCP: submit, monitor, control, and diagnose jobs and workers.
Submit shell jobs with routing constraints: GPU/VRAM, capability tags, OS/arch, host pin, project, cwd, env, priority, dependencies, retries, timeouts, checkpoint grace, and preemptibility.
Submit job arrays or parameter sweeps, run asynchronously or block/wait, and dry-run validation/routing without queueing.
Check a job’s full status, state, exit code, timings, host, dependency/cascade details, and scheduling internals; optionally wait until terminal.
Tail captured stdout/stderr for running or finished jobs, with adjustable byte window.
List jobs with state, project, and finished-time filters plus per-state counts.
Cancel queued/running jobs with a recorded reason, or preempt a running preemptible job.
Inspect broker events to diagnose why jobs were skipped, stalled, failed, preempted, retried, or affected by worker issues.
View fleet capacity and health: worker state, free VRAM/RAM/CPUs, tags, slot usage, last heartbeat, and overall health rollup.
Remove an offline worker from the broker registry.
jobd
jobd is a job broker with an MCP server that lets AI agents submit, route and babysit jobs on a few machines of your own. Like task-spooler or pueue, but across all your machines on Tailscale or a LAN, routing each job to a worker with the VRAM free, with no cloud, Kubernetes or Slurm.
# from any machine on your tailnet:
job submit --project myproj --gpu --vram-required 16 --wait -- python train.py
# → routed to whichever worker has ≥16 GB VRAM free, streamed back to your terminalVRAM routing tracks one GPU per host: the worker reports free memory on GPU index 0 only.
Why it exists
VRAM-fit routing. Each job goes to a worker whose live free VRAM, RAM, CPUs, capability tags and arch/OS fit it, instead of you guessing which box is free.
Preemption with a checkpoint window. A higher-priority job can preempt a running one:
SIGTERM, a grace window for the workload to save into itsJOBD_CHECKPOINT_DIR, thenSIGKILL. A preempted job is not re-run; to resume, submit a new job pointed at the checkpoint (docs/preemption.md).Survives sessions. Jobs live in the broker, not your shell. Submit, close the laptop, check back tomorrow.
Agent-native. An MCP server lets an AI agent submit, monitor and babysit jobs as tool calls (benchmark).
Yours. One broker process on a machine you own. No accounts, no telemetry, no per-GPU-hour billing. Only the optional self-update scripts go online (runbook).
Related MCP server: jungle-grid-mcp-server
Why not just use…?
Tool | What it gives you | Why jobd instead |
| Runs a command on one box | No queue, VRAM-aware routing, preemption, or record of what ran where |
A job queue on one machine | jobd queues across all your machines and routes by live VRAM/CPU fit | |
A mature single-machine queue daemon | Pueue's README puts distributed execution out of scope; jobd is that layer, plus GPU awareness | |
Multi-machine scheduling with HPC roots, single binary | Counts GPUs but doesn't track VRAM; no preemption/checkpoint contract or agent interface | |
Slurm | Datacenter-grade scheduling | Heavy to stand up and operate for 2–3 personal boxes |
SkyPilot / dstack | Runs on clouds and your own machines | SkyPilot's "existing machines" mode installs Kubernetes (k3s) on your boxes; dstack wants Docker and passwordless sudo on every host |
Modal | Serverless GPU compute | Runs on Modal's machines, not yours |
Ray | Distributed compute; Ray Jobs runs any shell command on a Ray cluster | You first stand up and run a Ray cluster |
Install
pip install jobd # broker + CLI
pip install "jobd[mcp]" # adds the MCP server
pip install "jobd[worker]" # adds the worker daemon (jobd-worker)Requires Python ≥ 3.11. scripts/install-worker.sh sets up a worker under ~/jobd-worker with its own venv and a generated config.
Quickstart (single host)
# 1. start the broker (binds 127.0.0.1:8765 by default)
JOBD_ALLOW_NO_AUTH=1 jobd # no-auth is fine for a loopback-only broker
# 2. in another shell, install + start a worker pointed at it
pip install "jobd[worker]"
JOBD_URL=http://127.0.0.1:8765 JOBD_WORKER_HOST=local jobd-worker
# 3. submit a job and wait for it
job submit --project demo --wait -- echo hello
job list
job logs <id>For a multi-host deployment (Docker broker, systemd workers, Tailscale binding, shared auth token), see docs/security.md and the templates in docker-compose.yml and scripts/. Adding a worker to a running fleet is one command:
job fleet add user@newbox # ssh in, install pinned to the broker's version,
# wire systemd units + the self-update timer,
# verify it registers. `job fleet status` shows drift.Day-2 operations (health, draining a worker, upgrades, token rotation, backups) are in docs/runbook.md.
How it works
The broker (FastAPI + SQLite) holds the queue, runs the matcher and resolves per-project priorities. Workers, one per host, poll it, so nothing connects in to a worker: each heartbeats its live capacity, claims jobs it can run, streams logs back and honors preemption. The argv you submit runs as-is (shell=False); job submit --stdin and the MCP jobd_submit tool are the exceptions and run a command string as bash -c <string>. Design notes: docs/ARCHITECTURE.md.
CLI
job submit -p PROJ [options] -- CMD... # queue a job
job list [--state STATE] [--project P] # queue + recent jobs
job status ID | A<id> [--watch] # one job, or an array's aggregate
job logs ID [-n BYTES] # tail captured output
job wait ID # block until terminal
job cancel ID / job preempt ID # stop a job
job adopt --pid N -p PROJ [--gpu] # track a process started outside jobd (Linux)
job workers # fleet snapshot + health
job projects list | set NAME PRI | nudge NAME DELTA
job audit [--project P] [--since 24h] # event historyJob arrays and sweeps, --explain dry runs, job adopt, and a pueue/task-spooler command map: docs/cli.md.
MCP / agent integration
jobd-mcp exposes the queue as nine tools: jobd_submit, jobd_status, jobd_logs, jobd_list, jobd_cancel, jobd_preempt, jobd_events, jobd_workers, jobd_worker_delete. docs/agent-cookbook.md walks through babysitting, preemption, sweeps, and asking why a job won't schedule.
For Claude Code:
claude mcp add jobd \
--env JOBD_URL=http://127.0.0.1:8765 \
--env JOBD_API_TOKEN=<your-token> \
-- jobd-mcpAny other MCP client:
{
"mcpServers": {
"jobd": {
"command": "jobd-mcp",
"env": {
"JOBD_URL": "http://127.0.0.1:8765",
"JOBD_API_TOKEN": "<your-token>"
}
}
}
}JOBD_API_TOKEN must match the broker's token, or every call returns 401. examples/claude-code-hooks/ has optional hooks that nudge (or hard-block) an agent toward submitting heavy commands through jobd.
What the benchmark shows
Claude with the server was compared with Claude using jobd's own job CLI, on 38 questions about a live broker, 3 runs each. With Claude Haiku 5.5 the server got all 38 right in every run and the CLI 29: a pre-registered win (Holm-adjusted McNemar p = 0.008). With Sonnet 5.5 it was 36 against 30 and with Opus 5.5 38 against 37, neither separated. On all three models the server's answers were cheaper and faster; with Sonnet, 0.20 times the cost and 0.17 times the time.
The chart is the Opus 5.5 run (2026-10-18). There the server's median answer cost 0.20 times as much (95% CI 0.15 to 0.28) and took 0.16 times as long (0.13 to 0.20), and it was cheaper and faster on all 38 questions. These were secondary endpoints, measured on an unreleased commit that adds a finished_at window to jobd_list. On an earlier, easier set both setups got 123 of 123.
We wrote the questions and ran the benchmark ourselves; an outside re-run is welcome. Method, controls and limitations: benchmarks/agent_qa; the Haiku and Sonnet runs: 2026-10-20 re-run.
Supported platforms
Component | Linux | macOS | Windows |
Broker ( | tested | expected | expected (WSL recommended) |
CLI ( | tested | expected | expected |
Worker ( | tested | degraded | untested |
CI runs on Ubuntu with no GPU runner (VRAM paths are tested against mocks), and without a systemd user instance the worker still runs jobs but drops its memory caps, process reaping and preemption guarantees.
Configuration
Config files (
projects.yaml,profiles.yaml,classifier.yaml) are all optional and read fromJOBD_CONFIG_DIR; see docs/projects-yaml.md.Environment variables: every
JOBD_*variable is in docs/configuration.md.Concurrency (several jobs per worker): docs/concurrency.md. Retention of logs and job records: docs/runbook.md.
Security
Run the broker on loopback or a Tailscale tailnet, never a public interface. Three stacked controls:
Bind:
JOBD_HOSTis127.0.0.1by default; set it to a Tailscale address (100.64.0.0/10), never0.0.0.0. The broker does not enforce this.Source IP: requests from outside loopback and
100.64.0.0/10get403(JOBD_DISABLE_TAILNET_ACL=1turns this off).Token: every host sets
JOBD_API_TOKEN;JOBD_ALLOW_NO_AUTH=1is for a loopback-only broker, and on any other bind exposes unauthenticated code execution to your tailnet.
/livez, /readyz and /metrics are exempt from both the source-IP check and the token; /metrics shows every worker's hostname and version, so don't port-forward the broker. Threat model and token rotation: docs/security.md.
License
MIT; see LICENSE.
Available Tools
9 toolsjobd_cancelB
Cancel a job (queued → cancelled; running → SIGTERM via worker signal poll, ~2s).
Inputs: job_id (required), reason.
jobd_eventsA
The broker's event stream — the surface that explains WHY, not just what. /jobs says a job is queued; only this says it has been skipped 400 times because no worker advertises cuda-32gb, or that its dependency was cancelled, or that a watchdog killed it. Filter by since (2h/3d/1w), event type, job_id, project, or source (broker|worker). Use when a job is not doing what you expect and jobd_status alone does not explain it.
Inputs: event, limit, since, job_id, +2 more.
jobd_listA
List jobs on the broker with per-state counts. Defaults to the active set (queued/assigned/running); filter by state (e.g. ['failed']), project, or a finished_at window (finished_after/finished_before) to find past runs. Each row is a compact summary: job_id, project, state, host, exit_code, queued_at, started_at, finished_at, duration_s — call jobd_status for a job's full record. Use to answer 'what is running / queued right now?', 'what failed overnight?', or to locate a job id you've lost.
Inputs: limit, state, project, finished_after, +1 more.
jobd_logsA
Tail the captured stdout/stderr of a job (workers stream output to the broker's per-job log as it runs). Returns log_tail (last tail_bytes, default 8 KiB, max 1 MiB) plus size_bytes/returned_bytes/truncated — works for running AND finished jobs. Use to check progress mid-run, diagnose a failure's traceback, or grab a job's final output.
Inputs: job_id (required), tail_bytes.
jobd_preemptA
Preempt a running/assigned preemptible job (worker SIGTERMs with grace; final state 'preempted'). Refused if not preemptible or not running.
Inputs: job_id (required).
jobd_statusA
The full JobInfo record for one job_id: state, exit_code, timings, host, and the scheduling internals — depends_on + cascade policy (depends_on_any_exit), pending cancel/preempt signal, resolved profile, requires (gpu/tags/idempotent), host pin, fast_path, timeouts, termination_reason. Use for a quick state check AND for debugging why a job routed/failed/stalled. Pass wait=true to block until terminal or wait_timeout_s.
Inputs: job_id (required), wait, wait_timeout_s.
jobd_submitA
Submit a job to the jobd broker. Default async; pass wait=true to block up to wait_timeout_s (server clamps to 270).
Inputs: cwd (required), command (required), project (required), gpu, +6 more.
jobd_worker_deleteA
Remove a worker from the broker registry. The broker refuses (409) if the worker is still online — caller stops the worker process or waits for the heartbeat sweeper first.
Inputs: host (required).
jobd_workersA
Fleet snapshot: every registered worker with state (online/stale/offline), live capacity ad (free_vram_gb, unregistered_vram_gb, free_ram_gb, idle_cpus), capability tags (cuda tiers, arch/os), slot usage (running/max_concurrent), and last_heartbeat — plus an overall health rollup (healthy|degraded|empty). Use before submitting GPU work to see what's free, or to diagnose why a job isn't being dispatched.
Inputs: no named parameters.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 9 tools
Each tool targets a distinct resource+action: list (summary rows), status (full record), events (causal 'why'), logs (output tail), with descriptions explicitly cross-referencing each other to prevent confusion. Cancel vs preempt and workers vs worker_delete are cleanly bounded. The list/status/events/logs cluster all touch job data but their scopes and use cases are clearly differentiated.
All names use a consistent jobd_ prefix with snake_case (jobd_worker_delete fits cleanly). The set mixes verb-first (list, cancel, preempt, submit) with noun-first (events, logs, status, workers), which is a minor deviation but reads predictably and clearly.
Nine tools is well-scoped for a job broker: submit/query/cancel/preempt for jobs, plus events, logs, and worker fleet management. Each tool earns its place with no filler.
Core job lifecycle (submit, list, status, cancel, preempt) and diagnostic surfaces (events, logs) plus fleet visibility are well covered. Minor gaps exist—no explicit job retry/resubmit, worker registration, or queue pause/resume—but agents can work around these.
Maintenance
Related MCP Connectors
AI cloud for agents: launch GPUs, run jobs that stop their own billing, schedule runs.
GPU compute for agents: run commands on an L4/A100/H100, watch logs, billed per second.
On-demand GPU nodes for agents: create nodes, run commands, and submit jobs, billed by the minute.
HiveCompute MCP Server — decentralized inference router for AI agents
Related MCP Servers
- AlicenseBqualityDmaintenanceagent-mq is a message queue that enables AI coding agents to communicate with each other across sessions and machines. Agents can send messages, delegate tasks, and coordinate work — all through MCP tools. Supports Claude Code, Cursor, Codex, OpenClaw, and any MCP-compatible tool. UUID-based authentication with per-user data isolation. Self-hostable with Docker.72MIT
- AlicenseAqualityDmaintenanceJungle Grid MCP Server lets AI agents submit, estimate, monitor, and retrieve logs for GPU workloads through Jungle Grid. It enables agentic execution for inference, training, fine-tuning, and batch jobs without manually choosing GPU providers or infrastructure.822 npm4MIT
- FlicenseNot gradedqualityAmaintenanceAn MCP server for monitoring and managing multi-cluster Slurm GPU jobs, enabling AI agents to execute commands, check allocations, and explore logs across HPC clusters.1-
- AlicenseNot gradedqualityDmaintenanceA local-first job broker with MCP and HTTP interfaces for orchestrating AI work, with cost-aware routing, observable state transitions, and human control.MIT