mcpField
S
stlink-mcp

stlink-mcp

本地服务
github.com开发工具06 天前更新

关于 stlink-mcp

stlink-mcp 是面向嵌入式开发者的 MCP 服务,让编码 Agent 通过 ST-LINK 操作和检查 STM32 目标板。它会验证操作结果并标明证据,以及调用是否暂停或重置了目标。

主要功能

  1. 01通过 ST-LINK 编程和运行 STM32
  2. 02读取变量、外设寄存器和内存
  3. 03捕获串口控制台与 SWO 输出
  4. 04检查故障状态并运行至指定条件
  5. 05为结果标注已验证、未验证或失败
  6. 06说明调用是否暂停或重置目标

适用场景

  1. 01让编码 Agent 在目标板上运行固件并检查结果
  2. 02读取运行中固件的变量和外设状态
  3. 03根据串口输出确认新固件是否启动
  4. 04检查故障并判断目标板的运行状态

MCP 相关

MCP 服务向编码 Agent 提供用于发现探针、检查板卡、编程、运行和观测 STM32 的工具。它通过 STM32CubeCLT 命令行工具驱动 ST-LINK,并在结果中报告验证状态和观测证据。

配置

本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。

mcp.json · 通用配置
{
"mcpServers": {
"dii-stlink": {
"command": "uvx",
"args": [
"stlink-mcp"
],
"env": {
"STLINK_MCP_CLT_ROOT": "<STLINK_MCP_CLT_ROOT>",
"STLINK_MCP_BOARDS": "<STLINK_MCP_BOARDS>",
"STLINK_MCP_NO_VENDOR_SEED": "<STLINK_MCP_NO_VENDOR_SEED>",
"STLINK_MCP_CONSOLE": "<STLINK_MCP_CONSOLE>",
"STLINK_MCP_CONSOLE_BAUD": "<STLINK_MCP_CONSOLE_BAUD>",
"STLINK_MCP_GDB_PORT": "<STLINK_MCP_GDB_PORT>"
}
}
}
}
启动命令uvx stlink-mcp
需要配置的环境变量
STLINK_MCP_CLT_ROOTSTM32CubeCLT (or STM32CubeIDE) install directory, when it is not in a standard place
STLINK_MCP_BOARDSTOML file describing boards this project has not characterised; defaults to ~/.config/stlink-mcp/boards.toml if it exists
STLINK_MCP_NO_VENDOR_SEEDSet to anything to run on measured and user-stated data only, with no seeding from ST's device databases
STLINK_MCP_CONSOLESerial console per probe, SERIAL=/dev/ttyUSB0, comma-separated for several; for a console that is not the probe's own
STLINK_MCP_CONSOLE_BAUDConsole baud rate
STLINK_MCP_GDB_PORTFirst GDB server port; each probe's server takes this port and the next

本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。

安装包
pypistlink-mcp0.9.0

工具

16

收录的工具列表 · 更新于 5 天前

此工具无需参数。

详细介绍

stlink-mcp

一个 MCP 服务,可让 AI 编码 Agent 通过 ST-LINK 为 STM32 目标板编程、运行程序并检查状态;每项结果都会标记为已验证、未验证或失败,并附上证据。它在本地运行,靠近开发板,并调用你已安装的 STM32CubeCLT 命令行工具。它根据 ELF 和 ST 的 SVD 文件按名称读取变量和外设寄存器,且不会暂停程序;除非得到请求,否则会拒绝可能改变固件所见状态的读取;每项结果还会说明调用是否暂停或重置了目标板。接入该服务后,Agent 会自行执行编辑、构建、加载、观察这一循环,你只需监督它的结论,而不必转述开发板的表现。

为什么报告必须属实

使用固件的 Agent,能力取决于工具如何报告开发板状态。开发者能发现工具夸大其词,因为他们掌握工具从未提供的信息:没有变化的 LED、看起来像上一次构建结果的控制台文本,以及手中的开发板。Agent 没有这些信息。工具返回的文本就是它对硬件的全部认知,因此,工具若对自己没有执行的操作报告成功,就会让 Agent 充满信心地犯错;随后 Agent 还会把错误写进代码、提交消息和文档,造成的代价远高于操作失败本身。

ST 的命令行工具确实会夸大其词。以下是 STM32N6 的三个示例:

  • STM32_Programmer_CLI -s <address> 打印了“Start operation achieved successfully”,但程序计数器始终没有离开启动 ROM。
  • ST-LINK_gdbserver 打印了“Failed to halt target”。内核没有问题;服务器连接到了错误的调试访问端口。
  • 对正在运行的目标板执行 GDB 加载,操作顺利完成,却没有产生任何变化,因此之前的程序继续运行,其输出看起来像是一次成功的新运行。

驱动这些工具的 Agent 也会继承它们的所有问题。因此,这里的规则是:

只有在检查确认所请求的操作确实发生后,工具才会报告成功。

run_firmware 只有在控制台输出了新映像,或确认程序计数器位于新映像中时才会报告成功,而不是因为 GDB 以零退出。每项结果都只有三种状态,且没有工具会手动设置 ok:

statusok含义
verifiedtrue已检查,且 verified_by 指明了观察依据
unverifiedfalse很可能已成功,但没有观察结果
failedfalse操作未成功,error 会说明原因

unverified 会特意设为非 ok:无人观察的运行不算成功,无论它成功的可能性有多大。unverified_because 会说明未检查的内容,以免它被误认为失败。

Agent 看到的是一个扁平的 JSON 对象。以下是 run_firmware 的结果,字段与服务返回的一致,值则来自 STM32F407 上的一次示例运行:

