Skip to main content
Glama
stm32-stlink-mcp

stm32-stlink-mcp

by phryniszak

stm32-stlink-mcp

MCP server for debugging STM32 microcontrollers over ST-LINK, built on STMicroelectronics' own STM32CubeCLT toolset — ST-LINK_gdbserver, STM32_Programmer_CLI, and arm-none-eabi-gdb (driven via GDB/MI2). No OpenOCD, J-Link, or probe-rs involved.

Architecture

A debug session is a pair of child processes, exactly mirroring ST's own documented workflow (UM2576, "STM32CubeIDE ST-LINK GDB server"):

 arm-none-eabi-gdb  --interpreter=mi2  --(TCP, target extended-remote)-->  ST-LINK_gdbserver  --(USB)-->  ST-LINK  --(SWD)-->  STM32

arm-none-eabi-gdb is driven in MI2 mode so the server gets source-level stepping, symbolic breakpoints, and symbol-aware expression evaluation for free, instead of hand-rolling the GDB Remote Serial Protocol. ST-LINK_gdbserver owns the USB handle to the probe for the lifetime of the session; flashing via gdb's load (MI: -target-download) is transparently delegated by the server to STM32CubeProgrammer, so no session teardown is needed to reflash. A standalone one-shot flash (flash_standalone, no session required) invokes STM32_Programmer_CLI directly and therefore conflicts with an already-open session on the same probe — see the tool description.

Related MCP server: dbgprobe-mcp-server

Setup

npm install
npm run build

Requires STM32CubeCLT to be installed and its bin/ directories reachable — either already on PATH (the CLT installer does this by default) or via STMCP_CUBECLT_PATH / per-tool overrides. Run npm run doctor to check.

Running

node dist/index.js serve     # starts the MCP server on stdio (default mode)
node dist/index.js doctor    # pre-flight check: tool resolution, connected probes, udev rules
node dist/index.js doctor --json

Registering with an MCP client

{
  "mcpServers": {
    "stm32-stlink": {
      "command": "node",
      "args": ["<path-to-this-repo>/stmcp/dist/index.js"]
    }
  }
}

Configuration (environment variables)

Variable

Default

Purpose

STMCP_GDBSERVER_PATH / STMCP_PROGRAMMER_CLI_PATH / STMCP_ARM_GDB_PATH

—

Per-binary override (highest priority)

STMCP_CUBECLT_PATH

—

CubeCLT install root; subpaths resolved via STM32CubeCLT_metadata.sh -j

STMCP_STLINK_SERIAL

—

Default probe serial (omit to auto-select if exactly one is attached)

STMCP_DEFAULT_DEVICE

STM32G431CBTx

Default MCU device string

STMCP_DEFAULT_INTERFACE

swd

swd or jtag

STMCP_DEFAULT_FREQUENCY_KHZ

4000

SWD/JTAG clock

STMCP_MAX_SESSIONS

1

Concurrent debug session cap

STMCP_GDBSERVER_READY_TIMEOUT_MS

8000

How long to wait for "Waiting for debugger connection..."

STMCP_LOG_LEVEL

info

error | warn | info | debug

STMCP_LOG_FILE

—

Optional log file (stderr always used regardless — stdout is reserved for MCP framing)

STMCP_ALLOW_FLASH_ERASE

false

Enables the erase path

STMCP_ALLOW_MEMORY_WRITE

true

Enables memory_write

STMCP_ALLOW_FLASH_ADDRESS_WRITE

false

Allows memory_write to target the flash address window (normally blocked — use the flash tools instead)

STMCP_ALLOWED_FILE_PATHS

(unrestricted)

Comma-separated allowlist roots for ELF/bin file arguments

STMCP_MAX_FILE_SIZE_BYTES

16777216

Max size for file arguments

STMCP_FLASH_RANGE_START / STMCP_FLASH_RANGE_END

0x08000000 / 0x08020000

Flash address window for the write guard (default: 128KB, STM32G431CB)

Tools

Domain

Tool

Purpose

Probe

list_probes

List connected ST-LINK probes

Session

debug_connect

Spawn gdbserver+gdb, load ELF symbols, connect

Session

debug_disconnect

Clean session teardown

Session

debug_session_status

Session info (one, or all)

Flash

flash_standalone

One-shot flash via STM32_Programmer_CLI, no session needed

Flash

flash_load_in_session

Reflash via gdb load inside an open session

Execution

debug_run

Resume/continue

Execution

debug_halt

Interrupt

Execution

debug_reset

Reset (monitor reset [halt])

Execution

debug_step

Step over/into/out

Breakpoints

breakpoint_set / breakpoint_clear / breakpoint_list

