Install the plugin in Claude Code, describe your bug in English, and your agent drives the debugger — sets breakpoints, launches the app, walks the stack, inspects objects, captures screenshots, even reads + edits the source to fix it. Three slash commands away.
Two prerequisites: a Windows host with an interactive RDP desktop (so the daemon can talk to GDI / DirectDraw), and an AI agent that can drive the rdbg wire — the plugin shipped today targets Claude Code, but the underlying MCP bridge speaks an open protocol. The plugin bundles all five rdbg binaries — no separate download or compile step. Free for Delphi 12 Community Edition targets; see the licensing tiers below for other Delphi versions.
Two slash commands inside Claude Code — the first registers the marketplace, the second installs the rdbg-debugger plugin from a static-file git mirror (no GitHub account or auth needed). This is the only shipped install path today; other MCP-capable agents can wire the bridge in directly via their own MCP config (see the plugin's .mcp.json for the shape).
/plugin marketplace add https://sundaymorning.app/rdbg/.claude-plugin/marketplace.json
/plugin install rdbg-debugger@rdbg
The plugin ships all five binaries bundled in its bin/ — rdbgd64.exe + rdbgd32.exe (Win64 / Win32 daemons), rdbgc.exe (CLI), rdbgui.exe (GUI), and rdbg-mcp.exe (the MCP bridge that auto-finds itself). Five skills register (delphi-debugging-workflow, exception-triage, gui-bug-reproduction, delphi-rtti-inspection, vcl-self-discovery), three slash commands (/rdbg:status, /rdbg:start-daemon, /rdbg:install-help), and the delphi-bug-hunter autonomous subagent.
Want the standalone bin zip (e.g. for the GUI, for CI scripts, or for wiring the MCP bridge into your own AI tooling)? Grab rdbg-0.19.31-delphi12ce-bin.zip from the downloads page — same binaries, no plugin wrapper.
/rdbg:statusRun it from any Claude Code session (the slash command is a Claude Code feature). A healthy response looks like:
Daemon connection: rdbgd64 v0.19.31 (protocol 1) · active sessions: 0
Current sessions: none
Diagnostics: ok
If the daemon isn't running, /rdbg:status tells you so — and the bridge will auto-spawn it on the next real tool call from the bundled binaries. RDBG_AUTO_START=1 is the default. The daemon refuses to spawn from Session 0 (no GDI) — if you're SSH'd in, run your agent's host from inside your interactive RDP desktop.
Need the manual daemon-start instructions? Run /rdbg:start-daemon from Claude Code at any time, or read the printed-out instructions for any other launch path.
Five things you can say once the plugin is installed. Each example is real chat — the user's prompt, the agent's plan, the MCP tool calls under the hood.
You: Set a breakpoint at TForm1.btnComputeClick in MainForm.pas, launch bin\myapp.exe, and stop when it hits.
Agent: Setting the BP, launching, then waiting for the loader BP and the click handler.
tool: set_breakpoint(source="MainForm.pas", line=128)
→ {"ok":true, "bp_id":1, "resolved":"TForm1.btnComputeClick+0x0"}
tool: launch(exe="bin\\myapp.exe")
tool: continue() (past the loader BP)
tool: events(timeout=30)
→ {"event":"breakpoint_hit", "bp_id":1, "tid":7892, ...}
Agent: Hit. Paused in MainForm.pas:128. Want me to step into it, inspect Self, or just continue?
You: What are the locals here?
Agent: Reading the params + stack-locals from the TD32 section.
tool: inspect_locals()
→ {"ok":true, "function_symbol":"MainForm.TForm1.btnComputeClick", "auto_walked_frames":0, "locals":[{"name":"Self","location":"reg","reg_id":0,...}, {"name":"Sender","location":"reg","reg_id":1,...}], "version_warning":null}
Agent: Self in RCX (this is a TForm1 method), Sender in RDX (the button object). Want me to chase Sender.Caption via evaluate?
You: Run myapp.exe and tell me about every exception that fires — even first-chance ones.
Agent: Setting a catch-all on kernelbase!RaiseException, enabling first-chance reporting, then launching.
tool: set_breakpoint(kind="symbol", symbol="kernelbase!RaiseException")
tool: set_exception_filter(first_chance=true)
tool: launch(exe="bin\\myapp.exe") & continue()
tool: events(timeout=60, filter="exception")
→ {"event":"exception", "class":"EConvertError", "message":"\"abc\" is not a valid integer", ...}
Frames: SysUtils.pas:18432 ConvertErrorFmt+0x4f → MainForm.pas:201 TForm1.OnEditChange+0x12
Agent: First-chance EConvertError from OnEditChange — the user typed non-numeric text into the value field. Want me to inspect the exception object or the form's state?
You: Launch myapp.exe, wait for the main form to paint, screenshot it and describe what you see.
Agent: Launching, then capturing once the message pump's flushed a paint cycle.
tool: launch(exe="bin\\myapp.exe") & continue()
tool: get_main_form()
→ {"hwnd":"0x3fe00d6", "class":"TFormMain", "title":"My App"}
tool: capture_window(hwnd="0x3fe00d6")
→ PNG, 800×600, 47 KB
Agent: The main form is up. Top-left has a menu bar (File / Edit / View / Help). Beneath: a toolbar with New, Open, Save, Compute buttons. Centre is split — left pane is a tree of "Projects" with one root node "MyProject", right pane is empty with placeholder text "Select a project to begin". Status bar at the bottom reads "Ready · 0 items". No modal dialogs visible.
You: I'll demo the bug myself. Start recording my clicks and keys; I'll reproduce, then tell me which control received what.
Agent: Starting the spy. Reproduce the bug now — I'm listening.
tool: spy(action="start")
tool: spy(action="stop")
→ 7 events captured
Agent: Here's what you did:
1. click Form1.PageControl1.tabExport.btnRunExport (left, 142,38) 2. key Form1.PageControl1.tabExport.edtPath: "C:\\reports\\test.csv" 3. click Form1.PageControl1.tabExport.cboFormat (drop-down) 4. click [TListBoxItem 'CSV'] 5. click Form1.PageControl1.tabExport.chkOverwrite 6. click Form1.PageControl1.tabExport.btnRunExport 7.
Agent: The crash fires after the second click on btnRunExport with overwrite enabled. I'll set a BP on the export handler and re-run from your trace — want me to do that now?
You: Launch the app, click Quit, then make sure the process is actually gone.
Agent: Launching, clicking Quit, polling until the PID disappears.
tool: launch(exe="bin\\myapp.exe") & continue()
tool: find_vcl_for_hwnd(name="btnQuit")
→ Form1.pnlBottom.btnQuit at hwnd 0x4002a8
tool: click_at(hwnd="0x4002a8", button="left")
tool: events(timeout=10, filter="exit_process")
→ {"event":"exit_process", "exit_code":0}
tool: list_processes(filter="myapp.exe")
→ []
Agent: Clean exit, code 0, no orphaned process. Done.
The full payoff: your agent reads the source, hypothesises the bug, sets BPs, drives the UI, inspects state at the BP, edits the file, and re-runs to confirm. No human in the loop after the initial prompt.
You: "There's a bug in MyApp where clicking Compute sometimes shows the wrong result — specifically when one of the inputs is negative. Find it and fix it."
Your agent's plan: read the relevant Pascal first, hypothesise, then verify at a breakpoint before patching. Cheaper than guessing.
Agent (1/6): Let me see how Compute wires up.
tool: Read(MainForm.pas) → btnComputeClick calls FCalc.Sum(StrToInt(edtFrom.Text), StrToInt(edtTo.Text)).
tool: Read(Calculator.pas) → TCalculator.Sum(A, B: Integer): Integer; var I: Integer; begin Result := 0; for I := A to B do Result := Result + I; end;
Agent: Hypothesis: when A > B (e.g. Sum(5, -3) after the user enters them in the wrong order), the for I := A to B do loop runs zero times because Delphi's for doesn't iterate when the start is past the end. Result stays at 0 instead of summing -3..5. I'll set a BP on Sum's entry and confirm.
Agent (2/6): Setting the BP and launching.
tool: set_breakpoint(source="Calculator.pas", line=42) (line of Result := 0)
tool: launch(exe="bin\\myapp.exe") & continue()
Agent (3/6): Driving the UI to reproduce. Entering 5 and -3 (deliberately backwards), then clicking Compute.
tool: find_vcl_for_hwnd(name="edtFrom") → Form1.edtFrom
tool: set_text_field(target="Form1.edtFrom", text="5")
tool: set_text_field(target="Form1.edtTo", text="-3")
tool: click_at(target="Form1.btnCompute")
tool: events(timeout=10)
→ {"event":"breakpoint_hit", "bp_id":1, ...}
Agent (4/6): At the BP. Reading the args from the registers + Self.
tool: get_registers() → RCX = Self (TCalculator), RDX = 5 (A), R8 = -3 (B)
tool: step_over() through the for loop to its end.
tool: get_self() + inspect_object(addr, max_depth=1)
→ TCalculator { FLastResult: 0 } (Result variable holds 0 at function exit)
Agent: Confirmed. Sum(5, -3) returns 0 because the for I := 5 to -3 do loop body never executes. The bug is that Sum doesn't normalise its bounds. Fix: swap A and B if A > B before the loop.
Agent (5/6): Detaching, then patching Calculator.pas.
tool: detach()
tool: Edit(Calculator.pas)
@@ -38,7 +38,11 @@ function TCalculator.Sum(A, B: Integer): Integer;
var
I: Integer;
+ T: Integer;
begin
+ if A > B then begin
+ T := A; A := B; B := T; // normalise bounds (fixes negative-range bug)
+ end;
Result := 0;
for I := A to B do
Result := Result + I;
Agent: Rebuild and re-test.
Agent (6/6): Re-launching to confirm.
tool: Bash(cd C:\\src\\myapp && dcc64 -B myapp.dpr)
tool: launch(exe="bin\\myapp.exe") & continue()
tool: set_text_field ×2 + click_at(btnCompute) (same inputs)
tool: events(timeout=10) → BP hit, step_over, get_self
→ TCalculator { FLastResult: 9 } (-3 + -2 + -1 + 0 + 1 + 2 + 3 + 4 + 5 = 9)
tool: kill_session()
Agent: Fixed. Calculator.pas:38—Sum now normalises its bounds before iterating; verified the (5, -3) case returns 9 instead of 0. Want me to add a unit test before committing?
For multi-step investigations like this, ask your agent to invoke the bundled bug-hunter subagent — it's purpose-built for the read → hypothesise → reproduce → inspect → fix loop, and keeps the conversation trail concise.
The plugin is the recommended path, but the underlying wire is open.
Run bin\rdbgui.exe from the same zip. Open a .dproj, hit Go — project tree, source tabs with gutter BPs, object explorer, stack walk. Local / SSH / attach connection modes.
The wire is line-delimited JSON over TCP. rdbgc.exe ships canonical CLI verbs (repl, debug, inspect, launch --spy, etc.); the agent guide walks the pause-loop semantics any client needs.
rdbg's binaries for Delphi 12 Community Edition are free under the MIT license. Other Delphi versions, source-code access, and integration work are available with a 12-month support contract.
No contract · MIT license
rdbgd64.exe + rdbgd32.exe for Delphi 12 Community Edition targetsrdbgc.exe CLI + rdbgui.exe GUIrdbg-debugger plugin for Claude Code — ships the MCP bridge, all five skills, and the bug-hunter subagentFor: hobbyists, evaluators, OSS Delphi devs, anyone using Delphi 12 CE.
Just download it.
12-month support contract · per-org
For: teams shipping production Delphi apps on a non-CE Delphi.
Contact: damian@sundaymorning.app
12-month support contract · per-org
For: teams that need to extend rdbg, audit it for compliance, or run it on infrastructure that won't accept opaque binaries.
Contact: damian@sundaymorning.app
One-off engagement · for IDE / RAD Studio integration
For: Embarcadero itself, or a foundation acquiring rdbg as upstream.
Contact: damian@sundaymorning.app
All tiers carry the MIT license on the public bits. The contract covers binaries, source access, support, and (for Embarcadero) integration work — not a license-fee restriction on what you can do with what you've already received.
Common first-run issues.
/rdbg:status says daemon unreachableEither the daemon hasn't been started or you're in Session 0 (a non-interactive Windows session). Run /rdbg:start-daemon from Claude Code for the manual start instructions; if you're SSH'd in headless, launch your agent's host from inside an interactive RDP desktop instead.
Run the marketplace add command first, then the install. Both lines from step 2, in order. If it still fails, run /rdbg:install-help — the slash command prints the full standalone instructions.
The .map file is missing or doesn't have an address for that source line. Confirm myapp.map sits next to myapp.exe, and the line you picked has executable code (not a blank line, comment, begin/end token). Build with dcc64 -GD myapp.dpr for Win64 or dcc32 -GD myapp.dpr for Win32.
capture_window returns blankThe daemon must run on a host with an interactive desktop. Headless servers (Session 0, no display device) return blank PNGs. Direct2D / Direct3D / Chromium-embedded controls bypass the GDI compositor and capture as black even with a display; VCL native controls capture correctly.
Three slash commands and a bug report away. The plugin install takes about 30 seconds; the closed-loop bugfix flow above is what you do with it.