{
  "probe_serial": "0670FF3433554B3043165632",
  "elf_path": "/home/you/blink/build/blink.elf",
  "interface": "swd",
  "interface_clock_khz": 4000,
  "console_port": "/dev/ttyACM0",
  "console_port_source": "the probe's own port",
  "load_address": "0x20000000",
  "load_address_provenance": "measured",
  "image_initial_sp": "0x20020000",
  "image_entry_point": "0x200001a5",
  "load_size_bytes": 6180,
  "target_was_reset": true,
  "console": "blink 0.3 starting\r\nLED on\r\n",
  "ok": true,
  "status": "verified",
  "verified_by": "console output from this board's own port"
}

如果开发板没有打印任何内容,ok 就会是 false,status 会是 unverified,而 unverified_because 会说明映像已加载并启动,但在捕获时间窗口内没有收到任何输出。

它如何融入工作流

Agent 按照原有方式编辑和构建固件。这个服务负责连接开发板:它驱动 ST 的工具,并将工具执行情况作为结果返回,说明哪些内容经过了检查。

flowchart LR
    agent["AI coding agent"] -- "builds" --> project["Your firmware project"]
    agent -- "MCP tool calls" --> server["stlink-mcp"]
    server -- "result: verified, unverified or failed" --> agent
    server --> clt["STM32CubeCLT: STM32_Programmer_CLI, ST-LINK_gdbserver, GDB"]
    clt --> probe["ST-LINK probe"]
    probe -- "SWD or JTAG, SWO" --> board["STM32 board"]
    board -- "serial console, through the probe" --> server

适用对象

如果你的开发板配有 ST-LINK,已安装 STM32CubeCLT,并且希望 Agent 的每份报告都说明在目标设备上观察到了什么,就可以使用它。这是一种面向 Agent 而非人的调试方式:提供 16 个工具,能不停下目标设备时就读取其状态;只有在收到要求时才停止它,并且每当一次调用导致目标设备暂停或复位时都会明确说明。以下四项功能没有纳入,每项都有原因:

  • 构建固件是构建系统的职责,而且 Agent 已经会运行它。
  • 单步执行是人探索代码的方式。相比每条指令调用一次,Agent 更能从一次调用中受益:运行到指定位置或直到某个变量被写入,然后返回调用栈、寄存器以及停止原因。
  • 写入内存暂不纳入,直到实际使用证明 Agent 需要它:错误的写入会改变程序行为,而结果中不会显示这一点。
  • 不支持 RTT(SEGGER 的实时传输,是由固件写入、调试器读取的环形缓冲区),因为 ST 的工具不支持它。sample_values 已能在程序运行时监视数值;读取 RTT 日志的功能以后可能会加入。

如果你需要单步执行、由你自行管理的断点或 RTT,请使用 stm32-gdb-mcp 或 embedded-debugger-mcp。如果你需要 J-Link 或 DAPLink 探针、非 STM32 目标设备,或本项目目前所测范围以外的 Windows 支持,请使用 embedded-debugger-mcp。相关项目中有对比。

状态

Alpha。已在硬件上测量过三个设备 ID:STM32F407 上的 0x413、STM32H743 上的 0x450,以及 STM32N657 上的 0x486。其余范围会在运行时根据 STM32CubeCLT 安装目录中 STMicroelectronics 自己的设备数据库进行填充(在 STM32CubeCLT 1.22.0 中,额外填充了 96 个设备 ID),并且这些设备的所有数值都会标注为来自数据库。对于数据库中列出了多个独立 SRAM 区块的设备 ID(该版本中的 0x42B、0x42C、0x472 和 0x49A),本项目会拒绝猜测;board_state 会列出无法确定的地址,使用板卡文件即可解决。数据库不认识的芯片仍可识别自身。除 run_firmware 外,所有工具均可用于这类芯片;flash_firmware 则需要提供 .bin 文件和 address。

版本遵循 Semantic Versioning 2.0.0,并从 git 标签读取;在最后一个标签之后构建时,版本会显示为标签加上自该标签以来的提交数,例如 0.1.0+3。从 GitHub“Download ZIP”下载的文件包也会携带相同的版本;如果只是复制文件且没有标签信息,则会报告 0.0.0+unknown。公开 API 包括以下 16 个工具、它们的参数和结果字段,以及 stlink-mcp 命令行。在 0.y.z 阶段,这些内容都可能发生变化。1.0 发布前必须完成的事项见 ROADMAP.md;本文所述结论背后的测量数据见 docs/findings.md;变更记录见 CHANGELOG.md;如何报告问题或提交你测量过的板卡见 CONTRIBUTING.md。

要求

  • STM32CubeCLT。它包含 STM32CubeProgrammer、GDB 服务和 Arm 工具链。程序会自动在 /opt/st、~/st 或 /usr/local/st 下的 stm32cubeclt* 目录中查找;在 macOS 上查找 /Applications/STMicroelectronics/STM32CubeCLT;在 Windows 上则查找安装程序放置文件的 C:\ST。如果安装在其他位置,请将 STLINK_MCP_CLT_ROOT 设为安装目录。如果找不到完整的 STM32CubeCLT,则会改用 STM32CubeIDE 安装中的同样三个工具,查找位置为上述目录中的 stm32cubeide* 目录,或 STLINK_MCP_CLT_ROOT 指定的安装目录;--check 会说明这一点。已在 Linux 上使用 STM32CubeIDE 2.2.0 进行测试,其中包含 SVD 文件。
  • Python 3.11 或更高版本。
  • 一个 ST-LINK;大多数开发板都内置了 ST-LINK。即使编程器仍能使用较旧的探针固件,ST 的 GDB 服务也会拒绝不受支持的固件,此时 run_firmware 会给出相应提示。安装目录中与 GDB 服务同级的 STLinkUpgrade.jar 可用于更新固件。请在使用该安装目录中 Java 运行时的 shell 中运行:java -jar STLinkUpgrade.jar -sn <serial> -update;只有固件版本较旧时才会执行更新。
  • 在 Linux 上,串行控制台需要将用户加入 dialout 组,探针还需要 ST 的 udev 规则(由 STM32CubeCLT 安装)。

安装

