| name | odu-mcp |
| description | odu MCP server launcher — drive CI from a coding agent. `bin/serve` resolves odu via Nix and runs `odu mcp` in the cwd. See the repo README for the tools/resources and override knobs. |
| user-invocable | false |
odu-mcp
The agent face of odu — an MCP stdio server
that re-exposes a live CI run as agent tools (run, node_rerun,
node_cancel, lane_cancel, wait_for_settle, cancel, lease, release) and subscribable resources
(surface://streams/nodes, surface://collections/logs/{id}), so Claude Code /
Codex / opencode / Gemini CLI drive CI with structured calls instead of
scraping terminal output.
lease / release are the agent-held venue layer: hold a free box across
discrete tool calls without re-queuing between runs. lease returns
immediately (held or waiting); re-call or inventory to observe the line.
run reuses held hosts and does not release them on exit.
wait_for_settle defaults to fail-fast: it returns the instant the first node
goes red (fail_fast_tripped: true, settled: false), so the agent drills into
the failure without blocking on the slow lanes — its failed[] is only what's
red so far, and only passed: true (a fully settled run) is a trustworthy green.
A verdict about an observed run is stamped with that run's identity — sha7
always, seq whenever the coordinator reserved an ordinal — so it's clear
which run it describes (seq is null only when none was reserved: a wait
that saw no frame, or the rare case the coordinator couldn't reserve one);
and unposted[] carries full owed GitHub status rows
({context, lastError, attempts}) not yet confirmed (reporting debt never
blocks settle — the test verdict stays the truth).
Called with no run live
it fails loud (an error mirroring odu status, not an empty settled: false),
and an optional expected_sha (prefix-matched against the run's sha7) refuses
loud when the live run's commit doesn't match. A run that is still PROVISIONING
— run returns as soon as the coordinator serves its socket, which is now
before it claims a machine — is a live run: wait_for_settle blocks on it, the
nodes resource shows _ci-setup@<platform> running, and its log resource
carries the runner closure's copying path … progress. A claim that never
succeeds arrives as a red _ci-setup@<platform> verdict rather than as a
run-time error. If the coordinator's socket
closes before it publishes a terminal frame, the verdict comes from the run's
finalized record on disk — never green for a run torn down mid-flight. The
nodes resource carries the same unposted. MCP run tees coordinator
stdout/stderr to .ci/<sha7>/runs/<seq>.log.
A settled node's surface://collections/logs/{id} is the last 64KB of its output
and now reliably ENDS at the recipe's final line: a run that settles on its own
holds its lanes open until every node has finished streaming, because a node's
status and its output travel on different streams and the status one arrives
first — so the recipe summary the agent came for used to be the part that never
made it. Read .ci/<sha7>/<platform>/<node>.log for the whole thing. A lane that stops streaming with output still owed stamps
[odu] log truncated: … into the log, so a drill-in never reads a cut log as a
complete one.
cancel stops the live run and waits until it's torn down; node_cancel stops
one node (ci::fmt@plat) and lane_cancel drops a whole platform while the rest
of the run settles (status cancelled, not errored/failed). run's supersede
cancels a run already live here before starting (the "stop this, run the fixed
commit" move), linger keeps the coordinator serving past settle so a node can
be rerun afterwards, and no_wait fails immediately when every host in a venue
pool is busy (default: wait in line). Together they let the agent loop call off
or replace a run instead of stranding it or hitting "a run is already in
progress".
bin/serve is self-contained — it resolves odu via nix run and serves over
stdio in the consumer's repo (dialing .ci/odu.sock). Set ODU_FLAKE to
override the odu flake-ref (default github:juspay/odu); a repo that
re-exports odu can point it at its own pinned output with ODU_FLAKE=.#odu.
Full docs in the repo README.
This skill primitive exists for APM's deployment convention — it lands
bin/serve at .agents/skills/odu-mcp/bin/serve in the consumer's working
tree (APM's skills-convergence path), which keeps the launcher available even
before apm install runs on a fresh clone. The package is mechanically a
"skill" in APM's primitive vocabulary; semantically it's a tool launcher.