Skip to main content

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 lifecyclesession_start, session_stop, session_status (reports image_digest, image_tag, protocol_version).
  • Probe operationsprogram (elf / bin / hex), halt, resume, reset (soft / hard), mem_read, blackbox_export.
  • ITM / SWO traceitm_stream_start, itm_stream_stop, recent_lines.
  • Lane supervisionlane_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 0x080000000x08080000 (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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

brontes_probe_mcp-0.2.0.tar.gz (43.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

brontes_probe_mcp-0.2.0-py3-none-any.whl (33.4 kB view details)

Uploaded Python 3

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

Hashes for brontes_probe_mcp-0.2.0.tar.gz
Algorithm Hash digest
SHA256 3b2f63a4b924578e48287b0b64c8691f2cfbaa3fcaff7753ecddbb6e7fef295b
MD5 01dbb2e14bd7d0653a9bc6306131de95
BLAKE2b-256 0c387acbd404f2b88a5d7d73f6d9831f748879c79eaa46162f709db3752fd278

See more details on using hashes here.

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

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file brontes_probe_mcp-0.2.0-py3-none-any.whl.

File metadata

File hashes

Hashes for brontes_probe_mcp-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 ae0bc19d471a00897bc1b66d08bcb97094f1ab2b1f3c95b6dad98543745f4d37
MD5 9b7d98cb081322728cc02bd98412227c
BLAKE2b-256 b47080ff9f706aed45dbfdca75ab2102175189b7674d727be8ce049814b29334

See more details on using hashes here.

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

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page