Betaflight MCP Server
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@Betaflight MCP Servercheck battery voltage and current draw"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Betaflight MCP Server
A Model Context Protocol server that exposes 26 tools to read and configure a Betaflight flight controller over USB serial using the MSP protocol.
MCP is not Claude-specific. Any MCP-compatible client works: Claude Desktop, Cursor, Cline, Continue, custom LLM agents, Alexa skills, or any application using the MCP SDK.
How it works
┌────────────────────────┐ MCP (stdio or SSE) ┌──────────────────────────┐
│ Any MCP Client │ ◄──────────────────────► │ betaflight-mcp server │
│ Claude / Cursor / ... │ │ Python · FastMCP │
└────────────────────────┘ └──────────┬───────────────┘
│ MSP protocol
│ pyserial · USB
┌──────────▼───────────────┐
│ Flight Controller │
│ Betaflight 4.x │
└──────────────────────────┘The server translates MCP tool calls into MSP (MultiWii Serial Protocol) frames, sends them to the FC over USB serial, parses the binary response and returns structured JSON.
Related MCP server: iNAV MCP Server
Prerequisites
Requirement | Version | Notes |
Python | ≥ 3.10 | 3.12+ recommended |
Betaflight firmware | ≥ 4.0 (API ≥ 1.40) | On any F4/F7/H7 FC |
USB cable | — | FC connected to host machine |
Installation
git clone https://github.com/your-username/betaflight-mcp.git
cd betaflight-mcp
python -m venv .venv
# Linux / macOS
source .venv/bin/activate
# Windows
.venv\Scripts\activate
pip install -r requirements.txtLinux — serial port permissions (one-time setup):
sudo usermod -aG dialout $USER
# then log out and back inConfiguration
All settings are controlled via environment variables — no config file to edit.
Variable | Default | Description |
|
| Serial port of the FC |
|
| Baud rate (must match Betaflight config) |
|
| Serial read timeout in seconds |
Set them inline or in your shell:
# inline
BETAFLIGHT_PORT=/dev/ttyACM0 python main.py
# or export
export BETAFLIGHT_PORT=/dev/ttyACM0
export BETAFLIGHT_BAUD=115200
python main.pyFind your FC's serial port
# Linux
ls /dev/ttyUSB* /dev/ttyACM*
# macOS
ls /dev/tty.usbmodem* /dev/tty.usbserial*
# Windows — PowerShell
[System.IO.Ports.SerialPort]::getportnames()
# Or use the built-in tool (any platform):
python -c "
from server.tools import tool_list_serial_ports
import json; print(json.dumps(tool_list_serial_ports(), indent=2))
"Important: close Betaflight Configurator before starting the MCP server. Both cannot hold the serial port open at the same time.
Client setup
Claude Desktop
Edit ~/Library/Application Support/Claude/claude_desktop_config.json (macOS)
or %APPDATA%\Claude\claude_desktop_config.json (Windows):
{
"mcpServers": {
"betaflight": {
"command": "/absolute/path/to/.venv/bin/python",
"args": ["/absolute/path/to/betaflight-mcp/main.py"],
"env": {
"BETAFLIGHT_PORT": "/dev/ttyACM0",
"BETAFLIGHT_BAUD": "115200"
}
}
}
}Restart Claude Desktop. The 26 tools appear automatically in the tool picker.
Claude Code (CLI)
claude mcp add -s user \
-e BETAFLIGHT_PORT=/dev/ttyACM0 \
-e BETAFLIGHT_BAUD=115200 \
-- betaflight \
/absolute/path/to/.venv/bin/python \
/absolute/path/to/betaflight-mcp/main.pyNote : le
--doit être placé avant le nom (betaflight), pas après. Sans lui, le parser variadique de-econsomme les arguments suivants.Scopes disponibles :
local(projet courant),project(.mcp.jsonversionné),user(global, tous les projets). Vérifier avecclaude mcp list.
Cursor
Add to .cursor/mcp.json in your project (or ~/.cursor/mcp.json globally):
{
"mcpServers": {
"betaflight": {
"command": "/absolute/path/to/.venv/bin/python",
"args": ["/absolute/path/to/betaflight-mcp/main.py"],
"env": {
"BETAFLIGHT_PORT": "/dev/ttyACM0"
}
}
}
}Cline / Continue (VS Code)
Same JSON format as above, placed in the respective extension's MCP server config.
Custom agent (SSE / HTTP transport)
For any client that talks to an HTTP endpoint instead of launching a subprocess:
# Start server in SSE mode (default port 8000)
BETAFLIGHT_PORT=/dev/ttyACM0 python -c "
from main import app
app.run(transport='sse', host='127.0.0.1', port=8000)
"Then point your client at http://127.0.0.1:8000/sse.
MCP SDK (Python / TypeScript)
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
server_params = StdioServerParameters(
command="python",
args=["/path/to/betaflight-mcp/main.py"],
env={"BETAFLIGHT_PORT": "/dev/ttyACM0"},
)
async with stdio_client(server_params) as (read, write):
async with ClientSession(read, write) as session:
await session.initialize()
result = await session.call_tool("connect", {"port": "/dev/ttyACM0"})
result = await session.call_tool("get_fc_status", {})Available tools
All read operations are safe at any time. Write operations (set_*) require calling save_config afterwards to persist changes to EEPROM.
Connection
Tool | Parameters | Description |
| — | List all serial ports on the host |
|
| Open serial connection to the FC |
| — | Close the connection |
FC identity
Tool | MSP | Description |
| BOARD_INFO · FC_VARIANT · FC_VERSION · API_VERSION | Firmware variant (BTFL), version, target name, MCU type |
Telemetry
Tool | MSP | Description |
| STATUS_EX (150) | Cycle time, CPU load, arming flags, profiles |
| RAW_IMU (102) | Accelerometer (g), gyroscope (°/s), magnetometer |
| ATTITUDE (108) | Roll / pitch / yaw in degrees |
| ALTITUDE (109) | Altitude (m) and variometer (cm/s) |
| RC (105) | All RC channel values (µs) |
| MOTOR (104) | Motor outputs (µs) |
Battery
Tool | MSP | Description |
| ANALOG (110) | Voltage (V), current (A), mAh drawn, RSSI |
| BATTERY_STATE (130) | Cell count, capacity, state (OK / WARNING / CRITICAL) |
| VOLTAGE_METERS (128) | All voltage meters |
| CURRENT_METERS (129) | All current meters |
PID tuning
Tool | MSP | Parameters | Description |
| PID (112) | — | P/I/D per axis (roll, pitch, yaw…) |
| SET_PID (202) |
| Write P/I/D for one axis |
| RC_TUNING (111) | — | All rates, expo, throttle curve |
| SET_RC_TUNING (204) | any rate field | Update one or more rate fields |
| PID_ADVANCED (94) | — | Feedforward, anti-gravity, TPA, D-Max, iterm relax |
Configuration
Tool | MSP | Description |
| MODE_RANGES (34) | AUX switch assignments (box_id, channel, µs range) |
| FEATURE_CONFIG (36) | Enabled features (AIRMODE, GPS, LED_STRIP…) |
| ADVANCED_CONFIG (90) | ESC protocol (DSHOT), gyro/PID denominators, PWM rate |
| FILTER_CONFIG (92) | Gyro/Dterm lowpass, notch filters, RPM filter |
| SENSOR_CONFIG (96) | Accelerometer, barometer, magnetometer hardware |
System
Tool | MSP | Description |
| EEPROM_WRITE (250) | Persist current config to EEPROM — always call after set_* |
| SET_REBOOT (68) | Reboot the flight controller |
Testing
Unit tests (no FC required)
python -m pytest tests/test_msp.py -v
# 34 tests — MSP v1/v2 framing, CRC, all response parsersInteractive inspector (no FC required)
pip install "mcp[cli]" # one-time
mcp dev main.py
# opens http://localhost:5173 — call any tool from the browserEnd-to-end with a real FC
# Quick smoke test
BETAFLIGHT_PORT=/dev/ttyACM0 python3 - <<'EOF'
import json
from server.tools import tool_connect, tool_get_fc_status, tool_get_battery, tool_disconnect
print(json.dumps(tool_connect("/dev/ttyACM0"), indent=2))
print(json.dumps(tool_get_fc_status(), indent=2))
print(json.dumps(tool_get_battery(), indent=2))
tool_disconnect()
EOFProject structure
betaflight-mcp/
├── main.py # Entry point — FastMCP app, tool registration
├── requirements.txt
├── pyproject.toml
│
├── betaflight/
│ ├── msp_codes.py # All MSP command codes (v1 + v2)
│ ├── msp.py # MSP v1 / v2 framing, CRC8/DVB-S2, serial I/O
│ ├── commands.py # Response parsers (DataReader), all 25+ commands
│ └── serial_conn.py # pyserial wrapper
│
├── server/
│ ├── tools.py # MCP tool functions + MCP_TOOLS registry
│ └── server.py # Standalone JSON-RPC server (fallback, no SDK dep)
│
├── config/
│ └── settings.py # Env-var based configuration
│
├── tests/
│ └── test_msp.py # 34 unit tests (mock serial)
│
└── examples/
└── claude_desktop_config.jsonSafety
⚠ Never arm the FC via MCP with propellers attached.
All
set_*operations are not automatically saved — always callsave_configto persistset_motorsends raw PWM values directly to ESCs — only use with props off and FC in motor-test modeThe server has no authentication — only expose SSE transport on trusted networks
Troubleshooting
SerialException: [Errno 16] Device or resource busy
→ Close Betaflight Configurator (or any other app using the port).
SerialException: [Errno 2] No such file or directory
→ Wrong port. Run list_serial_ports or ls /dev/ttyUSB* /dev/ttyACM* to find the correct one.
connect succeeds but all get_* return None
→ Check baudrate. Betaflight default is 115200. Verify in Betaflight Configurator → Ports tab → USB VCP.
Timeout on read (BETAFLIGHT_TIMEOUT=2.0)
→ Increase timeout: BETAFLIGHT_TIMEOUT=5.0. Can happen on busy systems or slow FCs.
Permission denied on Linux
→ sudo usermod -aG dialout $USER, then log out and back in.
api_version shows 0.0 after connect
→ The FC didn't respond to MSP_API_VERSION. Check that the FC is powered (USB provides power but some FCs need a battery for full boot).
License
MIT — see LICENSE.
This server cannot be deployed
Maintenance
Related MCP Connectors
A paid remote MCP for AI SDK benchmark dashboard, built to return verdicts, receipts, usage logs, an
Reuse your team's AI instructions (Bindings and Stacks) from Bindry in any MCP client.
A paid remote MCP for AI SDK eval dashboard, built to return verdicts, receipts, usage logs, and aud
A paid remote MCP for AI SDK MCP gateway registry, built to return verdicts, receipts, usage logs, a
Related MCP Servers
- FlicenseNot gradedqualityDmaintenanceEnables natural language interaction with Betaflight FPV drone flight controllers for configuration, PID tuning, sensor monitoring, VTX control, and blackbox management through Claude AI.4-
- AlicenseAqualityBmaintenanceEnables configuration, diagnostics, and troubleshooting of iNAV fixed-wing flight controllers over USB via Claude.3536 PyPI2MIT
- AlicenseNot gradedqualityAmaintenanceEnables AI agents to interact with an ArduPilot vehicle in real-time via MAVLink, including reading state, inspecting and changing parameters, switching flight modes, diagnosing arming failures, and gated arming/disarming.28 PyPI3MIT
- AlicenseBqualityBmaintenanceEnables AI assistants to control Betaflight flight controllers over serial via MSP and CLI, providing real-time sensor reads, full CLI access, and auto-generated variable tools for configuration and tuning.10015 npm2AGPL 3.0