MCP tool reference

Forty-four tools, grouped by what you would be doing when you reach for them. Most take no arguments beyond the obvious ones; where a tool refuses something, that refusal is noted, because it is usually the interesting part.

Nearly everything below requires the session to be stopped. get_loaded_modules, get_source_files, get_debug_session_status and the two output tools also work while the debuggee is running.

Session and process

ToolWhat it does
list_debuggable_processes Running processes with pid, executable name and path, command line, parent pid, start time and architecture. Optional name filter.
launch_debuggee Start a process under the debugger. Takes program plus optional args, mapFile, rsmFile, sourceRoot, sourceSearchPaths, stopAtEntry and the exception filters.
launch_from_config Launch from an existing VS Code launch.json — the same file the DAP debugger uses, JSONC and ${workspaceFolder} / ${env:} included — so nothing has to be restated. Stops at entry.
attach_to_process Attach by processId or processName. An ambiguous name fails and lists the candidates rather than picking one. A target of a different architecture is rejected. Detaching leaves the process running by default.
attach_from_config The same, from a launch.json attach configuration.
get_debug_session_status Session state — none, launching, attaching, running, stopped, exited, detached, terminated — plus the stop reason and location when stopped.
detach_debugger Detach, leaving the process running.
terminate_debuggee Kill the debuggee.
stop_debugging End the session: detaches an attached one, terminates a launched one, unless killOnDetach says otherwise.

Breakpoints

ToolWhat it does
set_breakpoint One source-line breakpoint, with optional condition, hitCondition ("5", ">5", ">=5", "%5") and logMessage. Reports whether the line resolved to real code.
set_breakpoints Several at once. Per file, this replaces that file’s previous set; files not mentioned are untouched.
list_breakpoints Everything currently set, source and address, with verified state.
remove_all_breakpoints Clears both kinds.
set_breakpoint_at_address Break at an absolute address instead of a line — what makes disassemble actionable in a frame with no symbols. Stored as (module, offset), never as the bare address, so it survives a relaunch or an ASLR-rebased package. An address in a module that is not loaded is refused with a reason.
remove_breakpoint_at_address Removes one address breakpoint by id.

Data breakpoints (hardware watchpoints)

ToolWhat it does
set_data_breakpoint Stop when memory is written, or read-or-written. Takes an expression, a size of 1, 2, 4 or 8 bytes, and an access of write or readWrite.
list_data_breakpoints Every watchpoint with its resolved address, owning module and RVA, size, access, whether it armed, the hardware slot it holds (0–3, or −1 when refused), and a message carrying the refusal reason or the read-or-write caveat.
remove_data_breakpoint Removes one by id and frees its slot. Requires the session to be stopped.

The expression is resolved by one rule, worth learning because it is what makes this tool sharp:

  • A bare name is watched at its own storage.
  • @X is already an address.
  • Anything else is watched where it lives — so Arr[High(Arr)] watches the last element, and PByte(@Arr[0]) - 1 watches the byte just before the buffer, which is how you find who writes past an array.

What it refuses, and why:

  • A fifth watchpoint. There are four CPU debug registers, shared by the whole process. The refusal names what already holds the slots.
  • access: "read". x86 and x64 have no read-only watchpoint. readWrite is the closest thing and it also fires on writes; the response says so rather than pretending otherwise.
  • A bare local variable, with a reason — its address is only valid for the lifetime of its frame. Watch the containing global or object field, or pass a literal address. This is the one place the MCP surface is narrower than VS Code, where locals can be watched and are withdrawn automatically when the frame dies.
  • A misaligned or odd-width request. The address must already be aligned to the size. It is refused, never quietly rounded.

When one fires, the stop reason is dataBreakpoint and dataBreakpointDescription names the watched expression, the old and new values, and the thread that wrote it — frequently the entire answer to “who did this?”.

Execution control

ToolWhat it does
continue_and_wait Resume and wait for the next stop or exit, returning a compact snapshot of the new state. timeoutMs defaults to 30000, after which it returns a still-running result.
step_over One source line, then wait. Optional threadId steps another thread — only that one advances, the rest stay frozen, and run control afterwards targets it.
step_into Into a call on the current line. Same threadId option.
step_out Run until the current function returns.
pause_execution Interrupt a running debuggee.
wait_until_stopped Wait without issuing a command, for a target left running.

All three step tools accept granularity: "instruction", which steps exactly one machine instruction and needs no line table — the way through a package built without debug info, where a source-level step has no terminating condition. A refused instruction step returns an error with the reason and nothing moves, not even partially: there is no fallback decoder in this project, so an unavailable disassembler or undecodable bytes are reported rather than worked around.

step_out at instruction granularity requires a proven return address — .pdata on x64, [EBP+4] on x86 — and refuses rather than run somewhere plausible.

Exceptions

ToolWhat it does
set_exception_filters Change which exceptions break, on the live session: any of delphi, av, all, unhandled. [] means never break on first-chance. delphiExceptionClasses narrows the delphi filter to named classes.
get_exception_details When stopped on an exception: its class, message, description and stack.

