Add MCP server for interacting with meshtastic devices and testing framework / TUI (#10194)
* Start of MCP server and test suite * Add MCP server for interacting with meshtastic devices and testing framework / TUI * Update mcp-server/README.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * fix mcp-server review feedback from thread Agent-Logs-Url: https://github.com/meshtastic/firmware/sessions/91dc128a-ed50-4d07-8bb2-3dc6623a05f7 Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com> * Enhance StreamAPI and PhoneAPI for improved log record handling and concurrency control * Semgrep fixes * Trunk and semgrep fixes * optimize pio streaming tee file writes Agent-Logs-Url: https://github.com/meshtastic/firmware/sessions/04e26c6b-6a2b-45be-bbeb-79ae4d0be633 Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com> * chore: remove redundant log handle assignment Agent-Logs-Url: https://github.com/meshtastic/firmware/sessions/04e26c6b-6a2b-45be-bbeb-79ae4d0be633 Co-authored-by: thebentern <9000580+thebentern@users.noreply.github.com> * Consolidate type imports and remove placeholder test files * Add tests for config persistence and more exchange messages * Refactor position test to validate on-demand request/reply behavior * Remove position request/reply test and update README for telemetry behavior * Fix transmit history file to get removed on factory reset --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
This commit is contained in:
co-authored by
GitHub
Copilot
copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
parent
aab4cd086f
commit
c8dac10348
@@ -0,0 +1,75 @@
|
||||
"""USB/serial device discovery.
|
||||
|
||||
Combines the canonical `meshtastic.util.findPorts()` allowlist/blocklist with
|
||||
the richer metadata (`serial.tools.list_ports.comports()`) so callers see
|
||||
VID/PID, descriptions, and manufacturer strings alongside the "is this likely
|
||||
a Meshtastic device" signal.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from serial.tools import list_ports
|
||||
|
||||
|
||||
def _to_hex(value: int | None) -> str | None:
|
||||
if value is None:
|
||||
return None
|
||||
return f"0x{value:04x}"
|
||||
|
||||
|
||||
def list_devices(include_unknown: bool = False) -> list[dict[str, Any]]:
|
||||
"""Return enriched info for serial ports, flagging Meshtastic candidates.
|
||||
|
||||
`likely_meshtastic` is True when the port's USB VID matches the Meshtastic
|
||||
allowlist (`0x239a` Adafruit/RAK, `0x303a` Espressif). When no allowlisted
|
||||
ports are present, ports whose VID is NOT in the blocklist (J-Link, ST-LINK,
|
||||
PPK2, etc.) are surfaced as `likely_meshtastic=False` candidates.
|
||||
|
||||
With `include_unknown=False` (default), we return only ports that are
|
||||
plausibly Meshtastic. With `include_unknown=True`, every serial port the
|
||||
OS knows about is returned (useful for debugging "why isn't my board
|
||||
detected").
|
||||
"""
|
||||
# Import lazily so the module loads even without the `meshtastic` package
|
||||
# (useful for introspection / schema generation).
|
||||
from meshtastic import util as mt_util # type: ignore[import-untyped]
|
||||
|
||||
meshtastic_ports: set[str] = set(mt_util.findPorts(eliminate_duplicates=True))
|
||||
whitelist = getattr(mt_util, "whitelistVids", {})
|
||||
blacklist = getattr(mt_util, "blacklistVids", {})
|
||||
|
||||
results: list[dict[str, Any]] = []
|
||||
for info in list_ports.comports():
|
||||
port_path = info.device
|
||||
vid = info.vid
|
||||
in_whitelist = vid is not None and vid in whitelist
|
||||
in_blacklist = vid is not None and vid in blacklist
|
||||
|
||||
likely = port_path in meshtastic_ports and in_whitelist
|
||||
# If no allowlisted ports were found, findPorts falls back to
|
||||
# everything-not-in-blacklist; treat those as plausible candidates
|
||||
# but not "likely".
|
||||
fallback_candidate = port_path in meshtastic_ports and not in_whitelist
|
||||
|
||||
if not likely and not fallback_candidate and not include_unknown:
|
||||
continue
|
||||
|
||||
results.append(
|
||||
{
|
||||
"port": port_path,
|
||||
"vid": _to_hex(vid),
|
||||
"pid": _to_hex(info.pid),
|
||||
"description": info.description or None,
|
||||
"manufacturer": info.manufacturer or None,
|
||||
"product": info.product or None,
|
||||
"serial_number": info.serial_number or None,
|
||||
"likely_meshtastic": likely,
|
||||
"blacklisted": in_blacklist,
|
||||
}
|
||||
)
|
||||
|
||||
# Stable ordering: likely_meshtastic first, then by port path
|
||||
results.sort(key=lambda r: (not r["likely_meshtastic"], r["port"]))
|
||||
return results
|
||||
Reference in New Issue
Block a user