Skip to main content

MCP server + agent skills for AI tooling to discover, drive, observe, and test Meshtastic devices and apps: device discovery, serial/TCP transport, admin, recorder/observability, PlatformIO build/flash (optional), and hardware-free Android-emulator e2e.

Project description

meshtastic-mcp

CI PyPI Python License: GPL-3.0 Ruff

A home for AI tooling to discover, drive, observe, and test Meshtastic devices and the apps that talk to them. An MCP server plus a bundled agent skill set for closed-loop device ↔ app end-to-end testing.

Extracted and decoupled from the Meshtastic firmware repo's mcp-server/ so it can be installed and distributed on its own. The firmware-build tools become an optional capability that lights up only when a firmware checkout is present.

Quick start (60 seconds)

Plug in a Meshtastic device, then:

uvx meshtastic-mcp doctor          # check what's available

Inside Claude Code (or any MCP client registered with meshtastic):

list_devices()                          → find your device port
device_info(port="/dev/cu.usbmodem101") → firmware, region, node count
list_nodes(port="/dev/cu.usbmodem101")  → mesh peers
send_text(port="/dev/cu.usbmodem101", text="hello mesh")
packets_window(port="/dev/cu.usbmodem101", start="-30s")  → confirm TX packet

No firmware checkout required. Core tools work against any serial or TCP device.

Install

# Install the CLI + MCP server (uv recommended; also works with pipx)
uv tool install 'meshtastic-mcp[ui]'       # includes camera/OCR support
# or without UI extras:
uv tool install meshtastic-mcp
# other extras: [web] FleetSuite bench UI · [sdr] RF-compliance oracle · [test] pytest harness

# Install from a local checkout (dev):
uv tool install --editable '/path/to/meshtastic-mcp[ui]'

# One step: register the server in your MCP client + install the bundled skills
meshtastic-mcp install                 # claude-code user scope; --client cursor|windsurf|claude-desktop
meshtastic-mcp install --local         # register the current interpreter (editable/dev install)
meshtastic-mcp install --print         # just emit the JSON snippet (edit nothing)
meshtastic-mcp uninstall               # remove the registration  (--purge-skills to drop skills too)

# Or do the pieces manually:
claude mcp add meshtastic -s user -- meshtastic-mcp     # register with Claude Code
meshtastic-mcp skills install           # → ~/.agents/skills/ (device-ops, e2e, org-knowledge)
meshtastic-mcp skills uninstall         # remove them

install edits the client's mcpServers JSON in place (preserving other entries) — it picks the right config path per --client (claude-code / claude-desktop / cursor / windsurf) and --scope (user / project), or pass --config PATH. Add capability env vars with --env KEY=VALUE. Restart the MCP client to pick up the change.

Source repos (optional capabilities)

Set env vars pointing at local checkouts to activate firmware-build, Android, and Apple capabilities. The provision subcommand clones everything automatically:

meshtastic-mcp provision              # clones firmware + android + apple, prints export commands
source ~/.local/share/meshtastic-mcp/repos/.env

# Then add to your shell profile:
export MESHTASTIC_FIRMWARE_ROOT=~/meshtastic/firmware
export MESHTASTIC_ANDROID_ROOT=~/meshtastic/android
export MESHTASTIC_APPLE_ROOT=~/meshtastic/apple

# Add to your MCP client config so the server process picks them up:
# { "env": { "MESHTASTIC_FIRMWARE_ROOT": "/path/to/firmware", ... } }

esptool — if PlatformIO is installed, esptool is auto-discovered from the PlatformIO penv and wrapped automatically. No manual setup required.

meshtastic-mcp doctor          # verify what's active after setup

Capabilities

