# 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](/keeperpam/endpoint-privilege-manager/integrations/examples/plugin-minimal-windows.md) 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

```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

<table data-header-hidden="false" data-header-sticky><thead><tr><th width="278.926025390625">Field</th><th>What to Put Here</th></tr></thead><tbody><tr><td><code>id</code></td><td>A stable, unique identifier matching the filename. All topic paths below derive from this ID by convention — change them together.</td></tr><tr><td><code>executablePath</code></td><td>Path to your binary. For Linux and macOS, remove the <code>.exe</code> extension and confirm the install root. See the <a href="https://claude.ai/chat/plugin-minimal-linux">Minimal Linux</a> and <a href="https://claude.ai/chat/plugin-minimal-macos">Minimal macOS</a> pages for platform-specific path and permission details.</td></tr><tr><td><code>supportedPlatforms</code></td><td>Remove any platforms you do not ship for.</td></tr><tr><td><code>Subscription.Topic</code></td><td>The primary MQTT topic for this plugin. By convention, use the plugin <code>id</code>.</td></tr><tr><td><code>metadata.mqttTopics.publish</code></td><td>Replace <code>MyWorker</code> throughout with your plugin <code>id</code>. Add or remove topics as your design requires.</td></tr><tr><td><code>metadata.mqttTopics.subscribe</code></td><td>Replace <code>MyWorker</code> with your plugin <code>id</code>. Tighten the wildcard if your command space is bounded — see below.</td></tr><tr><td><code>startupPriority</code></td><td><code>50</code> starts this plugin slightly earlier than the default <code>60</code>. Lower only if your plugin has dependencies that must start first.</td></tr></tbody></table>

## How This Works

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

<table data-header-hidden="false" data-header-sticky><thead><tr><th width="201">Topic</th><th width="115">Direction</th><th>Purpose</th></tr></thead><tbody><tr><td><code>MyWorker/Commands/+</code></td><td>Subscribe</td><td>Inbound commands. The <code>+</code> wildcard matches one level — <code>MyWorker/Commands/scan</code>, <code>MyWorker/Commands/reset</code>, and so on. Each command type gets its own sub-topic.</td></tr><tr><td><code>MyWorker/Responses/+</code></td><td>Publish</td><td>Outbound results, mirroring the command sub-topic. A response to <code>MyWorker/Commands/scan</code> publishes to <code>MyWorker/Responses/scan</code>. Callers subscribe to the response topic they care about.</td></tr><tr><td><code>MyWorker/Status</code></td><td>Publish</td><td>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.</td></tr></tbody></table>

**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:

```json
"subscribe": [
  "MyWorker",
  "MyWorker/Commands/scan",
  "MyWorker/Commands/reset"
]
```

And restrict publications to explicit response topics:

```json
"publish": [
  "KeeperLogger",
  "MyWorker/Responses/scan",
  "MyWorker/Responses/reset",
  "MyWorker/Status"
]
```

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


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.keeper.io/keeperpam/endpoint-privilege-manager/integrations/examples/plugin-command-response-mqtt.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
