MCP server for Sparkplug B (MQTT + protobuf convention for industrial/SCADA data): node/device discovery, metrics, commands, host state
Project description
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.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file sparkplug_b_mcp_server-0.1.0.tar.gz.
File metadata
- Download URL: sparkplug_b_mcp_server-0.1.0.tar.gz
- Upload date:
- Size: 98.2 kB
- Tags: Source
- Uploaded using 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}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ea2cd55c738ad888387f3c64f72dfb678261c08dfc11dbe7ab1cc5c15b6fa70f
|
|
| MD5 |
b37af3080ab8a00a90183f8859622894
|
|
| BLAKE2b-256 |
0e547f15855af59528f67ab78d02249a7c142e4d4cec00d6898ba1a6a8998d5a
|
File details
Details for the file sparkplug_b_mcp_server-0.1.0-py3-none-any.whl.
File metadata
- Download URL: sparkplug_b_mcp_server-0.1.0-py3-none-any.whl
- Upload date:
- Size: 11.2 kB
- Tags: Python 3
- Uploaded using 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}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5bfdbd7fae1fa64ecdf805b6b8cf7038b44323a5d9de487ff9cd0ee90c5edd67
|
|
| MD5 |
f25714ed975a6d2daa2e6d407f24cdcd
|
|
| BLAKE2b-256 |
e7dd6a837d82cd86af982dbee6b83d3aebad15db71303261f4de9b554f648562
|