Plugin: Command Response MQTT

Audience: Integrators building a plugin that receives commands over MQTT and publishes responses — a bidirectional pattern suitable for request-driven components like bridges, proxies, and action dispatchers.

The minimal plugin examples show a plugin that only publishes outbound. This example adds the inbound side: the plugin subscribes to a Commands/+ topic, processes each command, and publishes results to a corresponding Responses/+ topic. A separate Status topic provides a channel for health signals. This is a cross-platform JSON — the same file covers Windows, Linux, and macOS.

Read Plugin: Minimal Windows first if you have not already. This page focuses on the command/response topic layout and does not repeat the field-by-field explanation from that page.

The Plugin JSON

{
  "id": "MyWorker",
  "name": "My Worker",
  "description": "Receives commands and publishes responses over MQTT.",
  "version": "1.0.0",

  "pluginType": "Executable",
  "executablePath": "bin/MyWorker/MyWorker.exe",
  "supportedPlatforms": ["Windows", "Linux", "macOS"],

  "Subscription": {
    "Topic": "MyWorker",
    "Qos": 2,
    "CleanSession": true
  },

  "metadata": {
    "mqttRole": ["subscriber", "publisher"],
    "mqttTopics": {
      "publish": [
        "KeeperLogger",
        "MyWorker/Responses/+",
        "MyWorker/Status"
      ],
      "subscribe": [
        "MyWorker/Commands/+"
      ]
    }
  },

  "startupPriority": 50,
  "autoStart": true,
  "executionContext": "Service",
  "requiresMonitoring": true,
  "autoRestart": true
}

What to Change

Field
What to Put Here

id

A stable, unique identifier matching the filename. All topic paths below derive from this ID by convention — change them together.

executablePath

Path to your binary. For Linux and macOS, remove the .exe extension and confirm the install root. See the Minimal Linuxarrow-up-right and Minimal macOSarrow-up-right pages for platform-specific path and permission details.

supportedPlatforms

Remove any platforms you do not ship for.

Subscription.Topic

The primary MQTT topic for this plugin. By convention, use the plugin id.

metadata.mqttTopics.publish

Replace MyWorker throughout with your plugin id. Add or remove topics as your design requires.

metadata.mqttTopics.subscribe

Replace MyWorker with your plugin id. Tighten the wildcard if your command space is bounded — see below.

startupPriority

50 starts this plugin slightly earlier than the default 60. Lower only if your plugin has dependencies that must start first.

How This Works

The topic layout. The three topic patterns form a conventional command/response structure:

Topic
Direction
Purpose

MyWorker/Commands/+

Subscribe

Inbound commands. The + wildcard matches one level — MyWorker/Commands/scan, MyWorker/Commands/reset, and so on. Each command type gets its own sub-topic.

MyWorker/Responses/+

Publish

Outbound results, mirroring the command sub-topic. A response to MyWorker/Commands/scan publishes to MyWorker/Responses/scan. Callers subscribe to the response topic they care about.

MyWorker/Status

Publish

Health signals, heartbeats, or state changes. Subscribers use this to monitor whether the plugin is alive and what state it is in without sending a command.

The primary Subscription topic is separate. MyWorker (the Subscription.Topic) is the orchestrator-level identifier for the plugin. It is listed separately from the command/response topics in metadata.mqttTopics.subscribe — both are subscribed, but they serve different purposes. The primary subscription is how the agent knows the plugin is connected; the command topics are how callers drive it.

Tighten wildcards in production. Commands/+ permits any single-level subtopic. If your plugin only handles a known set of commands, restrict the subscription to explicit topic strings rather than a wildcard:

And restrict publications to explicit response topics:

Wildcards are convenient during development but give the broker less to enforce. Explicit topic lists are a tighter security posture for production.

Correlating commands and responses. The topic naming convention handles routing, but your payload should also carry a correlation ID so callers can match a response to the command that triggered it — especially when multiple callers may issue commands concurrently. This is a design responsibility in your binary, not something the plugin JSON or broker enforces.

startupPriority: 50 starts this plugin slightly earlier than plugins at the default 60. Use a lower priority only if something that starts before you needs to be ready first, or if callers expect the plugin to be available shortly after agent startup. Avoid racing to start as early as possible — earlier priority means fewer other components are ready when yours starts.

Before You Deploy

  1. Sign the binary for each platform you support. On Windows, use Authenticode. On macOS, sign with Apple Developer ID and notarize. On Linux, align with your deployment's signing enforcement.

  2. Ship platform-specific binaries under the correct paths. For a cross-platform plugin, bin/MyWorker/MyWorker.exe is the Windows binary. Linux and macOS binaries at bin/MyWorker/MyWorker (no extension) should live at the same relative path under their respective agent roots.

  3. Set executable permissions on Linux and macOS with chmod +x.

  4. Place the JSON file at {AgentRoot}/Plugins/MyWorker.json on each endpoint.

  5. Confirm that any component subscribing to MyWorker/Responses/+ or MyWorker/Status has MQTT subscribe permission for those topics before going live.

  6. Restart the agent so the orchestrator picks up the registration.

Verify

After the agent restarts, confirm the plugin connected by checking that its primary subscription topic is active in the agent logs. Then publish a test command from a trusted process and verify the response arrives on the expected topic. If the response does not arrive, check:

  • That the command topic string matches exactly what the plugin subscribes to

  • That the response topic is in metadata.mqttTopics.publish — a missing entry causes a silently denied publish, not an error visible to the caller

  • That the calling process has permission to publish to the command topic and subscribe to the response topic

Last updated

Was this helpful?