Sparkplug B MCP Server
A Model Context Protocol (MCP) server for Sparkplug B — the Eclipse-standard MQTT topic/payload convention for industrial/SCADA data (birth/death certificates, sequenced metrics, protobuf payloads). Works against any MQTT broker that carries Sparkplug B traffic; only the open Sparkplug B specification is used, no vendor-specific extensions.
This server acts as a Sparkplug B Primary Host Application: it subscribes to the entire spBv1.0 namespace, discovers Edge Nodes and Devices from their NBIRTH/DBIRTH messages, tracks their live metrics, and can send them commands (NCMD/DCMD) — the same role a SCADA/historian host application plays in a real deployment.
Built with FastMCP and pysparkplug (itself built on paho-mqtt).
Tools
| Tool | Description |
|---|---|
get_connection_status() |
Report the broker connection and this host application's identity. |
set_host_online(online) |
Publish this connector's own retained Sparkplug STATE message. |
list_edge_nodes(group_id?) |
List Edge Nodes discovered via NBIRTH/NDEATH. |
list_devices(group_id?, edge_node_id?) |
List Devices discovered via DBIRTH/DDEATH. |
get_node_metrics(group_id, edge_node_id) |
Latest known metric values for an Edge Node. |
get_device_metrics(group_id, edge_node_id, device_id) |
Latest known metric values for a Device. |
send_node_command(group_id, edge_node_id, metrics) |
Send an NCMD — write one or more of a node's metrics. |
send_device_command(group_id, edge_node_id, device_id, metrics) |
Send a DCMD — write one or more of a device's metrics. |
request_node_rebirth(group_id, edge_node_id) |
Standard Node Control/Rebirth command — asks a node to republish its full NBIRTH. |
start_watch(group_id?, edge_node_id?, device_id?, message_types?) |
Start buffering decoded Sparkplug traffic matching an optional filter. Returns a session_id. |
get_events(session_id, max_events, timeout_seconds) |
Drain buffered events from a watch session; can wait briefly for one to arrive. |
stop_watch(session_id) |
Stop a watch session and discard its buffered events. |
list_watches() |
List all active watch sessions. |
Why watching is session-based
Like the MQTT wire protocol underneath it, Sparkplug B is a continuous stream, not a request/response API — a single MCP tool call can't "wait forever" for the next birth or data message. start_watch registers an optional filter (group/node/device/message-type) and buffers matching decoded messages in the background (this connector is always subscribed to the whole spBv1.0 namespace, so no extra broker subscription is needed per watch); get_events drains that buffer, optionally waiting a bounded amount of time for new data. Call stop_watch when done.
Supported metric datatypes
INT8/INT16/INT32/INT64, UINT8/UINT16/UINT32/UINT64, FLOAT, DOUBLE, BOOLEAN, STRING, TEXT, UUID, DATETIME, BYTES, FILE, and their *_ARRAY variants. DATASET, TEMPLATE, PROPERTYSET, and PROPERTYSETLIST are part of the Sparkplug B spec but unsupported by the underlying pysparkplug codec, so they're rejected with a clear error rather than silently mishandled.
For send_node_command/send_device_command, value shape depends on datatype: plain int/float/bool/str for scalars, an ISO 8601 string for DATETIME, a base64 string for BYTES/FILE, and a JSON list for *_ARRAY types.
Configuration
| Env var | Required | Description |
|---|---|---|
SPARKPLUG_MQTT_HOST |
Yes | Broker hostname or IP. |
SPARKPLUG_HOST_ID |
Yes | This connector's Sparkplug Host Application ID (used in the spBv1.0/STATE/{id} topic). |
SPARKPLUG_MQTT_PORT |
No | Default 1883. |
SPARKPLUG_MQTT_USERNAME / SPARKPLUG_MQTT_PASSWORD |
No | Broker credentials. |
SPARKPLUG_MQTT_CLIENT_ID |
No | Defaults to a broker-assigned ID. |
SPARKPLUG_MQTT_KEEPALIVE |
No | Seconds, default 60. |
SPARKPLUG_MQTT_CONNECT_TIMEOUT |
No | Seconds to wait for the initial connect, default 10. |
SPARKPLUG_MQTT_USE_TLS |
No | true/false, default false. |
SPARKPLUG_MQTT_TLS_CA_CERTS |
No | Path to a CA bundle. Omit to use the system trust store. |
SPARKPLUG_MQTT_TLS_CLIENT_CERT / SPARKPLUG_MQTT_TLS_CLIENT_KEY |
No | For mutual TLS. |
SPARKPLUG_GROUP_ID |
No | Restrict discovery/watching to a single Sparkplug Group ID. Omit to subscribe to every group (spBv1.0/#). |
Running locally
uv sync
export SPARKPLUG_MQTT_HOST=localhost
export SPARKPLUG_MQTT_PORT=1883
export SPARKPLUG_HOST_ID=my-mcp-host
uv run sparkplugbmcpserver.py
See mcp.json for ready-to-use client configs (stdio and Docker).
Testing against a real broker
This repo's design principles call for testing against a real instance rather than mocks. docker-compose.yml spins up Eclipse Mosquitto (2.x) with an anonymous listener for local testing. Test fixtures use pysparkplug's own EdgeNode/Device classes to act as real simulated PLCs/devices — publishing genuine NBIRTH/DBIRTH/NDATA/DDATA/NDEATH messages and receiving genuine NCMD/DCMD commands over the wire — so coverage is protocol-level, not hand-rolled mocks. Only the "physical" node/device is simulated, since no free real PLC/edge-node hardware exists to test against; the Sparkplug B protocol handling on both sides is real.
docker compose up -d # start Mosquitto on localhost:1883 (anonymous, local testing only)
uv run pytest -v # tests/conftest.py also starts/stops the stack automatically
docker compose down # stop it manually if you started it yourself
License notes
docker-compose.yml and the files under docker/ configure the open-source Eclipse Mosquitto broker (EPL-2.0) for local testing only — they are not part of the MCP server itself.
Release files for sparkplug-b-mcp-server 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| sparkplug_b_mcp_server-0.1.0.tar.gz | 98.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| sparkplug_b_mcp_server-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size:109.4 kB
Release files / sparkplug_b_mcp_server-0.1.0.tar.gz
| Download URL | sparkplug_b_mcp_server-0.1.0.tar.gz |
|---|---|
| Size | 98.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
ea2cd55c738ad888387f3c64f72dfb678261c08dfc11dbe7ab1cc5c15b6fa70f
|
|
BLAKE2b-256 checksum How to use checksums |
0e547f15855af59528f67ab78d02249a7c142e4d4cec00d6898ba1a6a8998d5a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.1 {"installer":{"name":"uv","version":"0.12.1","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / sparkplug_b_mcp_server-0.1.0-py3-none-any.whl
| Download URL | sparkplug_b_mcp_server-0.1.0-py3-none-any.whl |
|---|---|
| Size | 11.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
5bfdbd7fae1fa64ecdf805b6b8cf7038b44323a5d9de487ff9cd0ee90c5edd67
|
|
BLAKE2b-256 checksum How to use checksums |
e7dd6a837d82cd86af982dbee6b83d3aebad15db71303261f4de9b554f648562
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.12.1 {"installer":{"name":"uv","version":"0.12.1","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|