cspy-debugger
OfficialClick on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@cspy-debuggerSet a breakpoint at main and run to it, then show the call stack and locals."
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
# MCP Thrift Server (Python)
This project runs an MCP server that talks to a Thrift backend (C-SPY style IDL) and exposes debugger capabilities as MCP tools.
Licensed under the MIT License.
Quick Start: Add To Your MCP Client
All examples use managed mode: the MCP server spawns CSpyServer2.exe itself
and auto-detects the registry port. Adjust the two paths (CSpyServer2.exe
and this repo) to your machine. Install dependencies first
(pip install -r requirements.txt).
Claude Code
Add to .mcp.json in your project root (or ~/.claude.json for user scope):
{
"mcpServers": {
"cspy-debugger": {
"command": "python",
"args": ["-m", "mcp_thrift_server", "--cspyserver2", "C:\\iar\\qtarm-10.2.1\\common\\bin\\CSpyServer2.exe"],
"env": { "PYTHONPATH": "C:\\path\\to\\this-repo" }
}
}
}Or from the terminal:
claude mcp add cspy-debugger --env PYTHONPATH=C:\path\to\this-repo -- python -m mcp_thrift_server --cspyserver2 "C:\iar\qtarm-10.2.1\common\bin\CSpyServer2.exe"Claude Desktop
Add the same mcpServers block to claude_desktop_config.json
(Settings > Developer > Edit Config):
{
"mcpServers": {
"cspy-debugger": {
"command": "python",
"args": ["-m", "mcp_thrift_server", "--cspyserver2", "C:\\iar\\qtarm-10.2.1\\common\\bin\\CSpyServer2.exe"],
"env": { "PYTHONPATH": "C:\\path\\to\\this-repo" }
}
}
}VS Code Copilot
Add to .vscode/mcp.json in your workspace (or run MCP: Add Server from the
Command Palette):
{
"servers": {
"cspy-debugger": {
"type": "stdio",
"command": "python",
"args": ["-m", "mcp_thrift_server", "--cspyserver2", "C:\\iar\\qtarm-10.2.1\\common\\bin\\CSpyServer2.exe"],
"cwd": "C:\\path\\to\\this-repo"
}
}
}Connecting to an already-running backend (external mode)
Replace the --cspyserver2 argument with registry flags in any config above:
"args": ["-m", "mcp_thrift_server", "--registry-host", "127.0.0.1", "--registry-port", "51926"]Environment variables (THRIFT_FILE, THRIFT_INCLUDE_DIRS, ...) are only
needed when your thrift IDLs live outside this repo; see
Backend Modes below.
Related MCP server: gdb-mcp
What it provides
MCP server over
stdio(default) orstreamable-httpManaged backend mode: spawns and supervises
CSpyServer2.exe, auto-detects the registry port, and restarts the backend on failureRuntime loading of the bundled
cspy.thriftIDL viathriftpy2Registry-aware service resolution (debugger, breakpoints, contextmanager, memory, disassembly, sourcelookup, symbols, listwindow, libsupport)
Tools for session lifecycle, run control, breakpoints/watchpoints, stack and locals inspection, memory read/write, disassembly, source lookup, symbol lookup, terminal I/O capture, error taxonomy, and arbitrary debugger RPC calls
AI-first response envelopes with machine-readable error codes and backend crash diagnostics
Prerequisites
Python 3.10+
An IAR toolchain installation providing
CSpyServer2.exe(managed mode), or an already-running CSpyServer2/Service Registry to connect to (external mode)Thrift IDLs are bundled in this repo (
thrift/cspy.thriftplus includes); nothing extra is needed unless your IDLs live elsewhere
Setup
Create and activate a virtual environment.
Install dependencies:
pip install -r requirements.txtOptional: configure environment variables (see
.env.example). With the bundled thrift files,THRIFT_FILEandTHRIFT_INCLUDE_DIRSare not needed; the server auto-detectsthrift/cspy.thriftand uses its directory as include path.
If you connect to an externally started CSpyServer2.exe -standalone:
The printed/known port may be the Service Registry, not the Debugger service itself.
Set
THRIFT_REGISTRY_PORT(or pass--registry-port) to that registry port and this bridge will auto-resolve the realdebuggerendpoint.
Backend Modes
This server supports managed and external backend operation.
managed(default):
MCP server starts
CSpyServer2.exeitself using:executable: provided via CLI (
--cspyserver2)args:
THRIFT_CSPYSERVER_ARGS(default-standalone -sockets)
It parses CSpyServer2 stdout for:
Service registry running on local socket on port: <port>
The detected registry port is used automatically for service resolution.
If the managed process is unhealthy, the server attempts restart when
THRIFT_CSPYSERVER_RESTART_ON_FAILURE=1.
external:
Connect to an existing CSpyServer2/registry using CLI flags:
--registry-host--registry-portoptional
--registry-service(default:debugger)
Run
Managed mode (spawns CSpyServer2, auto-detects registry port):
python -m mcp_thrift_server --cspyserver2 "C:\iar\qtarm-10.2.1\common\bin\CSpyServer2.exe"Optional custom CSpyServer2 args:
python -m mcp_thrift_server --cspyserver2 "C:\iar\qtarm-10.2.1\common\bin\CSpyServer2.exe" --cspyserver2-args "-standalone -sockets"External mode (connect to an already-running backend registry):
python -m mcp_thrift_server --registry-host 127.0.0.1 --registry-port 51926The server starts in stdio transport mode by default. In stdio mode, the
process is expected to block while waiting for an MCP client, and you should see:
MCP server ready (stdio). Waiting for an MCP client connection...
Simple web mode option (HTTP on localhost):
python -m mcp_thrift_server --web --web-port 8000Explicit HTTP transport via environment (use MCP_HOST="0.0.0.0" to listen on
all interfaces; the server listens on the single port MCP_PORT):
$env:MCP_TRANSPORT="streamable-http"
$env:MCP_HOST="127.0.0.1"
$env:MCP_PORT="8000"
python -m mcp_thrift_serverTerminal-only health probe (starts managed CSpyServer2, parses registry port, prints status, exits):
python -m mcp_thrift_server --cspyserver2 "C:\iar\qtarm-10.2.1\common\bin\CSpyServer2.exe" --probe-cspyserver2Testing (pytest)
Install test dependencies:
pip install -r requirements-dev.txtRun fast unit tests (mocked backend):
pytest -q tests/test_server_tools_unit.pyRun the full default suite (live tests are skipped unless enabled):
pytest -qRun live backend tests:
pytest -q tests -m live --cspyserver2 "C:\\iar\\qtarm-10.2.1\\common\\bin\\CSpyServer2.exe"Live test assets bundled in repo:
tests/live_assets/launch.jsontests/live_assets/test.ewptests/live_assets/Debug/Exe/test.out
So pytest -q -m live can run without external launch/project/output files.
You still need a working C-SPY installation/executable.
One-command validation (unit + live):
./scripts/run_validation.ps1 -CSpyServerExe "C:\iar\qtarm-10.2.1\common\bin\CSpyServer2.exe"Optional launch override:
./scripts/run_validation.ps1 -CSpyServerExe "C:\iar\qtarm-10.2.1\common\bin\CSpyServer2.exe" -LaunchJson "E:\path\to\launch.json"Auto handlers are always-on defaults:
Some backends require
debugger.eventhandlerbefore configure/start succeeds.Terminal I/O and exit/assert capture requires
libsupportcallbacks.Keeping these handlers on by default prevents lifecycle foot-guns.
Live test lifecycle expectation:
debugger_configure_session(launch_json)performs resolve + configure.debugger_start_smp_session()must be called after configure.Effective startup sequence is
resolve -> configure -> start.
MCP tools exposed
thrift_connection_info()debugger_list_methods()debugger_get_version()debugger_is_online()debugger_get_number_of_cores()debugger_get_core_state(core=0)debugger_session_status()debugger_configure_session(launch_json)debugger_start_smp_session()debugger_configure_and_start_session(launch_json)debugger_stop_session()debugger_strict_cleanup(reset_target=False)debugger_capabilities()debugger_error_taxonomy()debugger_load_module(filename)debugger_get_modules()debugger_register_snapshot(group="CPU Registers (ABI)", limit=64)debugger_go()debugger_stop()debugger_reset()debugger_step_over()debugger_get_thread_list()debugger_get_cycle_counter(core=0)debugger_eval_expression(expression, context_json="", format=0, dereference=False)debugger_wait_for_core_state(desired_state=0, core=0, timeout_ms=5000, poll_interval_ms=50)debugger_go_and_wait_for_core_state(desired_state=0, core=0, timeout_ms=5000, poll_interval_ms=50)debugger_call(method, args_json="[]")breakpoints_get_all()breakpoints_get(id)breakpoints_set_from_descriptor(descriptor)breakpoints_set_on_ule(ule, access_type=1)breakpoints_set_on_ule_with_category(ule, access_type, category_id)breakpoints_enable(id, enable=True)breakpoints_remove(id)breakpoints_recently_hit()contextmanager_get_stack(context_json="", low=0, high=20)contextmanager_get_stack_depth(context_json="", max_depth=256)contextmanager_get_context_info(context_json="")contextmanager_get_locals(context_json="")contextmanager_get_parameters(context_json="")symbols_list_visible(context_json="")symbols_lookup(name, context_json="", prefix=False)memory_read(zone_id, address, wordsize=1, bitsize=8, count=16)memory_write_hex(zone_id, address, data_hex, wordsize=1, bitsize=8, count=None)disassembly_disassemble_range(from_zone_id, from_address, to_zone_id, to_address, context_json="")sourcelookup_get_source_ranges(zone_id, address)libsupport_get_output(clear=False, max_chars=4000)libsupport_clear_output()libsupport_push_input(text, append_newline=False)libsupport_request_input_binary(len)libsupport_request_input(len)listwindow_list_services(name_filter="listwindow")listwindow_get_overview(service_name)listwindow_get_rows(service_name, first_row=0, max_rows=50)listwindow_get_notifications(clear=False)
debugger_list_methods returns the RPC names parsed from cspy.thrift.
debugger_register_snapshot returns register metadata and values (hex and unsigned little-endian integer) for a whole register group in one call.
Standard response envelope (AI-first tools):
The following tools return a stable envelope shape:
{"ok": <bool>, "tool": <name>, "data": <payload>, "error": <object|null>}Current enveloped tools:
debugger_session_statusdebugger_configure_sessiondebugger_start_smp_sessiondebugger_configure_and_start_sessiondebugger_stop_sessiondebugger_strict_cleanupdebugger_capabilitiesdebugger_wait_for_core_statedebugger_go_and_wait_for_core_state
Timeout-style outcomes use
ok=falsewith machine-readableerror.code(for exampleTIMEOUT).Use
debugger_error_taxonomy()to discover known error codes/categories and recovery hints.When available, structured error
detailsmay includebackend_diagnosticswith managed backend crash/output context to speed up recovery decisions.
Breakpoint usage notes:
breakpoints_set_on_uleis the primary creation API.For code breakpoints, set
access_type=1(fetch/execute).ULE is parsed by the debugger Universal Location Expression parser.
Supported ULE categories:
expression ULEs:
main,func+4,*ptrabsolute ULEs:
0x100,Memory:0x42source ULEs (reliable full form):
{E:/path/file.c}.123.1optional size suffix:
<ule>@<size>
Source shorthand like
file.c:123can be backend-dependent; prefer the full source ULE form shown above.breakpoints_set_on_ule*now fail explicitly if backend returns an invalid breakpoint object (for examplevalid=false/id=0) instead of silently returning it.breakpoints_set_from_descriptorexpects opaque descriptor values frombreakpoints_get_all()and is intended for round-trip restore/update, not free-form descriptor construction.breakpoints_set_on_ule_with_categorycategory IDs can be translated by backend (for exampleSTD_CODEtoSTD_CODE2).If breakpoint calls fail with backend transport resets, the backend session may have crashed/reset; restart C-SPY and reconfigure session.
AI usage notes:
Prefer dedicated tools over
debugger_callwhen available.Prefer calling
debugger_session_status()first to confirm lifecycle/backend state before deeper operations.Use
debugger_capabilities()when you need a one-shot view of backend mode, available services, and currently discoverable debugger methods.For
debugger_configure_session, pass one configuration object JSON, not the outer{"configurations": [...]}wrapper.Required startup flow (recommended):
debugger_configure_session(launch_json)debugger_start_smp_session()
One-call happy path:
debugger_configure_and_start_session(launch_json)
In
managedbackend mode, this wrapper always performs a strict cleanup first and starts from a fresh CSpyServer2 process before resolve/configure/start.In managed mode, no backend session/runtime state is expected to carry over between calls.
In
externalbackend mode, this wrapper performs best-effort handoff teardown when stale/active session state is detected.
Equivalent low-level flow:
debugger_call("resolveLaunchConfiguration", ...)debugger_call("configureSession", ...)debugger_start_smp_session()(ordebugger_call("startSession", ...)if applicable)
Important:
debugger_configure_sessiondoes not start the session; always calldebugger_start_smp_sessionafter configure before stack/context/breakpoint-heavy operations.The MCP server enforces this lifecycle invariant for most debugger-dependent tools and returns an explicit error if configure/start has not completed.
debugger.eventhandlerandlibsupportregistration are handled automatically by the MCP wrapper during configure/start flows; no manual registration tool call is required in normal usage.Stability note:
debugger_configure_sessionintentionally does not callstopSession()internally. In some backend lifecycle states, forcingstopSession()during reconfigure can trigger backend assertions/crashes. Usedebugger_stop_session()explicitly only when you intend to tear down the current session.debugger_stop_session()remains idempotent for local lifecycle state. Inmanagedbackend mode it also shuts down the managed CSpyServer2 process, so the next startup uses a fresh backend process.debugger_strict_cleanup(reset_target=False)is the strongest recovery tool: it best-effort stops session, clears local caches/buffers, and shuts down the managed backend process to restore a known-good baseline.If execution state becomes inconsistent, call
debugger_reset()before retrying start/go.Common non-intrusive attach flow (read state, avoid perturbing target):
Build an attach config with:
request: "attach"attachToTarget: truedownload.suppressAllDownloads: truedownload.suppressProgramDownload: trueleaveTargetRunning: true
Call
debugger_configure_session(launch_json).Call
debugger_start_smp_session().Check run state first (for example
debugger_call("getCoreState", "[0]")or stack/context).Read registers/state directly when possible.
Only call
debugger_stop()if state confirms the core is running and halt is required for the read.Avoid
debugger_reset()in attach mode unless explicitly requested.
Eventhandler listener timeout defaults to 3600000 ms (1 hour). Override with
THRIFT_EVENTHANDLER_CLIENT_TIMEOUT_MSif needed.debugger_callis best for simple scalar/list arguments; nested thrift structs may require dedicated wrappers. It accepts:JSON array for positional args, example:
"[123, \"abc\"]"JSON object for keyword args, example:
"{\"sessionConfig\": {...}}"
Listwindow/trace note: in standalone/headless sessions, instruction trace listwindow services may not be published in ServiceRegistry. Use
listwindow_list_services("")to confirm availability before attempting row reads.
AI Playbooks
These are compact, canonical flows intended for tool-using AI agents.
Playbook A: Standard debug session bootstrap
debugger_configure_and_start_session(launch_json)debugger_session_status()Continue only if
ok=trueanddata.started=true.
Playbook B: Safe capability probe before advanced calls
debugger_capabilities()Inspect
data.debugger_methods,data.services, anddata.errors.Branch behavior based on discovered methods/services.
Playbook C: Run and wait deterministically
debugger_go_and_wait_for_core_state(desired_state=0, core=0, timeout_ms=5000)If
ok=falseanderror.code=="TIMEOUT", either retry with higher timeout or calldebugger_stop().
Playbook D: Breakpoint round-trip
breakpoints_set_on_ule("main", 1)breakpoints_get_all()Persist
descriptorvalues only frombreakpoints_get_all()for future restore.
Playbook E: Failure recovery baseline
debugger_strict_cleanup(reset_target=true)If
ok=false, inspectdata.errors[*].details.backend_diagnosticswhen present.Re-run bootstrap from Playbook A.
Notes
If your backend uses custom transports/protocols (SSL, multiplexing, framed transport variants), adapt
mcp_thrift_server/thrift_client.py.Current bridge expects socket endpoints for the final service call. Registry-discovered non-socket (named pipe) endpoints are reported as unsupported.
Quick MCP protocol smoke test
This verifies MCP transport and tools/call over stdio, not only direct Python imports. Adjust the registry env values inside the script for your backend, then run:
python smoke_test_mcp_stdio.pyIt starts the MCP server as a stdio subprocess, lists tools, and calls
debugger_get_version, debugger_is_online, and debugger_list_methods.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Connectors
Live browser debugging for AI assistants — DOM, console, network via MCP.
Agent Replay Debugger MCP — record every agent step + deterministic replay. Step-debugger for
MCP server for AI access to SmartBear tools, including BugSnag, Reflect, Swagger, PactFlow, QTM4J.
The Cortex MCP server provides read-only access to real-time engineering context from the Cortex developer portal, allowing AI coding assistants to answer natural language questions about your organization's catalog (microservices, libraries, domains, teams, infrastructure), scorecards (engineering standards and best practices), initiatives (goals and deadlines), and Engineering Intelligence metrics. It includes tools for querying documentation, tracking personal entities, and accessing AI-assisted insights across the entire Cortex ecosystem.
Related MCP Servers
- AlicenseAqualityDmaintenanceEnables AI assistants to perform interactive Python debugging with breakpoints, step execution, and variable inspection using the Debug Adapter Protocol (DAP) through an MCP server interface.81MIT
- AlicenseBqualityDmaintenanceEnables AI assistants to control GDB debugger via MCP protocol for local and remote debugging, supporting CTF Pwn, crash analysis, and ELF inspection.131MIT
- AlicenseAqualityBmaintenanceStateful MCP server for driving debug probes (J-Link) to flash, debug, and inspect embedded targets. Enables AI agents to perform flash, memory, breakpoint, and ELF/SVD-aware operations conversationally.4110MIT
- AlicenseNot gradedqualityCmaintenanceEnables AI agents to interact with GDB for debugging via the MCP protocol. Supports setting breakpoints, stepping through code, inspecting memory and registers, and more.86MIT
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/iarsystems/cspy-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server