{
  "markdown": "<!-- mcp-name: io.github.microhenrio/openocd-mcp -->\n<p align=\"center\">\n  <!-- Estado del repositorio -->\n  <img src=\"https://img.shields.io/github/last-commit/microhenrio/openocd-mcp\" alt=\"Last Commit\" />\n  <img src=\"https://img.shields.io/github/license/microhenrio/openocd-mcp\" alt=\"License\" />\n\n  <!-- Compatibilidad MCP -->\n  <img src=\"https://img.shields.io/badge/MCP-Server-blue\" alt=\"MCP Server\" />\n  <img src=\"https://img.shields.io/badge/AI-Ready-purple\" alt=\"AI Ready\" />\n\n  <!-- Compatibilidad con uvx -->\n  <img src=\"https://img.shields.io/badge/uvx-compatible-green\" alt=\"uvx compatible\" />\n\n  <!-- Badges que funcionarán cuando publiques en PyPI -->\n  <img src=\"https://img.shields.io/pypi/v/openocd-mcp\" alt=\"PyPI Version\" />\n  <img src=\"https://img.shields.io/pypi/dm/openocd-mcp\" alt=\"PyPI Downloads\" />\n  <img src=\"https://img.shields.io/pypi/pyversions/openocd-mcp\" alt=\"Python Versions\" />\n</p>\n\n\n# OpenOCD MCP Server\n\nDebug microcontrollers directly from your AI assistant. This is an [MCP](https://modelcontextprotocol.io)\nserver that drives [OpenOCD](https://openocd.org/), letting any MCP-compatible AI\nflash firmware, control execution, and inspect a running target — and read your\nvariables and peripheral registers **by name** instead of raw addresses.\n\n## Description\n\nOnce connected to a target through a debug probe (ST-Link, J-Link, CMSIS-DAP, …),\nyour AI assistant can:\n\n- **Flash firmware** — program and verify `.elf` / `.bin` / `.hex` images\n- **Control execution** — halt, resume, single-step, reset\n- **Inspect state** — read/write CPU registers and memory\n- **Set breakpoints** — hardware & software, including **conditional** breakpoints\n  (halt only when an expression is true) and hit-count breakpoints\n- **Watch memory** — hardware **watchpoints** that halt on read/write/access to an address\n- **Read variables by name** — from your firmware's `.elf` symbols (e.g. `read_variable uart_rx_count`)\n- **Live-watch variables** — a window that samples variables over time *without halting* the CPU, with **expandable structs/arrays** auto-typed from DWARF (signed/float/pointer/enum)\n- **Read peripheral registers by name** — from a CMSIS-SVD file, decoded into named bitfields (e.g. `RCC.CR`, `GPIOA.MODER`)\n- **Safety gates** — permission layer (read-only mode, gated flash-erase, flash path/size limits) so the agent can't damage a target unexpectedly\n\nThe server is **chip-agnostic** — it works with any target OpenOCD supports; you\npoint it at your chip's config and (optionally) SVD/ELF. It can also **start\nOpenOCD for you** and **download OpenOCD automatically** for your platform, so\nthere's nothing else to install by hand.\n\n## Supported AI clients\n\nAny MCP-compatible client works. Tested and known to work:\n\n| Client | Platform |\n|---|---|\n| [Claude Code](https://claude.com/claude-code) | CLI / IDE |\n| [Claude Desktop](https://claude.ai/download) | macOS / Windows |\n| [Cursor](https://www.cursor.com/) | IDE |\n| [Windsurf](https://windsurf.ai/) | IDE |\n| [Cline](https://github.com/clinebot/cline) | VS Code extension |\n| [Continue](https://continue.dev/) | VS Code / JetBrains |\n| [Zed](https://zed.dev/) | Editor |\n| [VS Code + GitHub Copilot](https://code.visualstudio.com/) | IDE (agent mode) |\n| [Gemini CLI](https://github.com/google-gemini/gemini-cli) | CLI |\n\n## Installation\n\n**Prerequisites:** Python 3.10+ and a debug probe connected to your target.\n\nClone and install the package into a virtual environment:\n\n```bash\ngit clone https://github.com/microhenrio/openocd-mcp\ncd openocd-mcp\npython -m venv .venv\n```\n\nInstall it (creates the `openocd-mcp` command):\n\n```bash\n# Windows\n.venv\\Scripts\\python -m pip install -e .\n\n# macOS / Linux\n.venv/bin/python -m pip install -e .\n```\n\n> **Windows shortcut:** run `setup.bat` — it creates the environment, installs\n> the package, and registers it with Claude Code automatically.\n\n**OpenOCD** is obtained automatically: a build for your OS/architecture is\ndownloaded and cached on first connect (checksum-verified). You can also fetch it\nahead of time with `openocd-mcp install-openocd`, or use an existing install by\nsetting the `OPENOCD_BIN` environment variable.\n\n### Registering with your AI client\n\nThe server executable is:\n\n```\n# Windows\n<repo>\\.venv\\Scripts\\openocd-mcp.exe\n\n# macOS / Linux\n<repo>/.venv/bin/openocd-mcp\n```\n\n<details>\n<summary><strong>Claude Code</strong></summary>\n\n```bash\n# Windows\nclaude mcp add --scope user openocd -- \"%CD%\\.venv\\Scripts\\openocd-mcp.exe\"\n\n# macOS / Linux\nclaude mcp add --scope user openocd -- \"$PWD/.venv/bin/openocd-mcp\"\n```\n\nRestart Claude Code, then verify with `claude mcp list`.\n</details>\n\n<details>\n<summary><strong>Claude Desktop</strong></summary>\n\nAdd to `claude_desktop_config.json` (Edit → Settings → Developer → Edit Config):\n\n```json\n{\n  \"mcpServers\": {\n    \"openocd\": {\n      \"command\": \"/path/to/.venv/bin/openocd-mcp\"\n    }\n  }\n}\n```\n\nRestart Claude Desktop.\n</details>\n\n<details>\n<summary><strong>Cursor</strong></summary>\n\nAdd to `~/.cursor/mcp.json` (or `.cursor/mcp.json` in your project):\n\n```json\n{\n  \"mcpServers\": {\n    \"openocd\": {\n      \"command\": \"/path/to/.venv/bin/openocd-mcp\"\n    }\n  }\n}\n```\n\nRestart Cursor.\n</details>\n\n<details>\n<summary><strong>Windsurf</strong></summary>\n\nAdd to `~/.codeium/windsurf/mcp_config.json`:\n\n```json\n{\n  \"mcpServers\": {\n    \"openocd\": {\n      \"command\": \"/path/to/.venv/bin/openocd-mcp\"\n    }\n  }\n}\n```\n\nRestart Windsurf.\n</details>\n\n<details>\n<summary><strong>Cline (VS Code)</strong></summary>\n\nOpen the Cline panel → MCP Servers → Add Server → Manual, then enter:\n\n```json\n{\n  \"openocd\": {\n    \"command\": \"/path/to/.venv/bin/openocd-mcp\"\n  }\n}\n```\n\n</details>\n\n<details>\n<summary><strong>Continue (VS Code / JetBrains)</strong></summary>\n\nAdd to `~/.continue/config.json`:\n\n```json\n{\n  \"mcpServers\": [\n    {\n      \"name\": \"openocd\",\n      \"command\": \"/path/to/.venv/bin/openocd-mcp\"\n    }\n  ]\n}\n```\n\n</details>\n\n<details>\n<summary><strong>VS Code + GitHub Copilot</strong></summary>\n\nAdd to `.vscode/mcp.json` in your workspace (or user `settings.json`):\n\n```json\n{\n  \"servers\": {\n    \"openocd\": {\n      \"type\": \"stdio\",\n      \"command\": \"/path/to/.venv/bin/openocd-mcp\"\n    }\n  }\n}\n```\n\nEnable via **Chat → Agent mode** in VS Code.\n</details>\n\n### Updating\n\nThere's no self-update tool exposed over MCP — updating means running commands in\na terminal, either yourself or by asking an AI that has shell access (e.g. Claude\nCode). **After updating, restart your AI client** so it loads the new server;\nif it was mid-session, it may need to stop the old `openocd-mcp` process first\n(a file lock can block the reinstall while it's running).\n\n**PyPI install** (`pip install openocd-mcp` or `uvx`):\n\n```bash\npip install -U openocd-mcp\n\n# uvx caches by default — force a refresh:\nuvx --refresh openocd-mcp\n```\n\n**Editable git-clone install** (what `setup.bat` / this repo's instructions set up):\n\n```bash\ngit pull\n# only needed if dependencies changed:\n.venv/bin/python -m pip install -e .   # or .venv\\Scripts\\python on Windows\n```\n\nCheck the installed version with `pip show openocd-mcp`.\n\n## How to work with it\n\n### 1. Point it at your chip\n\nEach firmware project tells the server which target it's debugging. Create an\n`openocd-mcp.json` in your project root (a template is in\n[`openocd-mcp.example.json`](openocd-mcp.example.json)):\n\n```json\n{\n  \"target_cfg\": \"target/stm32g0x.cfg\",\n  \"svd_file\": \"path/to/STM32G0B0.svd\",\n  \"elf_file\": \"path/to/build/firmware.elf\"\n}\n```\n\n- `target_cfg` / `interface_cfg` — OpenOCD configs (relative to its scripts dir).\n  Defaults to an ST-Link probe; set `target_cfg` for your chip.\n- `transport` — `\"swd\"` or `\"jtag\"`. Set `\"swd\"` for a **J-Link** on Cortex-M\n  (with `interface_cfg: \"interface/jlink.cfg\"`); leave empty for ST-Link.\n- `svd_file` — CMSIS-SVD file for the chip (enables peripheral registers by name).\n- `elf_file` — your firmware build output (enables variables by name).\n\n> **J-Link on Windows:** OpenOCD reaches J-Links via libusb, so bind the J-Link's\n> debug interface to **libusbK** (or WinUSB) with [Zadig](https://zadig.akeo.ie/)\n> once (Interface 2 / MI_02). Newer SEGGER software (v7.x+) is compatible with\n> libusbK, so both OpenOCD and SEGGER tools can coexist. ST-Link works without\n> that step.\n\nOr simply tell the AI the chip you're using and it will configure the session for\nyou. `show_config` reports the active settings at any time.\n\n### 2. Describe what you want\n\nWith the board plugged in, describe what you want — the AI picks the right tools:\n\n| You say… | What happens |\n|---|---|\n| \"connect and halt the target\" | Starts OpenOCD if needed, attaches, halts the CPU |\n| \"what's the status?\" | Reports running/halted and the current program counter |\n| \"read the variable `sensor_value`\" | Looks it up in the `.elf` and reads it off the chip |\n| \"set `motor_enabled` to 1\" | Writes the variable by name |\n| \"watch `tick_count` live for 2 seconds\" | Calls `watch_variables` — samples it repeatedly *without halting* and returns a table of values over time, right in the chat |\n| \"read `GPIOA.MODER`\" | Reads the register and decodes its named bitfields |\n| \"list the `RCC` registers\" | Lists registers from the SVD |\n| \"break at `0x08001234`, then reset and run\" | Sets a breakpoint and resets |\n| \"break at `0x08001234` when `r0` is 42\" | Sets a conditional breakpoint (using `[get_reg r0] == 42`) |\n| \"break at `0x08001234` after 5 hits\" | Sets a hit-count breakpoint |\n| \"watch for writes to `0x20000000`\" | Sets a hardware data watchpoint |\n| \"flash `build/firmware.elf` and run it\" | Programs, verifies, and restarts |\n| \"dump 64 bytes of RAM at `0x20000000`\" | Reads memory |\n\nYou don't call tools by name — describe the goal and the AI maps it to the\nunderlying tools.\n\n### 3. Conditional breakpoints & watchpoints\n\n**Conditional breakpoints** halt only when a condition holds — useful for catching\none specific case in code that runs constantly. Conditions are TCL expressions and\ncan use two helpers:\n\n- `get_reg <name>` — a CPU register value (e.g. `get_reg r0`, `get_reg pc`)\n- `get_mem <addr> [width]` — a memory value (e.g. `get_mem 0x20000000`)\n\nJust describe the intent; the AI builds the condition:\n\n| You say… | Condition used |\n|---|---|\n| \"break at `0x08001234` only when `r0` > 100\" | `expr {[get_reg r0] > 100}` |\n| \"break at `parse_packet` when the byte at `0x20000005` is `0xFF`\" | `expr {[get_mem 0x20000005 8] == 0xFF}` |\n| \"stop at `0x08001234` on the 10th time it's hit\" | `incr ::hits; expr {$::hits >= 10}` |\n\nWhen the condition is false the server resumes automatically and keeps going until\nit's true (or you stop it).\n\n**Watchpoints** halt the CPU when it accesses a memory location — ideal for finding\nwhat corrupts a variable:\n\n- \"watch for writes to `0x20000000`\"\n- \"watch address `0x20000010` for any read or write\"\n\n### Live-watching variables\n\nThere are **two separate ways** to watch a variable without halting the CPU —\none the AI can trigger, one you run yourself:\n\n| | `watch_variables` (MCP tool) | `openocd-watch` (standalone GUI) |\n|---|---|---|\n| Triggered by | Asking the AI in chat | Running the command yourself in a terminal |\n| Output | A text table of samples returned to the chat | A live-updating window with a tree view |\n| Duration | One-shot: N samples, then it returns | Keeps running until you close it |\n| Structs/arrays | Flat values only | Expandable, DWARF-typed |\n\n**Ask the AI** for a quick, bounded look at a value over time — \"watch `tick_count`\nfor 10 samples\", \"sample `sensor_value` every 200ms for 2 seconds\". This calls the\n`watch_variables` tool directly; no extra setup needed beyond an ELF loaded.\n\n**Run the GUI yourself** for an open-ended live view, especially of structs/arrays.\nIt's a standalone app that opens its own connection to OpenOCD, so it works fine\nalongside any AI session driving the same target — but it is *not* an MCP tool, so\nthe AI cannot open it for you; run it directly:\n\n```bash\nopenocd-watch tick_count sensor_value --elf path/to/firmware.elf\n# or, if elf_file is set in openocd-mcp.json:\nopenocd-watch tick_count sensor_value\n```\n\nIt samples the variables **without halting** the CPU and refreshes a tree in a\nwindow. Add entries with the box (press Enter or **Add**) and remove a selected\nrow with **Remove** (or the Delete key). Each entry is resolved automatically and\ncan be:\n\n- a **variable name** (`uwTick`, `commsService`) → looked up in the ELF; if it's a\n  **struct, union, or array** it gets an expand triangle, and its members/elements\n  are shown **auto-typed from DWARF** (signed, float, pointer, enum, bitfields,\n  nested structs). **Enums** show the symbolic name (`STATE_HEATING (1)`), and\n  **C bitfields** show just their own bit(s) rather than the whole storage word;\n- a **hex address** with optional size (`0x20000000`, `0x20000000:2`) → read directly.\n\nA **Format** dropdown switches how values are shown — **Auto (by C type) / Hex /\nDecimal / Signed / Float (f32) / Binary** — and re-renders instantly. Use\n`--interval` to change the poll rate, `--format` to set the initial format, or\n`--samples N` for a headless printout. Requires Tkinter (ships with standard Python)\nand a debug build (`-g`) for the type info.\n\nIf OpenOCD isn't already running, add `--autostart` and the window launches it\nfor you (and stops it on close) — fully standalone, no AI client or `.bat` needed:\n\n```bash\nopenocd-watch uwTick xTickCount --elf path/to/firmware.elf --autostart --target target/stm32g0x.cfg\n```\n\n> **CPU core registers** (`r0`, `pc`, `sp`, …) require the target to be halted —\n> the AI halts first when needed. Memory, variables, and peripheral registers are\n> memory-mapped and can be read while the CPU is running (as the live-watch window\n> does). The first `connect` of a session starts OpenOCD automatically.\n\n## Safety / permissions\n\nMutating operations are gated so the agent can't damage a target unexpectedly.\nReads are always allowed; the gates apply to writes, flashing, erasing, and the\nraw-command escape hatch.\n\n| Permission | Default | Gates |\n|---|---|---|\n| `read_only` | `false` | master switch — blocks **all** writes/flash/erase/raw |\n| `allow_memory_write` | `true` | `write_memory`, `write_variable`, `write_register`, `write_peripheral_register` |\n| `allow_flash` | `true` | `flash_write` (program) |\n| `allow_flash_erase` | **`false`** | `flash_erase_sector` (destructive — opt in) |\n| `allow_raw_command` | `true` | `run_command` (can bypass other limits) |\n| `flash_allowed_paths` | `[]` (any) | restrict `flash_write` to files under these dirs |\n| `flash_max_bytes` | `0` (no limit) | reject flashing files larger than this |\n\nSet them three ways (later wins):\n\n1. A **`permissions`** object in `openocd-mcp.json` (see `openocd-mcp.example.json`).\n2. The **`set_permissions`** tool at runtime — e.g. ask the AI to \"make the target\n   read-only\" or \"allow flash erase for this session\".\n3. The **`OPENOCD_MCP_READONLY=1`** environment variable (forces read-only).\n\nA blocked call returns a clear `BLOCKED: …` message explaining which permission to\nenable. `show_config` lists the active permissions.\n",
  "bytes": 15141,
  "sha": "88e4580ed0337640ebb81475f689bcece68216ff20a75fbf27583a617a05e0c8",
  "repo_slug": "microhenrio/openocd-mcp",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_microhenrio_openocd_mcp_669d9535/readme"
}