@kadbbz/mqtt-channel
v2.3.1
Published
MQTT channel plugin for OpenClaw - bidirectional messaging via MQTT brokers
Maintainers
Readme
@kadbbz/mqtt-channel
MQTT channel plugin for OpenClaw — bidirectional messaging via MQTT brokers.
Compatibility note: current releases target the OpenClaw 2026.5.x channel SDK and ship the newer channel/setup entrypoints plus configured-state metadata.
Features
- 🔌 Bidirectional messaging — subscribe and publish to MQTT topics
- 🔁 Robust reconnection — recovers from broker restarts and cold starts
- 🔒 TLS support — secure connections to cloud brokers
- ⚡ QoS levels — configurable delivery guarantees (0, 1, 2)
Installation
openclaw plugins install @kadbbz/mqtt-channelIf OpenClaw is already loading an untracked local copy from ~/.openclaw/extensions/mqtt-channel,
that local directory takes precedence over a global npm install. Check the active source with:
openclaw plugins info mqtt-channelIf the plugin source is ~/.openclaw/extensions/mqtt-channel/dist/index.js, update that managed
plugin copy with:
openclaw plugins update mqtt-channelIf plugins info shows an old untracked local copy instead of an installed plugin record:
- Move that backup or old copy out of
~/.openclaw/extensions/. - Run
openclaw plugins install @kadbbz/mqtt-channel. - Restart the gateway.
Do not keep backup directories such as mqtt-channel.bak-* inside ~/.openclaw/extensions/,
or OpenClaw will detect duplicate plugin ids.
Configuration
Add to ~/.openclaw/openclaw.json:
{
"channels": {
"mqtt-channel": {
"accounts": {
"admin": {
"brokerUrl": "mqtts://your_server:8883",
"username": "your_name",
"password": "your_password",
"topics": {
"inbound": "openclaw/inbound-admin",
"outbound": "openclaw/outbound-admin"
},
"qos": 1,
"disableBlockStreaming": false
},
"lowcode": {
"brokerUrl": "mqtts://your_server:8883",
"username": "your_name",
"password": "your_password",
"topics": {
"inbound": "openclaw/inbound-lowcode",
"outbound": "openclaw/outbound-lowcode"
},
"qos": 1,
"disableBlockStreaming": false
}
}
}
},
"plugins": {
"allow": [
"mqtt-channel"
],
"entries": {
"mqtt-channel": {
"enabled": true
}
}
},
}Each key under channels["mqtt-channel"].accounts is an OpenClaw account ID. The gateway will start one MQTT client per account and route inbound/outbound topics independently.
Legacy single-account config is still accepted and treated as default, but new setups should use accounts.
Then restart the gateway process:
systemctl restart openclawUsage
Sessions IDs (important)
- Routing uses the MQTT account id plus the inbound
senderId→ the plugin resolves an OpenClaw route withpeer.id = senderId, then OpenClaw derives the finalagentIdandsessionKeyfrom yourbindingsand session settings. sessionIdis request‑level only → if you include it in inbound JSON, it’s echoed back in the outbound reply for client-side matching. It does not create a new session or change memory.
If you want separate conversations, use distinct senderIds.
Receiving messages (inbound)
Messages published to an account's inbound topic will be processed by OpenClaw.
You can send either plain text or JSON (recommended):
# Plain text (admin account)
mosquitto_pub -t "openclaw/inbound-admin" -m "Alert: Service down on playground"
# JSON (recommended, lowcode account)
mosquitto_pub -t "openclaw/inbound-lowcode" -m '{"senderId":"pg-cli","text":"hello","sessionId":"abc-123"}'Example inbound JSON:
{"senderId":"conn-test2","text":"What is your agent name?","sessionId":"conn-test2"}Per-message block streaming override:
{"senderId":"conn-test2","text":"Give me the final answer only","sessionId":"conn-test2","disableBlockStreaming":true}Notes:
disableBlockStreamingonly affects the current MQTT request.- When omitted, the plugin uses the account-level
disableBlockStreamingvalue from config. trueforces this request down the non-block-streaming path, so OpenClaw can emitfinalreplies for that request.falseexplicitly allows block streaming for that request, even if the account default disables it.
Sending messages (outbound)
Agent replies are published to that account's outbound topic as JSON:
{"senderId":"openclaw","text":"...","kind":"final","sessionId":"conn-test2","ts":1700000000000}If the inbound JSON includes sessionId, the same value is echoed in the outbound reply, including buffered or deferred replies that go through OpenClaw's generic outbound path.
Example:
mosquitto_sub -t "openclaw/outbound-lowcode" -vExpected reply shape:
{"senderId":"openclaw","text":"...","kind":"final","ts":1700000000000,"sessionId":"abc-123"}Real example reply:
{"senderId":"openclaw","text":"I am **OpenClaw** — a local AI assistant running on your machine.\n\nIDENTITY.md is not filled in yet, so I do not have a customized name at the moment. Do you want to give me one, or should I just stay OpenClaw?","kind":"block","ts":1774576295258,"sessionId":"conn-test2"}Notes:
- The plugin forwards OpenClaw's outbound
kindas-is. It does not rewriteblocktofinalor vice versa. kindmay beblockduring block streaming orfinalfor the final reply chunk.- If the inbound message sets
disableBlockStreaming: true, that request asks OpenClaw to avoid block streaming so it can producefinalreplies for that request. - If the inbound message omits
disableBlockStreaming, outboundkindbehavior follows the account-leveldisableBlockStreamingsetting from config. sessionIdis echoed only when the inbound JSON includes it.- Numeric inbound
sessionIdvalues are normalized to strings in outbound JSON. outbound.sendText()publishes the same JSON envelope format and will also includesessionIdwhen OpenClaw suppliesthreadIdotherwise return -1.
Troubleshooting
Check which plugin copy OpenClaw is actually loading:
openclaw plugins info mqtt-channelUseful live logs:
journalctl -u openclaw -f | grep -E "MQTT channel ready|MQTT route resolved|Inbound MQTT message|sent reply"When using multiple MQTT accounts, a healthy startup should show one subscription line per account, for example:
[admin] MQTT channel ready, subscribed to openclaw/inbound-admin
[lowcode] MQTT channel ready, subscribed to openclaw/inbound-lowcodeFor each inbound message, the plugin also logs the resolved route:
MQTT route resolved topic=openclaw/inbound-lowcode inboundAccount=lowcode routeAccount=lowcode agent=lowcode session=...If that log line shows agent=main, the message matched your MQTT topic but OpenClaw routed it to the main agent based on current bindings.
Security
Important: Any client that can publish to the inbound topic has full access to your OpenClaw agent. Treat MQTT as a trusted channel only (restricted broker, auth, private network). If you need untrusted access, add a validation layer before publishing to openclaw/inbound.
Development
# Clone (replace with your host)
git clone ssh://<host>/opt/git/openclaw-mqtt.git
cd openclaw-mqtt
# Install deps
npm install
# Run tests
npm test
# Type check
npm run typecheck
# Build
npm run buildArchitecture
MQTT Broker (Mosquitto/EMQX)
│
├─► inbound topic ──► OpenClaw Gateway ──► Agent
│
└─◄ outbound topic ◄── OpenClaw Gateway ◄── AgentLicense
MIT © kadbbz
See Also
- openclaw-mqtt - Forked from this repo
- OpenClaw — The AI assistant platform
- MQTT.js — MQTT client library
- Mosquitto — Popular MQTT broker
