Skip to content

Three layers: core, skill, MCP

This is a settled design decision. Changes to fleet fit into it; they do not reopen it.

The layers

Layer What it is What it is not
Core The fleet CLI, plus its state and configuration (the machines you added, the telemetry cache). Installed once per machine you work on; the machines it looks at need nothing but SSH. A person can always run it by hand in a terminal. Not per agent. Nothing else holds fleet state or logic.
Skill A prompt (SKILL.md, or a marked region in a file an agent reads) that tells an agent how to use the CLI properly: which command for which request, what to ask the user, what never to do. One per agent format; the same text everywhere. No code, no state. A skill never does anything the CLI cannot.
MCP For agents that cannot run commands. It executes the same CLI for them and returns its output. Not a second implementation. Every tool is a thin wrapper over one CLI command.

So every way into fleet — a person in a terminal, an agent following its skill, a desktop app through MCP — ends in the same fleet command on the same machine, reading and writing the same state. Many agents on one machine share one core.

Rules that follow

  1. A capability is added to the CLI first. The skill then describes it, and MCP exposes it by calling the CLI. Neither ever imports fleet's internals to do something the CLI does not.
  2. Reads run in parallel and never wait. State files are only ever replaced atomically (a unique temporary file, then a rename), so any reader sees a complete file, never a half-written one.
  3. Writes go through one queue. A write is a small change ("tag box", "add this endpoint"). Each writer takes a ticket in a first-come, first-served queue beside the file; when its turn comes it loads the file as it is now, applies its own change and replaces the file — never by saving a copy loaded earlier. A turn holds only local file work, milliseconds, never a network call (network first, then the change), so a writer waits only for the few changes queued ahead of it and nobody waits on a slow machine. A writer that died mid-turn is skipped. A change to a file on another machine (its machine.yaml) is written only if the file is still what was read, and otherwise read and made again, so two people editing it never erase each other.
  4. The installer never overwrites an existing core. It detects fleet first (uv tool, pipx, an editable or source install, anything on PATH). It keeps what is there, upgrades only a uv-installed older copy, and never modifies fleet's state. Installing fleet for another agent only adds that agent's skill (or MCP entry).
  5. Irreversible commands stay with the user. fleet rm is not an MCP tool, and the skill tells agents to leave it, and anything that touches every machine, to the user.
  6. fleet manages no access. It reaches machines exactly as the user's own ssh does (their config, keys, agent, Tailscale SSH, a rental provider's key), writes no keys anywhere, and keeps no center or list of who may reach what. When a machine refuses, fleet doctor names the fix for the user to make; fleet never makes it. Every machine running fleet is equal: there is nothing to sync between them.
  7. What a machine says about itself lives on that machine (machine.yaml: cost, tags, notes, where its models, datasets and media are). The probe reads it, so everyone who looks at the machine sees the same answer.

Where it lives

  • Core: src/fleet/ (CLI in cli.py, state in state/, operations in ops/).
  • Skill text: src/fleet/agents/usage.py; where each agent reads it: src/fleet/agents/registry.py; written by fleet setup. The copy shipped in the repo (skills/fleet/SKILL.md) is generated by scripts/build_dist.py.
  • MCP: src/fleet/mcpserver.py.