The rule engine itself is configured in launch.json, not through a tool — see Exception handling. An agent reaches it via launch_from_config.

Location, stack and threads

ToolWhat it does
get_compact_debug_snapshot Session state, stop reason, current thread and location, top frames, top-frame locals and exception info — in one call. The round-trip saver.
get_current_source_location Function, source file and line at the stop.
get_call_stack Innermost frame first, with function, file and line. Optional threadId walks another thread.
get_threads Threads at the current stop: id, name, whether stopped, and which one is current. After a pause, current is the main application thread, not the transient thread the OS injected to break in.
get_raw_stack_scan Last resort, and not a call stack — see below.
get_raw_stack_scan brute-forces the stack for words that could be return addresses. Use it only when get_call_stack stops short, which on 32-bit targets happens at the first routine built without a frame pointer. Each hit is marked: proven=true means the instruction ending there was decoded and is a call, proven=false means there was no line table to decode from. Neither means the routine is still on the chain — a call that already returned leaves its return address behind and no sweep can tell the difference. These are places the program has been, never callers, and they must never be merged into a call stack.

Variables and expressions

ToolWhat it does
get_locals A frame’s locals with formatted values and types. Anything expandable carries a handle.
get_variable One named variable. Prefers a frame local — so a class, record or array result carries an expansion handle — and otherwise evaluates the name, including fields, globals and dotted paths.
expand_variable The child fields of an expandable value, by handle. Nested results carry their own handles, so an object graph is walked step by step. Handles are valid until the next stop.
evaluate_expression A Pascal expression in the context of a frame: locals, fields, globals, arithmetic, casts, method calls.
All four default to the top frame, and after a pause_execution that is usually inside a system DLL where nothing resolves. Read the stack first and pass the frameIndex of a frame in your own code. Each also takes a threadId.

Expression evaluation executes method and getter calls in the debuggee, with their side effects — see Expression evaluation.

Modules and sources

ToolWhat it does
get_loaded_modules Every image mapped in the debuggee with load base, size, symbol state and the debug-info formats that actually loaded. symbols is loaded, noSymbols or indexing; formats lists what registered — td32, map, rsm, dcp, jdbg, tds — not what was looked for. Also how you tell whether an unbound breakpoint is waiting for a package that has not loaded yet.
get_source_files The source files the loaded debug info can name, grouped by module. This is the file spelling set_breakpoint expects — use it rather than guessing from a unit name. A listedBy of null means the module’s loaded formats cannot enumerate files (.rsm, .dcp and .jdbg map addresses but hold no file index), which means unknown, not no source files; a breakpoint there may still bind.

Registers

ToolWhat it does
get_registers General-purpose registers and flags of the stopped thread, named for the target’s bitness. Values are hex strings, never bare numbers — a 64-bit register does not survive an IEEE double intact.
set_register Write one by name; either spelling works on either bitness, and on x64 an E-name writes the low 32 bits and zero-extends as the hardware does. Returns the value re-read from the thread, so the response proves the write instead of echoing the request. Naming a register the target does not have — R8R15 on 32-bit — is an error, not a silent no-op.

Memory and disassembly

ToolWhat it does
read_memory 1 to 4096 bytes, returned as hex plus little-endian integer interpretations, for structures the evaluator does not surface.
write_memory Write contiguous hex bytes at an address. Mutates the live process.
disassemble Zydis, x86 and x64, from an address or a frame’s instruction pointer. Each instruction reports its address, raw bytes, Intel-syntax text and — where a symbol provider knows one — the nearest function plus offset and the source file and line.

Three things about disassemble that matter to an agent:

  • Use it instead of reading bytes with read_memory and decoding them yourself. Hand-decoding x86 produces a confident wrong answer; this tool exists to prevent that.
  • Bytes Zydis cannot decode come back as db XX with decoded:false — never a guessed mnemonic.
  • before asks for preceding instructions. x86 cannot be decoded backwards, so this is answered only when a proven earlier boundary exists and decoding forward from it lands exactly on the requested address. Otherwise before comes back refused:true with a reason — while the forward instructions are still returned. A refused before is not a failed call, and the result never mixes proven and unproven instructions in one list.
If the Zydis DLL is missing or the wrong version, disassemble returns available:false with a reason and no instructions. That is the ordinary answer on a machine without it, not a failure.

Output

ToolWhat it does
get_debuggee_output The program’s own stdout since the previous call. Incremental.
get_debugger_output Debugger-generated output since the previous call — notably logpoint messages. Incremental.

What is not here

The engine is shared with the VS Code debugger, but the tool surface is not a complete mirror of it. Absent by design or not yet built:

  • No Set Next Statement.
  • No setVariable equivalent — write_memory and set_register are the only ways to mutate state.
  • expand_variable does not expand getter-backed properties or Variant arrays.
  • Module loading is currently eager: every module carrying debug-info sidecars loads once breakpoints exist, rather than lazily as each package maps.
  • No get_scopes or get_arguments. This one is deliberate — locals are returned directly with opaque handles, because arguments are not reliably separable from locals in the underlying debug information, and a scope indirection would have promised a distinction the data cannot support.