安装 uv 后,无需安装即可运行 PyPI 上的软件包;使用 uvx [email protected] 可固定版本。能浏览 MCP Registry 的 MCP 客户端可通过 io.github.dii/stlink-mcp 找到它。

uvx stlink-mcp --check

如需参与代码开发,请克隆仓库并从仓库中运行:

git clone https://github.com/dii/stlink-mcp
cd stlink-mcp
uv sync
uv run stlink-mcp --check

--check 会报告找到的工具链、供应商数据库、任何板卡文件以及已连接的探针,然后退出,不会启动服务。它以与 list_probes 相同的方式列出探针;该操作既不会暂停探针,也不会将其复位。

在 MCP 客户端中使用

{
  "mcpServers": {
    "stlink": {
      "command": "uvx",
      "args": ["stlink-mcp"],
      "env": { "STLINK_MCP_CLT_ROOT": "/opt/st/stm32cubeclt_1.22.0" }
    }
  }
}

env 块用于填写设置,且只需填写你要更改的设置。使用 Claude Code 时,配置方式相同:

claude mcp add stlink -e STLINK_MCP_CLT_ROOT=/opt/st/stm32cubeclt_1.22.0 -- uvx stlink-mcp

从克隆的仓库运行时,请将命令替换为 uv --directory /path/to/stlink-mcp run stlink-mcp,而不是 uvx stlink-mcp。

告知 Agent 如何使用

连接时,服务会向每个客户端提供一份简短指南:说明用途、如何读取结果,以及通常的调用顺序。即使 Claude Code 尚未加载工具描述,也会向 Agent 显示这份指南。有些客户端不会传递这份指南;对于这些客户端,请将相同的文本放入项目的 AGENTS.md,或放入 Agent 用来读取指令的文件:

stlink-mcp drives STM32 boards through an ST-LINK probe with ST's STM32CubeCLT tools.

Every result has a status. verified: verified_by says what was observed. unverified: it may have worked, but nothing checked it, and unverified_because says why; this is not success. failed: error says why. Read the status before the values.

A usual session: `list_probes`, then `board_state` to identify the board. `run_firmware` loads an ELF linked for RAM and starts it; a build linked for flash, the default from STM32CubeIDE and CubeMX, goes `flash_firmware`, then `reset_target`, then `capture_console`. Without halting the program: `capture_console`, `capture_swo`, `sample_values`, `read_memory`, `read_fault_status`, `read_option_bytes` and `disassemble`. Halting it: `run_until`, `core_registers` and `locate_fault`. `reset_target` restarts it; `release_probe` when done.

Give `probe_serial` on every call: without it each call first lists the probes, which takes a second or more and holds up every other board. Give addresses as "0x..." strings and files as absolute paths. `run_firmware` lets go of the probe once the program starts; with `keep_probe=True` a GDB server keeps holding it, and while it does, a core that locks up is reset, which clears its fault registers. `reset_target` and `flash_firmware` stop a holding server first and say so. A call that halts or resets the target says so. If the running program has closed the debug port, `board_state` can get back in by resetting the target, and `reset_target` cannot; on an STM32N6 running ST's start-up code, only a power cycle does. An unknown argument is refused, not ignored.

工具

工具功能验证方式
list_probes列出已连接的探针、其固件和串行控制台,并标记被 GDB 服务占用的探针编程器自身提供的列表;它打印的错误会与列表一同返回,因为当前用户无法打开的探针会打印错误且不显示条目
board_state识别目标、探针和芯片系列,并说明内核是在运行、睡眠、暂停还是锁死,不会暂停内核尝试访问端口 0 和 1。读取 DHCSR 中的内核状态,并在 elf_path 指定的源文件中放入五个程序计数器样本。如果普通连接无法访问内核,则通过复位重新连接,并以 target_was_reset 标明;如果探针被 GDB 服务占用,则绝不复位
run_firmware复位目标,将 ELF 加载到 RAM 并启动加载后的控制台输出,否则提供镜像内的程序计数器样本。运行前探针保留的内容不计入结果。如果 GDB 未完成加载,则标记为 failed。拒绝未链接到 RAM的镜像。程序启动后释放探针,除非 keep_probe 为 true
flash_firmware将原始二进制文件(.bin)、ELF 或 Intel HEX 文件写入闪存,以便开发板启动回读每个写入的范围;只有回读内容完全相同才标记为 verified。说明除镜像本身以外擦除的内容。不会启动镜像。请参阅写入闪存
read_memory读取 32 位字、按名称读取变量,或按名称读取外设寄存器,不会暂停或复位目标返回读取到的字。仅当目标运行的是用于解析该变量的 ELF 时,变量才会标记为 verified。短读时会拒绝返回结果;对于读取会改变状态的寄存器,除非指定 read_effects_ok,否则也会拒绝读取
sample_values每秒采样数次,最长 60 s,同时采样最多八个变量、地址或寄存器,不会暂停程序如果值从未改变,则返回值本身;否则返回首次值、末次值、范围、变化次数以及首次变化时间,并说明有多少次采样发现内核处于运行、暂停或其他状态。until 会在首次满足条件的采样时停止。两次采样之间发生后又恢复的变化不会显示
core_registers读取内核寄存器,读取时会暂停内核;如果提供 ELF,还会读取调用栈中每一帧的参数和局部变量返回寄存器本身,并说明读取后内核是否运行。如果未提供 ELF,而内核在运行时无法访问,则会通过复位来访问,并在结果中说明。如果提供了 ELF,则宁可拒绝操作也不会复位,因此返回的调用栈始终属于正在运行的程序
run_until运行目标,直到到达某个函数、行或地址,或写入某个变量,然后暂停内核仅当芯片的调试故障状态寄存器记录了停止位置的断点或观察点时,才标记为 verified。超出时间限制时标记为 unverified。请参阅停止目标
read_fault_status读取并解码 Cortex-M 故障寄存器,不会暂停或复位目标返回原始寄存器、通俗易懂的诊断,以及硬件记录的故障地址。如果读取结果正常,但被占用的 GDB 服务捕获了异常向量,则标记为 unverified。Cortex-M0、M0+、M1 和 M23 内核没有这类寄存器,因此会拒绝操作
locate_fault根据异常帧查找发生故障的指令,读取时会暂停内核返回根据 LR 中的 EXC_RETURN 找到的栈中 PC;如果提供 ELF,还会返回函数、行号和指令。如果找到的字看起来不像帧,则标记为 unverified。绝不复位,因为复位会丢失故障信息
disassemble反汇编函数或地址周围的代码,数据来自 ELF返回每条指令及其源文件和行号。如果提供 probe_serial,还会在不暂停目标的情况下读取其代码;仅当代码与 ELF 中的代码一致时,才标记为 verified
read_option_bytes按照 ST 编程器的说明读取选项字节:读取保护、欠压复位电平、用户配置、启动地址和写保护仅执行读取:此处没有任何工具会写入选项字节。如果器件没有编程器已知的选项字节(例如 STM32N6),则会返回 ST 提供的原因并失败
capture_console捕获开发板的串行输出读取与探针串行端口匹配的端口,绝不读取第一个 /dev/ttyACM*。没有收到内容时标记为 unverified,因为未连接到目标的端口和静默端口读取结果相同
capture_swo通过 SWO 引脚捕获目标写入 ITM 激励端口的内容返回该端口收到的文本。没有收到内容时标记为 unverified。请参阅 SWO
reset_target复位目标并检查其是否重新启动在复位向量处短暂停止,然后将 PC 与向量表中的复位入口进行比较,之后让内核运行。会先停止占用探针的 GDB 服务,并说明此操作。无法绕过程序关闭的调试端口;board_state 可以做到
release_probe停止占用探针的 GDB 服务,使被 run_until 暂停的内核恢复运行如果没有探针被占用,则标记为 unverified。说明内核是否重新运行
保持探针连接

run_firmware 通过 GDB 服务加载程序,并在程序启动后释放探针。如果程序通过将 SWD 引脚用于其他用途、关闭调试端口所需的时钟,或在关闭调试功能的情况下休眠来关闭调试端口,那么此时调用 board_state 会通过复位重新连接,并重启程序;reset_target 则无法做到这一点。

run_firmware(keep_probe=True) 会让 GDB 服务继续保持探针连接,而 run_until 会在内核停止期间一直保持探针连接。当服务保持探针连接时,读取工具会以 ST 的共享模式访问旁边的内核;如各自表格行所述,locate_fault 和 core_registers 会在读取时暂停内核。reset_target 和 flash_firmware 会先停止服务,并在 released_probe_first 中说明这一点,因为在服务运行期间执行复位或写入闪存会破坏其 GDB 会话;release_probe 则会释放探针。保持探针连接有两项代价,而且无法阻止调试端口关闭:

  • 如果内核在故障处理程序内部发生故障并锁死,保持探针连接的服务会将其复位,并在复位向量处暂停,从而清除故障寄存器。发现内核以这种方式暂停的结果会明确说明。
  • 共享模式需要 ST 的 stlink-server,STM32CubeCLT 会将其安装到 PATH 中。如果没有它,服务会独占探针;因此,提供了探针的 read_memory、sample_values、read_fault_status、locate_fault、read_option_bytes、capture_swo 和 disassemble 会拒绝执行,直到调用 release_probe。在共享模式下,如果程序在探针由服务保持连接时关闭了调试端口,探针可能会卡住;请参阅遇到问题时。

ST 的 stlink-server 是一个守护进程,可让 GDB 服务和烧录器共享一个探针;只有在 GDB 服务保持探针连接时,本服务才需要它。其他时候,这个守护进程会妨碍操作:被它保持连接的探针无法由烧录器访问。因此,在 Linux 上,如果机器上没有正在运行的 ST-LINK GDB 服务,本服务会在下一次调用烧录器之前停止 stlink-server,--check 也会执行相同操作。其他系统上不会这样做。本服务假定自己是探针的唯一使用者,因为它的使用者是 Agent;不支持让 IDE 中的调试器与本服务共享探针。

从 RAM 运行映像

开发时应使用 run_firmware:它比烧录更快,而且由调试器放入 RAM 的映像会绕过启动 ROM,因此即使某些芯片需要经过签名的映像才能启动,也无需签名。每次加载前都会复位目标设备。映像必须为 RAM 链接,向量表必须位于 board_state 报告的 vector_table_addr,并且加载的每个段都必须位于 0x20000000 到 0x3FFFFFFF 之间。默认的闪存链接构建会在加载任何内容之前被拒绝,因为 GDB 加载会将每个段写入映像中携带的地址。该工具会从向量表设置栈指针和程序计数器,但不会设置 VTOR,因此使用中断的映像必须在启动代码中将 VTOR 指向自身的向量表。

写入闪存

flash_firmware 会将 .bin 写入你指定的 address,如果芯片的闪存基址为 measured,则写入该基址(参见地址的来源)。ELF 或 Intel HEX 文件会写入文件中指定的地址,并且每个字节都必须位于芯片的闪存范围内,即从已测得的基址开始,到 connect 报告的闪存大小为止;使用这类文件时,若指定了 address,则会被拒绝。ELF 或 HEX 文件会写入已测得的基址;如果基址来自 ST 的数据库或你的板卡文件,且位于主闪存窗口 0x08000000 至 0x0800FFFF 内(每款带内部闪存的 STM32 都从此处开始),也会写入。若基址缺失、为假定值或位于此窗口之外,若芯片的闪存位于外部加载器之后(STM32N6),或 connect 未报告闪存大小,则会拒绝写入,并提示使用 address 写入 .bin。connect、拒绝或写入完成后,ELF 或 HEX 的每个结果都会带有 flash_base_provenance 和 flash_range_checked,分别说明基址的来源以及检查文件各段时所依据的范围,因此 Agent 可以看出写入所依据的基址是否未经测量。S-record 文件会被拒绝。

