SmartTune MCP server
Commit Sep 13, 2026Analyzes flight logs from ArduPilot, Betaflight, and PX4 to provide PID, FFT, and filter tuning recommendations for drone flight controllers.
Also known as raylanlin/smarttune-cli
- Stars
- 33
- Tools
- —
- Downloads, 30 days
- —
- Latest version
- —
- Last commit
- Sep 13, 2026
- License
- MIT
What is SmartTune MCP?
SmartTune analyzes flight logs from ArduPilot, Betaflight, and PX4 to provide PID, FFT, and filter tuning recommendations for drone flight controllers.
Summary generated from the project's README
About SmartTune MCP server
Drone engineers and developers can use SmartTune to transform raw flight logs from ArduPilot, Betaflight, and PX4 into actionable tuning parameters. By analyzing step-response, FFT vibration spectra, and magnetometer calibration, the system provides specific recommendations for PID, FFT, and filter settings. It is designed to work offline without requiring API keys or network access, making it suitable for air-gapped environments.
Users can interact with the system via a command-line interface or a read-only MCP server for LLM agent workflows. The MCP server provides 16 specialized tools, including smarttune_analyze_log for full analysis and smarttune_validate_params to ensure suggested values match specific firmware versions and ranges. The server operates via stdio transport and requires Python 3.10 or higher for MCP functionality. All recommendations are capped at a ±25% change to maintain safety during the tuning process.
What you can do with it
- 1Automated PID tuning recommendationsAn AI agent can analyze a flight log to suggest specific PID parameter deltas based on step-response patterns.
- 2Vibration source identificationA developer can use FFT spectrum analysis to distinguish between motor, propeller, or frame resonance.
- 3Firmware parameter validationBefore applying changes, an agent can verify that a suggested parameter name and value are valid for a specific firmware version.
- 4Batch flight log processingUsers can run a loop to analyze hundreds of logs at once and output the results in a structured JSON format.
Written from the project's README. Every use case is checked against a quote from it.
Configuration
SMARTTUNE_MCP_ALLOWED_ROOTSSMARTTUNE_MCP_MAX_FILE_MB
| Name | Description | |
|---|---|---|
| SMARTTUNE_MCP_ALLOWED_ROOTS | — | |
| SMARTTUNE_MCP_MAX_FILE_MB | — |
Automated checks
Ran Nov 14, 2026Full report →Not verified yet. A server needs a score of 80, no serious finding, and enough data for us to check. How checks work · What to fix for more points
- Source repository linked and reachableraylanlin/smarttune-cli is public and reachablegithub.com/raylanlin/smarttune-cli ↗Pass
- Package names this repository as its sourceNot checked
- Build provenance (Sigstore attestation)Not checked
- Open source license (OSI)MITspdx.org/licenses/MIT.html ↗Pass
- Commit or release in the last yearLast commit or release 2026-09-13github.com/raylanlin/smarttune-cli ↗Pass
- OpenSSF ScorecardNot checked
- Package name not close to a popular packageNot checked
- No critical advisory in the package or its dependenciesNot checked
- No hidden instructions to the model0 tool descriptions and the README read; no override phrases, credential paths or hidden charactersPass
- Destructive tools are labelledNot checked
- Remote endpoint answers the MCP handshakeNot checked
README
View on GitHubPip-install SmartTune, point it at a flight log, and your agent comes back with exact parameter deltas — validated against real firmware parameter tables.
No more guessing:stune params --validatechecks every recommendation before it reaches the user.Under the hood: ArduPilot step-response analysis replicates WebTools PIDReview.js via Wiener deconvolution. Betaflight blackbox logs are parsed by a 1000+ line pure-Python decoder (no C extensions, no Node.js). All three platforms output to one
FlightDatadataclass so analyzers work identically across APM/BF/PX4. The 6-layer knowledge base is plain JSON — agents can read rules and propose new ones by editing~/.smarttune/knowledge/.
Install
pip install git+https://github.com/raylanlin/smarttune-cli.git
# With all platform extras
pip install "git+https://github.com/raylanlin/smarttune-cli.git#egg=smarttune[all]"
# Development
git clone https://github.com/raylanlin/smarttune-cli.git
cd smarttune-cli
pip install -e ".[dev,all]"
Requires Python 3.9+
For Agents
SmartTune was designed specifically for LLM agent tool-calling workflows. Every aspect of the CLI follows agent-friendly principles:
| Principle | Implementation |
|---|---|
| Deterministic output | No interactive prompts, no TUI, no progress bars when piped to files. Same input → same output. |
| Structured by default | JSON on stdout via --format json (CLI) or smarttune_analyze_log (MCP) — one shared schema. Markdown/HTML via --report md|html. No parsing fragile ANSI-escaped terminal dumps. |
| Self-describing | stune platforms lists available adapters. Error codes are standardized (E10xx–E50xx). Exit codes are meaningful. |
| Fail-fast & isolated | Single-module failure doesn't abort the full analysis. Each module gets its own try/except block. |
| Config-free | Zero config files needed. Everything is flags or auto-detected. No env vars required. |
| Offline-first | No network calls. No API keys. No rate limits. Safe for isolated/air-gapped environments. |
| Machine-recommendable | Tuning suggestions include confidence scores and reasoning, not just parameter values. Agents can weigh multiple recommendations. |
What agents can learn through SmartTune
SmartTune isn't just a tool agents call — it's how agents learn the craft of flight controller tuning:
| Skill | How SmartTune teaches it |
|---|---|
| PID tuning intuition | Step-response analysis with confidence scores. Agents learn which overshoot/rise-time patterns call for higher Kp vs. damping. |
| Frequency-domain reasoning | FFT spectra with peak detection. Agents learn to distinguish vibration sources (prop/ motor/ frame resonance) from the spectrum shape. |
| Filter design logic | Notch and low-pass filter recommendations with Bode plots. Agents see the tradeoff between filtering and phase lag. |
| Platform differences | ArduPilot vs Betaflight parameter conventions. ParamRef maps between them — agents learn to translate tuning knowledge across platforms. |
| Safety awareness | All recommendations are capped at ±25%. Agents learn conservative tuning by default. |
| Rule-based reasoning | The 6-layer knowledge base is plain JSON. Agents can read, understand, and even propose rule changes by writing to their user layer. |
What agents can do with SmartTune
- Batch analysis — analyze hundreds of logs with a loop; JSON output per file
- Auto-tuning — feed recommendations back into a flight controller via MAVLink or CLI
- Fleet monitoring — aggregate vibration/PID metrics across multiple aircraft
- CI/CD integration — run
stune analyzeas part of a pre-flight validation pipeline - Collaborative diagnosis — have the agent compare logs from before/after a crash
MCP Server (Model Context Protocol)
SmartTune includes a read-only MCP server that lets LLM agents call analysis tools directly — no shell, no subprocess, no arbitrary file writes.
MCP requires Python 3.10+ (the MCP SDK does not support 3.9). The
stuneCLI itself still runs on Python 3.9. On 3.9,pip install ".[mcp]"silently skips the mcp package andsmarttune-mcpexplains why instead of crashing.
Install with MCP support:
pip install -e ".[all,mcp]"
Run the MCP server:
smarttune-mcp # stdio transport (for agent frameworks)
# or
python -m smarttune.mcp_server
Available MCP tools (16 total):
| Tool | Purpose |
|---|---|
smarttune_list_platforms |
List supported platforms, extensions, and capabilities |
smarttune_log_quality |
Parse log and return quality score, data availability, validation issues |
smarttune_analyze_log |
Full analysis (PID + FFT + Filter + Mag + SysID + Hardware) as JSON/Markdown |
smarttune_analyze_pid |
PID step response analysis per axis |
smarttune_analyze_fft |
FFT vibration spectrum with peak detection |
smarttune_analyze_magfit |
Magnetometer calibration analysis |
smarttune_analyze_sysid |
ARX system identification (natural freq, damping ratio) |
smarttune_analyze_filter |
Filter transfer function analysis (Bode plot data) |
smarttune_analyze_hardware |
Hardware configuration report |
smarttune_generate_plot |
Generate analysis chart as base64 PNG |
smarttune_list_param_groups |
NEW v3.2 — Browse a platform's parameter groups (start here) |
smarttune_list_params |
List parameters in one group or category — compact rows |
smarttune_get_param |
NEW v3.2 — Full definition of one parameter, incl. what each enum value means |
smarttune_search_params |
Ranked keyword search across names, descriptions and enum labels |
smarttune_validate_param |
⚠️ Validate one param name + value (enum membership as well as range) |
smarttune_validate_params |
NEW v3.2.1 — Validate a whole recommendation set in one call |
All tools are annotated readOnlyHint=True, destructiveHint=False, idempotentHint=True.
Response contract (v3.2) — one shape for every tool, so clients branch on fields instead of prose:
{ "ok": true, "platform": "ArduPilot", "...": "payload" }
{ "ok": false, "error_code": "E3002", "message": "Insufficient PID data in log",
"hint": "…", "retryable": false }
A rejected parameter value is a successful call with valid: false plus a verdict field
(ok / not_found / out_of_range / not_a_member / not_an_integer / unverifiable) —
not a transport error. All six parameter tools accept fw_version (e.g. "copter-4.5"; unknown versions return E4011 with the available list). And as of v3.2.1 the analysis tools attach validated /
validation_status to every recommendation they return, so the "always validate before
recommending" rule is enforced by the payload itself; explicit validation is only needed for
values the agent adjusted afterwards.
stdout carries JSON-RPC only: every service call runs with stdout redirected to stderr, so a
stray print from a third-party log parser can no longer corrupt the stream.
⚠️ Parameter validation is mandatory. Before recommending ANY parameter change, call smarttune_validate_param(param_name, value, platform). It checks enum membership as well as numeric range, and returns a status: ok / not_found / out_of_range / not_a_member / not_an_integer / unverifiable. unverifiable means the table cannot confirm the value — the call did not approve it. This prevents agents from suggesting parameters that don't exist in the target firmware — a critical issue because:
- Betaflight 4.5+ renamed many parameters (
d_min_roll→d_max_roll,gyro_lowpass_hz→gyro_lpf1_static_hz) - Parameter names differ between firmware versions
- Some parameters have strict value ranges that must be respected
Security boundary:
- No shell execution — library calls only
- No arbitrary file writes — results are returned inline
- No parameter mutation — no MAVLink writes, no firmware flashing
- Path validation — allowed roots, extensions (
.bin,.log,.tlog,.bbl,.bfl,.ulg), file size limits, symlink resolution - Configurable via environment variables:
export SMARTTUNE_MCP_ALLOWED_ROOTS="/path/a:/path/b"
export SMARTTUNE_MCP_MAX_FILE_MB="300"
OpenClaw / Claude Desktop configuration:
{
"mcp": {
"servers": {
"SmartTune": {
"command": "smarttune-mcp",
"args": [],
"env": {
"SMARTTUNE_MCP_ALLOWED_ROOTS": "/home/user/.openclaw/workspace/files/inbox:/home/user/.openclaw/workspace/files/output:/tmp",
"SMARTTUNE_MCP_MAX_FILE_MB": "300"
}
}
}
}
}
Quick Start
# Full analysis (auto-detect platform)
stune analyze -i flight.bin
# With charts (human-friendly)
stune analyze -i flight.bbl --visual
# Per-module deep dive
stune pid -i flight.bin -a roll --visual
stune fft -i flight.bin --visual
stune magfit -i flight.bin
stune sysid -i flight.bin -a pitch
stune hardware -i flight.bin
# Export to Markdown report
stune analyze -i flight.bin --report md -o report.md
# Export to HTML report
stune analyze -i flight.bin --report html -o report.html
# List supported platforms
stune platforms
# Machine-readable output (same schema as the MCP server)
stune analyze -i flight.bin --format json
# Run a subset of modules / cap recommendations
stune analyze -i flight.bin --modules pid,fft --max-recommendations 10 -f json
Output Formats
SmartTune supports multiple output formats, each designed for a specific consumption mode:
| Format | Use Case | Example |
|---|---|---|
| Terminal | Human inspection in the shell | stune analyze -i flight.bin |
| JSON | Agent/script consumption | stune analyze -i flight.bin --format json |
| Markdown | Reports, READMEs, documentation | stune analyze -i flight.bin --report md -o report.md |
| HTML | Visual reports with embedded charts | stune analyze -i flight.bin --report html -o report.html |
--format json 🆕 v3.1
Every analysis command takes -f/--format json. The payload comes from the same services-layer
functions the MCP server calls, so CLI JSON and MCP JSON are the same schema — no second
serializer to drift.
stune analyze -i flight.bin -f json | jq '.modules.pid.axes.roll'
stune pid -i flight.bin -f json -a roll
stune fft -i flight.bin -f json
stune quality -i flight.bin -f json -o quality.json
stune filter -i flight.bin -f json
stune params --search notch -f json
stune platforms -f json
Contract:
| Guarantee | Detail |
|---|---|
| stdout is JSON only | Progress, hints and error panels go to stderr — | jq never chokes. -o file.json writes the payload to a file instead. |
| Envelope | schema_version, tool {name, version}, command, status, generated_at, then the command's own fields. |
| Errors are JSON too | status: "error" + error {code, type, message, hint}, exit code 1. No screen-scraping the failure path. |
| Strict JSON | NaN/Infinity are emitted as null — safe for strict parsers. |
| Reproducible | SMARTTUNE_DETERMINISTIC=1 omits generated_at so runs diff byte-for-byte in CI. |
{
"schema_version": "1.0",
"tool": { "name": "smarttune", "version": "3.1.0" },
"command": "pid",
"status": "error",
"error": {
"code": "E3002",
"type": "InsufficientPIDDataError",
"message": "Insufficient PID data in log",
"hint": ""
}
}
JSON output example
{
"platform": "ArduPilot",
"timestamp": "2026-05-03T22:30:00",
"pid": {
"roll": {
"rating": "GOOD",
"confidence": 0.87,
"kp": {"current": 0.12, "recommended": 0.14, "reason": "Slight oscillation at 8 Hz"},
"ki": {"current": 0.05, "recommended": 0.05, "reason": "No steady-state error"},
"max_overshoot_pct": 8.2,
"rise_time_ms": 85,
"settling_time_ms": 210
}
},
"fft": {
"vibration": {
"level_rms": 2.1,
"grade": "EXCELLENT"
},
"peaks": [
{"freq_hz": 47.5, "magnitude_db": -12.3, "source": "propeller"}
]
}
}
Commands
stune analyze
Full-spectrum analysis: PID + FFT + MagFit + hardware — all in one pass.
stune analyze -i flight.bin # Auto-detect
stune analyze -i flight.bbl --platform betaflight # Force platform
stune analyze -i flight.bin --visual # With charts
stune analyze -i flight.bin --report md -o report.md # Markdown export
stune analyze -i flight.bin --report html -o report.html # HTML export
stune pid
PID step-response analysis with per-axis tuning recommendations.
stune pid -i flight.bin # All axes
stune pid -i flight.bin -a roll # Single axis
stune pid -i flight.bbl --visual # Betaflight
stune fft
Frequency-domain vibration analysis with notch filter suggestions.
stune fft -i flight.bin # Full spectrum
stune fft -i flight.bin --visual # With spectrum plot
stune magfit
Magnetometer calibration — hard/soft iron offset, coverage, field strength.
stune magfit -i flight.bin # ArduPilot only
stune sysid
ARX system identification — natural frequency and damping ratio.
stune sysid -i flight.bin -a roll # Single axis
stune sysid -i flight.bin -a pitch --na 4 --nb 3 # Custom order
stune hardware
Full hardware configuration report: firmware version, sensors, battery, parameters.
stune hardware -i flight.bin
stune hardware -i flight.bbl
stune filter
Filter chain analysis with Bode plots.
stune filter -i flight.bin --gyro-filter 40 --visual
stune filter -i flight.bin --auto # Auto-derive from params
stune platforms
List all available platform adapters and their capabilities.
stune platforms
stune params — firmware parameter tables
Browse, look up, search and validate real firmware parameters. Tables are generated from
official firmware metadata by tools/build_param_tables.py —
see Parameter tables.
# What's available
stune params # tables, param counts, group counts, firmware
# Browse by parameter group (the firmware's own grouping)
stune params ap --groups # 194 ArduPilot groups
stune params ap --group ATC_ # attitude controller group
stune params bf --group PID_PROFILE # Betaflight PG_PID_PROFILE
stune params px4 --group "Multicopter Rate Control"
# Pick a firmware-version table (default: Copter-4.1 for ArduPilot)
stune params ap --fw-version copter-4.5 --group ATC_
stune params --validate ATC_RAT_RLL_P 0.45 -p ap --fw-version copter-4.5 # 4.5: max 0.5
# Browse by topic
stune params ap -c pid # pid / filter / mag / battery / rate / …
# One parameter: description, range, default, and what each enum value MEANS
stune params BATT_MONITOR
stune params MC_ROLLRATE_P
# Ranked keyword search — names, descriptions and enum labels
stune params --search notch
stune params --search "analog voltage" # finds BATT_MONITOR
# ⚠️ Validate before recommending (exit 0 = valid, 1 = invalid)
stune params --validate BATT_MONITOR 4 -p ap # ✓ 4 = Analog Voltage and Current
stune params --validate BATT_MONITOR 99 -p ap # ✗ not a valid value (lists allowed)
stune params --validate p_roll 999 -p bf # ✗ exceeds max 250
# Validate a whole recommendation set in one call (exit 0 only if all valid)
echo '[{"param":"BATT_MONITOR","value":4},{"param":"p_roll","value":45}]' \
| stune params --validate-batch - -p ap
# Data health (CI gate)
stune params --lint # exit 1 if any table has defects
Every subcommand supports -f json.
Supported Platforms
| Platform | Log Format | Parser | Status |
|---|---|---|---|
| ArduPilot | .bin / .log (onboard DataFlash) |
pymavlink | ✅ Full support |
| ArduPilot | .tlog (GCS telemetry) 🆕 v3.4 |
pymavlink | ⚠️ Screening only — see below |
| Betaflight | .bbl / .bfl (Blackbox) |
Pure Python | ✅ Full support |
| PX4 | .ulg (ULog) |
pyulog | ✅ PID / FFT / SysID / Quality (v3.0+) |
Telemetry logs (.tlog) 🆕 v3.4
A .tlog is what Mission Planner / MAVProxy / QGroundControl records over the radio link —
a fundamentally thinner source than the onboard log, and SmartTune treats it that way:
onboard .bin |
telemetry .tlog |
|
|---|---|---|
| Written by | flight controller (SD card) | ground station (PC) |
| Rate | loop rate, 400 Hz+, lossless | stream rates, 1–50 Hz, lossy |
| Rate controller | PIDR/PIDP/PIDY with P/I/D terms |
not streamed at all |
| Gyro | IMU at 400 Hz+ | RAW_IMU/ATTITUDE, 2–50 Hz |
| Coverage | whole flight | only while the link was up |
stune quality -i "2026-09-13 10-00-00.tlog" # states every limit up front
stune analyze -i flight.tlog -f json | jq '.telemetry_notes'
What SmartTune does with one:
- PID is reconstructed from
ATTITUDE_TARGET(desired body rates) vsATTITUDE(actual). P/I/D term breakdown does not exist in telemetry, so those series read zero — like the existing legacy-RATEfallback. If the vehicle never streamedATTITUDE_TARGET, PID analysis is skipped with a reason, not faked. - FFT is band-limited by the stream rate: a 10 Hz stream can only show 0–5 Hz, far below the 40–120 Hz band where prop and frame resonances live. The report says so.
- Parameters only exist if the GCS downloaded them during the recording.
- Radio dropouts, missing accel stream, and EKF-filtered rate sources are all reported
in
telemetry_notes(JSON) or the "Telemetry Log Limits" block (terminal). - A
.tlogrecorded from a PX4 vehicle is refused with a pointer to the onboard.ulg.
Every caveat is scored: telemetry logs cannot reach the top quality bands, and
quality.advice points at the onboard log for tuning work.
Auto-Detection
SmartTune identifies your log format from file headers — no --platform flag needed:
| Bytes | Platform |
|---|---|
0xA3 0x95 |
ArduPilot DataFlash |
8-byte timestamp + 0xFD/0xFE |
ArduPilot MAVLink telemetry (.tlog) |
H Product:Blackbox |
Betaflight Blackbox |
| ULog magic | PX4 ULog |
Architecture
┌─────────────────────────────────────────────┐
│ CLI Layer │
│ stune analyze / pid / fft / ... │
│ --report md / html │
└──────────────────┬──────────────────────────┘
│
┌──────────────────┤ ┌───────────────────────┐
│ │ │ MCP Server (stdio) │
│ │ │ smarttune-mcp │
│ │ │ JSON / Markdown out │
│ │ │ Read-only · No shell │
│ │ └───────────┬───────────┘
│ │ │
│ ┌───────────────▼──────────────▼────────────┐
│ │ Services Layer (shared) │
│ │ services/analysis.py · services/serialize │
│ └───────────────┬──────────────────────────┘
│ │
┌──────────────────▼──────────────────────────┐
│ Platform Adapter Layer │
│ ArduPilot · Betaflight · PX4 │
│ Parsers → FlightData (unified IR) │
└──────────────────┬──────────────────────────┘
│
┌──────────────────▼──────────────────────────┐
│ Analysis Engine (platform-aware) │
│ PID / FFT / SysID / MagFit / Filter / HW │
│ Per-platform modules: │
│ ardupilot/ → WebTools-aligned FFT │
│ betaflight/ → Wiener deconvolution FFT │
│ px4/ → stubs │
│ BF: Feedforward · RPM Filter · D-term │
│ Protocol-based interface constraints │
└──────────────────┬──────────────────────────┘
│ AnalysisResult + ParamRef
┌──────────────────▼──────────────────────────┐
│ Knowledge Base (6-layer deep merge) │
│ common → platform → user → Pro │
│ JSON-based rules — inspectable & editable │
└──────────────────┬──────────────────────────┘
│
┌──────────────────▼──────────────────────────┐
│ Output Layer │
│ Terminal (Rich) / JSON / Markdown / HTML │
│ ParamRef → platform-native parameter names │
└─────────────────────────────────────────────┘
Knowledge Base
A 6-layer deep-merge rule engine powers all tuning recommendations. Each layer overrides the previous:
| # | Layer | Location | Editable |
|---|---|---|---|
| 1 | Common physics rules | smarttune/knowledge/rules/common/ |
❌ Built-in |
| 2 | Platform rules | smarttune/knowledge/rules/{platform}/ |
❌ Built-in |
| 3 | User common | ~/.smarttune/knowledge/common/ |
✅ |
| 4 | User platform | ~/.smarttune/knowledge/{platform}/ |
✅ |
| 5 | Pro common | smarttune-knowledge-pro (optional) |
🔒 |
| 6 | Pro platform | smarttune-knowledge-pro (optional) |
🔒 |
Rules are standard JSON files. Add a file, restart the command, and the engine picks it up. No compilation, no database, no setup.
SmartTune MCP questions
What flight controller platforms are supported?
The tool supports ArduPilot, Betaflight, and PX4.
Does SmartTune require an internet connection to work?
No, it is designed to be offline-first and works in isolated or air-gapped environments.
Can the tool suggest parameter changes that might break my firmware?
No, all recommendations are validated against real firmware parameter tables and capped at ±25% for safety.
What is the requirement for running the MCP server?
The MCP server requires Python 3.10 or higher.