By file:line, symbol, or *addr

Memory

memory_read / memory_write

Raw memory access (write is guarded)

Registers

register_read / register_write

Named core registers

Registers

read_fault_registers

One-call Cortex-M SCB fault register dump (CFSR/HFSR/... decoded)

Expressions

evaluate_expression

Symbol-aware evaluation via gdb MI

Deferred to v2

SVD peripheral register tools (memory_read/write + evaluate_expression already reach everything by address), live/streaming memory polling, a plugin system, per-chip memory-region allowlists, arbitrary gdb monitor passthrough, and option-bytes/RDP tools (bricking-capable, intentionally out of scope).

RTT

RTT (SEGGER Real Time Transfer — live, non-halting console/variable tracing) is intentionally not implemented in this server. ST-LINK_gdbserver's GDB/MI stub has no non-stop mode, so reading memory through this server's debug_connect session requires halting the core first — which defeats RTT's purpose. The correct mechanism is direct AP memory access that never halts the core (confirmed by reading ST's own STM32CubeMonitor source, which uses exactly this, and by STM32_Programmer_CLI's -r32fast).

That's what strtt already does, and strtt-mcp wraps it as its own MCP server (strtt_start/strtt_stop/strtt_status/strtt_read/strtt_write). Register it alongside this server rather than through it:

{
  "mcpServers": {
    "stm32-stlink": { "command": "node", "args": ["<...>/mcp-server/dist/index.js"] },
    "strtt": {
      "command": "node",
      "args": ["<path-to-strtt-repo>/mcp/dist/index.js"],
      "env": { "STRTT_BIN": "<path-to-strtt-binary>" }
    }
  }
}

Start strtt_start with tcp: true to connect through the shared ST-LINK Server instead of claiming the USB device directly — this lets it run concurrently with an open debug_connect session here, since GdbServerProcess always passes -t/--shared to ST-LINK_gdbserver. Without tcp: true, strtt and an open debug session will contend for the same probe.

Hardware verification runbook

With an ST-LINK and target attached:

node dist/index.js doctor                     # confirm probe + tools resolve
npx @modelcontextprotocol/inspector node dist/index.js   # interactive tool testing

Then, via the inspector or an MCP client:

  1. list_probes → the probe's serial appears.

  2. debug_connect { elfPath, device, interface: "swd", serial } → returns a sessionId.

  3. breakpoint_set { sessionId, location: "main" } → returns a breakpoint number.

  4. debug_run { sessionId } → halts with reason: "breakpoint-hit".

  5. register_read { sessionId, registers: ["pc","sp","lr","r0"] }.

  6. evaluate_expression { sessionId, expression: "<a known global>" }.

  7. read_fault_registers { sessionId } → benign/zero flags right after reset.

  8. debug_disconnect { sessionId } → confirm no orphaned processes: ps aux | grep -E 'ST-LINK_gdbserver|arm-none-eabi-gdb'.

  9. flash_standalone { file, reset: "hard", run: true } with no session open.

  10. Negative test: open a session, then call flash_standalone on the same serial → expect DEVICE_BUSY.

Note: debug_connect halts the target's CPU. Don't attach to a board that's actively driving actuators/outputs in a way where an unplanned halt would be unsafe, without first confirming that's OK.

Available Tools

19 tools
breakpoint_clearClear a breakpointA

Deletes a previously set breakpoint by its number.

Inputs: sessionId (required), breakpointNumber (required).

breakpoint_listList breakpointsA

Lists breakpoints currently tracked on a session (those set via breakpoint_set).

Inputs: sessionId (required).

breakpoint_setSet a breakpointB

Sets a breakpoint by file:line (e.g. main.c:42), symbol name (e.g. main), or address (e.g. *0x08000200).

Inputs: location (required), sessionId (required), condition, temporary.

debug_connectConnect a debug sessionA

Starts a debug session: spawns ST-LINK_gdbserver and arm-none-eabi-gdb (MI2), loads ELF symbols, and connects via target extended-remote.

Inputs: elfPath (required), apId, device, serial, +3 more.

debug_disconnectDisconnect a debug sessionA

Cleanly tears down a debug session's gdb and ST-LINK_gdbserver child processes.

Inputs: sessionId (required).

debug_haltHalt executionB

Interrupts the running target.

Inputs: sessionId (required).

debug_resetReset the targetA

Resets the target via ST-LINK_gdbserver's monitor reset command, optionally resuming afterward.

Inputs: sessionId (required), halt.

debug_runResume executionA

Resumes (continues) the target and waits for it to stop again (e.g. a breakpoint), up to a timeout.

Inputs: sessionId (required), waitForStopMs.

debug_session_statusDebug session statusA

