Multi-client debug-probe broker MCP server — session lifecycle, probe operations, ITM/SWO trace, and lane supervision over three concurrent transports
Project description
brontes-probe-mcp
A Model Context Protocol (MCP) server that exposes a debug-probe broker for embedded-systems development. The server maintains one persistent probe session (SWD / JTAG, pyOCD-backed) accessible to multiple client tools — AI assistants, CLI tooling, test runners — without requiring teardown and re-establishment of the hardware connection between operations.
Status: 0.1.0 — fully operational. Session lifecycle, probe operations, ITM/SWO trace, and lane supervision over three concurrent transports (stdio MCP, Unix socket, loopback TCP).
What it does
- Session lifecycle —
session_start,session_stop,session_status(reportsimage_digest,image_tag,protocol_version). - Probe operations —
program(elf / bin / hex),halt,resume,reset(soft / hard),mem_read,blackbox_export. - ITM / SWO trace —
itm_stream_start,itm_stream_stop,recent_lines. - Lane supervision —
lane_status,lane_release,lane_resume.
Three transport adapters bind concurrently over one shared BrokerCore
instance, controlled by PROBE_BROKER_TRANSPORTS:
| Transport | Default | Use case |
|---|---|---|
stdio |
✓ | MCP stdio — one AI client |
socket |
✓ | Unix-domain socket — local tool access |
tcp |
— | Loopback TCP with bearer token — sandbox / Docker Desktop |
Quick start
Two deployment paths are available: the container image (no local Python install required) and a native pip install (no Docker required).
Option A — Unix socket (Linux, recommended)
docker run -d --name brontes-probe-mcp \
-v "$HOME/.brontes-probe-mcp:/run/brontes-probe-mcp" \
--device=/dev/bus/usb \
ghcr.io/cms-pm/brontes-probe-mcp:0.1.0
Option B — Probe agent split (macOS / Docker Desktop)
Docker Desktop on macOS cannot pass USB devices into containers. Install the wheel on the host to manage the probe agent, then point the container at it:
# Install the CLI on the host (once)
pip install brontes-probe-mcp
# Start probe agent (auto-detects target if only one probe is connected)
brontes-probe-mcp-cli probe-agent start --target <your-target>
# .mcp.json — container connects to the host agent, no --device needed
{
"brontes-probe-mcp": {
"command": "docker",
"args": [
"run", "--rm", "-i",
"-v", "${HOME}/.brontes-probe-mcp:/run/brontes-probe-mcp",
"-v", "${HOME}/.brontes-probe-mcp/packs:/packs",
"-e", "PROBE_BROKER_TRANSPORTS=stdio,socket",
"-e", "CMSIS_PACK_ROOT=/packs",
"-e", "PROBE_BROKER_GDB_HOST=host.docker.internal",
"ghcr.io/cms-pm/brontes-probe-mcp@sha256:77e58b86015ddf0b36fba47d267669ed7493ea5ff6794dbe80628fa4dce13ae7"
]
}
}
session_start will connect to the running probe agent rather than spawning
pyocd internally. session_stop removes the Brontes session record but does
not terminate the agent — it persists for reuse.
Option C — TCP loopback (no probe agent required)
docker run -d --name brontes-probe-mcp \
-e PROBE_BROKER_TRANSPORTS=stdio,tcp \
-e PROBE_BROKER_TOKEN=your-token-here \
-p 127.0.0.1:7172:7172 \
--device=/dev/bus/usb \
ghcr.io/cms-pm/brontes-probe-mcp:0.1.0
Option D — Docker Compose (socket, auto-restart)
curl -fsSL https://raw.githubusercontent.com/cms-pm/brontes-probe-mcp/main/docker-compose.yml \
-o docker-compose.yml
docker compose up -d
Option E — pip install (no Docker, all platforms)
Requires arm-none-eabi-gdb on PATH (install via your OS package manager or
ARM toolchain download).
pip install brontes-probe-mcp
# Start probe agent (auto-detects target if unambiguous)
brontes-probe-mcp-cli probe-agent start [--target <target>]
Add to .mcp.json:
{
"mcpServers": {
"brontes-probe-mcp": {
"command": "brontes-probe-mcp-cli",
"args": ["serve"]
}
}
}
The probe-agent start command writes a state file to ~/.brontes-probe-mcp/agent.json.
session_start detects it automatically — no PROBE_BROKER_GDB_HOST configuration needed.
To stop the agent: brontes-probe-mcp-cli probe-agent stop.
Pin by digest for production use — the digest is the binary-level reproducibility contract:
ghcr.io/cms-pm/brontes-probe-mcp@sha256:<digest>
Digests are published in CHANGELOG.md for each release.
First session (Claude Code)
Once the server is registered, paste Phase 1 into Claude Code to write
.mcp.json and pull the image, then Phase 2 after the restart to connect.
Phase 1 — configure (paste into Claude Code, press Enter):
Add brontes-probe-mcp to .mcp.json in this project, creating it if needed:
{
"mcpServers": {
"brontes-probe-mcp": {
"command": "docker",
"args": [
"run", "--rm", "-i",
"--device=/dev/bus/usb",
"-v", "${HOME}/.brontes-probe-mcp:/run/brontes-probe-mcp",
"-v", "${HOME}/.brontes-probe-mcp/packs:/packs",
"-e", "PROBE_BROKER_TRANSPORTS=stdio,socket",
"-e", "CMSIS_PACK_ROOT=/packs",
"ghcr.io/cms-pm/brontes-probe-mcp@sha256:77e58b86015ddf0b36fba47d267669ed7493ea5ff6794dbe80628fa4dce13ae7"
]
}
}
}
Then run this shell command to pre-fetch the image:
docker pull ghcr.io/cms-pm/brontes-probe-mcp@sha256:77e58b86015ddf0b36fba47d267669ed7493ea5ff6794dbe80628fa4dce13ae7
Then tell me to restart Claude Code to load the new server.
Restart Claude Code (or run /mcp to reload servers).
Phase 2 — discover and connect (paste after restart):
Call probe_discover to list attached debug probes. Then call target_suggest
with my MCU family (e.g. "stm32g4") to find the target string. If
target_suggest returns no results, call pack_search with the MCU family to
find the right CMSIS pack name, then call pack_install to install it
(this may take a minute), then retry target_suggest. Once you have the
probe UID and target string, call session_start.
probe_discover, target_suggest, pack_search, and pack_install do
not require an active session. Installed packs are written to
~/.brontes-probe-mcp/packs/ on the host and persist across container
restarts — pack_install only runs once per MCU family.
Client configuration
Configure your AI client to launch the container via the MCP stdio transport.
Claude Desktop
Add the following entry to the mcpServers object in ~/Library/Application Support/Claude/claude_desktop_config.json (Linux: ~/.config/Claude/claude_desktop_config.json):
{
"brontes-probe-mcp": {
"command": "docker",
"args": [
"run", "--rm", "-i",
"--device=/dev/bus/usb",
"-v", "${HOME}/.brontes-probe-mcp:/run/brontes-probe-mcp",
"-e", "PROBE_BROKER_TRANSPORTS=stdio,socket",
"ghcr.io/cms-pm/brontes-probe-mcp@sha256:77e58b86015ddf0b36fba47d267669ed7493ea5ff6794dbe80628fa4dce13ae7"
]
}
}
Claude Code
Add to .mcp.json in your project root (or ~/.claude.json for global config):
{
"mcpServers": {
"brontes-probe-mcp": {
"command": "docker",
"args": [
"run", "--rm", "-i",
"--device=/dev/bus/usb",
"-v", "${HOME}/.brontes-probe-mcp:/run/brontes-probe-mcp",
"-e", "PROBE_BROKER_TRANSPORTS=stdio,socket",
"ghcr.io/cms-pm/brontes-probe-mcp@sha256:77e58b86015ddf0b36fba47d267669ed7493ea5ff6794dbe80628fa4dce13ae7"
]
}
}
}
Codex
Add to ~/.codex/config.json:
{
"mcpServers": {
"brontes-probe-mcp": {
"command": "docker",
"args": [
"run", "--rm", "-i",
"--device=/dev/bus/usb",
"-v", "${HOME}/.brontes-probe-mcp:/run/brontes-probe-mcp",
"-e", "PROBE_BROKER_TRANSPORTS=stdio,socket",
"ghcr.io/cms-pm/brontes-probe-mcp@sha256:77e58b86015ddf0b36fba47d267669ed7493ea5ff6794dbe80628fa4dce13ae7"
]
}
}
}
OpenCode
Add to opencode.json in your project root:
{
"mcp": {
"servers": {
"brontes-probe-mcp": {
"type": "stdio",
"command": "docker",
"args": [
"run", "--rm", "-i",
"--device=/dev/bus/usb",
"-v", "${HOME}/.brontes-probe-mcp:/run/brontes-probe-mcp",
"-e", "PROBE_BROKER_TRANSPORTS=stdio,socket",
"ghcr.io/cms-pm/brontes-probe-mcp@sha256:77e58b86015ddf0b36fba47d267669ed7493ea5ff6794dbe80628fa4dce13ae7"
]
}
}
}
}
Replace sha256:TBD with the pinned digest for your release. The digest is
the binary-level reproducibility contract — pin it, don't float on a tag.
CLI
The brontes-probe-mcp-cli console script manages the server and probe agent:
brontes-probe-mcp-cli --version
brontes-probe-mcp-cli --config-dump # print resolved config as JSON
# Start all configured transports (MCP server entry point for pip install)
brontes-probe-mcp-cli serve
# Probe agent — manages the host-side pyocd gdbserver daemon
brontes-probe-mcp-cli probe-agent start [--target TARGET] [--port PORT] [--probe-uid UID]
brontes-probe-mcp-cli probe-agent status # prints JSON; exits 1 if not healthy
brontes-probe-mcp-cli probe-agent stop [--force]
Target auto-detection: if --target is omitted and exactly one probe is connected
with a deterministic target, probe-agent start uses it automatically.
Configuration
All configuration is via PROBE_BROKER_* environment variables:
| Variable | Default | Description |
|---|---|---|
PROBE_BROKER_TRANSPORTS |
stdio,socket |
Comma-separated active transports |
PROBE_BROKER_SOCKET_PATH |
/run/brontes-probe-mcp/probe.sock |
Unix socket path |
PROBE_BROKER_TCP_HOST |
127.0.0.1 |
TCP bind address |
PROBE_BROKER_TCP_PORT |
7172 |
TCP port |
PROBE_BROKER_LANES |
swd,itm_swo |
Active probe lanes |
PROBE_BROKER_BACKEND |
pyocd |
Debug backend (pyocd or openocd) |
PROBE_BROKER_GDB_HOST |
127.0.0.1 |
GDB server host — set to host.docker.internal to connect to an external probe agent instead of spawning pyocd locally |
PROBE_BROKER_DEFAULT_PACK |
(none) | Default CMSIS pack path — used by target_suggest and session_start when no pack= argument is supplied |
PROBE_BROKER_AGENT_STATE_DIR |
~/.brontes-probe-mcp |
Directory where probe-agent start writes its state file and where session_start looks for a running agent. If probe-agent start --state-dir is set to a non-default path, this variable must be set to match |
PROBE_BROKER_DIGEST_CHECK |
enforce |
Image digest verification (enforce, warn, skip) |
Flash memory snapshot (probe_blackbox_export)
Capture a binary snapshot of the target's flash for archiving or diff:
{
"tool": "probe_blackbox_export",
"arguments": {
"out": "/tmp/snapshot.bin"
}
}
Defaults to 0x08000000–0x08080000 (512 KB). Requires an active session.
Response includes bytes_written and snapshot_at (UTC ISO-8601).
See docs/tutorials/blackbox-export.md for custom address ranges, error cases, and snapshot comparison examples.
Why "Brontes"
Brontes ("Thunderer") is one of the cyclops smiths in Hephaestus's forge — the worker who hammers metal at the master's direction. The metaphor maps onto the broker's role: client code directs the operation, the broker performs the probe work.
Changelog
See CHANGELOG.md.
License
Apache-2.0. See LICENSE.
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 brontes_probe_mcp-0.2.0.tar.gz.
File metadata
- Download URL: brontes_probe_mcp-0.2.0.tar.gz
- Upload date:
- Size: 43.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3b2f63a4b924578e48287b0b64c8691f2cfbaa3fcaff7753ecddbb6e7fef295b
|
|
| MD5 |
01dbb2e14bd7d0653a9bc6306131de95
|
|
| BLAKE2b-256 |
0c387acbd404f2b88a5d7d73f6d9831f748879c79eaa46162f709db3752fd278
|
Provenance
The following attestation bundles were made for brontes_probe_mcp-0.2.0.tar.gz:
Publisher:
publish.yml on cms-pm/brontes-probe-mcp
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
brontes_probe_mcp-0.2.0.tar.gz -
Subject digest:
3b2f63a4b924578e48287b0b64c8691f2cfbaa3fcaff7753ecddbb6e7fef295b - Sigstore transparency entry: 1774386115
- Sigstore integration time:
-
Permalink:
cms-pm/brontes-probe-mcp@3c564c24baa86b0f11577e6b004726f1f691f233 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/cms-pm
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@3c564c24baa86b0f11577e6b004726f1f691f233 -
Trigger Event:
push
-
Statement type:
File details
Details for the file brontes_probe_mcp-0.2.0-py3-none-any.whl.
File metadata
- Download URL: brontes_probe_mcp-0.2.0-py3-none-any.whl
- Upload date:
- Size: 33.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ae0bc19d471a00897bc1b66d08bcb97094f1ab2b1f3c95b6dad98543745f4d37
|
|
| MD5 |
9b7d98cb081322728cc02bd98412227c
|
|
| BLAKE2b-256 |
b47080ff9f706aed45dbfdca75ab2102175189b7674d727be8ce049814b29334
|
Provenance
The following attestation bundles were made for brontes_probe_mcp-0.2.0-py3-none-any.whl:
Publisher:
publish.yml on cms-pm/brontes-probe-mcp
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
brontes_probe_mcp-0.2.0-py3-none-any.whl -
Subject digest:
ae0bc19d471a00897bc1b66d08bcb97094f1ab2b1f3c95b6dad98543745f4d37 - Sigstore transparency entry: 1774386208
- Sigstore integration time:
-
Permalink:
cms-pm/brontes-probe-mcp@3c564c24baa86b0f11577e6b004726f1f691f233 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/cms-pm
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@3c564c24baa86b0f11577e6b004726f1f691f233 -
Trigger Event:
push
-
Statement type: