jlink-mcp
by l1amyi
README.md
# jlink-mcp
An MCP server that drives a [SEGGER J-Link](https://www.segger.com/products/debug-probes/j-link/) probe to debug microcontrollers: flash firmware, halt and resume the CPU, read and write memory, registers and breakpoints, and diagnose Cortex-M faults.
Built for use with [pi](https://github.com/earendil-works/pi) and any other MCP client.
## Status
The full tool surface is implemented and the server runs end to end. Everything below was **verified on real hardware** — a J-Link V11 (S/N 941000024, firmware 2025-04-01) driving an **STM32F411CE** over SWD at 4000 kHz, with J-Link software V7.52a on Windows:
| Area | Tools | Evidence |
| --- | --- | --- |
| Probe | `jlink_status` | Reads S/N, firmware, hardware version, VTref, ITarget and pin states. |
| Device database | `jlink_devices` | 8621 built-in devices exported via `ExpDevList`, plus 241 XML-only names. |
| Connection and state | `jlink_connect`, `jlink_halt`, `jlink_resume`, `jlink_reset`, `jlink_cpu_state`, `jlink_step` | Connect reports the Cortex-M4 r0p1 core; reset and step confirmed against the running firmware. |
| Memory | `jlink_read_memory`, `jlink_write_memory` | Read the vector table and SCB; write-and-read-back confirmed in RAM. |
| Registers | `jlink_read_registers`, `jlink_write_registers` | Wrote `R0`, `R1` and `LR`, confirmed by read-back; `PC` routed through `SetPC`. |
| Breakpoints | `jlink_run_to` | Hit a breakpoint in the firmware's main loop; also verified the timeout path. |
| Faults | `jlink_fault_info` | Induced a controlled `IACCVIOL` HardFault and recovered the stacked frame, reporting the exact faulting PC. |
| Escape hatch | `jlink_exec`, `jlink_command_reference` | Ran raw Commander commands; the reference is taken from this J-Link's own `?` output. |
| Flash | `jlink_flash`, `jlink_erase` | Chip erase, range erase, and programming `.bin` / `.hex` / `.srec` with verification — see below. |
| Source locations | `elf` on every PC-reporting tool | Addresses resolve to `file:line in function`, verified address-by-address against `addr2line`. |
| Fault localisation | `jlink_fault_info` + `elf` | A deliberate bus fault was traced back to the exact HAL line that dereferenced the bad pointer — see below. |
**Everything is now verified on hardware.** The flash tools were the last gap, tested once the board's firmware was expendable: `scripts/flash-test.mjs` backs the whole flash up, validates the image, then exercises every flash path and finally restores the original byte-for-byte (SHA-256 identical, CPU running again).
Several bugs were found by testing against hardware rather than by reading the documentation:
- `jlink_write_memory` emitted `w32`, but J-Link names its write commands by width in *bytes* (`w1`/`w2`/`w4`), so every write failed with `Unknown command`. Reads are different: those really are `mem8`/`mem16`/`mem32`.
- `jlink_run_to` forced a **software** breakpoint (`SetBP <addr> S`). In flash that makes J-Link reprogram the containing sector: 6538 ms versus 1764 ms for a hardware breakpoint at the same address. It now defaults to `H`.
- `jlink_reset` reported "CPU running" unconditionally, and both it and `jlink_flash` relied on `r` to leave the CPU running. `r` actually leaves it halted at the reset vector when vector catch is active, so both now issue an explicit `g`.
- `decodeExcReturn` silently failed on a JavaScript trap: bitwise operators coerce to signed int32, so `(0xFFFFFFFD & 0xff000000) === 0xff000000` compared `-16777216` against `4278190080`. Exception-frame recovery never ran until this was fixed.
- `jlink_resume` treated J-Link's `Error: CPU is not halted` as a failure; it is a state report, and failure detection had to become line-aware to express that.
## Requirements
- Node.js 18 or newer.
- SEGGER J-Link software installed (`JLink.exe` on Windows, `JLinkExe` elsewhere). Verified against V7.52a; newer releases should work but the exact command surface has not been checked.
Path resolution order: the `JLINK_PATH` environment variable, then a scan of `C:\Program Files\SEGGER`, `C:\Program Files (x86)\SEGGER`, `/opt/SEGGER` and `/usr/local/SEGGER` (including versioned `JLink_V*` directories, newest first).
## Build and verify
```bash
npm install
npm run build
```
`npm run smoke` boots the built server over stdio and exercises the tools that need no MCU: `jlink_status`, `jlink_devices`, and the error paths (a misspelled device name, an invalid Commander command).
`npm run hardware` is the hardware-in-the-loop suite. It needs a powered target and it **halts and resets** the CPU, so do not point it at something that must keep running:
```bash
node scripts/hardware-check.mjs STM32F411CE
```
It defaults to `JLINK_DEVICE`, then `STM32F411CE`, and exits non-zero if any check misbehaves.
`npm run flash` is a separate, **destructive** suite for the flash tools: it erases the target's flash. It requires an explicit flag so it cannot run by accident, backs the flash up first, validates that the image is a plausible Cortex-M image before erasing anything, and restores the original byte-for-byte at the end. A second copy of the backup is written to the git-ignored `tmp/`.
```bash
npm run flash -- --yes-destroy-flash STM32F411CE
```
### End-to-end against real firmware
[`test_project/`](test_project/) is a CubeMX project for this exact board (STM32F411CEU6, PC13 LED) that blinks at 2 Hz. It is the one firmware whose behaviour is known in advance, which makes it the end-to-end fixture: build it, flash it, then prove from the debugger that the pin really toggles.
```bash
cd test_project
"/c/Keil_v5/UV4/UV4.exe" -b "$(cygpath -w MDK-ARM/test_project.uvprojx)" -j0 -o "$(cygpath -w build.log)"
cd ..
HEX="$(cygpath -m "$PWD/test_project/MDK-ARM/test_project/test_project.hex")" node scripts/verify-blink.mjs
```
The project ships CMake presets for a GCC toolchain as well. That path is verified end to end with Arm GNU Toolchain 15.3.1 plus Ninja:
```bash
export PATH=/path/to/arm-gnu-toolchain-15.3.rel1/bin:$PATH
cd test_project
cmake --preset Debug && cmake --build --preset Debug
arm-none-eabi-objcopy -O ihex build/Debug/test_project.elf build/Debug/test_project.hex
```
No C23 problems: GCC 15 defaults to C23, which the GCC project warns breaks many older codebases, but this STM32F4 HAL builds clean with 0 warnings. If that ever changes, `-std=gnu11` is the escape hatch.
The `objcopy` step is not optional. J-Link 7.52a's `loadfile` does not accept `.elf`, so `jlink_flash` refuses it with the exact `objcopy` command to run instead.
**Trap worth knowing:** do not unpack that toolchain zip with Git Bash's `unzip`. It can write `arm-none-eabi/bin/ld.exe` (2.1 MB) as a **0-byte file** while `unzip -t` still reports "No errors detected", and the only symptom is `collect2.exe: fatal error: CreateProcess: No such file or directory` at link time. Use 7-Zip or the official installer — or verify afterwards by comparing every zip entry's size against the extracted tree (7360 files, exactly one was damaged here).
`scripts/verify-blink.mjs` is worth reading as a technique: it samples `GPIOC->ODR` **inside a single J-Link session**, using `Sleep` between reads. Sampling across separate tool calls would alias badly against a 250 ms half period and show nothing. It measured 300/200 ms runs, which is exactly how a 250 ms square wave quantises at 100 ms sampling.
### Reference documentation
Everything this project depends on for reference lives under `docs/`, collected by:
```bash
npm run docs:sync
```
- **`docs/reference/`** is committed. It holds facts extracted from the installed J-Link itself: its version banner, its complete command list, its reset-type list, and a manifest of what was copied. These can be grepped without a probe attached.
- **`docs/vendor/`** holds SEGGER's own documents (UM08001 and friends) as PDFs plus `pdftotext` output. **These files are SEGGER's copyright and are not covered by this project's MIT licence.** SEGGER's licence forbids redistribution without written authorisation and this repository is public, so including them here is a known conflict, deliberately accepted by the repository owner. Provenance and removal instructions are in [docs/vendor/NOTICE.md](docs/vendor/NOTICE.md); they live in a separate commit, so a single `git revert` undoes it without touching any code.
Note that the J-Link software installed here is 7.52a while its bundled UM08001 manual is labelled 7.50, so the two are not strictly the same revision — prefer the tool's own output when they disagree.
If you are working on this codebase with an AI agent, point it at [AGENTS.md](AGENTS.md) first.
## Configuration
All settings are environment variables; most can also be overridden per tool call.
| Variable | Default | Purpose |
| --- | --- | --- |
| `JLINK_PATH` | auto-detected | Full path to `JLink.exe` / `JLinkExe`. |
| `JLINK_DEVICE` | none | Default MCU name, e.g. `STM32F411CE`. Required for anything that touches the target. |
| `JLINK_INTERFACE` | `SWD` | `SWD`, `JTAG`, `FINE` or `ICSP`. |
| `JLINK_SPEED` | `4000` | Interface speed in kHz. |
| `JLINK_SERIAL` | none | Probe serial number, for setups with several probes. |
| `JLINK_TIMEOUT_MS` | `30000` | Per-invocation timeout. Raise it for large flash images. |
| `JLINK_MAX_OUTPUT` | `4194304` | Hard cap on captured J-Link output per invocation. |
| `JLINK_SKIP_DEVICE_VALIDATION` | unset | Set to `1` to stop checking device names against J-Link's list. |
| `JLINK_PERSIST_HALT` | unset | Set to `1` to stop J-Link resuming the target when a session ends, so a halt persists across calls. |
| `JLINK_DISABLE_FLASH_BP` | unset | Set to `1` to disable the FlashBP feature, so a breakpoint in flash never reprograms flash. |
The default timeout is deliberately below the 60 s request timeout most MCP clients apply, so a stuck operation returns a diagnosable error instead of the client giving up first.
## Tools
| Tool | Target needed | Purpose |
| --- | --- | --- |
| `jlink_status` | no | Probe serial number, firmware, VTref, pin states. Start here. |
| `jlink_devices` | no | Search J-Link's device list for core, flash banks and RAM. |
| `jlink_connect` | yes | Establish a debug connection and report state. |
| `jlink_cpu_state` | yes | Halted or running, and why it stopped. |
| `jlink_halt` | yes | Halt the CPU and confirm it stopped. |
| `jlink_resume` | yes | Resume the CPU. Already-running is reported, not treated as an error. |
| `jlink_reset` | yes | Reset, optionally with a specific reset strategy or a delayed halt, then report the resulting state. |
| `jlink_step` | yes | Single step N instructions and report PC. |
| `jlink_read_memory` | yes | Read memory at 8/16/32-bit width, optionally halting first for a consistent snapshot. |
| `jlink_write_memory` | yes | Write values, then read back and report mismatches. |
| `jlink_read_registers` | yes | Halt and dump core registers. |
| `jlink_write_registers` | yes | Write core registers with read-back verification. |
| `jlink_run_to` | yes | Set breakpoints, resume, wait for a hit, and report where it stopped. |
| `jlink_fault_info` | yes | Decode CFSR/HFSR and recover the stacked exception frame. |
| `jlink_erase` | yes | Chip erase, or erase an address range. |
| `jlink_flash` | yes | Program `.bin` (address required) or `.hex`/`.mot`/`.srec`. |
| `jlink_exec` | optional | Escape hatch: run arbitrary J-Link Commander commands. |
| `jlink_command_reference` | no | The commands this J-Link accepts, for use with `jlink_exec`. |
Addresses are hexadecimal by default — both `0x20000000` and `20000000` mean the same thing; pass a JSON number for decimal.
### Device names are checked against J-Link's own list
`jlink_devices` builds its answer from J-Link itself: `ExpDevList` exports the DLL's built-in list (8621 devices on 7.52a) and `JLinkDevices.xml` supplies 241 further names that the built-in list lacks. The parsed result is cached on disk and keyed to the J-Link executable's mtime, so the export cost is paid once per J-Link install.
Before connecting, the target tools check the device name against that list and fail early with suggestions:
```
Error: J-Link has no device called "STM32F411ZZ", so the target could not be connected.
Hint: J-Link knows similarly named devices: STM32F411CC, STM32F411CD, ... Set JLINK_SKIP_DEVICE_VALIDATION=1 to bypass this check.
```
This matters because J-Link's own behaviour is unhelpful: an unknown name either retries until the timeout, or is silently swapped for a different device. The check is best-effort — if the device list cannot be built at all, the server logs to stderr and proceeds.
### Register names
`rreg`/`wreg` accept `R0`-`R12`, `R14` (or `LR`), `XPSR`, `MSP`, `PSP`, `RAZ`, `CFBP`, `APSR`, `EPSR`, `IPSR`, `PRIMASK`, `BASEPRI`, `BASEPRI_MAX`, `FAULTMASK`, `CONTROL`, `IAPSR`, `EAPSR`, `IEPSR`, `FPSCR`, `FPS0`-`FPS31` and `CycleCnt`.
J-Link rejects `R13`, `R15`, `SP`, `PC` and `LR` with `Illegal register name`, which is surprising because it prints all of them in its own list. `jlink_write_registers` therefore routes `PC` through `SetPC` automatically and rejects `SP`/`R13` with a hint to use `MSP` or `PSP`.
## Using it from pi
pi's MCP gateway installs servers by URL, so a stdio server needs a small bridge. Any stdio-to-HTTP proxy works, for example:
```bash
npx -y supergateway --stdio "node dist/index.js" --port 8321 --sse
```
then install `http://localhost:8321/sse` through the gateway. Run the bridge with `JLINK_DEVICE` and friends set in its environment.
If your MCP client supports stdio servers directly, point it at `node dist/index.js` with no bridge and no arguments.
## Design notes
J-Link Commander is driven in **script mode, one fresh process per tool call**, rather than as a long-lived interactive session. Four behaviours of J-Link 7.52 on Windows forced this:
1. **Piped stdout is block-buffered.** When stdout is not a console, output is held until the buffer fills or the process exits. An interactive session that waits for the `J-Link>` prompt never receives it, so prompt-based synchronisation is impossible.
2. **Reading stdin at EOF spins forever.** If a script finishes and J-Link then reads stdin, it loops printing `Unknown command` — hundreds of megabytes in seconds. Every generated script therefore ends with an `exit` terminator so EOF is never reached, and `exit`-family commands are stripped from caller input so the terminator always stays last.
3. **Script files must use CRLF line endings.** A LF-only script is mis-parsed.
4. **Closing a session restarts the target, by default.** SEGGER states that when the debug connection is closed the target is left running, or its execution is *restarted from the pause point*. So by default a halt does not survive the call that requested it, and neither does a breakpoint. This is not a hard limit: SEGGER's remedy is the command string `SetRestartOnClose`, which is reachable only through J-Link Commander's `exec` command and applies **per session** — it does not carry over between processes, so it must be reapplied in every session. Setting `JLINK_PERSIST_HALT=1` makes this server add `exec SetRestartOnClose = 0` to every script it generates.
Note that even with that option, a bare memory read resumes the CPU: `mem32` performs a halt/restore cycle, so it leaves the core running. Use `halt: true` to keep it stopped.
Because each call is a fresh process, each one reconnects to the target (roughly a second). The compensating benefit is that no session can be left in a broken state by a crashed call.
Two consequences shape the API:
- **Tools that need a halted CPU halt within their own call.** `jlink_read_registers`, `jlink_step` and `jlink_fault_info` all issue `h` first, and `jlink_read_memory` takes `halt: true`. This is what makes them correct regardless of `JLINK_PERSIST_HALT`, since a fresh session's connect sequence cannot be assumed to preserve the state you left behind.
- **Breakpoints are set and waited on inside one session.** `jlink_run_to` combines `SetBP`, `g` and `WaitHalt` in a single process, because a breakpoint set by an earlier call is already gone. It accepts up to four addresses, all armed in that one session.
Safety rails around the J-Link process:
- Hard timeout, then the process tree is killed (`taskkill /T /F` on Windows).
- Output capped in memory; the process is killed if it exceeds the cap.
- A detector kills the process early if J-Link asks an interactive question it can never receive an answer to.
- Failure detection is **line-aware**, so a message that reports a state rather than an error can be excluded per call. This is what lets `jlink_resume` treat `****** Error: CPU is not halted` as "already running".
- Temporary script files are always cleaned up.
### Reset behaviour
SEGGER documents that **every** Cortex-M reset strategy halts the CPU after the reset, because J-Link sets `VC_CORERESET` in the DEMCR so the core stops before executing user code. A bare `r` therefore leaves the target *stopped at the reset vector*, which is a genuine surprise: `jlink_reset` used to report "CPU running" unconditionally and was simply wrong.
The tool now follows a plain reset with an explicit `g`, and asks the probe for the resulting state instead of assuming it. `haltAfterMs` maps to the `rx <ms>` form, which is the documented way to let a ROM bootloader run before the core is stopped. `resetType` exposes `RSetType`; SEGGER recommends leaving it at type 0, which lets J-Link pick the best strategy for the selected device — which is another reason the device name matters.
### Breakpoints: force hardware, or pay for flash
`SetBP` takes an `S`/`H` suffix which the [SEGGER Commander reference](https://kb.segger.com/J-Link_Commander) defines as **"S: Force software BP"** and **"H: Force hardware BP"**. That distinction matters enormously in flash, because SEGGER describes flash breakpoints as *"The J-Link software reprograms a flash sector to set or clear a breakpoint."*
Measured on the STM32F411CE, waiting 1500 ms for a breakpoint that is never reached:
| Breakpoint | Total |
| --- | --- |
| none (baseline) | 1770 ms |
| RAM, `S` | 1770 ms |
| RAM, `H` | 1774 ms |
| flash, `H` | 1764 ms |
| flash, **`S`** | **6538 ms** |
| flash, `S`, with `JLINK_DISABLE_FLASH_BP=1` | **1771 ms** |
Forcing a software breakpoint in flash cost ~4.8 s extra for set plus clear, while a hardware breakpoint at the same address was free. This is a trap worth knowing: by design *"J-Link prioritizes the use of hardware breakpoints and automatically switches to flash breakpoints once the available hardware breakpoints are exhausted"*, but an explicit `S` overrides that preference and forces the slow path. `jlink_run_to` therefore defaults to hardware breakpoints and only forces software when asked.
Setting `JLINK_DISABLE_FLASH_BP=1` goes further and turns the FlashBP feature off entirely, so J-Link can only ever use hardware comparators. Worth doing as a safety rail, because SEGGER documents two hazards on the flash-breakpoint path:
- It temporarily uses **the first 2-4 KiB of internal RAM** as a flash loader buffer (contents preserved and restored).
- **DMA engines keep running while the CPU is halted**, so a DMA that touches that RAM will corrupt the operation. SEGGER states this *"cannot be supported in a generic way to pause/temporarily disable all DMAs"*.
On a part that uses DMA heavily, disable flash breakpoints.
### Flash programming
`jlink_flash` and `jlink_erase` are verified end to end by `npm run flash`. That script backs the target's flash up first, refuses to erase unless the backup validates as a plausible Cortex-M image, and restores the original byte-for-byte at the end. What it proved:
| Check | Result |
| --- | --- |
| Chip erase leaves the flash blank | yes |
| A `.bin` with no address is refused | yes |
| `erase` with only `start` is refused | yes |
| An unsupported extension is refused up front | yes |
| `.bin` at an address, verified with `verifybin` | restored byte-identical |
| `.hex` data lands at the address in the file | yes |
| `.srec` data lands at the address in the file | yes |
| Range erase clears only the requested sector | yes |
| The neighbouring sector and the firmware region stay untouched | yes |
| The original image is restored with a matching SHA-256, CPU running | yes |
For a `.bin`, the tool additionally cross-checks the load address against the flash and RAM ranges J-Link reports for the device, and says what it found:
```
Address check: 0x08000000 is inside a flash bank (0x08000000 (512 KiB)).
```
That check is advisory rather than a gate, because loading into RAM or into an external bank J-Link does not model is legitimate.
One quirk this release cannot avoid: `loadfile` resets the device when it finishes and its `?` output documents no way to suppress that, so programming resets twice (once inside `loadfile`, once for the explicit run afterwards).
### Source-level reporting
Every tool that reports a program counter takes an optional `elf` argument. Given one, addresses come back as source locations:
```
Breakpoint hit at 0x0800112E on STM32F411CE, SWD, 4000 kHz.
Source: Core/Src/main.c:126 in main
Code: led_set(1);
```
Two layers do this:
1. **A built-in ELF symbol-table reader** (`src/symbols.ts`) — no external tool needed, so it always works, and it also covers firmware built by a toolchain whose binutils are not on PATH (Keil, for instance). It supplies the function name.
2. **`addr2line`**, when it is on PATH or set via `JLINK_MCP_ADDR2LINE`, for real DWARF file/line. It is the canonical tool and handles DWARF 5, discriminators and inlining correctly, which a hand-rolled `.debug_line` parser would not.
Paths are shortened against a project root derived from the ELF's own location: CMake emits to `build/<config>/` and Keil to `MDK-ARM/<target>/`, both two directories below the project root, so a single rule covers both layouts.
The resolver is checked against `addr2line` address by address — `npm run symbols -- <elf> <addr>...` — and agrees on every case including the boundaries: an address past the end of the image resolves to nothing rather than to the nearest symbol.
`jlink_fault_info` resolves the *stacked* faulting PC, which is the most valuable case of all. That needed one more fix: when a fault is taken in Thread mode on MSP, the C handler's own prologue pushes **below** the exception frame, so the frame sits *above* the current MSP and reading eight words from MSP misses it. The tool now reads a 256-byte window and scans upward, validating each candidate against the executable sections from the ELF so stack garbage cannot produce a false positive. A worked example, from a deliberately corrupted pointer:
```
Bus fault address (BFAR) = 0xDEADBEE0
Stacked exception frame at 0x2001FF94 (Thread mode, MSP):
Faulting instruction PC = 0x0800028C
Source: Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_rcc.c:232 in HAL_RCC_OscConfig
Code: if (((RCC_OscInitStruct->OscillatorType) & RCC_OSCILLATORTYPE_HSE) == RCC_OSCILLATORTYPE_HSE)
The frame sits 4 bytes above MSP, because the fault handler's own prologue pushed below it;
it was found by scanning.
```
`npm run debug` runs a complete session end to end, breakpoint and fault included.
### Fault diagnosis
`jlink_fault_info` reads `SHCSR`, `CFSR`, `HFSR`, `MMFAR` and `BFAR`, decodes every set bit, and — when the CPU is inside a fault handler — follows the `EXC_RETURN` value in `LR` to the stack holding the exception frame and reports the **address of the faulting instruction**. Example from a deliberately induced fault:
```
A fault is latched in the System Control Block.
Current exception: HardFault (IPSR = 3)
MemManage: IACCVIOL - instruction access violation (MPU or XN region)
HardFault: FORCED - escalated from a configurable fault (see CFSR)
Stacked exception frame at 0x200003C0 (Thread mode, PSP):
Faulting instruction PC = 0xFFFFFFF0
LR at the fault = 0x08004495, xPSR = 0x61000000
```
Frame recovery needs `LR` to still hold `EXC_RETURN`, which is true for the common `HardFault_Handler: b .` idiom. If the handler has called a function, the tool says so and suggests breaking at the handler entry with `jlink_run_to` instead.
## Known limitations
- **No persistent session**, so each tool call pays a connection round trip (~200 ms here) and breakpoints cannot outlive a call. Halts outlive a call only with `JLINK_PERSIST_HALT=1`, and even then a bare memory read resumes the CPU.
- **Flash breakpoints cost seconds and wear flash**, unless you keep to hardware breakpoints. See above.
- **Programming flash resets the target twice.** This J-Link release's `loadfile` resets the device on completion and its `?` output documents no way to suppress that (the `noreset` keyword in the online documentation belongs to a newer release), so `jlink_flash` cannot help but reset once itself and once again when it runs the CPU afterwards. Harmless, but visible as two resets.
- **`verify` on flash is only supported for `.bin`** (`verifybin`); other formats rely on J-Link's own load verification.
- **This J-Link release loads only `.bin`, `.mot`, `.hex` and `.srec`.** The online SEGGER documentation lists `.elf`, `.s19` and `.s37` as well, but that describes a newer J-Link: 7.52a's own `?` output does not. `jlink_flash` rejects other extensions up front with the `objcopy` command needed to convert.
- **RTT is not reachable through Commander on 7.52a.** Its `?` output lists no RTT commands, so RTT needs `JLinkRTTLogger.exe` or the JLinkARM DLL rather than `jlink_exec`.
- **Watchpoints** are not wrapped: `SetWP`/`ClrWP` exist and are documented in `jlink_command_reference`, but there is no dedicated tool.
## Roadmap
- RTT, to read `SEGGER_RTT` output for `printf`-style debugging without a UART. Needs `JLinkRTTLogger.exe` or the DLL, since Commander does not expose it.
- SWO/ITM trace. Commander does expose `SWOStart`/`SWORead`/`SWOShow`, so this is scriptable but needs the SWO pin wired.
- ELF symbol resolution, so `jlink_run_to` and `jlink_fault_info` can accept `main` or `HardFault_Handler` instead of raw addresses.
- Watchpoint tools built on `SetWP`/`ClrWP`.
- GDB Server management, for stepping through code with a real debugger in parallel.
- A persistent session, if a way to force line-buffered output on Windows turns up. That would also make breakpoints durable, which `JLINK_PERSIST_HALT` alone cannot do.
## License
MIT — see [LICENSE](LICENSE).
**Exception:** the files under [`docs/vendor/`](docs/vendor/) are copyrighted documentation of SEGGER Microcontroller GmbH. They are included for reference only and are **not** covered by the MIT licence above. See [docs/vendor/NOTICE.md](docs/vendor/NOTICE.md).
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues