|
| 1 | +--- |
| 2 | +description: Produce a device health report using the meshtastic MCP tools (device_info, list_nodes, get_config, short serial log capture) |
| 3 | +argument-hint: [role=all|nrf52|esp32s3|<port>] |
| 4 | +--- |
| 5 | + |
| 6 | +# `/diagnose` — device health report |
| 7 | + |
| 8 | +Call the meshtastic MCP tool bundle and format a structured health report for one or all detected devices. Zero guesswork for the operator. |
| 9 | + |
| 10 | +## What to do |
| 11 | + |
| 12 | +1. **Enumerate hardware.** Call `mcp__meshtastic__list_devices(include_unknown=True)`. For each entry where `likely_meshtastic=True`, capture `port`, `vid`, `pid`, `description`. |
| 13 | + |
| 14 | +2. **Filter by `$ARGUMENTS`**: |
| 15 | + - No args, `all` → every likely-meshtastic device. |
| 16 | + - `nrf52` → only devices with `vid == 0x239a`. |
| 17 | + - `esp32s3` → only devices with `vid == 0x303a` or `vid == 0x10c4`. |
| 18 | + - A `/dev/cu.*` path → only that one port. |
| 19 | + - Anything else → treat as a substring match against the `port` string. |
| 20 | + |
| 21 | +3. **For each selected device, in sequence (NOT parallel — SerialInterface holds an exclusive port lock):** |
| 22 | + - `mcp__meshtastic__device_info(port=<p>)` — captures `my_node_num`, `long_name`, `short_name`, `firmware_version`, `hw_model`, `region`, `num_nodes`, `primary_channel`. |
| 23 | + - `mcp__meshtastic__list_nodes(port=<p>)` — count of peers, which ones have `publicKey` set, SNR/RSSI distribution. |
| 24 | + - `mcp__meshtastic__get_config(section="lora", port=<p>)` — region, preset, channel_num, tx_power, hop_limit. |
| 25 | + - Optionally, if the device seems unhappy (fails to connect, `num_nodes==1` when ≥2 are plugged in, missing firmware*version), open a short firmware log window: `mcp__meshtastic__serial_open(port=<p>, env=<inferred-env>)`, wait 3s, `serial_read(session_id=<s>, max_lines=100)`, `serial_close(session_id=<s>)`. The env should be inferred from the VID map in `mcp-server/run-tests.sh` (nrf52 → rak4631, esp32s3 → heltec-v3) unless `MESHTASTIC_MCP_ENV*<ROLE>` is set. |
| 26 | + |
| 27 | +4. **Hub health** (call once, not per-device): `mcp__meshtastic__uhubctl_list()` — enumerates every USB hub the host can see. Note which hubs advertise `ppps=true` and which hub hosts each Meshtastic device (cross-reference by VID). Flag it in the report if: |
| 28 | + - No hub advertises PPPS → `tests/recovery/` can't run on this setup; hard-recovery via `uhubctl_cycle` isn't available. |
| 29 | + - A Meshtastic device is on a non-PPPS hub → note it; operator may want to move the device to a PPPS hub to unlock auto-recovery. |
| 30 | + - `uhubctl_list` raises `ConfigError: uhubctl not found` → just say `uhubctl not installed` in the report; don't treat as a fault. |
| 31 | + |
| 32 | +5. **Render per-device report** as: |
| 33 | + |
| 34 | + ```text |
| 35 | + [nrf52 @ /dev/cu.usbmodem1101] fw=2.7.23.bce2825, hw=RAK4631 |
| 36 | + owner : Meshtastic 40eb / 40eb |
| 37 | + region/band : US, channel 88, LONG_FAST |
| 38 | + tx_power : 30 dBm, hop_limit=3 |
| 39 | + peers : 1 (esp32s3 0x433c2428, pubkey ✓, SNR 6.0 / RSSI -24 dBm) |
| 40 | + primary ch : McpTest |
| 41 | + hub : 1-1.3 port 2 (PPPS, uhubctl-controllable) |
| 42 | + firmware : no panics in last 3s; NodeInfoModule emitted 2 broadcasts |
| 43 | + ``` |
| 44 | + |
| 45 | + Keep it scannable. If a field is missing or abnormal (no pubkey for a known peer, region=UNSET, num_nodes inconsistent with the hub, device on non-PPPS hub), flag it inline with a short `⚠︎ <one-line reason>`. |
| 46 | + |
| 47 | +6. **Cross-device correlation** (only when >1 device is inspected): |
| 48 | + - Do both sides see each other in `nodesByNum`? If one does and the other doesn't, that's asymmetric NodeInfo — flag it. |
| 49 | + - Do the LoRa configs match? (region, channel_num, modem_preset should all agree; mismatch = no mesh) |
| 50 | + - Do the primary channel NAMES match? Mismatch = different PSK = no decode. |
| 51 | + |
| 52 | +7. **Suggest next actions only for specific, recognisable failure modes**: |
| 53 | + - Stale PKI pubkey one-way → "run `/test tests/mesh/test_direct_with_ack.py` — the retry + nodeinfo-ping heals this in the test path." |
| 54 | + - Region mismatch → "re-bake one side via `./mcp-server/run-tests.sh --force-bake`." |
| 55 | + - Device unreachable, reachable via DFU → `touch_1200bps(port=...)` + `pio_flash`. If not even DFU responds AND the device is on a PPPS hub, escalate to `uhubctl_cycle(role=..., confirm=True)`. |
| 56 | + - CP2102-wedged-driver on macOS → see the note in `run-tests.sh`. |
| 57 | + |
| 58 | +## What NOT to do |
| 59 | + |
| 60 | +- No writes. No `set_config`, no `reboot`, no `factory_reset`. This is a read-only diagnostic skill — if the operator wants to change state, they'll ask explicitly. |
| 61 | +- No `flash` / `erase_and_flash`. Those are separate escalations. |
| 62 | +- No holding SerialInterface across tool calls — open, query, close; next device. The port lock is exclusive. |
0 commit comments