A portable core always works against any connected device (serial or tcp://host:port) with no firmware checkout. Optional capabilities activate when their prerequisite is present:

Capability Prerequisite Adds
core discovery, serial+TCP transport, device_info/list_nodes/admin/send_text/reboot, recorder + log/telemetry/packet/event queries, replay (simulated TCP device + synthetic mesh + fuzzer), input-events, uhubctl, esptool/nrfutil/picotool
firmware MESHTASTIC_FIRMWARE_ROOT + PlatformIO pio build / clean / flash / OTA / board enum / userPrefs
android android CLI + adb Android-emulator + native-node orchestration for hardware-free e2e
apple xcrun (+ idb for UI) iOS Simulator / macOS-app orchestration for hardware-free e2e
local-model a reachable Ollama or OpenAI-compatible llama-server (or a llama binary to start one) offload tools that push token-heavy work onto a local GPU — summarize/triage recorder windows, e2e-failure first pass, and an offline vision oracle; see docs/local-models.md
sdr [sdr] extra + librtlsdr + an RTL-SDR dongle RF-compliance oracle: rf_scan occupancy checks and rf_confirm_tx on-air verification, no second radio needed
sdk-cli (experimental) Kotlin SDK headless CLI alternate device-IO backend over the JVM CLI; see docs/sdk-cli-bridge.md

The active set is logged at startup (meshtastic-mcp capabilities active: …).

Beyond tools, the server exposes MCP resources (meshtastic://doctor, meshtastic://capabilities, and the templated meshtastic://e2e/{loop} for bundled e2e recipes) and prompts (triage_e2e_failure, bringup_device, inbound_loop). The android_docs_search/ android_docs_fetch tools answer Android/Compose questions grounded in the official Knowledge Base.

Source repos — two modes

Build tools (build_meshtasticd.sh, build_android_apk.sh, build_apple.sh) work in two modes:

Dev with existing checkouts — point env vars at your local trees:

export MESHTASTIC_FIRMWARE_ROOT=~/firmware
export MESHTASTIC_ANDROID_ROOT=~/Meshtastic-Android
export MESHTASTIC_APPLE_ROOT=~/Meshtastic-Apple

Portable / fresh environment — clone everything in one shot:

meshtastic-mcp provision              # clones all three repos, writes a .env file
source ~/.local/share/meshtastic-mcp/repos/.env

provision respects any env vars already set (skips those repos) and writes a sourceable .env for the ones it clones. Run doctor afterwards to confirm all binary deps are present too.

doctor — probe the environment

Run before starting e2e work (also exposed as the doctor MCP tool, so an agent can self-provision):

meshtastic-mcp doctor          # per-dependency status + exact, platform-aware install commands
meshtastic-mcp doctor --json   # machine-readable; `fix_commands[]` is ready to run

Probes binaries (pio, adb, xcrun, idb_companion, fb-idb, ffmpeg, uhubctl, OCR) and source repo roots (MESHTASTIC_FIRMWARE_ROOT, MESHTASTIC_ANDROID_ROOT, MESHTASTIC_APPLE_ROOT), reporting ok/missing/degraded with the exact command to fix each — including non-obvious ones (e.g. idb_companion lives in the facebook/fb tap, fb-idb requires Python ≤ 3.12).

Build scripts

Script Builds Key flags
scripts/build_meshtasticd.sh Native meshtasticd daemon --env native|native-macos, --ref, --firmware-dir / MESHTASTIC_FIRMWARE_ROOT
scripts/build_android_apk.sh Meshtastic-Android APK --ref, --variant, --source-dir / MESHTASTIC_ANDROID_ROOT
scripts/build_apple.sh Meshtastic iOS-Simulator .app --ref, --sim, --source-dir / MESHTASTIC_APPLE_ROOT

All scripts clone fresh if no source dir is provided, accept --ref to pin a git sha/tag/branch, and print a machine-readable <component>-sha=<sha> line for CI provenance.

Hardware-free e2e (emulator + TCP)

Portduino meshtasticd native nodes run as TCP daemons (:4403) and mesh over UDP multicast (224.0.0.69:4403) with no LoRa hardware. The Android emulator connects over TCP to 10.0.2.2:<port> as its DUT radio; the MCP server connects to other native nodes as testers. Every e2e loop — inbound/outbound message, node-sync, config-writeback, resilience — runs in software, CI-able on Linux runners. See src/meshtastic_mcp/emulator/native_node.py and the bundled meshtastic-e2e skill.

Replay — a simulated Meshtastic device (the inverse of the recorder)

Where the recorder subscribes to a live mesh and writes packets out, replay serves a capture as a fake radio over TCP. An app (or AVD at 10.0.2.2:<port>) connects to the listen port, does the want-config handshake, and receives a paced packet stream restamped to "now" — behaving like a radio sitting in the mesh. Useful for app/UI development and testing with zero hardware and a fully controllable, reproducible mesh.

replay_start(source="meshcon")                       # generated synthetic mesh (no file)
replay_start(source="capture.db", speed=30)          # SQLite capture (full-fidelity payloads)
replay_start(source="event.db", channels=[             # split by OTA hash + decrypt
    {"name": "Primary", "psk": "AQ==", "primary": True},
    {"name": "Secret", "psk": "<base64-key>"},
    {"name": "Unknown", "catch_all": True}])
replay_start(source="meshcon", fuzz="adversary")     # lace it with bad actors
replay_start(source="capture.db", announce_interval=30)  # in-app "Replay Clock" progress
replay_inject(sid, "waypoint", {"lat":37,"lon":-122,"geofence_radius":500,"notify_on_enter":True})
replay_status(); replay_stop()
  • Sources (replay/capture.py): SQLite captures (*.db/.gz, the Burning Man / DEF CON / MeshCon schema), the recorder's own packets.jsonl, or an in-memory synthetic mesh.
  • Multi-channel captures: pass a channels list (name + PSK, optional explicit OTA hashes, optional catch_all bucket) to route packets into the real channels by their OTA channel hash and advertise the keys so a connecting app shows the true channels and live-decrypts the encrypted half. The channel set is caller-supplied data — e.g. for an event capture you provide that event's channel names + PSKs; nothing event-specific is baked in.
  • Synthetic mesh (replay/sim.py): seeded, PII-free MeshCon generator — tunable node count (default 800, scales to thousands) / channels / duration, a diurnal activity envelope, and every portnum/flavor (incl. RANGE_TEST). Its default distributions (hardware/role mix, telemetry value ranges, position precision, hop-limit spread, text rate, node presence/churn — a persistent core plus transient attendees, which reproduces the real heavy activity skew — short-message lengths, and a share of encrypted/foreign traffic) are informed by the aggregate statistics of real ~1,800-node captures (Burning Man + DEF CON 33) — proportions only; every identity, position, and message is generated. sim.fit_profile(capture) derives such a profile from any capture.
  • Live injection (replay_inject): push exact packets into a running session to drive app features on demand — a waypoint with a geofence, a node position crossing it, a text, a NodeInfo, or raw. Builders (replay/build.py) set proto fields the bundled lib predates (e.g. geofence) via raw-wire append; fuzz=True injects a deliberately malformed packet (shares the fuzzer's emit path). capture.from_events([…]) turns a scripted scenario into a replayable capture. (Validated end-to-end against the Android waypoint-geofence PR.)
  • App-facing polish: the connected node is placed at the capture's median position (sane map
    • distances); announce_interval adds a "Replay Clock" node posting kickoff + live ETA/progress to the busiest channel; modem_preset / firmware_edition set the advertised LoRa preset and the app's event banner (e.g. DEFCON, HAMVENTION); replay_status returns connect host:port hints; a send timeout keeps a stalled app from hanging a session.
  • Fuzzer (replay/fuzz.py, replay_start(fuzz=…), replay_fuzz_presets): turn the stream hostile to test decoder + UI robustness. Protocol faults (corrupt/garbage/truncated payloads, portnum↔body mismatch, invalid-UTF-8 text, impossible telemetry, teleporting positions, hop anomalies, drop/duplicate) and bad-actor campaigns (evil-twin impersonation, flooding, GPS spoofing, forged ACKs, rogue ADMIN reboot/factory-reset, malicious waypoints). Seeded so a crash reproduces; activity surfaces under fuzz in replay_status. Presets: light, parser, adversary, chaos.

Bundled skills

Three skills ship in the wheel (meshtastic-mcp skills install copies them to your skills dir):

  • meshtastic-device-ops — discover/connect/configure/observe/recover/flash via the MCP tool surface (the non-e2e workflows).
  • meshtastic-e2e — cross-plane (device + app) testing: per-loop references, a verified device-plane helper (mesh_e2e.py), journey-driven UI (references/journeys/), triage, and the vision-oracle fallback.
  • meshtastic-org-knowledge — answer questions that span the Meshtastic GitHub org: which project does X, status of Y, where Z is documented, what changed recently.

FleetSuite — web control plane for a device bench

A local web UI for running a multi-board hardware bench: live device registry (discovery + auto-enrichment with firmware/hw/region), build queue + flash, an escalating recovery ladder (reboot → USB power-cycle → bootloader → reflash), per-device camera streams for screen assertions, the tiered test runner, serial monitors, and optional Datadog log/metric shipping.

uv tool install 'meshtastic-mcp[web]'
meshtastic-mcp-web              # desktop window at http://127.0.0.1:8765
meshtastic-mcp-web --browser    # serve only (headless / open it yourself)

Binds to 127.0.0.1 by default. Destructive actions (reflash / factory-reset) require an explicit confirmation. While FleetSuite runs it owns the bench's serial ports (monitors + enrichment) — pause it or use its own controls rather than pointing a second tool at the same ports; its test runner already suspends the monitors for the duration of a run. From a source checkout, ./scripts/fleetsuite.sh bootstraps everything (venv + npm + SPA build) in one command; ./scripts/web-dev.sh runs the backend + Vite dev server with HMR. Point MESHTASTIC_FIRMWARE_ROOT at a firmware checkout to enable builds, reflash recovery, and exact per-board PlatformIO env resolution — without it FleetSuite still discovers, enriches, and drives devices.

Hardware test suite

tests/ is a tiered pytest suite (unit, mesh, telemetry, monitor, recovery, ui, fleet, admin, provisioning). unit runs with no hardware; the hardware tiers target a USB-hub bench with per-board roles keyed by hub slot (reference bench: T-Echo, Heltec T114, RAK4631, ESP32-S3 — see tests/_bench.py and tests/README.md). Drive it via run-tests.sh, the meshtastic-mcp-test-tui terminal UI, or FleetSuite's test runner. Setting up your own bench (any boards, any hub): docs/bench-setup.md.

License

GPL-3.0-only.

Project details


Download files

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

Source Distribution

meshtastic_mcp-0.1.0.tar.gz (854.5 kB view details)

Uploaded Source

Built Distribution

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

meshtastic_mcp-0.1.0-py3-none-any.whl (452.3 kB view details)

Uploaded Python 3

File details

Details for the file meshtastic_mcp-0.1.0.tar.gz.

File metadata

  • Download URL: meshtastic_mcp-0.1.0.tar.gz
  • Upload date:
  • Size: 854.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for meshtastic_mcp-0.1.0.tar.gz
Algorithm Hash digest
SHA256 dc72654d6b3a80c8c4b1bc033f5bdfb077689a91ac200e36b9d634e9ad5cfb03
MD5 ad771375a672dc2a50dbd63f73d3556e
BLAKE2b-256 4791042bb3e5ee3fd0f28eef87f78186cfef6ef3fd4f6f567d8e9458e2fd2de1

See more details on using hashes here.

Provenance

The following attestation bundles were made for meshtastic_mcp-0.1.0.tar.gz:

Publisher: release.yml on meshtastic/meshtastic-mcp

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

File details

Details for the file meshtastic_mcp-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: meshtastic_mcp-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 452.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for meshtastic_mcp-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 17ef8cc8f94cbd7a90865a001abbebfd283d639c9d9f024983ad3e94b67ba265
MD5 ebe82bd412c685256aa0fce231ad06c4
BLAKE2b-256 70ab627e7291a2be59723c853134563b7fc2d8b1a1042d88f50911ad541bbc59

See more details on using hashes here.

Provenance

The following attestation bundles were made for meshtastic_mcp-0.1.0-py3-none-any.whl:

Publisher: release.yml on meshtastic/meshtastic-mcp

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

Supported by

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