Reports the status of one debug session (by id), or lists all active sessions if id is omitted.

Inputs: sessionId.

debug_stepSingle-stepB

Steps the target: 'over' (next line, step over calls), 'into' (step into calls), or 'out' (finish the current function).

Inputs: sessionId (required), mode, waitForStopMs.

evaluate_expressionEvaluate an expressionB

Symbol-aware expression evaluation in the current frame via gdb's -data-evaluate-expression — reads globals/locals by name, struct/array member access, pointer dereference, arithmetic, etc.

Inputs: sessionId (required), expression (required).

flash_load_in_sessionReflash firmware within a debug sessionA

Reflashes the currently loaded ELF via gdb's load (MI: -target-download) inside an already-open debug session. No USB conflict — ST-LINK_gdbserver retains ownership of the probe throughout.

Inputs: sessionId (required), runToMain, resetAfter.

flash_standaloneFlash firmware (standalone)A

One-shot flash via STM32_Programmer_CLI — no debug session required. Fails with DEVICE_BUSY if a debug session is already open on the same probe (it holds exclusive USB access); disconnect first, or use flash_load_in_session instead.

Inputs: file (required), run, reset, serial, +3 more.

list_probesList ST-LINK probesA

Lists connected ST-LINK probes via STM32_Programmer_CLI. Returns an empty list (not an error) if none are attached.

Inputs: no named parameters.

memory_readRead memoryA

Reads raw bytes from target memory at an address or symbol-address expression (e.g. '&my_global').

Inputs: length (required), address (required), sessionId (required).

memory_writeWrite memoryA

Writes raw bytes to target RAM/registers at an address. Blocked for addresses inside the flash window (use flash_standalone / flash_load_in_session for flash) unless STMCP_ALLOW_FLASH_ADDRESS_WRITE is set.

Inputs: address (required), bytesHex (required), sessionId (required).

read_fault_registersRead Cortex-M fault registersA

One-call dump of Cortex-M SCB fault registers (CFSR/HFSR/MMFAR/BFAR/SHCSR/CPUID/ICSR) plus PC/SP/LR, with CFSR/HFSR bits decoded to flag names. Raw evidence only — no root-cause interpretation.

Inputs: sessionId (required).

register_readRead core registersA

Reads named core registers (default: all general-purpose + sp/lr/pc/xpsr) via gdb MI.

Inputs: sessionId (required), registers.

register_writeWrite a core registerB

Writes a value to a named core register via gdb's -gdb-set $<register>=<value>.

Inputs: value (required), register (required), sessionId (required).

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

Loading tool schema changelog…

    TDQS

    A3.7/5.0

    Scored across 19 tools

    Disambiguation5/5

    Each tool targets a distinct action: session lifecycle, run control, breakpoints, register/memory access, and flashing are clearly separated. The only similar pair, flash_standalone and flash_load_in_session, is explicitly differentiated by requiring or not requiring an active debug session.

    Naming Consistency4/5

    All tool names are lowercase snake_case and mostly follow a predictable domain-prefixed verb pattern such as breakpoint_set, memory_read, and debug_run. Minor exceptions like debug_session_status and flash_standalone break the strict verb_noun pattern but remain easy to anticipate.

    Tool Count4/5

    19 tools is on the heavier side, but the embedded debug workflow legitimately spans session management, execution control, breakpoints, register/memory inspection, fault diagnosis, and flashing. Each tool has a justifiable role, so the count feels slightly over ideal rather than bloated.

    Completeness4/5

    The toolset covers the full core debug loop: connect/disconnect, run/halt/reset/step, breakpoint management, register and memory access, expression evaluation, fault register dumps, and both standalone and in-session flashing. Missing conveniences like stack backtraces or watchpoints are minor gaps, not workflow-breaking omissions.

    Maintenance

    ActivityInactive
    ResponsivenessNo recent community issues

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      A
      quality
      D
      maintenance
      Stateful MCP server for driving debug probes (J-Link) to flash, debug, and inspect embedded targets. Enables AI agents to perform flash, memory, breakpoint, and ELF/SVD-aware operations conversationally.
      41
      49 PyPI
      10
      MIT
    • A
      license
      Not graded
      quality
      D
      maintenance
      MCP server for embedded debugging based on probe-rs, providing 22 tools for ARM Cortex-M and RISC-V microcontrollers, including connection, memory operations, breakpoints, flash programming, and RTT communication.
      2
      MIT
    • A
      license
      Not graded
      quality
      D
      maintenance
      An MCP server that provides comprehensive debugging capabilities for J-Link debuggers, enabling memory, flash, register, and RTT operations through AI assistants.
      24 PyPI
      40
      MIT