Fleet

Run Orchestrator Zero from an MCP client

Ask Claude Code or another MCP client why a job waits, which node is unhealthy, and have it block the node, with your rights and your confirmation.

For: cluster operators.

Connect a client with your operator identity or permitted tenant key. Inspect the changes it proposes and confirm those you intend to apply.

Orchestrator Zero has an MCP server for operators. An MCP client, such as Claude Code, lists nodes and their health, explains why a job waits, reads traces, costs and the audit log, and acts: it blocks a node, cancels a job, rolls back a rollout or decides an approval. Every call runs with your role, which the APIs check as they do for the CLI, and every change asks you first.

This is not the MCP server for agents, which runs a tenant's agents as tools on the edge. This one operates the cluster, on the management port. Which MCP clients were run against the agents' server, and what each did, is in the interop matrix.

Connect

In one command: oz0 setup

oz0 setup writes what a coding assistant reads to use Orchestrator Zero, in the project's own files, or in your own configuration with --global: this server's entry where the tool reads one, and the build kit's skills in .agents/skills for every tool:

Terminal
oz0 setup claude-code      # .mcp.json, and the kit in .agents/skills and .claude/skills
oz0 setup codex            # .codex/config.toml, a section of AGENTS.md, the kit in .agents/skills
oz0 setup gemini           # .gemini/settings.json, a section of GEMINI.md, the kit
oz0 setup cursor           # .cursor/mcp.json, the kit
oz0 setup copilot          # .mcp.json (GitHub Copilot's CLI, coding agent and VS Code), the kit
oz0 setup opencode         # opencode.json, the kit
oz0 setup goose --global   # ~/.config/goose/config.yaml, the kit in ~/.agents/skills
AssistantThe MCP serverAlsoWith --global
claude-code.mcp.jsonThe kit in .claude/skills as well, which Claude Code reads instead of .agents/skills~/.claude.json, ~/.agents/skills and ~/.claude/skills
codex.codex/config.toml, [mcp_servers.orchestrator-zero]A section of AGENTS.md$CODEX_HOME/config.toml and $CODEX_HOME/AGENTS.md (~/.codex by default)
gemini.gemini/settings.jsonA section of GEMINI.md~/.gemini/settings.json and ~/.gemini/GEMINI.md ($GEMINI_CLI_HOME instead of ~)
cursor.cursor/mcp.json~/.cursor/mcp.json
copilot.mcp.json, which VS Code reads too$COPILOT_HOME/mcp-config.json (~/.copilot by default)
opencodeopencode.json, under mcp~/.config/opencode/opencode.json ($XDG_CONFIG_HOME instead of ~/.config)
goosenone: goose reads MCP servers only from your own configuration~/.config/goose/config.yaml, under extensions

The server it adds is orchestrator-zero, which runs oz0 mcp: the bridge below, which reads your CLI context when the assistant starts it. No token, key or password is written into any file, so the files can be committed and shared; each person's own context decides what the tools may do. oz0 setup --context <file> names a context file (only its path goes into the entry), and --command the binary to start, such as /usr/local/bin/oz0.

The section of AGENTS.md or GEMINI.md says what the server is for, that a secret never goes into a file or a prompt (oz0 secret set reads it from standard input), and where the documentation for assistants is: llms.txt lists every page, and each page is also served as Markdown under /raw/, such as /raw/fleet/mcp-client.md. --docs-url points it at a mirror of these docs.

  • Safe to run again. A second run changes nothing. A file it changes is copied to <file>.oz0-backup-<time> first; the rest of the file (your other servers, your own instructions) stays as it is.
  • --update also replaces the kit's skills of another version that nobody changed since setup wrote them, and says what changed.
  • --dry-run prints the changes as a diff and writes nothing.
  • --remove takes out only what setup wrote: its server entry, its marked section, the skills as it wrote them. A skill you edited, or an orchestrator-zero entry that does not start oz0 mcp, stays; setup says so.
  • It only writes files. It never runs the assistant, installs, downloads or signs in to anything. A project file that is a symbolic link is refused; in your home folder a link is followed.

Claude Code also writes ~/.claude.json itself, so run oz0 setup claude-code --global while no Claude Code session runs, or use the project's .mcp.json. The assistant asks before it uses a project's MCP server the first time: Claude Code asks you to approve the project's servers, and Codex reads a project's .codex/config.toml only in a project you trust; Gemini CLI and GitHub Copilot read a project's skills and servers once you trust its folder.

Claude Code on your machine, with your operator certificate

The CLI serves the tools on stdio and sends each request to the management port with the operator certificate of its context (OZ0_CONTEXT, or --context), as every other command does:

Terminal
claude mcp add orchestrator-zero -- oz0 mcp

With another context, or another client, run the same command, oz0 mcp, as the server. For a client's JSON configuration:

.mcp.json
{
  "mcpServers": {
    "orchestrator-zero": {
      "command": "orchestrator-zero",
      "args": ["mcp"],
      "env": { "OZ0_CONTEXT": "/home/ada/.config/orchestrator-zero/prod/context.json" }
    }
  }
}

An operator certificate is a cluster admin's, so the tools can do what orchestrator-zero can. Logs go to stderr; stdout carries only MCP.

Over HTTP, with a tenant API key

A client that connects to remote servers uses https://<management>:8443/admin/mcp with a tenant API key as the bearer token. The key's role is its role here:

RoleCan
readerRead the tenant: nodes, jobs, why they wait, plugins, rollouts, costs, quotas, findings
operatorAlso cancel and retry jobs and decide approvals
adminAlso block and unblock nodes, start and roll back rollouts, set kill switches, read the audit log
Terminal
oz0 apikey create claude-ops --tenant acme --role admin --expires 30d
claude mcp add --transport http orchestrator-zero https://mgmt.example.com:8443/admin/mcp \
  --header "Authorization: Bearer oz0_..."

An admin key works here too, with the permissions it names instead of a role: each tool's call needs the permission its admin API call does, so a key with audit.read reads the audit log and is refused the rest. A tool that reaches the runtime API (starting, cancelling or retrying jobs, deciding approvals) needs a runtime key of the role above.

Terminal
oz0 apikey create claude-audit --tenant acme --admin --permission audit.read --permission tenant.read --expires 30d

The management port's certificate comes from the cluster's CA: the client must trust ca.crt (for Node.js clients, NODE_EXTRA_CA_CERTS=ca.crt). A key works only for its own tenant, and a runtime key reaches the admin API only through this endpoint. A console session and a CLI session (oz0 login) do not work here: the endpoint refuses them.

Tools

Reading tools are annotated readOnlyHint: true; acting tools are not, and those that stop, cancel or replace something are destructiveHint: true, so your client can ask before it calls them.

ToolWhat it doesNeeds
list_tenantsThe tenants you may see, with budgets and this month's spendreader
list_nodes, get_nodeNodes: state, online, health and why, labels, runtime, queuesreader
list_jobs, get_jobRecent jobs from Temporal, running ones too; one job's status, cost and approvalsreader
why_waitingWhat a job waits for, most important first: a node, a person, a budget, a retry, a child jobreader
get_job_trace, get_job_stepA job's steps and findings; what one step read and wrotereader; the step needs content
list_approvalsWhat waits for a person, with deadlinesreader
list_findingsRecent jobs with warnings, such as a possible loopreader
list_plugins, list_rollouts, get_rollout, list_runtimesPlugins, rollouts and their gates, runtime versionsreader
get_costs, list_quotasCosts by model and agent; quotas and what they have usedreader
list_kill_switchesKill switches, active and releasedreader
list_auditThe audit log, newest first: 25 entries a page, limit up to 100, and page_token for the nextadmin
cancel_jobCancel a job and the jobs it delegated tooperator, confirmed
retry_jobRun an ended job again under a new IDoperator
decide_approvalApprove, deny, or answer a questionoperator, confirmed
block_node, unblock_nodeBlock a node, or let it work againadmin; blocking is confirmed
start_rollout, roll_back_rolloutMove nodes to a runtime version step by step, or roll a rollout backadmin, confirmed
set_kill_switch, release_kill_switchStop a tenant, an agent or a tool across the fleet, or release itadmin; setting is confirmed

Tools that act in a tenant take tenant; without it, an API key acts in its own tenant and an operator in default. Answers are the APIs' own, as JSON text and as structuredContent, with the proto field names.

Every caller sees every tool. When your role is not enough, the tool answers with an error that says which role it needs, for example permission_denied: apikey:3f9c... is a reader in tenant acme, and blocking a node needs an admin. What jobs read and wrote, prompts, answers and tool data, needs the content permission: without it, get_job leaves the answer out and get_job_step is refused.

Confirmations

A destructive tool requires confirmation through the client before it acts:

  • Clients that can ask (MCP 2026-07-28 with form elicitation, such as the Python SDK, or any client through oz0 mcp) show a question such as Block node node-3f2a? It stops taking work and its certificate stops working. Reason: disk is failing, with one checkbox. Only a yes carries it out; a no answers The person did not confirm block_node: nothing was done. These clients are asked even when the call says confirm: true.
  • Clients that cannot ask (over HTTP in the 2025 revisions) get the question back as an error, and must call again with "confirm": true once you agreed. The client must obtain your agreement before sending that flag.

The server verifies the client's response; it cannot prove that a person saw the question or answered it. Use a client that presents the confirmation to you and prevents the model from supplying the answer itself.

An elicitation confirmation is good for ten minutes, for the same caller, tool and arguments.

The audit log

Every call is in the audit log as mcp.call, by you (operator:<name> or apikey:<id>), with via: mcp, the tenant, the tool, the IDs it acted on and the outcome: ok, denied, error, invalid, confirmation_asked, declined or unconfirmed, and for a change whether you confirmed it when asked or with the flag. The entries the change itself writes, such as node.block, job.retry and an approval's run.approval.decide, say via: mcp too. When management runs apart from the edges, the entries an edge writes (run.approval.decide, run.start, job.retry) do not, and the mcp.call entry is the record.

The entry goes to the tenant the call names when you may read that tenant, and otherwise to your own (an API key's tenant, or the cluster's entries for an operator). It keeps the IDs the tool takes, up to 200 bytes each, and no other argument: an approval's comment stays out of the audit log.

Terminal
oz0 audit --action mcp.
oz0 audit --action node.block

Arguments are not recorded, so an approval's comment or answer stays out of the log.

Copyright © 2026