Intelligent tools for flight controllers.
MAVForge is an open-source engineering toolkit for configuring, diagnosing and (soon) calibrating MAVLink flight controllers, starting with ArduPilot. One tested core is exposed three ways:
- a Python API for scripts and other programs
- a one-shot CLI (
mavforge) with--jsonoutput and stable exit codes - an MCP server (
mavforge-mcp) so AI agents can use the same operations under the same safety policy
MAVForge complements interactive ground stations such as MAVProxy and Mission Planner. It does not replace them: there is no console, map, mission planning or flight control. See docs/mavproxy.md.
Status: pre-alpha (0.1.0.dev0). Parameter changes work, behind a reviewed and approved plan with read-back verification. Gyro, level, compass and 6-position accel calibration work as verified state machines; see docs/calibration.md. Nothing MAVForge reports means a vehicle is safe to fly.
| Operation | API | CLI | MCP tool | Verified against |
|---|---|---|---|---|
| Identify vehicle (autopilot, firmware, git hash, board, capabilities, SITL detection) | vehicle.info() |
mavforge info |
vehicle_info |
fake vehicle, ArduCopter 4.7.2 SITL, ArduPlane 4.6.2 hardware |
| Status snapshot (armed, mode, sensors, pre-arm flag, battery, GPS, STATUSTEXT) | vehicle.status() |
mavforge status |
vehicle_status |
fake vehicle, ArduCopter 4.7.2 SITL, ArduPlane 4.6.2 hardware |
Download all parameters, glob filter, gap recovery; prefers MAVFTP's packed param.pck (far fewer round trips) when the vehicle reports FTP support, falling back to PARAM_REQUEST_LIST on any FTP-specific failure |
vehicle.params.list() |
mavforge params list |
params_list |
fake vehicle, ArduCopter 4.7.2 SITL (1369 params), ArduPlane 4.6.2 hardware (1117 params over USB serial: 1.11s via FTP vs 16.28s via PARAM_REQUEST_LIST) |
| Read one parameter | vehicle.params.get() |
mavforge params get |
params_get |
fake vehicle, ArduCopter 4.7.2 SITL |
Back up all parameters with vehicle identity (JSON, optional .param export) |
vehicle.params.backup() |
mavforge params backup |
params_backup |
fake vehicle, ArduCopter 4.7.2 SITL |
Diff vehicle vs a backup or .param file; diff two files offline |
vehicle.params.diff(), mavforge.diff_backups() |
mavforge params diff |
params_diff |
fake vehicle, ArduCopter 4.7.2 SITL |
| Diff vehicle vs the firmware's own shipped defaults (what's changed from stock); needs MAVFTP | vehicle.params.diff_defaults() |
mavforge params diff-defaults |
params_diff_defaults |
fake vehicle, ArduCopter 4.7.2 SITL (42/1369 differ), ArduPlane 4.6.2 hardware (76/1117 differ) |
Diagnose: classified findings (fault / warning / suspected / not checked / passed) from vehicle pre-arm reports, sensor health and ArduPilot configuration rules; propose_fixes also builds a change plan for findings with a safe, judgment-free default; baseline also compares against a saved backup (the same comparison params diff does on its own), surfaced as report.baseline |
vehicle.diagnose(propose_fixes=..., baseline=...) |
mavforge diagnose [--propose-fixes] [--baseline ID-OR-FILE] |
diagnose (baseline_backup_id - agents name a backup by id only, same as params_diff) |
fake vehicle, ArduCopter 4.7.2 SITL, ArduPlane 4.6.2 hardware (all incl. propose_fixes) |
Setup status: ArduPilot's own first-time setup sequence as a checklist (done / needed / recommended / unknown / not_applicable per step), derived from diagnose; next_step names what to run next |
vehicle.setup_status() |
mavforge setup |
setup_status |
fake vehicle, ArduCopter 4.7.2 SITL |
| List backups | mavforge.list_backups() |
mavforge params backups |
params_backups |
fake vehicle |
| Change parameters: plan (validated against firmware metadata), human approval, backup, write, read-back verification | vehicle.params.plan(), mavforge.approve_plan(), vehicle.params.apply() |
mavforge params set, params plan, approve, params apply, params plans |
params_plan, params_apply, params_plans (with --allow-configure) |
fake vehicle (incl. rejected, clamped, ignored writes, link loss), ArduCopter 4.7.2 SITL, ArduPlane 4.6.2 hardware |
| Calibrate: gyro, level, onboard compass, 6-position accelerometer (guided, approved, evidence-based success) | vehicle.calibration.request()/start()/status()/confirm()/cancel() |
mavforge calibrate gyro|level|compass|accel|list |
calibration_procedures, calibration_request, calibration_start, calibration_status, calibration_cancel (with --allow-calibrate; accel not via MCP) |
fake vehicle, ArduCopter 4.7.2 SITL (all four), ArduPlane 4.6.2 hardware (compass, gyro, level) |
Radio (RC) calibration: guided stick sweep that proposes RCn_MIN/MAX/TRIM as a change plan |
vehicle.calibration.start_radio(), then approve_plan and params.apply |
mavforge calibrate radio [--plan-only] |
not via MCP | fake vehicle, ArduCopter 4.7.2 SITL, ArduPlane 4.6.2 hardware |
Board orientation check: three guided poses that find how the flight controller is mounted and propose AHRS_ORIENTATION as a change plan |
vehicle.calibration.start_orientation(), then approve_plan and params.apply |
mavforge calibrate orientation [--plan-only] |
not via MCP | fake vehicle, ArduCopter 4.7.2 SITL (simulated rotated board), ArduPlane 4.6.2 hardware |
Flight mode assignment: sweeps the FLTMODE_CH switch to confirm each requested position is reachable, then proposes FLTMODE1..FLTMODE6 as a change plan |
vehicle.calibration.start_flight_modes(), then approve_plan and params.apply |
mavforge calibrate flight-modes FLTMODEn=VALUE ... [--plan-only] |
not via MCP | fake vehicle, ArduCopter 4.7.2 SITL, ArduPlane 4.6.2 hardware |
| Restore from a backup (identity-checked) | vehicle.params.plan_restore() |
mavforge params restore |
params_plan_restore |
fake vehicle, ArduCopter 4.7.2 SITL, ArduPlane 4.6.2 hardware |
Retry whatever a previous apply left unresolved (unknown/not_attempted only — typically a link loss) at its original value |
vehicle.params.plan_retry() |
mavforge params retry <plan-id> |
params_plan_retry (with --allow-configure) |
fake vehicle |
| List operations and permissions | mavforge.capabilities() |
mavforge capabilities |
mavforge_capabilities |
unit tests |
Hardware evidence so far is one board: ArduPlane 4.6.2 on a USB-connected flight controller.
- Python API (2026-10-21): the read-only tests passed (tests/hardware).
- CLI (2026-10-21 to 23):
status,diagnose, backup/diff, and a verified two-parameter change and restore were run. - MCP has not been run against hardware.
Only ArduPilot has a platform adapter. Other autopilots get a generic read-only adapter that has not been tested.
Requires Python ≥ 3.12 and uv.
git clone https://github.com/msmith626/mavforge.git && cd mavforge
uv sync
uv run mavforge --help# ArduPilot SITL in Docker (see docs/sitl.md)
docker build -t mavforge-sitl:4.7.2 docker/sitl
docker run --rm -p 127.0.0.1:5760:5760 mavforge-sitl:4.7.2 &
uv run mavforge -c tcp:127.0.0.1:5760 --timeout 30 info
uv run mavforge -c tcp:127.0.0.1:5760 status
uv run mavforge -c tcp:127.0.0.1:5760 diagnose # add --fail-on fault in scripts
uv run mavforge -c tcp:127.0.0.1:5760 params list 'COMPASS_*'
uv run mavforge -c tcp:127.0.0.1:5760 --json params get FRAME_CLASS
# Snapshot before changing anything, then see what changed later
uv run mavforge -c tcp:127.0.0.1:5760 params backup --param-file before.param
uv run mavforge params backups
uv run mavforge -c tcp:127.0.0.1:5760 params diff <backup-id>
uv run mavforge params diff known-good.param --against <backup-id> # offline
# Change parameters: shows the plan and asks before writing; verifies by read-back
uv run mavforge -c tcp:127.0.0.1:5760 params set BATT_FS_LOW_ACT=2 ARMING_CHECK=1
uv run mavforge -c tcp:127.0.0.1:5760 params restore <pre-change-backup-id> # undo
# Calibrate: shows what you'll need to do, asks, then guides you live
uv run mavforge calibrate list
uv run mavforge -c /dev/ttyACM0 calibrate compass
uv run mavforge -c /dev/ttyACM0 calibrate radio # sweep the sticks, review the plan, apply
uv run mavforge -c /dev/ttyACM0 calibrate orientation # tilt three ways: is AHRS_ORIENTATION right?To use a real flight controller over USB, run mavforge -c /dev/ttyACM0 info. For a
wireless link instead (tested against a SpeedyBee F405 WING's onboard WiFi), see
docs/wireless.md.
Global options go before the command. You can also put the connection in
MAVFORGE_CONNECTION instead of passing -c. Exit codes:
| Code | Meaning |
|---|---|
| 0 | OK |
| 1 | Failed |
| 2 | Usage error |
| 3 | Connection failed |
| 4 | Timeout or unknown outcome |
| 5 | Not permitted |
| 6 | Unsupported |
| 7 | Partial result |
import mavforge
with mavforge.connect("tcp:127.0.0.1:5760", timeout=30) as vehicle:
print(vehicle.identity.firmware) # e.g. 4.7.2
result = vehicle.params.list("INS_*")
if result.status != "succeeded": # partial downloads are reported, not hidden
print("missing:", result.missing_indices)
for p in result.params:
print(p.name, p.value, p.type)uv run mavforge-mcp --connection tcp:127.0.0.1:5760See docs/mcp-setup.md for client configuration.
- Changes are planned, approved, verified. Every parameter change is a saved plan.
- The plan shows current and new values, documented ranges and value names, and reboot and safety warnings.
- A human approves it: by prompt,
mavforge approve, or an MCP elicitation. The approval is bound to the plan's SHA-256 and the vehicle's identity, expires after 10 minutes, and is single-use. - MAVForge then takes a backup, refuses values that changed since planning, writes each value and reads it back. Results per parameter are verified, failed, unknown, skipped or not attempted.
- Writes are refused while the vehicle is armed.
- Read-only by default for agents. The MCP server exposes change tools only with
--allow-configure, and touches real hardware only with--allow-real-vehicle. Agents cannot approve plans. - No arming, motor tests or flight control, by design.
- The operator picks the vehicle link when starting the MCP server; agents can't choose it.
- Backups are never overwritten and are created owner-only (mode 0600) under
~/.local/state/mavforge/backups/. - Every operation is audited to
~/.local/state/mavforge/audit.jsonl(mode 0600), with the interface (api/cli/mcp), the vehicle identity and the outcome. - Vehicle text is treated as untrusted. It is rendered inertly in the terminal and marked as data for agents.
- MAVForge never sends HEARTBEATs, so it cannot mask a GCS-failsafe condition.
Details: docs/architecture.md, docs/risks.md.
uv run ruff format --check . && uv run ruff check . && uv run pyright
uv run pytest # unit + integration (fake vehicle), no hardware needed
uv run pytest -m sitl # ArduCopter SITL in Docker
MAVFORGE_HW_CONNECTION=/dev/ttyACM0 uv run pytest -m hardware # opt-in, read-only- Assessment: project state, positioning and gaps
- Research notes: MAVLink, ArduPilot, Mission Planner and MCP findings, with sources
- Architecture · Risks · Security review · Roadmap
- Calibration · SITL · MCP setup · MAVForge and MAVProxy · Wireless links
- Contributing · Third-party notices
GPL-3.0-or-later. See LICENSE and THIRD_PARTY_NOTICES.md.