擦除操作以完整扇区为单位,因此写入前后都会读取所触及的每个 128 KB 块中图像数据旁边的字节,并在结果中列出任何被擦除的字节。对于闪存扇区大于 128 KB 的芯片,擦除范围的一部分可能不在检查范围内。如果写入中途失败,结果会说明闪存可能已经发生变化。该工具不会启动图像,也不会为其签名;其 next_step 会说明芯片的启动 ROM 要启动该图像还需要什么,以及相关记录的位置。

STM32N6

STM32N6 没有内部闪存:其启动 ROM 会将图像从板卡的外部闪存复制到 RAM 中并运行。

  • 若要从闪存启动(BOOT1 为低电平),图像需要 ST 的头部。在安全启动熔丝未熔断的芯片上,不含密钥的头部即可:STM32_SigningTool_CLI -bin image.bin -nk -t fsbl -hv 2.3 -align -o image-boot.bin,然后写入 image-boot.bin。

  • 闪存启动时,启动 ROM 会关闭调试端口。若要让调试器访问从闪存启动的图像,图像必须在每次启动时将打开调试端口作为首个操作,且仅用于开发构建:ST 的 HAL_BSEC_ConfigDebug 和 HAL_BSEC_UnlockDebug 会向 BSEC_DBGCR 写入 0xB451B400,并向 BSEC_AP_UNLOCK 写入 0xB4。复位后,图像会再次打开调试端口,调试探针便可重新连接。

  • 在开发启动模式下(BOOT1 为高电平)写入外部闪存。闪存启动后,ST 的闪存加载器会使内核挂起,只有断电再上电才能让板卡恢复,因此 flash_firmware 会先读取启动引脚,并在闪存启动模式下拒绝操作。board_state 会报告读取到的启动模式。

  • 从 ST 的 FSBL 模板构建的图像启动时会关闭调试访问端口 1,无论调试探针是否处于保持状态;只有断电再上电才能重新打开该端口。原因是 ST 的 system_stm32n6xx_fsbl.c 末尾有一行代码,它会在退出 SystemInit 时关闭 SYSCFG 时钟:

    RCC->APB4ENCR2 = RCC_APB4ENCR2_SYSCFGENC;   /* deactivate SYSCFG clock */
    

    删除这条写入语句,端口便会在多次运行期间保持开启。

多块板卡

除 list_probes 外,每个工具都需要 probe_serial。连接了多块板卡时,省略此参数会导致错误,而不会进行猜测:重新插拔后,调试探针的顺序并不固定,而且把固件加载到错误的板卡上会造成一种看似成功的故障。

list_probes 会报告哪个串口对应哪个调试探针。这种对应关系并非按位置排列,因此按顺序将调试探针与 /dev/ttyACM* 配对会出错。在 Linux 上,若工具指定了 probe_serial,就会在 /dev/serial/by-id 中查找该串口;udev 会根据对应的调试探针为每个串口命名。每块板卡都会在各自的端口上运行自己的 GDB 服务。

不同调试探针上的调用会并行运行,同一调试探针上的调用则会依次等待。需要列出调试探针的调用(list_probes 或任何未指定 probe_serial 的调用)会等到所有调试探针均未被占用后独占运行;调试探针被占用时,列出操作会很慢。因此,每次调用都要指定调试探针。

谁请求了什么

每次工具调用都会向服务器的标准错误输出写入一行日志,MCP 客户端会将其保留在日志中:客户端声明的名称和版本、调用所经过的连接和请求、工具、探测对象以及判定。如果因 GDB 服务端占用探针而拒绝调用,held_by 会指出哪个客户端的运行正在占用探针,release_probe 会指出它原本属于谁。这里没有身份验证:会记录调用者,但不会核验。

SWD 还是 JTAG

除 capture_swo(仅支持 SWD)和 release_probe(使用它所释放的服务端的接口)外,每个会建立连接的工具都会接受 interface 参数,可设为 swd(默认值)或 jtag。结果会报告所用接口,以及在编程器已连接的情况下所用的时钟。并非每个探针都支持 JTAG:NUCLEO-H743ZI2 自带的 STLINK-V3E 就不支持;收到 JTAG 连接请求时,它会像没有连接目标一样响应,因此结果中的提示会首先指出这一点。工具不会在两种接口之间回退:它会使用指定的接口建立连接,而且为一种接口保留的 GDB 服务端不会用于另一种接口。

用名称代替地址

read_memory 根据名称读取变量,需提供 symbol 和目标所运行程序的 elf_path,并使用 ELF 自带的类型进行解码:整数、浮点数、枚举、位域、数组、结构体、联合体和指针。同一次调用中,它还会比较 ELF 代码的起始部分与目标内存,因此只有目标运行的确实是该 ELF 时,结果才会是 verified。如果名称匹配到两个变量(例如文件静态变量和函数静态变量),工具会拒绝请求并列出两者;如果局部变量位于寄存器或栈上,工具会拒绝请求,并说明它没有固定地址。

按名称读取外设寄存器时,需将 register 指定为 PERIPHERAL.REGISTER,例如 RCC.CR;工具会返回寄存器值和各字段,数据来自 STM32CubeCLT 安装目录中的 ST SVD 文件。指定了 svd 时使用该文件;否则使用探针报告的板卡名称对应的文件;如果没有对应文件,则使用设备名称中的芯片型号对应的文件。如果由此选出多个文件,只有当所有文件对该寄存器的描述一致时,才会读取它。NVIC 等核心寄存器位于 ST 的核心文件中,使用 svd 指定文件,例如 svd="Core/Cortex-M7"。

程序是否正在运行,以及运行位置

board_state 会读取核心的调试状态寄存器,并对程序计数器采样五次;这些操作都不会暂停核心。它会报告核心是在运行、睡眠、暂停还是锁定状态,以及当前执行位置;如果提供了 ELF,还会报告函数和行号。采样仅在调试跟踪单元开启时有效;GDB 会话会开启它,复位则会关闭它。因此,复位后立即调用时不会返回数据,并会说明原因。如果核心既未设置暂停位,也未设置锁定位,且两次读取之间没有执行任何指令,结果会报告 not seen running,而不会猜测。

