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
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 ownpackets.jsonl, or an in-memory synthetic mesh. - Multi-channel captures: pass a
channelslist (name + PSK, optional explicit OTA hashes, optionalcatch_allbucket) 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, orraw. Builders (replay/build.py) set proto fields the bundled lib predates (e.g. geofence) via raw-wire append;fuzz=Trueinjects 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_intervaladds a "Replay Clock" node posting kickoff + live ETA/progress to the busiest channel;modem_preset/firmware_editionset the advertised LoRa preset and the app's event banner (e.g.DEFCON,HAMVENTION);replay_statusreturnsconnecthost:port hints; a send timeout keeps a stalled app from hanging a session.
- distances);
- 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 underfuzzinreplay_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
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
dc72654d6b3a80c8c4b1bc033f5bdfb077689a91ac200e36b9d634e9ad5cfb03
|
|
| MD5 |
ad771375a672dc2a50dbd63f73d3556e
|
|
| BLAKE2b-256 |
4791042bb3e5ee3fd0f28eef87f78186cfef6ef3fd4f6f567d8e9458e2fd2de1
|
Provenance
The following attestation bundles were made for meshtastic_mcp-0.1.0.tar.gz:
Publisher:
release.yml on meshtastic/meshtastic-mcp
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
meshtastic_mcp-0.1.0.tar.gz -
Subject digest:
dc72654d6b3a80c8c4b1bc033f5bdfb077689a91ac200e36b9d634e9ad5cfb03 - Sigstore transparency entry: 2051008367
- Sigstore integration time:
-
Permalink:
meshtastic/meshtastic-mcp@21b65fbe98ddaf9e09824c143a335c9595b9bb18 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/meshtastic
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@21b65fbe98ddaf9e09824c143a335c9595b9bb18 -
Trigger Event:
push
-
Statement type:
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
17ef8cc8f94cbd7a90865a001abbebfd283d639c9d9f024983ad3e94b67ba265
|
|
| MD5 |
ebe82bd412c685256aa0fce231ad06c4
|
|
| BLAKE2b-256 |
70ab627e7291a2be59723c853134563b7fc2d8b1a1042d88f50911ad541bbc59
|
Provenance
The following attestation bundles were made for meshtastic_mcp-0.1.0-py3-none-any.whl:
Publisher:
release.yml on meshtastic/meshtastic-mcp
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
meshtastic_mcp-0.1.0-py3-none-any.whl -
Subject digest:
17ef8cc8f94cbd7a90865a001abbebfd283d639c9d9f024983ad3e94b67ba265 - Sigstore transparency entry: 2051008871
- Sigstore integration time:
-
Permalink:
meshtastic/meshtastic-mcp@21b65fbe98ddaf9e09824c143a335c9595b9bb18 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/meshtastic
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@21b65fbe98ddaf9e09824c143a335c9595b9bb18 -
Trigger Event:
push
-
Statement type: