Hi all,
I have been building an application on top of ChirpStack and wanted my coding agent
(Claude Code, but any MCP client works) to be able to enqueue a downlink, watch the
uplink come back, tweak the payload codec and check link quality without me copying
things between the web UI and the editor. So I wrote an MCP server for it:

- GitHub: https://github.com/oliveres/chirpstack-mcp-server (MIT)
- Install:
uvx chirpstack-mcp-serverwithCHIRPSTACK_SERVER=host:8080and
CHIRPSTACK_API_KEY=…— talks to the native gRPC API (same port as the web UI),
no REST proxy needed.
What it does:
- 81 tools in 10 toolsets (devices, debug, applications, profiles, gateways on by
default; multicast, fuota, integrations, tenants, relay opt-in) — a thin, faithful
layer over the gRPC services, nothing vendor-specific. - The interesting part is
debug:capture_startopens the device event/frame
stream (InternalService.StreamDeviceEventsetc.) in the background into a ring
buffer, the agent enqueues a downlink and reads what arrived withcapture_read
(decoded uplinks with fPort/fCnt/hex/codec object/RSSI/SNR, ack/txack, log events).
wait_for_eventanddevice_recent_eventscover the quick-look cases. profile_set_codecupdates only the codec so an agent can iterate a decoder while
watching uplinks.- Session keys and stored integration credentials are redacted from tool output unless
explicitly requested; destructive tools carry the MCPdestructiveHint.
Things I learned about the API on the way, in case they are useful to others:
GetVersion/ListRegionsonInternalServicereject API keys (user sessions
only), andTenantService.Listrejects tenant-scoped keys — the server uses
DeviceProfileService.ListAdrAlgorithmsas its connectivity/key check because it is
the one call every key kind can make.- The stream endpoints accept plain API keys and replay the last ~10 items from Redis
on connect.
Tested against ChirpStack 4.19.1 (the integration tests spin up chirpstack-docker).
My question to people who know the API better than I do: are there calls you would
expose differently, and does the event/frame normalization match what you would expect
from the MQTT integration payloads? Bug reports and PRs welcome on GitHub.