run_firmware 使用相同的采样方式:即使镜像没有输出任何内容,或板卡没有控制台,只要每次采样都位于刚加载的镜像范围内,结果就会是 verified。只有在共享同一探针的服务端旁边才能进行采样,因此没有 stlink-server 时,需要设置 keep_probe=False,结果也会说明这一点。

识别操作以及每次内存读取还会读取核心的 CPUID 寄存器;所有 Cortex-M 芯片的该寄存器最高字节均为 0x41。如果核心在未启用调试的情况下处于睡眠状态,那么读取任意地址都可能无错误地返回相同的字;这种读取会以该原因为由判定失败,而不会将其作为数据返回。

会改变状态的读取

有些寄存器在读取时会发生变化。读取 UART 的接收寄存器会取出其中的字符,因此固件永远不会看到它;读取 ADC 的数据寄存器会清除“转换完成”状态;读取 RTC 的亚秒寄存器会锁定时间和日期,直到读取日期寄存器。ST 的 SVD 文件中有一个字段用于标记此类情况,但几乎没有文件使用它。

因此,read_memory 会维护自己的列表,按外设块而非芯片型号分类,因为 ST 在整个产品系列中复用这些外设块:UART、SPI、I2C 和 ADC 的数据寄存器,以及 RTC 的时间寄存器。每个条目都会注明其依据:在指定芯片上实测或推定,同时也会采用 ST 自己的标记。读取列表中的寄存器时,无论通过名称还是地址,都会在实际读取前拒绝操作,除非调用传入 read_effects_ok;此时会执行读取,并在结果中说明其影响。读取外设范围内的其他地址时,会列出读取覆盖到的寄存器,并说明目前未发现影响,但这并不表示没有影响。首次查询时会建立 ST 标记的索引,这需要几秒钟,之后会保存在用户的缓存目录中。sample_values 使用相同的列表;run_until 则拒绝监视列表中的寄存器,因为 GDB 设置监视点时会读取被监视的地址。

停止目标

run_until 会运行程序,直到程序到达 location、某个函数、file.c:line 或 0x 地址,或者写入 watch、某个变量或地址;运行时间最长为 seconds。停止后,它会让内核保持暂停,并返回调用栈、每个栈帧的参数和局部变量,以及寄存器。函数的内联副本也会触发停止。它需要目标正在运行的 ELF 文件,并会拒绝使用其他程序,因为否则断点可能会被写入与该文件描述内容不符的代码。它从程序当前的运行状态开始;若要在程序较早的位置停止,请先用 run_firmware 加载程序,然后立即调用 run_until。

只有根据芯片本身的状态确认的停止才算已验证:GDB 服务会在每次停止时读取调试故障状态寄存器并记录日志;当该寄存器的值指示断点或监视点,且对于断点来说,内核正位于指定位置时,停止才算已验证。监视点会在写入发生后的几条指令处停止,而不会停在写入指令上,因此结果会给出停止前的指令,并说明写入发生在停止之前。监视范围必须是地址处对齐的 1、2 或 4 字节,其他情况都会被拒绝。若运行时间达到上限,停止状态为未经验证,内核会保持在当时的位置。若芯片的断点单元无法覆盖某个地址,则会拒绝在该地址设置硬件断点;在 RAM 中,GDB 会使用软件断点。

内核暂停时,芯片的其他部分不会暂停:定时器、DMA、USB 和看门狗仍会继续运行,每个结果都会说明这一点。再次调用 run_until 会从停止处继续运行;带上 ELF 调用 core_registers 会读取此处的调用栈;release_probe 会恢复内核运行并说明它是否正在运行。run_until 会使用当前占用探针的 GDB 服务,包括 run_firmware 启动的服务;仅当没有服务占用探针时,它才会启动自己的服务,因此停止程序不会释放探针。

SWO

capture_swo 会通过探针的 SWO 引脚读取固件写入 Cortex-M ITM 的内容。ITM 是调试单元自带的 printf 通道,因此即使开发板没有串口控制台,也能验证运行情况:先用 run_firmware 加载程序,然后进行捕获。它需要 core_clock_hz,即内核运行时的时钟频率,因为 SWO 速率由此计算;只有固件知道这个值,填错会导致没有输出或文本无法读取。它仅适用于 SWD,因为 SWO 引脚就是 JTAG 的 TDO。

捕获操作会改变目标,其结果会说明这一点:它会启用 ITM,并将 SWO 引脚从应用程序切换给跟踪单元。在经过实测的 STM32F407 上,该设置在 reset_target 后仍然有效,只有断电后才会清除。

配置

设置项是环境变量,在 MCP 客户端的 env 块中设置(参见在 MCP 客户端中使用):

变量含义
STLINK_MCP_BOARDS用于描述本项目尚未完成特性确认的板卡的 TOML 文件。如果 ~/.config/stlink-mcp/boards.toml 存在,则默认使用该文件
STLINK_MCP_NO_VENDOR_SEED设置为任意值后,仅使用实测数据和用户声明的数据运行,不从 ST 的设备数据库中填充数据
STLINK_MCP_CLT_ROOTSTM32CubeCLT 安装目录
STLINK_MCP_CONSOLE每个探针对应的串行控制台,用于控制台并非探针自带控制台的板卡,例如使用 USB 串行适配器时:SERIAL=/dev/ttyUSB0,多个配置以逗号分隔。此设置优先于探针列表中的映射。仅连接一个探针时,才会使用不带前缀的设备路径
STLINK_MCP_CONSOLE_BAUD控制台波特率,默认值为 115200
STLINK_MCP_GDB_PORT第一个 GDB 服务端口,默认值为 61234。每个探针的服务端使用此端口及后续端口,下一个探针的起始端口则增加 4

无法使用的设置会导致服务在启动时停止,并显示一行消息,处理方式与错误的板卡文件相同。

地址的来源

此工具使用的每个地址都有来源标记,board_state 会在值旁报告该标记:

来源含义
measured有人曾在此芯片上运行并确认其正常工作
vendor-db从 STMicroelectronics 的设备数据库中读取
user-stated运行此服务的人在板卡文件中填写
assumed无人实测,且没有来源声明;这是该范围的默认值

只有 measured 表示该值已被验证可用,且此标记会影响行为。flash_firmware 不会根据未标记为 measured 的基址来选择 .bin 文件的地址,因为写入会持久保留,而之后读回数据只能确认写入了什么,不能确认写入位置是否正确;ELF 或 HEX 文件自带地址,因此此时基址仅用于决定是否拒绝操作,未实测的基址会被限制在主闪存窗口内(参见写入闪存)。run_firmware 会加载到 vendor-db 地址,因为地址错误可通过重置撤销,结果也仍会注明该地址未经实测。实测条目始终优先于填充的数据:数据库可能会将向量表地址报告为 RAM 基址,而且不包含访问端口信息,因此,填充了数据的芯片会将端口 0 标记为 assumed,并由 board_state 发出警告。

vendor-db 值会在运行时从你自己的 STM32CubeCLT 安装目录中的 STM32CubeProgrammer 设备数据库读取,该数据库仍受 ST 的许可协议(SLA0048)约束。本项目不随附这些文件,也不包含由它们衍生的任何表格。

描述自己的开发板

编写一个 TOML 文件,每个设备 ID 对应一个表,设备 ID 以编程器打印的内容为准。将文件放在 ~/.config/stlink-mcp/boards.toml,或放在 STLINK_MCP_BOARDS 指向的位置:

["0x419"]
name = "STM32F429 on a NUCLEO-F429ZI"
access_port = 0
ram_load_base = 0x20000000
vector_table_addr = 0x20000000
flash_base = 0x08000000
notes = "addresses from the reference manual, not yet run on the board"

服务器在接受任何请求前就会加载该文件;如果无法使用该文件,服务器便会停止。对于该设备 ID,你的条目优先于内置表。

同一文件也可以为内置列表未涵盖的芯片或外设添加读取时会改变状态的寄存器:

[[read_effects]]
block = "USART"         # the SVD file's groupName, a regular expression
registers = ["RDR"]     # names without the peripheral's prefix
effect = "takes the received character and clears RXNE"

该文件中的所有内容都标记为 user-stated,且无法将任何值标记为 measured:正是这个标签让 flash_firmware 能够根据基地址确定 .bin 的地址,而配置文件中的拼写错误不应解锁不可逆写入。只有当声明的 flash_base 位于 0x08000000 到 0x0800FFFF 的主闪存窗口内时,才会将其用于 ELF 和 HEX 写入。该项目检查过的 STM32 中,没有任何芯片会在此窗口内存放 RAM、选项字节或 OTP(已检查 F405/F407 和 H742/H743 系列的参考手册;L0 和 L1 在 0x08080000 保留数据 EEPROM,错误的基地址可能会触及该区域),因此拼写错误不会导致写入 RAM 或无法重新写入的存储器,结果也会将该基地址标记为用户声明。若要使用声明的基地址刷写 .bin,请传入 address。要将测得的系列添加到内置表,请参阅 CONTRIBUTING.md。

遇到问题时

首先运行 stlink-mcp --check,并在命令前加上 uvx 或 uv run(取决于你的安装方式)。该命令会列出找到的内容以及未找到的内容。

  • 未找到 STM32CubeCLT 安装。 将 STLINK_MCP_CLT_ROOT 设置为包含 STM32CubeProgrammer/bin 的目录。
  • 未找到 ST-LINK 探针。 在 Linux 上,探针需要 ST 的 udev 规则,这些规则由 STM32CubeCLT 安装;安装后拔下探针再重新插入。若编程器能列出探针却无法读取,通常是 USB 问题,list_probes 会说明这一点。
  • 无法打开串口。 在 Linux 上,用户需要加入 dialout 组;新的组成员身份会在下次登录后生效。
  • 无法连接到内核。 结果中的 hint 会列出可能原因。其中一种情况是另一个进程占用了探针,例如 IDE 或之前会话遗留的 GDB 服务;可使用 ss -ltnp 按端口检查。
  • 未找到序列号,或固件过旧。 STM32CubeProgrammer 会拒绝过旧的 ST-LINK 固件。STM32CubeCLT 安装目录中的 STLinkUpgrade 可用于更新固件(选择探针,点击 Open in update mode,再点击 Upgrade)。
  • run_firmware 拒绝该映像。 映像必须针对 RAM 链接;请参阅从 RAM 运行映像。
  • flash_firmware 拒绝 ELF 或 HEX 文件。 拒绝信息会指出基地址的来源,并说明原因:没有基地址、基地址为假定值、开发板文件中的基地址超出主闪存窗口、使用了外部加载器,或连接时未能获取闪存大小。请参阅写入闪存;带有 address 的 .bin 始终可以使用。
  • 从闪存启动的 STM32N6 无法连接。 映像必须自行打开调试端口,或者开发板必须处于开发启动模式;请参阅 STM32N6。
  • 探针对每次连接都返回 DEV_TCP_CMD_ERR。 探针本身卡住了;STLINK-V3 曾在某个程序关闭调试端口而服务器仍占用探针时发生此情况。切断探针的 USB 电源:拔下探针,或者在集线器支持时使用 uhubctl 关闭并重新打开对应端口。若开发板的调试线路上连接了多个探针,请同时切断它们的电源。
  • 其他工具报告探针已被占用。 GDB 服务占用了探针,而未安装 stlink-server,因此其他程序都无法连接。请安装 STM32CubeCLT 的 stlink-server,或调用 release_probe。若某个程序关闭了调试端口(例如 STM32N6 上 ST 的标准启动代码),之后开发板可能需要断电重启才能再次运行。

