cspy-mcp

mcp
Security Audit
Warn
Health Warn
  • License — License: MIT
  • Description — Repository has a description
  • Active repo — Last push 0 days ago
  • Low visibility — Only 5 GitHub stars
Code Pass
  • Code scan — Scanned 12 files during light audit, no dangerous patterns found
Permissions Pass
  • Permissions — No dangerous permissions requested

No AI report is available for this listing yet.

SUMMARY

MCP Connector to use with IAR C-SPY Debugger

README.md

cspy-mcp: MCP server for IAR C-SPY

CI

An MCP server, iar-cspy-mcp, that exposes
the IAR C-SPY debugger, and the Embedded Workbench project and option
services, to AI assistants (Claude Code, Claude Desktop, VS Code Copilot, ...)
as tools. It drives the backend over its Thrift interfaces, on a simulator or
real hardware.

The server is a thin layer over the iar-cspy
Python API: each tool parses its arguments, calls the API, and returns plain
JSON. For scripts and pytest hardware-in-the-loop tests, use that API
directly.

Licensed under the MIT License.

Getting started

Install the server (it brings the iar-cspy API along) and point it at your
IAR installation, the directory with common/bin under it:

pip install "iar-cspy-mcp @ git+https://github.com/iarsystems/cspy-mcp"
iar-cspy-mcp --iar-path /opt/iar/ewarm       # or: python -m iar_cspy_mcp

To pin a version, add @<branch, tag or commit> after the repository URL.
Normally your MCP client starts the server for you; see below.

Add it to your MCP client

All examples use managed mode: the server starts the backend itself from an
IAR installation or build stage (the directory with common/bin under it).
Adjust the path to your machine. See Backend modes
for what managed and standalone mode do.

Claude Code: .mcp.json in your project (or ~/.claude.json), or
claude mcp add cspy-debugger -- iar-cspy-mcp --iar-path /opt/iar/ewarm:

{
  "mcpServers": {
    "cspy-debugger": {
      "command": "iar-cspy-mcp",
      "args": ["--iar-path", "/opt/iar/ewarm"]
    }
  }
}

Claude Desktop: the same mcpServers block in claude_desktop_config.json
(Settings > Developer > Edit Config).

VS Code Copilot: .vscode/mcp.json (or MCP: Add Server):

{
  "servers": {
    "cspy-debugger": {
      "type": "stdio",
      "command": "iar-cspy-mcp",
      "args": ["--iar-path", "C:\\iar\\ewarm-9.60"]
    }
  }
}

If iar-cspy-mcp is not on the client's PATH, use the virtualenv's Python:
"command": "/abs/path/.venv/bin/python", "args": ["-m", "iar_cspy_mcp", ...].
From a checkout without installing, run_headless.sh <iar-path> does the same
over stdio (see Helper scripts).

An already-running backend (standalone mode): replace --iar-path with
its service registry:

"args": ["--registry-host", "127.0.0.1", "--registry-port", "51926"]

Requirements

Python 3.10+ on Linux or Windows, and either an IAR installation or an
already-running backend to connect to. The installation is the directory with
common/bin under it (for example IAR Embedded Workbench for Arm), and it
needs to ship CSpyServer2; the backend is then started for you. The project
and options tools also need IarServiceLauncher.

The minimum required IAR product version depends on the architecture:

Architecture Minimum version
Arm v8.40.1
RISC-V v3.10.1
AVR v8.10.1
RH850 v3.10.1
RL78 v5.10.1
RX v5.10.1
MSP430 v8.10.2

Some functionality may only be available on newer versions;
we recommend using the latest IAR product version for your architecture, if possible.

Backend modes

  • managed (default): given an IAR installation (--iar-path or
    IAR_INSTALL_PATH), the server starts IarServiceLauncher, which owns the
    service registry and hosts the IDE services, and a CSpyServer2 that joins
    that registry with -registry <port>, so the project_*/options_* tools
    work alongside the debugger ones. It supervises both and restarts them when
    they fail. CSpyServer2 supports one debug session per process, so every
    session gets a fresh one. Installations without the launcher, or
    --no-ide-services, run CSpyServer2 alone, without the project and options
    tools.
  • standalone: --registry-host/--registry-port (or
    THRIFT_REGISTRY_PORT) connect to the registry of a backend someone else
    runs, such as a Thrift-enabled iaride or a hand-started
    CSpyServer2/IarServiceLauncher. The port is that of the service registry,
    not of the debugger service itself. Nothing is started or stopped.

See docs/ide-services.md for the IDE services, and
the backend notes
for known backend behavior.

Command line

