The full upstream README, mirrored here for reference. Install config, tool schemas, adoption signals, and an original overview live on the J Link MCP listing page.
Give AI hands to touch silicon.
An MCP server that lets LLMs debug embedded devices through SEGGER J-Link probes.
Real MCP tool calls against a real nRF52840-DK. Reproduce it yourself: npm run demo
Your firmware just crashed. One tool call:
Fault decoded, exception frame unwound, faulting instruction named, and the device's own log correlated — from one call, without a human reading a datasheet to find out what bit 24 of CFSR means.
Point it at your target's CMSIS-SVD file and peripheral registers stop being hex:
0x400 means 1024 KB of flash — but only if you know that, and an LLM guessing
at bit layouts is exactly the failure this avoids. The addresses come from the
vendor's own description, and the meanings from its enumerations.
Both transcripts are verbatim output from an nRF52840-DK in this project's hardware test suite.
jlink-mcp connects AI assistants (Claude, Copilot, etc.) to your embedded hardware via SEGGER J-Link debug probes using the Model Context Protocol.
Instead of manually typing J-Link commands, your AI assistant can:
Also supports OpenOCD (ST-Link, CMSIS-DAP, FTDI) and Black Magic Probe backends.
You need the SEGGER J-Link software
— that is what this server drives — a probe connected to an ARM Cortex-M
target, and Node 18+. For the other backends, OpenOCD or arm-none-eabi-gdb
instead.
Claude Code — from the plugin marketplace. This brings the
embedded-debugging skill along with the server, which is what stops an
assistant guessing at your hardware:
Any other agent — point it at this repository and ask it to set itself up:
Install the MCP server at https://github.com/Klievan/jlink-mcp and configure it for my board.
It will find what it needs here. If you would rather do it by hand, the server
is on npm and needs no build step: npx -y jlink-mcp, with JLINK_DEVICE set
to your target. mcp-config.json is a config you can paste.
VS Code — install the extension. It registers the server for Copilot Chat, Claude, and any MCP-aware client, so there is nothing to configure.
Ask for check_setup. One call, and it says what is missing and what to do:
You do not need your device's exact part number up front. search_devices
searches all 9800 that J-Link supports, by part number, manufacturer, or core.
| Tool | Description |
|---|---|
start_debug_session | One-call setup. Starts GDB server + connects RTT + returns boot log. |
snapshot | Captures full device state: registers, fault status, stack dump, RTT output. |
diagnose_crash | Auto-reads and decodes ARM Cortex-M fault registers (CFSR, HFSR, MMFAR, BFAR) with exception stack frame. |
| Tool | Description |
|---|---|
list_devices | Scan for connected probes and show the configured target |
set_device | Change the target device at runtime — no restart needed |
get_config | Current probe, target device, and GDB server state |
| Tool | Description |
|---|---|
device_info | Probe type, target CPU, compact register summary |
halt | Halt CPU |
resume | Resume CPU |
reset | Reset device. halt stops it at the reset vector; strategy picks a J-Link reset type, or omit it and let J-Link choose |
step | Single-step one instruction |
Set jlinkMcp.svdPath to your target's SVD — the same file Cortex-Debug takes
as svdFile. Vendors publish one per part.
| Tool | Description |
|---|---|
list_peripherals | Every peripheral and base address on the chip |
read_peripheral | Read a peripheral's registers and decode each one's bit fields by name |
decode_register | Decode one register — read from the device, or interpret a value you already have |
| Tool | Description |
|---|---|
read_memory | Read memory at address (clean hex dump output) |
write_memory | Write 32-bit value to address |
read_registers | All CPU registers in compact format |
read_register | Read specific register (PC, SP, R0-R12, etc.) |
| Tool | Description |
|---|---|
flash | Flash .hex/.bin/.elf firmware to device |
erase | Erase entire flash |
| Tool | Description |
|---|---|
set_breakpoint | Set hardware breakpoint at address |
clear_breakpoints | Clear all breakpoints |
| Tool | Description |
|---|---|
gdb_server_start | Start probe's GDB server |
gdb_server_stop | Stop GDB server + disconnect RTT |
gdb_server_status | GDB server, RTT, and proxy status |
Attach a real GDB client for symbol-aware work — backtraces, variable inspection, and stepping by source line rather than by instruction.
| Tool | Description |
|---|---|
gdb_connect | Attach a GDB client (auto-starts the server; optional ELF for symbols) |
gdb_load | Load an ELF for debug symbols, optionally flashing it too |
gdb_backtrace | Call stack, optionally with locals in each frame |
gdb_command | Run any GDB command — print sensor_state, info threads, break main |
gdb_wait | Wait for the target to stop (after a continue or a breakpoint) |
gdb_disconnect | Detach the client, clearing breakpoints and debug hardware |
| Tool | Description |
|---|---|
rtt_connect | Connect to RTT telnet port |
rtt_disconnect | Disconnect from RTT |
rtt_read | Read recent log lines (ANSI stripped, Zephyr format parsed) |
rtt_search | Filter logs by level (err/wrn/inf/dbg), module, or regex |
rtt_send | Send data to device via RTT down-channel |
rtt_clear | Clear RTT buffer |
| Tool | Description |
|---|---|
telnet_proxy_start | Start TCP proxy that tees RTT for external detokenizers |
telnet_proxy_stop | Stop proxy |
telnet_proxy_status | Proxy connection status |
telnet_proxy_read | Read raw proxy buffer |
| Tool | Description |
|---|---|
probe_command | Execute raw probe commands |
get_config | Current probe and server configuration |
A J-Link serves one client at a time, so a GDB server left running blocks everything else — and the usual way that happens is an assistant starting one through MCP and nobody noticing. The extension cannot detect that by remembering what it did: the MCP server runs in a separate process, so an LLM-started server is invisible to it.
So it watches the GDB port instead, which is true whoever is responsible. When something is listening the status bar turns amber and reads J-Link · MCP · 47m — who started it, and how long ago. Those are the two facts that turn "something has the probe" into "the assistant has had it since before lunch". Clicking offers to stop it.
The tooltip carries the rest: device, ports, whether RTT is up, and why it matters. And it never claims an uptime it did not watch — a server that was already running when the window opened is reported as known about for that long, not up for it.
It will not kill a process it cannot identify as a J-Link GDB server. A listening port proves something is there, not that it is ours.
The tools tell a model what it can do. They do not tell it what is worth doing, and two things go wrong constantly without that: models never think to load the ELF, so every backtrace is bare addresses; and they reason about what the hardware should be doing instead of spending one call asking it.
skills/embedded-debugging/SKILL.md is a Claude Code skill that covers both —
the two files that change everything (ELF for names, SVD for meanings), the
halt/read/resume rule, workflows for crashes, hangs, peripherals and silent
devices, and a table of beliefs paired with the tool call that actually checks
each one.
Claude Code picks it up if you install this repo as a plugin
(.claude-plugin/plugin.json), or you can copy the directory into
.claude/skills/ in your firmware project. MCP itself has no notion of skills
— its portable equivalent is prompts, and this server ships four.
Its claims are tested. test/hil/s04-symbols.test.ts proves on real silicon
that a backtrace without symbols has no function names, that loading the ELF
gives them, and that a symbols-only load does not reprogram the device.
jlink-mcp supports multiple debug probe backends through a common ProbeBackend abstraction:
| Backend | Probe Hardware | Status | RTT Support |
|---|---|---|---|
| J-Link | SEGGER J-Link, J-Link OB, J-Link EDU | Production | Yes |
| OpenOCD | ST-Link, CMSIS-DAP, FTDI, J-Link (via OpenOCD) | Beta | No |
| Black Magic Probe | BMP (built-in GDB server on serial) | Beta | No |
| probe-rs | All probe-rs supported probes | Planned | Planned |
This server was built by having an AI use it against real hardware, then fixing every friction point.
read_registers would give youRaw JLinkExe output for halt; regs — 77 lines, of which about six carry
information. Every token here costs context, and the register values are buried
in the middle:
Three lines. Same information, grouped by what you would ask for:
Both captured from the same board. The rest of the design follows the same rule — return what was asked for, and nothing else:
start_debug_session, snapshot, diagnose_crash) replace multi-step workflows with single calls.rtt_search lets you find errors without reading the entire log.RAM = K256 rather than guessing what 0x100 means in that
field of that register.Most of what can go wrong between an LLM and a debug probe fails quietly: a tool returns success with an empty payload, a parser drops half a line, a session dies and the next command reports something plausible instead. None of that is visible from reading the code, and a test suite that asserts "the call did not error" passes on all of it.
So this project runs a hardware tier: 58 tests against a real nRF52840-DK on a self-hosted runner, driving the actual MCP server over stdio exactly as a client would. It covers probe discovery, flash and verify, halt/step/resume under a live GDB session, memory and peripheral reads, RTT streaming and filtering, and crash diagnosis against faults injected on demand.
It has caught bugs that had been shipping green, including:
diagnose_crash reporting "No faults detected" during real crashes — the
memory-dump parser was dropping half of every linereset reporting success while doing nothing at all — the GDB server is a
synchronous remote and refuses commands while the target runs, so every
command of the reset sequence was rejected in turn and the failure discardedGDB_RUNNING, when the question it meant to ask was whether the GDB server
was runningEvery one of those reported success. That is the point of the hardware tier, and the reason the suites assert on parsed content rather than on the call having returned without error — a suite that checked for errors would have passed on all of them.
Raw probe output captured during those runs is committed as golden transcripts, so a fast unit tier replays real device bytes in seconds on any machine — no probe required to catch a format regression.
| Variable | Default | Description |
|---|---|---|
PROBE_TYPE | jlink | Probe backend: jlink, openocd, blackmagic |
JLINK_DEVICE | Unspecified | Target device (e.g., nRF52840_XXAA, STM32F407VG) |
JLINK_INSTALL_DIR | Auto-detect | Path to SEGGER J-Link installation |
JLINK_INTERFACE | SWD | Debug interface: SWD or JTAG |
JLINK_SPEED | 4000 | Connection speed in kHz |
JLINK_SERIAL | J-Link serial number (multi-probe) | |
JLINK_GDB_PORT | 2331 | GDB server port |
JLINK_RTT_PORT | 19021 | RTT telnet port |
JLINK_RTT_ADDR | Auto | Address of the RTT control block (your _SEGGER_RTT symbol). J-Link finds this by scanning RAM but never reports it back — supplying it is what lets RTT survive a target reset |
| Variable | Default | Description |
|---|---|---|
OPENOCD_BINARY | openocd | Path to openocd binary |
OPENOCD_INTERFACE | interface/stlink.cfg | Interface config file |
OPENOCD_TARGET | target/stm32f4x.cfg | Target config file |
OPENOCD_GDB_PORT | 3333 | GDB server port |
OPENOCD_TELNET_PORT | 4444 | Telnet command port |
| Variable | Default | Description |
|---|---|---|
BMP_GDB_PATH | arm-none-eabi-gdb | Path to GDB binary |
BMP_SERIAL_PORT | /dev/ttyACM0 | BMP serial port |
BMP_TARGET_INDEX | 1 | Target index after scan |
Adding a new probe backend:
src/probe/yourprobe.ts implementing ProbeBackendsrc/probe/factory.tsMIT - see LICENSE
Built by The Sprk Factory