限制

  • 它不会构建固件。这是构建系统的工作。
  • 它不会对映像进行签名,因为签名需要的密钥和选项属于项目,而非编程器。
  • 仅支持搭配 STM32CubeCLT 使用 ST-LINK。没有为第二种后端预留抽象层:要添加 OpenOCD,必须先引入这一层。
  • 已在 Fedora Linux 44、STM32CubeCLT 1.22.0 和 Python 3.12 环境下运行,使用的探针包括 NUCLEO-N657X0-Q、NUCLEO-H743ZI2 和 STM32H753I-EVAL2 板载的 ST-LINK V3 探针,以及连接到 STM32H743 的 JTAG 接口和自定义 STM32F407 板的 ST-LINK/V2 探针。其他 Linux 发行版应该也能运行,但尚未测试。尚未尝试 macOS。在一块 NUCLEO-N657X0-Q 上运行过 Windows 11,该板的所有检查均已通过。唯一无法运行的检查用于确认 ST 的共享守护进程是否仍在运行;该服务仅由 MCP 服务在 Linux 上管理。尚未在 Windows 上运行 SWO、JTAG、闪存写入和其他板卡。
  • 尚未尝试 ST-LINK/V2-1。如果任何探针出现解析问题,捕获的 STM32_Programmer_CLI -l 输出就是有用的错误报告。
  • 本项目与 STMicroelectronics 无关。

相关项目

其他 MCP 服务也能驱动 STM32 板卡,其中好几个功能比本项目更多:构建、单步执行、内存写入、RTT、其他探针和其他芯片。本项目的特色在于结果会说明具体检查了什么。以下项目已通过阅读源代码进行了解,最近一次检查时间为 2026 年 10 月 7 日;在这些项目中,成功意味着命令已返回,或厂商工具显示了自己的横幅信息。

  • phryniszak/stmcp 的工具最接近:它是一个基于相同 STM32CubeCLT 工具构建的 MCP 服务,以 GDB 会话为核心,支持运行、暂停、复位和单步执行、断点、内存和寄存器读写、解码后的故障寄存器转储,以及表达式求值。正如其 README 所述,它通过 GDB 读取数据,因此会暂停内核。命令返回时即视为运行完成;复位后返回的程序计数器未经检查。**在相同任务上进行比较:**在三块开发板上执行七项基准测试任务(崩溃、故障处理程序内部发生故障、卡死、不打印任何内容的固件镜像、不干扰固件读取 ADC 结果、会关闭调试端口的程序、确认能否启动的闪存写入),使用相同的四个 AI Agent(两个 Claude 模型和两个 Gemini 模型),每个 Agent 对每项任务运行一次,每个项目共 28 次运行。stmcp:10 次正确、10 次部分正确、5 次错误、3 次未作答。stlink-mcp:23 次正确、4 次部分正确、0 次错误、1 次未作答。当工具没有进行检查时,stmcp 的 Agent 未得到任何帮助:两个 Agent 始终未能访问 STM32N6 的内核,该内核位于访问端口 1;一个 Agent 将编程器显示的 “Start operation achieved successfully” 当作开发板正在运行;其他 Agent 则将 ELF 的入口点当作程序的运行位置。该项目的作者制定并评分了这些任务,且未采用盲测,因此请将结果视为参考;详情见 docs/findings.md。
  • Zeraissh/stm32-gdb-mcp 的用途最接近:专为 STM32 和 Agent 打造,通过 GDB 配合 OpenOCD、ST-LINK 或 J-Link 工作,支持断点、单步执行、FreeRTOS 感知、故障重建、RTT 和 SWO 日志记录,以及带有建议后续步骤的解码结构化结果。正如其 README 所述,读取数据需要先暂停内核。
  • Adancurusul/embedded-debugger-mcp(Rust;底层使用 probe-rs 或 OpenOCD)支持断点、单步执行、RTT、带可选回读比较的闪存编程、ST-LINK、J-Link、DAPLink 和其他 probe-rs 探针、多种架构,以及一个会报告寄存器但不会断言故障原因的故障工具。它的成功文本表示后端调用已返回:run 会报告 “resumed execution successfully”,但之后没有任何观测结果;reset 会打印快照中的程序计数器,该快照允许读取失败,失败时显示零;无论是否连接 RTT,run_firmware 都会以 “Firmware is now running on target” 结束。文件引用见 docs/findings.md。
  • shieldyguy/stm32-mcp 使用 STM32CubeIDE 无头构建器进行构建,通过 OpenOCD 烧录和读取内存,并提供一整套串口工具、按符号写入内存和实时内存监控。它会自行报告结果行:根据 OpenOCD 自身的提示显示 “Flash: OK” 和 “Verify: OK”;只要发出复位请求就显示 “Reset: OK”,但不会进行任何检查。
  • Helistana/mcp-stm32cubeide-server 驱动相同的 CubeIDE 工作流,支持故障解码,并提供了中文文档。
  • BlaineMoon30/STM32-DEBUG-MCP 仅支持 Windows,通过 OpenOCD 和 GDB 或 STM32_Programmer_CLI 进行构建、烧录和调试,并像本项目一样,通过编程器的热插拔连接读取正在运行的开发板。
  • Klievan/jlink-mcp 面向 Cortex-M 的 SEGGER J-Link 探针,印证了本项目自行测试的理由:在真实开发板上,出错的调用曾报告成功,因此它的测试会检查返回结果,而不只是确认调用已返回。

当你需要本项目未涵盖的功能时,请选择其他工具(适用对象)。截至 2026 年 10 月 7 日(含)已阅读这些项目;它们此后可能已有变化。

许可证

MIT。请参阅 LICENSE。

评论

支持 Markdown

第一次接触 MCP?

从协议是什么,到在客户端里装上第一个服务,这几条回答覆盖了大多数入门问题。

一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。