Option Meaning
--iar-path PATH IAR installation (managed mode). Normally the only option needed.
--cspyserver2 PATH, --service-launcher PATH Individual program paths, overriding --iar-path.
--cspyserver2-args ARGS CSpyServer2 arguments when it runs alone (default -standalone -sockets).
--no-ide-services, --ide-services LIST Host no IDE services, or only projectmanager/options.
--registry-host HOST, --registry-port PORT Connect to a running backend (standalone mode).
--registry-service NAME Registry name of the debugger service (default debugger).
--web, --web-port PORT Serve MCP over streamable HTTP at http://127.0.0.1:8000/mcp (or PORT) instead of stdio.
--probe-cspyserver2 Start the managed backend, print its registry, exit.

Every option also has an environment variable; see
.env.example. MCP_TRANSPORT=streamable-http,
MCP_HOST and MCP_PORT select the network transport without --web;
MCP_HOST=0.0.0.0 listens on all interfaces, which exposes the debugger to
the network.

On stdio the server logs every backend RPC to stderr ([thrift] call ...),
which MCP hosts collect as server logs; stdout carries only the protocol.

Helper scripts (Linux/macOS)

Bash wrappers that run the server from a checkout without installing it. Each
takes the IAR path as its first argument or from IAR_INSTALL_PATH, and uses
.venv/bin/python3 when present.

Script Transport Backend
run_headless.sh <iar-path> stdio managed: IarServiceLauncher + CSpyServer2
run_web.sh <iar-path> HTTP on MCP_PORT the same managed backend
run_iaride.sh <iar-path> HTTP on MCP_PORT standalone: starts IarIde and uses its registry

NO_IDE_SERVICES=1 and THRIFT_IDE_SERVICES=projectmanager restrict what
the managed backend hosts. On Windows, run iar-cspy-mcp (or
python -m iar_cspy_mcp) directly; scripts/run_validation.ps1 -IarPath <path>
runs the unit and live tests.

Tools

Area Tools
Session debugger_configure_and_start_session, debugger_configure_session, debugger_start_smp_session, debugger_stop_session, debugger_strict_cleanup, debugger_session_status, debugger_capabilities
Run control debugger_go, debugger_stop, debugger_reset, debugger_step_over, debugger_wait_for_core_state, debugger_go_and_wait_for_core_state
Target state debugger_eval_expression, debugger_register_snapshot, debugger_get_core_state, debugger_get_number_of_cores, debugger_get_cycle_counter, debugger_get_thread_list, debugger_get_modules, debugger_load_module, debugger_get_version, debugger_is_online
Breakpoints breakpoints_set_on_source, breakpoints_set_code, breakpoints_set_data, breakpoints_set_log, breakpoints_get_modes, breakpoints_set_on_ule, breakpoints_set_on_ule_with_category, breakpoints_set_from_descriptor, breakpoints_get_all, breakpoints_get, breakpoints_enable, breakpoints_remove, breakpoints_recently_hit
Stack and symbols contextmanager_get_stack, contextmanager_get_stack_depth, contextmanager_get_context_info, contextmanager_get_locals, contextmanager_get_parameters, symbols_list_visible, symbols_lookup
Memory and code memory_read, memory_write_hex, disassembly_disassemble_range, sourcelookup_get_source_ranges
Terminal I/O libsupport_get_output, libsupport_clear_output, libsupport_push_input, libsupport_wait_for_input_request, libsupport_request_input, libsupport_request_input_binary
List windows listwindow_list_services, listwindow_get_overview, listwindow_get_rows, listwindow_sliding_navigate, listwindow_get_notifications, listwindow_trace_status, listwindow_trace_set_enabled, listwindow_trace_clear
Projects project_load_workspace, project_status, project_get_files, project_build, project_get_launch_config, project_configure_and_start_debug
Options options_create_session, options_get_category_tree, options_get_option_tree, options_update_state, options_commit, options_destroy_session
IDE services ide_services_status, ide_services_ensure, ide_services_stop_launcher
Discovery thrift_connection_info, debugger_list_methods, debugger_error_taxonomy
Raw RPC debugger_call, projectmanager_call, options_call

Each tool's description (what the MCP client shows the model) documents its
arguments. The raw RPC tools take args_json: a JSON array for positional
arguments ("[123, \"abc\"]"), an object for keyword arguments, with
structs as objects and enums by name. Headless backends may not publish
instruction trace list windows; listwindow_list_services("") shows what is
registered before you read rows.

Response envelope and errors

The lifecycle, status, wait, project and options tools return
{"ok": bool, "tool": name, "data": {...}, "error": null | {...}}. Expected
negative outcomes (a timeout, a failed build, rejected option values) are
ok: false with a machine-readable error.code (TIMEOUT, BUILD_FAILED,
OPTION_VERIFICATION_FAILED, ...) rather than a tool error.
debugger_error_taxonomy() lists the codes. When a failure looks like a
backend crash, details.backend_diagnostics carries the backend's recent
output.

Most debugger tools need a started session and refuse with an explicit
message otherwise.

Playbooks

These are compact flows for tool-using agents.

Start a session: debugger_configure_and_start_session(launch_json), then
debugger_session_status(); continue when data.started is true.
launch_json is one entry of a C-SPY VS Code launch.json (a whole file with
configurations also works: the first one is used). In managed mode every
call starts from a fresh backend process. The session runs to the
configuration's stopOnSymbol; data.ranToSymbol reports whether it got there.

From an Embedded Workbench project: project_load_workspace(path), then
project_build() (a failed build is ok: false with data.output_tail),
then project_configure_and_start_debug(). No launch.json is needed.

Run and wait: debugger_go_and_wait_for_core_state(desired_state=0, timeout_ms=5000).
Core states are 0 = stopped, 1 = running, 2 = sleeping, 3 = unknown, 4 = no power.
On error.code == "TIMEOUT", retry with a longer timeout or call debugger_stop().

Breakpoints: breakpoints_set_on_source("/abs/path/main.c", 82),
breakpoints_set_code("main", mode="hardware"),
breakpoints_set_data("g_state", 3) (2/3/4 = read/write/read-write) and
breakpoints_set_log("phase_step", "phase {g_phase}") pick the breakpoint
category for the session's driver, so they also work on hardware probes;
breakpoints_get_modes() lists the driver's modes and debugger_get_log()
returns log breakpoint output. breakpoints_set_on_ule("main", 1) leaves
the category to the backend (1 = code, 2/3/4 watchpoints). ULEs are
expressions (main, func+4), addresses (0x100), or full-form source
locations ({/abs/path/file.c}.123.1); main() and file.c:123 shorthand
are backend-dependent. breakpoints_set_from_descriptor only takes
descriptors returned by breakpoints_get_all().

Probe before advanced calls: debugger_capabilities() returns the backend
mode, services, debugger methods, and any probe errors.

Recover: debugger_strict_cleanup(reset_target=true), inspect
data.errors[*].details.backend_diagnostics, then start again.

Non-intrusive attach: use a launch configuration with "request": "attach",
"attachToTarget": true, "leaveTargetRunning": true and all downloads
suppressed. Check debugger_get_core_state before deciding to call
debugger_stop(), and avoid debugger_reset().

Options: options_create_session → options_get_category_tree →
options_get_option_tree → options_update_state → options_commit or
options_destroy_session; see docs/ide-services.md.
For plain option reading and writing, projectmanager_call("GetOptionsForConfiguration", ...)
needs no session.

When a tool cannot do it: for multi-step work such as loops, conditional
logic, or collecting a lot of state, writing a short script against the
iar_cspy Python API is often simpler and faster than many
tool calls. Its objects mirror these tools.

How it works

 MCP client ──MCP──▶ iar-cspy-mcp ──▶ iar-cspy ──Thrift──▶ service registry ──▶ CSpyServer2  (debugger, breakpoints, memory, ...)
                                                                         └─▶ IarServiceLauncher (ProjectManager, OptionsService)

iar-cspy resolves every service through the registry, supervises the
managed processes, and hosts the callback services the backend expects from
a frontend (debug events, terminal I/O, list window notifications).

Repository layout

src/iar_cspy_mcp/    the server: _app.py (app, client, envelope), cli.py, server.py
  tools/             one module of MCP tools per area
tests/               tool tests (against iar_cspy.testing fakes) and live tests
examples/
  firmware/          Cortex-M3 simulator program + launch.json used by the live tests
  mcp/               MCP client configs and a stdio protocol client
docs/                IDE services
scripts/             run_validation.ps1: unit and live tests on Windows
mcp_thrift_server/   deprecated shim for the server's old module name (see below)
run_headless.sh, run_web.sh, run_iaride.sh   start the server from a checkout (Linux/macOS)

Development

pip install -r requirements-dev.txt                 # the server editable + pytest
pip install -e ../cspy-py                           # optional, afterwards: your checkout of iar-cspy
pytest                                              # unit tests; live tests skip without a backend
pytest -m live --cspy-iar-path=/opt/iar/ewarm       # live tests, managed backend
pytest -m live --cspy-cspyserver2=/path/CSpyServer2 # live tests, CSpyServer2 only

The tools live in src/iar_cspy_mcp/tools/, one module per area. Keep them
thin: argument parsing and result shaping belong here, behavior belongs in
iar_cspy. The tool tests run against iar_cspy.testing.fake_client(), and
tests/test_live.py covers a real backend on the bundled firmware
(examples/firmware), including a full MCP stdio round trip. CI
(.github/workflows/ci.yml) runs the unit tests on Python 3.10-3.12, builds
the distribution, and runs the live tests against the public cxarm toolchain.

python -m mcp_thrift_server, the server's old name, still works from a
checkout (with a deprecation notice), so existing MCP client configs keep
working. Point them at iar-cspy-mcp / python -m iar_cspy_mcp instead.

Reviews (0)

No results found