8.8 KiB
8.8 KiB
Changelog
v0.6.0 — 2026-09-24 18:28
- New MCP (Model Context Protocol) server endpoint at
POST /mcp(outside the/apiprefix, like Scalar at/), served in-process via the official@modelcontextprotocol/sdkStreamable HTTP transport in stateless mode (sessionIdGenerator: undefined; a freshMcpServer+ transport per request). Six tools map 1:1 to existingMeshServicemethods with no new logic:mesh_status,send_message(text, to?)(same target resolution asPOST /api/send— omitted/""/"!b"/"broadcast" → broadcast, numbers pass through, names via the node DB; resolution errors surface as tool errors with the same messages),list_channels,list_nodes,get_node(id)(tool error when missing/ambiguous), andget_messages. Tool results are JSON in a text content block. - New dependency:
@modelcontextprotocol/sdk(^1.30.1; zod comes with it). Note: the SDK's root entry is broken in all published 1.x tarballs (upstream issue #2273 — the exports map points to a dist index that was never shipped), so imports use the documented subpaths (@modelcontextprotocol/sdk/server/mcp.js,.../server/streamableHttp.js,.../inMemory.js,.../client/index.js). - Test helpers shared by both specs extracted from
mesh.service.spec.tsintosrc/mesh/fake-bridge.ts(fake bridge, waitFor, nodeInfoFrame, KAMIL fixtures); newmcp.controller.spec.tsdrives the MCP server over an in-memory transport pair — all six tools listed with descriptions/schemas, and mesh_status / send_message (broadcast + name resolution + error messages) / get_node / list_channels / list_nodes / get_messages return correct shapes. - README gains an MCP section; AGENTS.md endpoint list updated for
/mcp.
v0.5.0 — 2026-09-24 17:37
- Publishing channel moved from
testowy(index 2) to the newtestchannel (index 3);MESH_SEND_CHANNELdefault is now3. All docs/comments referencing the old pinning updated accordingly.
v0.4.0 — 2026-09-24 14:39
POST /api/sendnow accepts node names and explicit broadcast sentinels into: omitted/"" or"!b"/"broadcast"(case-insensitive, trimmed) → broadcast address0xffffffff; numbers pass through as-is (must be an integer in 1..0xffffffff); numeric strings are parsed the same way; anything else is looked up by name via the existing node DB lookup (exact long/short name, then unique substring). Unknown or ambiguous targets are a 400 withno node matching '<to>'. New service methodMeshService.resolveDest(to?)does the resolution —sendText()keeps its resolved-number signature.- Swagger:
SendTextDto.todocumented asnumber | stringwith the full resolution order and the DM-vs-broadcast caveat (DMs are unicast wantAck-tracked viaGET /api/messages; broadcasts stay pending because firmware strips want_ack);@ApiOperationnotes all sends still ride channel index 2 (testowy) regardless of DM or broadcast. - README + AGENTS.md endpoint lists updated for the new
tosemantics. - Unit tests:
resolveDestcoverage on the fake bridge — omitted/""/"!b"/"BROADCAST" → broadcast, number passthrough, numeric string, exact longName match → num (node DB seeded via framed FromRadio nodeInfo), unknown name →no node matching, and invalid numbers (0, negative, non-integer, > 0xffffffff) rejected.
v0.3.0 — 2026-09-24 14:08
- Message history with delivery tracking, exposed as
GET /api/messages(newest first): inbound TEXT_MESSAGE_APP packets (dir:'rx', withfrom) and our sends (dir:'tx', withto+status). tx records startpendingand move todelivered/failedwhen the routing ACK/NACK arrives — ROUTING_APP packets carry a serializedmeshtastic.RoutinginData.payloadwhose oneof variant iserrorReason(Routing_Error: 0 NONE = delivered, anything else failed with a human-readableackError, e.g. NO_ROUTE/GOT_NAK/TIMEOUT, fallbackreason <n>), matched to our sends viaData.requestId. Broadcasts staypendingbecause firmware strips want_ack pre-TX (documented behavior, no timer). History is a bounded in-memory buffer of the last 100 records (newest kept) and resets on every bridge reconnect alongside channels/nodes. - Codec: new
PORT_ROUTING_APP = 5constant (verified against the installed@meshtastic/protobufsPortNum enum),decodeRouting()for ROUTING_APP payloads, anddescribeRoutingError()which maps a Routing.Error code to its name from the installed package'sMesh.Routing_Errorenum. - Swagger:
GET /api/messagesdocumented with the pending→delivered/failed lifecycle, the broadcast caveat, and an example matching the record shape; README + AGENTS.md endpoint lists updated.
v0.2.0 — 2026-09-24 13:08
- Node database collected during the initial sync (
FromRadio.nodeInfoframes, upserted by node number) and kept fresh by livePOSITION_APPpackets (the Position is serialized inData.payload; unknown senders get a stub entry). The map resets on every bridge reconnect alongside channels. - New endpoints:
GET /api/nodes— lean summaries sorted by node number as{ num, longName?, shortName?, hwModel?, batteryLevel?, lastHeard (epoch secs | null), position? { lat, lon, alt? } }[]; names/hwModel/battery appear only when the device reported them,hwModelis the enum's string name (UNSET omitted), andpositiononly when both coordinates are known.GET /api/nodes/:id— lookup by exact node number (numbers/numeric strings), then case-insensitive exact long/short name, else unique case-insensitive substring; 404 with a clear message when nothing or more than one node matches. - Codec: new
PORT_POSITION_APP = 3constant (verified against the installed@meshtastic/protobufsPortNum enum),decodePosition()for POSITION_APP payloads, andloadHwModelNames()reverse lookup of the HardwareModel enum. - Swagger docs made genuinely useful:
Meshtag on the controller (description viaDocumentBuilder.addTag— v12'sApiTagOptionshas no description field) plus@ApiOperationsummary/description and@ApiResponseexamples on all endpoints, existing and new. - Unit tests build real framed FromRadio bytes for nodeInfo/POSITION_APP: sync collection + summary shape (lat/lon = value × 1e-7, battery), stub entry from an unknown POSITION_APP sender, lookup ladder (exact num / numeric string / exact name / unique substring) with ambiguous and missing → null, and reconnect reset.
v0.1.4 — 2026-09-23 20:33
- Send a
ToRadiowithwant_config_id(random non-zero uint32 nonce) on every bridge connect, so the firmware streams its initial sync data (myInfo, channels,config_complete_id) — previously nothing was written on connect, which is why/api/channelsstayed empty andmyNodeNumstayed null in production. New codec helperbuildWantConfigToRadio; unit test captures the outbound frame on the fake bridge, asserts thewantConfigIdoneof case with a non-zero value, then feeds backmyInfo+configCompleteIdframes and checks service state.
v0.1.3 — 2026-09-23 19:58
- Sends are now pinned to the test channel
testowy(channel index 2) instead of the hardcoded primary channel (channel: 0). The target channel is configurable viaMESH_SEND_CHANNEL(default2) and parsed once at service construction;buildTextToRadiotakes the channel index as a parameter. Unit tests now decode the outbound ToRadio frame on the fake bridge and assertpacket.channel === 2on the default send path.
v0.1.2 — 2026-09-23 19:51
- Added a Gitea Actions CI pipeline (
.gitea/workflows/ci.yml), mirroring flight's runner conventions: on push/PR tomain(plus manual dispatch) one job runsnpm ci,nest build, the vitest unit suite, and a production Docker image build — with retry loops for the runner's flaky uplink. No app code changed.
v0.1.1 — 2026-09-23 19:38
- Fixed the default bridge port to
4403, the meshtastic-serial-bridge's actual TCP port (the old value pointed at a closed port). Updated theMESH_PORTdefault in code, the docker-compose environment, and docs.
v0.1.0 — 2026-09-23 18:54
- New
GET /api/channelsendpoint listing the device's Meshtastic channels as{ index, name, uplinkEnabled, downlinkEnabled }; psk/keys are redacted and never exposed over HTTP. Channels are collected fromFromRadio.channelframes streamed during initial sync (beforeconfig_complete_id) and reset on every bridge reconnect so stale data is dropped after a disconnect. - All endpoints moved under the
/apinamespace viaapp.setGlobalPrefix("api"):GET /status→GET /api/status,POST /send→POST /api/send. - Scalar API docs served at
/(OpenAPI JSON at/docs-json) using@nestjs/swagger+@scalar/nestjs-api-reference; added light swagger decorators (ApiPropertyon the send body,ApiOperationsummaries) so the docs are useful. - Reconnect interval is now configurable via
MESH_RECONNECT_MS(default 5000 ms).