Skip to main content

mavctl

Headless, agent-first MAVLink ground-control CLI for ArduPilot vehicles.

mavctl is ArduPilot-first and built to be driven by both humans on a terminal and AI coding agents (Claude Code, Codex, OpenClaw, …). A resident daemon keeps the MAVLink link alive and caches vehicle state; every CLI call is one short, structured request to that daemon.

Status: developed and verified against ArduPilot SITL. It has not been proven across the breadth of real MAVLink vehicles and is not presented as ready for production flight on a real aircraft.

Why mavctl

GUI ground stations such as Mission Planner or QGroundControl are excellent for a human at the controls — and a poor interface for a shell script or an LLM agent: clickable UIs, no stable exit codes, no machine-readable output.

mavctl takes the other side of that trade:

  • the daemon owns the MAVLink connection and continuously caches telemetry, so each command is quick and stateless;
  • every command prints human-readable output by default and structured JSON with --json;
  • failures carry explicit exit codes (3 daemon down, 4 link lost, 5 guard rejection, 6 vehicle NACK / timeout) instead of stack traces;
  • dangerous operations pass safety guards before anything reaches the vehicle;
  • mavctl embeds no LLM — it is designed to be called by agents such as Claude Code, Codex or OpenClaw, or by plain bash.

Current capabilities

Implemented commands — this is the complete list:

mavctl daemon start|stop|status
mavctl status
mavctl telemetry
mavctl arm
mavctl disarm
mavctl mode <MODE>
mavctl takeoff --alt <metres>
mavctl land
mavctl rtl

Cross-cutting behaviour:

Flag / behaviour Meaning
--json structured output on stdout; errors as {"error": {...}} on stderr
--confirm required on every state-changing command; without it exit code 5
--dry-run run the exact same guards, never reach the vehicle
--wait --timeout <s> block until the target state is reached (default 60 s)
idempotent repeats re-applying an achieved change succeeds ("already armed")
transaction safety ACK/NACK handling, serialized commands, link-loss abort

Not implemented — current scope only, not a roadmap promise:

Mission upload/download/start
Parameters
Geofence
Log download / analysis
Firmware flashing
Multi-vehicle orchestration

Quickstart with ArduPilot SITL

Requires Python >= 3.10 and uv. Always bring up SITL first; do not point an agent-driven workflow at a real vehicle.

Terminal 1 — start ArduPilot SITL:

sim_vehicle.py -v ArduCopter --out udp:127.0.0.1:14550

Terminal 2 — install from source and connect:

uv sync
uv run mavctl daemon start --connect udp:127.0.0.1:14550
uv run mavctl status --json

Safe takeoff to 10 m and return to launch:

uv run mavctl mode GUIDED --confirm --wait
uv run mavctl arm --confirm
# Poll status --json until armed=true (the arm ACK can beat the heartbeat)
uv run mavctl takeoff --alt 10 --confirm --wait --timeout 45
uv run mavctl rtl --confirm --wait --timeout 120
uv run mavctl daemon stop

Safety notes — read before pointing mavctl at anything that flies:

  • Validate every workflow in SITL first; treat real-aircraft use as its own review process.
  • After arm, poll status --json until armed=true before takeoff: the COMMAND_ACK can arrive about one heartbeat before reported state catches up.
  • End flights with rtl / land, not disarm. Ordinary disarm requires provable ground contact (ground_state_unknown otherwise).
  • disarm --force is an emergency motor stop only — in flight it can cause a crash.
  • There is no arm --force anywhere in mavctl; pre-arm checks cannot be bypassed.

Installation

From PyPI

mavctl's first production release (0.2.0) is being prepared. After the PyPI release is published, install with:

uv tool install mavctl
uvx mavctl --help
pipx install mavctl

Until that release is published these commands have nothing to fetch from production PyPI; TestPyPI rehearsal artifacts are not production releases. Release status and the publishing runbook live in docs/PUBLISHING.md.

From source

For development from source, use uv sync and uv run mavctl …:

git clone https://github.com/LeaderOnePro/mavctl.git
cd mavctl
uv sync
uv run mavctl --help

Agent Skill

The repository ships a portable agent Skill under skills/mavctl-flight/ (entrypoint plus workflows / safety / troubleshooting references). It is a source asset of this repo, not an installed package.

To use it with an agent runtime, install or symlink this directory according to that runtime's current Skill discovery convention.

Example for Claude Code, project-local to this repository:

mkdir -p .claude/skills
ln -s ../../skills/mavctl-flight .claude/skills/mavctl-flight

That is one concrete example, not a universal convention — runtimes differ.

Safety model

Short version; full details in skills/mavctl-flight/references/safety.md:

  • the daemon owns the vehicle link; CLI calls are short transactions;
  • state-changing commands require --confirm; --dry-run previews decisions;
  • exit 4 = no live vehicle state (link lost / heartbeat expired), including mid---wait loss (link_lost_during_wait);
  • exit 5 = guard rejection with structured reason + hint;
  • exit 6 = vehicle NACK / ACK timeout / wait timeout;
  • force-arm does not exist at any layer (CLI option, RPC field, adapter verb);
  • ordinary disarm needs positive ground evidence, else ground_state_unknown (exit 5);
  • after link loss, status reflects stale cache: armed renders unknown/n/a, never silently disarmed.

Development

uv sync
uv run ruff check .
uv run mypy .
uv run pytest -m "not sitl"
uv run pytest -m sitl   # requires a running ArduPilot SITL

Further reading:

License

MIT — Copyright (c) 2026 LeaderOnePro.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

mavctl-0.2.0.tar.gz (136.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

mavctl-0.2.0-py3-none-any.whl (37.1 kB view details)

Uploaded Python 3

File details

Details for the file mavctl-0.2.0.tar.gz.

File metadata

  • Download URL: mavctl-0.2.0.tar.gz
  • Upload date:
  • Size: 136.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for mavctl-0.2.0.tar.gz
Algorithm Hash digest
SHA256 8b076de80f9eb091ccc7e308e64a02c4308b5c227b335ca51924b3dc5e09628d
MD5 be7bb1cf55a6f3a7b091ed545f392bff
BLAKE2b-256 66d1b9719f7475b705fe8185a03d5a03e0c7df63b54846a87b9abb6a72fdf6f0

See more details on using hashes here.

Provenance

The following attestation bundles were made for mavctl-0.2.0.tar.gz:

Publisher: publish.yml on LeaderOnePro/mavctl

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file mavctl-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: mavctl-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 37.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for mavctl-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 03c35f5b16c26f666540a34b00822beb7c77e0a3abd53cf79fc0176c8b94aaa1
MD5 e87ae369006afd5892c517e8ee008fdf
BLAKE2b-256 48a7fa5a4b4ef130142a7ad9c8070dea7a3ca85550091a1f22326c86101b8060

See more details on using hashes here.

Provenance

The following attestation bundles were made for mavctl-0.2.0-py3-none-any.whl:

Publisher: publish.yml on LeaderOnePro/mavctl

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 files

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page