Installation
MacTools MQTT Explorer is a native macOS app built with Rust and Tauri. It bundles a full MQTT broker (no mosquitto, no Homebrew, no Docker) and a GUI client in one app. Runs natively on Apple Silicon and Intel Macs — no Rosetta, no Parallels, no Node.js. One-time purchase, no subscription.
Download
- Mac App Store: search "MacTools MQTT" in the App Store, or use the button on the product page
- Product page: /mqtt-explorer/
System Requirements
| Requirement | Minimum |
|---|---|
| macOS | 12 Monterey or newer (Apple Silicon & Intel) |
| Broker (local) | Loopback or LAN — listens on TCP/1883 (plain), TCP/8883 (TLS), or TCP/9001 (WebSocket) |
| Connecting to remote brokers | Network reachability to the broker host on its MQTT port |
First Launch
If you downloaded the direct build outside the App Store, macOS Gatekeeper may block the unsigned binary on first run. Right-click the app, choose Open, then confirm. This is only required once.
0.0.0.0. For LAN testing, set the bind address explicitly and ensure macOS Firewall allows incoming connections on the broker port.
Quick Start
1. Start the local broker and connect (2 minutes)
This is the most common path — you want a local broker on your Mac to test IoT devices, Node-RED flows, or an ESP32 firmware without spinning up mosquitto.
- Open the app. The Broker tab shows the local server status.
- Click Start Broker. It binds to
127.0.0.1:1883and is ready immediately. - Switch to the Client tab. A preconfigured connection to
localhost:1883is already listed — click Connect. - The connection indicator turns green; the broker log shows a CONNECT and CONNACK.
If the client fails to connect, jump to Troubleshooting — the most frequent cause is the broker not running or a port conflict with a leftover mosquitto process.
2. Subscribe to a topic and publish a message
- In the Subscriptions panel, enter
sensors/#and click Subscribe. - In the Publish panel, enter topic
sensors/temperatureand payload23.5. - Set QoS to
1and click Publish. - The message appears instantly in the Messages log — you have a working pub/sub loop.
3. Test a retained message
- Publish a payload to
device/statuswith the Retain flag on. - Subscribe to
device/statusfrom a fresh client — you immediately receive the last retained message, even though it was published before you subscribed. - To clear it, publish an empty (zero-length) payload to the same topic with Retain on.
4. Connect to a remote broker (Mosquitto, EMQX, HiveMQ, AWS IoT)
- Create a new connection: host, port (
8883for TLS,1883plain), Client ID. - If the broker requires auth, set username/password. For AWS IoT / mTLS, attach the client certificate, private key, and CA.
- Enable TLS and (for managed brokers) set the SNI / ALPN if required.
- Click Connect.
Configuration
Broker Settings
| Parameter | Options |
|---|---|
| Bind address | 127.0.0.1 (loopback, default), 0.0.0.0 (all interfaces / LAN) |
| Plain TCP port | 1883 (default) |
| TLS port | 8883 — requires a server certificate |
| WebSocket port | 9001 — for browser-based clients |
| Max connections | Default 1000 — lower for low-RAM Macs |
| Anonymous access | Allow / deny — deny requires username/password on every client |
Client Connection Settings
| Parameter | Options |
|---|---|
| Host / Port | Any reachable broker |
| Client ID | Unique per session — empty string lets the broker assign one (only MQTT v5) |
| Protocol version | MQTT v3.1.1 (default, widest compat) or MQTT v5 |
| Clean session | true (no persistence, default) or false (broker keeps subscriptions + queued QoS 1/2 messages) |
| Keep alive | Default 60 s — the interval at which the broker pings if idle |
| Authentication | None, Username/Password, Certificate (mTLS) |
| Last Will | A message published automatically if the client disconnects ungracefully |
QoS Levels
| QoS | Guarantee | Overhead |
|---|---|---|
| 0 | At most once — fire and forget, no ACK. Best for high-frequency telemetry you can afford to lose. | 1 round-trip |
| 1 | At least once — PUBACK required. Duplicates possible on retry. The industrial default. | 2 round-trips |
| 2 | Exactly once — four-step handshake (PUBREC/PUBREL/PUBCOMP). No duplicates. | 4 round-trips |
QoS is set per-publish and per-subscribe independently. A QoS 1 message delivered to a QoS 0 subscription is delivered at most once. Match the subscription's QoS to the message's QoS to get the guarantee you want.
Topic Wildcards
| Pattern | Matches |
|---|---|
sensors/temperature | Exactly that topic — no wildcards |
sensors/+ | Any one level: sensors/temperature, sensors/humidity — but not sensors/room1/temperature |
sensors/# | All levels below: sensors/, sensors/a, sensors/a/b/c — multi-level. Must be the last character. |
$SYS/# | Broker system topics (stats, uptime) — note the leading $ excludes these from # subscriptions |
Retained Messages & Last Will
Retained messages are stored by the broker and delivered to any future subscriber to that topic — the classic pattern for "last known state" (device status, current setpoint). Last Will and Testament (LWT) is a message the broker publishes on your behalf if your client drops ungracefully (network loss, crash) — used to flag a device as offline. Set both in the connection profile before connecting.
Key Features
Broker and client in one app
The bundled Rust broker handles thousands of concurrent connections and MQTT v3.1.1 + v5. You no longer need mosquitto in Homebrew, a Docker container, or a cloud broker just to test a firmware or a Node-RED flow. Start the broker, connect the client tab to localhost, done.
Topic tree with live subscriber count
As messages flow, the broker builds a live topic tree showing each topic, its subscriber count, and its message rate. Spot orphan publishers (topics with no subscribers) and congested topics (rate spikes) at a glance.
Payload inspector with JSON pretty-print
Every message is logged with timestamp, topic, QoS, retain flag, and payload. JSON payloads are pretty-printed and folded; binary payloads are shown as hex. Filter by topic pattern to follow one device's traffic in isolation.
MQTT v5 support
Beyond v3.1.1, the app supports MQTT v5 features: shared subscriptions ($share/group/topic for load-balanced consumers), user properties, content type, message expiry, and reason codes on every ACK.
TLS / mTLS and AWS IoT
For TLS brokers, supply a CA or use the system trust store. For mutual TLS (AWS IoT, Azure IoT Hub), attach the device certificate and private key — the app handles SNI and the TLS handshake. AWS IoT's device-shadow topics work like any other subscription.
Scripted publish (Sparkplug B ready)
Build a publish script that emits Sparkplug B-encoded payloads (birth/death certificates, data metrics) for IIoT testing. The script runs in the app, publishing on a schedule or triggered by a subscribe-side event.
Troubleshooting
CONNACK return codes (MQTT v3.1.1)
| Code | Name | Likely Cause |
|---|---|---|
| 0 | Connection Accepted | Success |
| 1 | Unacceptable protocol version | Broker wants v3.1.1 but you sent v5, or vice versa. Switch the client protocol version. |
| 2 | Identifier rejected | Empty or duplicate Client ID and the broker doesn't allow either |
| 3 | Server unavailable | Broker process not running or not ready |
| 4 | Bad username or password | Wrong credentials, or the broker rejects the auth scheme |
| 5 | Not authorized | Auth succeeded but the user has no connect permission |
Broker won't start (port in use)
Symptom: "Address already in use" when clicking Start Broker.
Fix: Another process holds the port — usually a leftover mosquitto or a second instance. Find it:
sudo lsof -i :1883
Stop that process (or change the bundled broker's port in settings to 1884). On macOS, a previous app launch that crashed can also hold the socket — a reboot clears it.
Client connects but no messages arrive
Symptom: Connection is green, you published a message, but the subscriber sees nothing.
Fix: Check the subscription topic pattern against the publish topic. sensors/+ does not match sensors/room1/temp (two levels). Use sensors/# for multi-level. Also confirm you're not publishing with a leading slash mismatch (/sensors/temp vs sensors/temp are different topics — the leading slash adds an empty first level).
TLS handshake fails against a public broker
Symptom: Plain 1883 works, TLS 8883 fails with a handshake/cert error.
Fix: Either the broker's certificate isn't trusted by your system keychain (import the CA), or the broker requires SNI and you didn't enable it. For brokers like HiveMQ Public and EMQX, SNI is mandatory. AWS IoT additionally requires a client certificate (mTLS) — plain TLS without it returns an auth failure.
Duplicate messages on QoS 1
Symptom: The subscriber receives the same message twice.
Fix: This is by design — QoS 1 is at-least-once. If the PUBACK is delayed (slow network, busy subscriber), the publisher retransmits after its retry interval, and the broker may redeliver. Either accept duplicates and idempotently handle them, or move to QoS 2 (exactly-once) for state-changing commands.
Retained message persists after clear attempt
Symptom: You "cleared" a retained message but new subscribers still receive it.
Fix: A retained message is only cleared by publishing a zero-length payload with the Retain flag set — not by publishing a space or the word "null." Re-publish with an empty payload and Retain on, then re-subscribe to confirm.
Support
- Email: support@voltrus.id
- Product page: /mqtt-explorer/
When reporting a broker or connection issue, attach the broker log (with client IDs redacted), the broker software/version you're connecting to, and the exact CONNACK code or TLS error message.